1297 lines
55 KiB
Java
1297 lines
55 KiB
Java
package net.minecraft.server.level;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import net.minecraft.world.level.dimension.Dimension;
|
|
import net.minecraft.world.level.TickList;
|
|
import net.minecraft.world.level.chunk.ChunkSource;
|
|
import net.minecraft.world.scores.Scoreboard;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
import net.minecraft.util.CsvOutput;
|
|
import java.io.IOException;
|
|
import java.io.Writer;
|
|
import net.minecraft.CrashReport;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.OpenOption;
|
|
import java.nio.file.Path;
|
|
import net.minecraft.world.entity.ReputationEventHandler;
|
|
import net.minecraft.world.entity.ai.village.ReputationEventType;
|
|
import net.minecraft.world.entity.raid.Raid;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
|
import java.util.Optional;
|
|
import net.minecraft.network.protocol.game.DebugPackets;
|
|
import java.util.Objects;
|
|
import net.minecraft.world.entity.ai.village.poi.PoiType;
|
|
import it.unimi.dsi.fastutil.longs.LongSets;
|
|
import net.minecraft.world.level.ForcedChunksSavedData;
|
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
|
import net.minecraft.util.Unit;
|
|
import net.minecraft.world.level.saveddata.maps.MapIndex;
|
|
import net.minecraft.world.level.saveddata.SavedData;
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import net.minecraft.tags.TagManager;
|
|
import net.minecraft.world.item.crafting.RecipeManager;
|
|
import net.minecraft.core.Position;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
|
import javax.annotation.Nonnull;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.level.Explosion;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundAddGlobalEntityPacket;
|
|
import net.minecraft.util.ClassInstanceMultiMap;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import java.util.Collection;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
import java.util.function.BiFunction;
|
|
import net.minecraft.world.entity.Mob;
|
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|
import net.minecraft.world.entity.MobCategory;
|
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
|
import java.util.function.Predicate;
|
|
import net.minecraft.world.level.LevelConflictException;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.TranslatableComponent;
|
|
import net.minecraft.util.ProgressListener;
|
|
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
|
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;
|
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
|
import net.minecraft.world.level.levelgen.feature.Feature;
|
|
import net.minecraft.world.level.biome.BiomeSource;
|
|
import net.minecraft.tags.BlockTags;
|
|
import java.util.Random;
|
|
import net.minecraft.world.level.LevelSettings;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.TickNextTickData;
|
|
import net.minecraft.server.ServerScoreboard;
|
|
import java.util.Iterator;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.LevelReader;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.entity.global.LightningBolt;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.animal.horse.SkeletonHorse;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import java.util.stream.Collector;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
|
import net.minecraft.world.entity.boss.EnderDragonPart;
|
|
import net.minecraft.world.entity.npc.Npc;
|
|
import net.minecraft.world.entity.animal.WaterAnimal;
|
|
import net.minecraft.world.entity.animal.Animal;
|
|
import net.minecraft.world.level.LevelType;
|
|
import net.minecraft.world.Difficulty;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.level.GameRules;
|
|
import java.util.function.BooleanSupplier;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import com.google.common.collect.Sets;
|
|
import net.minecraft.world.level.material.Fluids;
|
|
import net.minecraft.core.Registry;
|
|
import com.google.common.collect.Queues;
|
|
import com.google.common.collect.Maps;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
|
import com.google.common.collect.Lists;
|
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
import net.minecraft.util.profiling.ProfilerFiller;
|
|
import net.minecraft.world.level.dimension.DimensionType;
|
|
import net.minecraft.world.level.storage.LevelData;
|
|
import java.util.concurrent.Executor;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
|
|
import net.minecraft.world.level.BlockEventData;
|
|
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
|
import net.minecraft.world.entity.raid.Raids;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import java.util.Set;
|
|
import net.minecraft.world.level.material.Fluid;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.ServerTickList;
|
|
import net.minecraft.world.level.PortalForcer;
|
|
import net.minecraft.world.level.storage.LevelStorage;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import java.util.Queue;
|
|
import java.util.UUID;
|
|
import java.util.Map;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
import net.minecraft.world.entity.Entity;
|
|
import java.util.List;
|
|
import org.apache.logging.log4j.Logger;
|
|
import net.minecraft.world.level.Level;
|
|
|
|
public class ServerLevel extends Level {
|
|
private static final Logger LOGGER;
|
|
private final List<Entity> globalEntities;
|
|
private final Int2ObjectMap<Entity> entitiesById;
|
|
private final Map<UUID, Entity> entitiesByUuid;
|
|
private final Queue<Entity> toAddAfterTick;
|
|
private final List<ServerPlayer> players;
|
|
boolean tickingEntities;
|
|
private final MinecraftServer server;
|
|
private final LevelStorage levelStorage;
|
|
public boolean noSave;
|
|
private boolean allPlayersSleeping;
|
|
private int emptyTime;
|
|
private final PortalForcer portalForcer;
|
|
private final ServerTickList<Block> blockTicks;
|
|
private final ServerTickList<Fluid> liquidTicks;
|
|
private final Set<PathNavigation> navigations;
|
|
protected final Raids raids;
|
|
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents;
|
|
private boolean handlingTick;
|
|
@Nullable
|
|
private final WanderingTraderSpawner wanderingTraderSpawner;
|
|
|
|
public ServerLevel(final MinecraftServer minecraftServer, final Executor executor, final LevelStorage crk, final LevelData crj, final DimensionType cbf, final ProfilerFiller aim, final ChunkProgressListener xm) {
|
|
super(crj, cbf, (bjt, cbe) -> new ServerChunkCache(bjt, crk.getFolder(), crk.getFixerUpper(), crk.getStructureManager(), executor, cbe.createRandomLevelGenerator(), minecraftServer.getPlayerList().getViewDistance(), xm, () -> minecraftServer.getLevel(DimensionType.OVERWORLD).getDataStorage()), aim, false);
|
|
this.globalEntities = Lists.newArrayList();
|
|
this.entitiesById = (Int2ObjectMap<Entity>)new Int2ObjectLinkedOpenHashMap();
|
|
this.entitiesByUuid = Maps.newHashMap();
|
|
this.toAddAfterTick = Queues.newArrayDeque();
|
|
this.players = Lists.newArrayList();
|
|
this.blockTicks = new ServerTickList<Block>(this, bpe -> bpe == null || bpe.defaultBlockState().isAir(), Registry.BLOCK::getKey, Registry.BLOCK::get, this::tickBlock);
|
|
this.liquidTicks = new ServerTickList<Fluid>(this, cof -> cof == null || cof == Fluids.EMPTY, Registry.FLUID::getKey, Registry.FLUID::get, this::tickLiquid);
|
|
this.navigations = Sets.newHashSet();
|
|
this.blockEvents = (ObjectLinkedOpenHashSet<BlockEventData>)new ObjectLinkedOpenHashSet();
|
|
this.levelStorage = crk;
|
|
this.server = minecraftServer;
|
|
this.portalForcer = new PortalForcer(this);
|
|
this.updateSkyBrightness();
|
|
this.prepareWeather();
|
|
this.getWorldBorder().setAbsoluteMaxSize(minecraftServer.getAbsoluteMaxWorldSize());
|
|
this.raids = this.getDataStorage().<Raids>computeIfAbsent(() -> new Raids(this), Raids.getFileId(this.dimension));
|
|
if (!minecraftServer.isSingleplayer()) {
|
|
this.getLevelData().setGameType(minecraftServer.getDefaultGameType());
|
|
}
|
|
this.wanderingTraderSpawner = ((this.dimension.getType() == DimensionType.OVERWORLD) ? new WanderingTraderSpawner(this) : null);
|
|
}
|
|
|
|
@Override
|
|
public Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3) {
|
|
return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(integer1, integer2, integer3);
|
|
}
|
|
|
|
public void tick(final BooleanSupplier booleanSupplier) {
|
|
final ProfilerFiller aim3 = this.getProfiler();
|
|
this.handlingTick = true;
|
|
aim3.push("world border");
|
|
this.getWorldBorder().tick();
|
|
aim3.popPush("weather");
|
|
final boolean boolean4 = this.isRaining();
|
|
if (this.dimension.isHasSkyLight()) {
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
|
|
int integer5 = this.levelData.getClearWeatherTime();
|
|
int integer6 = this.levelData.getThunderTime();
|
|
int integer7 = this.levelData.getRainTime();
|
|
boolean boolean5 = this.levelData.isThundering();
|
|
boolean boolean6 = this.levelData.isRaining();
|
|
if (integer5 > 0) {
|
|
--integer5;
|
|
integer6 = (boolean5 ? 0 : 1);
|
|
integer7 = (boolean6 ? 0 : 1);
|
|
boolean5 = false;
|
|
boolean6 = false;
|
|
}
|
|
else {
|
|
if (integer6 > 0) {
|
|
if (--integer6 == 0) {
|
|
boolean5 = !boolean5;
|
|
}
|
|
}
|
|
else if (boolean5) {
|
|
integer6 = this.random.nextInt(12000) + 3600;
|
|
}
|
|
else {
|
|
integer6 = this.random.nextInt(168000) + 12000;
|
|
}
|
|
if (integer7 > 0) {
|
|
if (--integer7 == 0) {
|
|
boolean6 = !boolean6;
|
|
}
|
|
}
|
|
else if (boolean6) {
|
|
integer7 = this.random.nextInt(12000) + 12000;
|
|
}
|
|
else {
|
|
integer7 = this.random.nextInt(168000) + 12000;
|
|
}
|
|
}
|
|
this.levelData.setThunderTime(integer6);
|
|
this.levelData.setRainTime(integer7);
|
|
this.levelData.setClearWeatherTime(integer5);
|
|
this.levelData.setThundering(boolean5);
|
|
this.levelData.setRaining(boolean6);
|
|
}
|
|
this.oThunderLevel = this.thunderLevel;
|
|
if (this.levelData.isThundering()) {
|
|
this.thunderLevel += (float)0.01;
|
|
}
|
|
else {
|
|
this.thunderLevel -= (float)0.01;
|
|
}
|
|
this.thunderLevel = Mth.clamp(this.thunderLevel, 0.0f, 1.0f);
|
|
this.oRainLevel = this.rainLevel;
|
|
if (this.levelData.isRaining()) {
|
|
this.rainLevel += (float)0.01;
|
|
}
|
|
else {
|
|
this.rainLevel -= (float)0.01;
|
|
}
|
|
this.rainLevel = Mth.clamp(this.rainLevel, 0.0f, 1.0f);
|
|
}
|
|
if (this.oRainLevel != this.rainLevel) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(7, this.rainLevel), this.dimension.getType());
|
|
}
|
|
if (this.oThunderLevel != this.thunderLevel) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(8, this.thunderLevel), this.dimension.getType());
|
|
}
|
|
if (boolean4 != this.isRaining()) {
|
|
if (boolean4) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(2, 0.0f));
|
|
}
|
|
else {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(1, 0.0f));
|
|
}
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(7, this.rainLevel));
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(8, this.thunderLevel));
|
|
}
|
|
if (this.getLevelData().isHardcore() && this.getDifficulty() != Difficulty.HARD) {
|
|
this.getLevelData().setDifficulty(Difficulty.HARD);
|
|
}
|
|
if (this.allPlayersSleeping && this.players.stream().noneMatch(xe -> !xe.isSpectator() && !xe.isSleepingLongEnough())) {
|
|
this.allPlayersSleeping = false;
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
final long long5 = this.levelData.getDayTime() + 24000L;
|
|
this.setDayTime(long5 - long5 % 24000L);
|
|
}
|
|
this.wakeUpAllPlayers();
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
|
|
this.stopWeather();
|
|
}
|
|
}
|
|
this.updateSkyBrightness();
|
|
this.tickTime();
|
|
aim3.popPush("chunkSource");
|
|
this.getChunkSource().tick(booleanSupplier);
|
|
aim3.popPush("tickPending");
|
|
if (this.levelData.getGeneratorType() != LevelType.DEBUG_ALL_BLOCK_STATES) {
|
|
this.blockTicks.tick();
|
|
this.liquidTicks.tick();
|
|
}
|
|
aim3.popPush("raid");
|
|
this.raids.tick();
|
|
if (this.wanderingTraderSpawner != null) {
|
|
this.wanderingTraderSpawner.tick();
|
|
}
|
|
aim3.popPush("blockEvents");
|
|
this.runBlockEvents();
|
|
this.handlingTick = false;
|
|
aim3.popPush("entities");
|
|
final boolean boolean7 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
|
|
if (boolean7) {
|
|
this.resetEmptyTime();
|
|
}
|
|
if (boolean7 || this.emptyTime++ < 300) {
|
|
this.dimension.tick();
|
|
aim3.push("global");
|
|
for (int integer6 = 0; integer6 < this.globalEntities.size(); ++integer6) {
|
|
final Entity akn2 = this.globalEntities.get(integer6);
|
|
this.guardEntityTick(akn -> {
|
|
++akn.tickCount;
|
|
akn.tick();
|
|
return;
|
|
}, akn2);
|
|
if (akn2.removed) {
|
|
this.globalEntities.remove(integer6--);
|
|
}
|
|
}
|
|
aim3.popPush("regular");
|
|
this.tickingEntities = true;
|
|
final ObjectIterator<Int2ObjectMap.Entry<Entity>> objectIterator6 = (ObjectIterator<Int2ObjectMap.Entry<Entity>>)this.entitiesById.int2ObjectEntrySet().iterator();
|
|
while (objectIterator6.hasNext()) {
|
|
final Int2ObjectMap.Entry<Entity> entry7 = (Int2ObjectMap.Entry<Entity>)objectIterator6.next();
|
|
final Entity akn3 = (Entity)entry7.getValue();
|
|
final Entity akn4 = akn3.getVehicle();
|
|
if (!this.server.isAnimals() && (akn3 instanceof Animal || akn3 instanceof WaterAnimal)) {
|
|
akn3.remove();
|
|
}
|
|
if (!this.server.isNpcsEnabled() && akn3 instanceof Npc) {
|
|
akn3.remove();
|
|
}
|
|
aim3.push("checkDespawn");
|
|
if (!akn3.removed) {
|
|
akn3.checkDespawn();
|
|
}
|
|
aim3.pop();
|
|
if (akn4 != null) {
|
|
if (!akn4.removed && akn4.hasPassenger(akn3)) {
|
|
continue;
|
|
}
|
|
akn3.stopRiding();
|
|
}
|
|
aim3.push("tick");
|
|
if (!akn3.removed && !(akn3 instanceof EnderDragonPart)) {
|
|
this.guardEntityTick(this::tickNonPassenger, akn3);
|
|
}
|
|
aim3.pop();
|
|
aim3.push("remove");
|
|
if (akn3.removed) {
|
|
this.removeFromChunk(akn3);
|
|
objectIterator6.remove();
|
|
this.onEntityRemoved(akn3);
|
|
}
|
|
aim3.pop();
|
|
}
|
|
this.tickingEntities = false;
|
|
Entity akn2;
|
|
while ((akn2 = this.toAddAfterTick.poll()) != null) {
|
|
this.add(akn2);
|
|
}
|
|
aim3.pop();
|
|
this.tickBlockEntities();
|
|
}
|
|
aim3.pop();
|
|
}
|
|
|
|
private void wakeUpAllPlayers() {
|
|
this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(xe -> xe.stopSleepInBed(false, false));
|
|
}
|
|
|
|
public void tickChunk(final LevelChunk cai, final int integer) {
|
|
final ChunkPos bje4 = cai.getPos();
|
|
final boolean boolean5 = this.isRaining();
|
|
final int integer2 = bje4.getMinBlockX();
|
|
final int integer3 = bje4.getMinBlockZ();
|
|
final ProfilerFiller aim8 = this.getProfiler();
|
|
aim8.push("thunder");
|
|
if (boolean5 && this.isThundering() && this.random.nextInt(100000) == 0) {
|
|
final BlockPos fk9 = this.findLightingTargetAround(this.getBlockRandomPos(integer2, 0, integer3, 15));
|
|
if (this.isRainingAt(fk9)) {
|
|
final DifficultyInstance ajg10 = this.getCurrentDifficultyAt(fk9);
|
|
final boolean boolean6 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < ajg10.getEffectiveDifficulty() * 0.01;
|
|
if (boolean6) {
|
|
final SkeletonHorse aug12 = EntityType.SKELETON_HORSE.create(this);
|
|
aug12.setTrap(true);
|
|
aug12.setAge(0);
|
|
aug12.setPos(fk9.getX(), fk9.getY(), fk9.getZ());
|
|
this.addFreshEntity(aug12);
|
|
}
|
|
this.addGlobalEntity(new LightningBolt(this, fk9.getX() + 0.5, fk9.getY(), fk9.getZ() + 0.5, boolean6));
|
|
}
|
|
}
|
|
aim8.popPush("iceandsnow");
|
|
if (this.random.nextInt(16) == 0) {
|
|
final BlockPos fk9 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(integer2, 0, integer3, 15));
|
|
final BlockPos fk10 = fk9.below();
|
|
final Biome bkq11 = this.getBiome(fk9);
|
|
if (bkq11.shouldFreeze(this, fk10)) {
|
|
this.setBlockAndUpdate(fk10, Blocks.ICE.defaultBlockState());
|
|
}
|
|
if (boolean5 && bkq11.shouldSnow(this, fk9)) {
|
|
this.setBlockAndUpdate(fk9, Blocks.SNOW.defaultBlockState());
|
|
}
|
|
if (boolean5 && this.getBiome(fk10).getPrecipitation() == Biome.Precipitation.RAIN) {
|
|
this.getBlockState(fk10).getBlock().handleRain(this, fk10);
|
|
}
|
|
}
|
|
aim8.popPush("tickBlocks");
|
|
if (integer > 0) {
|
|
for (final LevelChunkSection caj12 : cai.getSections()) {
|
|
if (caj12 != LevelChunk.EMPTY_SECTION && caj12.isRandomlyTicking()) {
|
|
final int integer4 = caj12.bottomBlockY();
|
|
for (int integer5 = 0; integer5 < integer; ++integer5) {
|
|
final BlockPos fk11 = this.getBlockRandomPos(integer2, integer4, integer3, 15);
|
|
aim8.push("randomTick");
|
|
final BlockState byg16 = caj12.getBlockState(fk11.getX() - integer2, fk11.getY() - integer4, fk11.getZ() - integer3);
|
|
if (byg16.isRandomlyTicking()) {
|
|
byg16.randomTick(this, fk11, this.random);
|
|
}
|
|
final FluidState cog17 = byg16.getFluidState();
|
|
if (cog17.isRandomlyTicking()) {
|
|
cog17.randomTick(this, fk11, this.random);
|
|
}
|
|
aim8.pop();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
aim8.pop();
|
|
}
|
|
|
|
protected BlockPos findLightingTargetAround(final BlockPos fk) {
|
|
BlockPos fk2 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, fk);
|
|
final AABB cvc4 = new AABB(fk2, new BlockPos(fk2.getX(), this.getMaxBuildHeight(), fk2.getZ())).inflate(3.0);
|
|
final List<LivingEntity> list5 = this.<LivingEntity>getEntitiesOfClass(LivingEntity.class, cvc4, akw -> akw != null && akw.isAlive() && this.canSeeSky(akw.getCommandSenderBlockPosition()));
|
|
if (!list5.isEmpty()) {
|
|
return list5.get(this.random.nextInt(list5.size())).getCommandSenderBlockPosition();
|
|
}
|
|
if (fk2.getY() == -1) {
|
|
fk2 = fk2.above(2);
|
|
}
|
|
return fk2;
|
|
}
|
|
|
|
public boolean isHandlingTick() {
|
|
return this.handlingTick;
|
|
}
|
|
|
|
public void updateSleepingPlayerList() {
|
|
this.allPlayersSleeping = false;
|
|
if (!this.players.isEmpty()) {
|
|
int integer2 = 0;
|
|
int integer3 = 0;
|
|
for (final ServerPlayer xe5 : this.players) {
|
|
if (xe5.isSpectator()) {
|
|
++integer2;
|
|
}
|
|
else {
|
|
if (!xe5.isSleeping()) {
|
|
continue;
|
|
}
|
|
++integer3;
|
|
}
|
|
}
|
|
this.allPlayersSleeping = (integer3 > 0 && integer3 >= this.players.size() - integer2);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ServerScoreboard getScoreboard() {
|
|
return this.server.getScoreboard();
|
|
}
|
|
|
|
private void stopWeather() {
|
|
this.levelData.setRainTime(0);
|
|
this.levelData.setRaining(false);
|
|
this.levelData.setThunderTime(0);
|
|
this.levelData.setThundering(false);
|
|
}
|
|
|
|
@Override
|
|
public void validateSpawn() {
|
|
if (this.levelData.getYSpawn() <= 0) {
|
|
this.levelData.setYSpawn(this.getSeaLevel() + 1);
|
|
}
|
|
int integer2 = this.levelData.getXSpawn();
|
|
int integer3 = this.levelData.getZSpawn();
|
|
int integer4 = 0;
|
|
while (this.getTopBlockState(new BlockPos(integer2, 0, integer3)).isAir()) {
|
|
integer2 += this.random.nextInt(8) - this.random.nextInt(8);
|
|
integer3 += this.random.nextInt(8) - this.random.nextInt(8);
|
|
if (++integer4 == 10000) {
|
|
break;
|
|
}
|
|
}
|
|
this.levelData.setXSpawn(integer2);
|
|
this.levelData.setZSpawn(integer3);
|
|
}
|
|
|
|
public void resetEmptyTime() {
|
|
this.emptyTime = 0;
|
|
}
|
|
|
|
private void tickLiquid(final TickNextTickData<Fluid> bkj) {
|
|
final FluidState cog3 = this.getFluidState(bkj.pos);
|
|
if (cog3.getType() == bkj.getType()) {
|
|
cog3.tick(this, bkj.pos);
|
|
}
|
|
}
|
|
|
|
private void tickBlock(final TickNextTickData<Block> bkj) {
|
|
final BlockState byg3 = this.getBlockState(bkj.pos);
|
|
if (byg3.getBlock() == bkj.getType()) {
|
|
byg3.tick(this, bkj.pos, this.random);
|
|
}
|
|
}
|
|
|
|
public void tickNonPassenger(final Entity akn) {
|
|
if (!(akn instanceof Player) && !this.getChunkSource().isEntityTickingChunk(akn)) {
|
|
return;
|
|
}
|
|
akn.setPosAndOldPos(akn.getX(), akn.getY(), akn.getZ());
|
|
akn.yRotO = akn.yRot;
|
|
akn.xRotO = akn.xRot;
|
|
if (akn.inChunk) {
|
|
++akn.tickCount;
|
|
this.getProfiler().push(() -> Registry.ENTITY_TYPE.getKey(akn.getType()).toString());
|
|
akn.tick();
|
|
this.getProfiler().pop();
|
|
}
|
|
this.updateChunkPos(akn);
|
|
if (akn.inChunk) {
|
|
for (final Entity akn2 : akn.getPassengers()) {
|
|
this.tickPassenger(akn, akn2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void tickPassenger(final Entity akn1, final Entity akn2) {
|
|
if (akn2.removed || akn2.getVehicle() != akn1) {
|
|
akn2.stopRiding();
|
|
return;
|
|
}
|
|
if (!(akn2 instanceof Player) && !this.getChunkSource().isEntityTickingChunk(akn2)) {
|
|
return;
|
|
}
|
|
akn2.setPosAndOldPos(akn2.getX(), akn2.getY(), akn2.getZ());
|
|
akn2.yRotO = akn2.yRot;
|
|
akn2.xRotO = akn2.xRot;
|
|
if (akn2.inChunk) {
|
|
++akn2.tickCount;
|
|
akn2.rideTick();
|
|
}
|
|
this.updateChunkPos(akn2);
|
|
if (akn2.inChunk) {
|
|
for (final Entity akn3 : akn2.getPassengers()) {
|
|
this.tickPassenger(akn2, akn3);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateChunkPos(final Entity akn) {
|
|
this.getProfiler().push("chunkCheck");
|
|
final int integer3 = Mth.floor(akn.getX() / 16.0);
|
|
final int integer4 = Mth.floor(akn.getY() / 16.0);
|
|
final int integer5 = Mth.floor(akn.getZ() / 16.0);
|
|
if (!akn.inChunk || akn.xChunk != integer3 || akn.yChunk != integer4 || akn.zChunk != integer5) {
|
|
if (akn.inChunk && this.hasChunk(akn.xChunk, akn.zChunk)) {
|
|
this.getChunk(akn.xChunk, akn.zChunk).removeEntity(akn, akn.yChunk);
|
|
}
|
|
if (akn.checkAndResetTeleportedFlag() || this.hasChunk(integer3, integer5)) {
|
|
this.getChunk(integer3, integer5).addEntity(akn);
|
|
}
|
|
else {
|
|
akn.inChunk = false;
|
|
}
|
|
}
|
|
this.getProfiler().pop();
|
|
}
|
|
|
|
@Override
|
|
public boolean mayInteract(final Player ayg, final BlockPos fk) {
|
|
return !this.server.isUnderSpawnProtection(this, fk, ayg) && this.getWorldBorder().isWithinBounds(fk);
|
|
}
|
|
|
|
public void setInitialSpawn(final LevelSettings bjx) {
|
|
if (!this.dimension.mayRespawn()) {
|
|
this.levelData.setSpawn(BlockPos.ZERO.above(this.getChunkSource().getGenerator().getSpawnHeight()));
|
|
return;
|
|
}
|
|
if (this.levelData.getGeneratorType() == LevelType.DEBUG_ALL_BLOCK_STATES) {
|
|
this.levelData.setSpawn(BlockPos.ZERO.above());
|
|
return;
|
|
}
|
|
final BiomeSource bkt3 = this.getChunkSource().getGenerator().getBiomeSource();
|
|
final List<Biome> list4 = bkt3.getPlayerSpawnBiomes();
|
|
final Random random5 = new Random(this.getSeed());
|
|
final BlockPos fk6 = bkt3.findBiomeHorizontal(0, this.getSeaLevel(), 0, 256, list4, random5);
|
|
final ChunkPos bje7 = (fk6 == null) ? new ChunkPos(0, 0) : new ChunkPos(fk6);
|
|
if (fk6 == null) {
|
|
ServerLevel.LOGGER.warn("Unable to find spawn biome");
|
|
}
|
|
boolean boolean8 = false;
|
|
for (final Block bpe10 : BlockTags.VALID_SPAWN.getValues()) {
|
|
if (bkt3.getSurfaceBlocks().contains(bpe10.defaultBlockState())) {
|
|
boolean8 = true;
|
|
break;
|
|
}
|
|
}
|
|
this.levelData.setSpawn(bje7.getWorldPosition().offset(8, this.getChunkSource().getGenerator().getSpawnHeight(), 8));
|
|
int integer9 = 0;
|
|
int integer10 = 0;
|
|
int integer11 = 0;
|
|
int integer12 = -1;
|
|
final int integer13 = 32;
|
|
for (int integer14 = 0; integer14 < 1024; ++integer14) {
|
|
if (integer9 > -16 && integer9 <= 16 && integer10 > -16 && integer10 <= 16) {
|
|
final BlockPos fk7 = this.dimension.getSpawnPosInChunk(new ChunkPos(bje7.x + integer9, bje7.z + integer10), boolean8);
|
|
if (fk7 != null) {
|
|
this.levelData.setSpawn(fk7);
|
|
break;
|
|
}
|
|
}
|
|
if (integer9 == integer10 || (integer9 < 0 && integer9 == -integer10) || (integer9 > 0 && integer9 == 1 - integer10)) {
|
|
final int integer15 = integer11;
|
|
integer11 = -integer12;
|
|
integer12 = integer15;
|
|
}
|
|
integer9 += integer11;
|
|
integer10 += integer12;
|
|
}
|
|
if (bjx.hasStartingBonusItems()) {
|
|
this.generateBonusItemsNearSpawn();
|
|
}
|
|
}
|
|
|
|
protected void generateBonusItemsNearSpawn() {
|
|
final ConfiguredFeature<?, ?> ccz2 = Feature.BONUS_CHEST.configured(FeatureConfiguration.NONE);
|
|
ccz2.place(this, this.getChunkSource().getGenerator(), this.random, new BlockPos(this.levelData.getXSpawn(), this.levelData.getYSpawn(), this.levelData.getZSpawn()));
|
|
}
|
|
|
|
@Nullable
|
|
public BlockPos getDimensionSpecificSpawn() {
|
|
return this.dimension.getDimensionSpecificSpawn();
|
|
}
|
|
|
|
public void save(@Nullable final ProgressListener abs, final boolean boolean2, final boolean boolean3) throws LevelConflictException {
|
|
final ServerChunkCache xb5 = this.getChunkSource();
|
|
if (boolean3) {
|
|
return;
|
|
}
|
|
if (abs != null) {
|
|
abs.progressStartNoAbort(new TranslatableComponent("menu.savingLevel", new Object[0]));
|
|
}
|
|
this.saveLevelData();
|
|
if (abs != null) {
|
|
abs.progressStage(new TranslatableComponent("menu.savingChunks", new Object[0]));
|
|
}
|
|
xb5.save(boolean2);
|
|
}
|
|
|
|
protected void saveLevelData() throws LevelConflictException {
|
|
this.checkSession();
|
|
this.dimension.saveData();
|
|
this.getChunkSource().getDataStorage().save();
|
|
}
|
|
|
|
public List<Entity> getEntities(@Nullable final EntityType<?> akr, final Predicate<? super Entity> predicate) {
|
|
final List<Entity> list4 = Lists.newArrayList();
|
|
final ServerChunkCache xb5 = this.getChunkSource();
|
|
for (final Entity akn7 : this.entitiesById.values()) {
|
|
if ((akr == null || akn7.getType() == akr) && xb5.hasChunk(Mth.floor(akn7.getX()) >> 4, Mth.floor(akn7.getZ()) >> 4) && predicate.test(akn7)) {
|
|
list4.add(akn7);
|
|
}
|
|
}
|
|
return list4;
|
|
}
|
|
|
|
public List<EnderDragon> getDragons() {
|
|
final List<EnderDragon> list2 = Lists.newArrayList();
|
|
for (final Entity akn4 : this.entitiesById.values()) {
|
|
if (akn4 instanceof EnderDragon && akn4.isAlive()) {
|
|
list2.add((EnderDragon)akn4);
|
|
}
|
|
}
|
|
return list2;
|
|
}
|
|
|
|
public List<ServerPlayer> getPlayers(final Predicate<? super ServerPlayer> predicate) {
|
|
final List<ServerPlayer> list3 = Lists.newArrayList();
|
|
for (final ServerPlayer xe5 : this.players) {
|
|
if (predicate.test(xe5)) {
|
|
list3.add(xe5);
|
|
}
|
|
}
|
|
return list3;
|
|
}
|
|
|
|
@Nullable
|
|
public ServerPlayer getRandomPlayer() {
|
|
final List<ServerPlayer> list2 = this.getPlayers(LivingEntity::isAlive);
|
|
if (list2.isEmpty()) {
|
|
return null;
|
|
}
|
|
return list2.get(this.random.nextInt(list2.size()));
|
|
}
|
|
|
|
public Object2IntMap<MobCategory> getMobCategoryCounts() {
|
|
final Object2IntMap<MobCategory> object2IntMap2 = (Object2IntMap<MobCategory>)new Object2IntOpenHashMap();
|
|
for (final Entity akn4 : this.entitiesById.values()) {
|
|
if (akn4 instanceof Mob) {
|
|
final Mob akx5 = (Mob)akn4;
|
|
if (akx5.isPersistenceRequired()) {
|
|
continue;
|
|
}
|
|
if (akx5.requiresCustomPersistence()) {
|
|
continue;
|
|
}
|
|
}
|
|
final MobCategory aky5 = akn4.getType().getCategory();
|
|
if (aky5 == MobCategory.MISC) {
|
|
continue;
|
|
}
|
|
if (!this.getChunkSource().isInAccessibleChunk(akn4)) {
|
|
continue;
|
|
}
|
|
object2IntMap2.mergeInt(aky5, 1, (BiFunction)Integer::sum);
|
|
}
|
|
return object2IntMap2;
|
|
}
|
|
|
|
@Override
|
|
public boolean addFreshEntity(final Entity akn) {
|
|
return this.addEntity(akn);
|
|
}
|
|
|
|
public boolean addWithUUID(final Entity akn) {
|
|
return this.addEntity(akn);
|
|
}
|
|
|
|
public void addFromAnotherDimension(final Entity akn) {
|
|
final boolean boolean3 = akn.forcedLoading;
|
|
akn.forcedLoading = true;
|
|
this.addWithUUID(akn);
|
|
akn.forcedLoading = boolean3;
|
|
this.updateChunkPos(akn);
|
|
}
|
|
|
|
public void addDuringCommandTeleport(final ServerPlayer xe) {
|
|
this.addPlayer(xe);
|
|
this.updateChunkPos(xe);
|
|
}
|
|
|
|
public void addDuringPortalTeleport(final ServerPlayer xe) {
|
|
this.addPlayer(xe);
|
|
this.updateChunkPos(xe);
|
|
}
|
|
|
|
public void addNewPlayer(final ServerPlayer xe) {
|
|
this.addPlayer(xe);
|
|
}
|
|
|
|
public void addRespawnedPlayer(final ServerPlayer xe) {
|
|
this.addPlayer(xe);
|
|
}
|
|
|
|
private void addPlayer(final ServerPlayer xe) {
|
|
final Entity akn3 = this.entitiesByUuid.get(xe.getUUID());
|
|
if (akn3 != null) {
|
|
ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", xe.getUUID().toString());
|
|
akn3.unRide();
|
|
this.removePlayerImmediately((ServerPlayer)akn3);
|
|
}
|
|
this.players.add(xe);
|
|
this.updateSleepingPlayerList();
|
|
final ChunkAccess bzv4 = this.getChunk(Mth.floor(xe.getX() / 16.0), Mth.floor(xe.getZ() / 16.0), ChunkStatus.FULL, true);
|
|
if (bzv4 instanceof LevelChunk) {
|
|
bzv4.addEntity(xe);
|
|
}
|
|
this.add(xe);
|
|
}
|
|
|
|
private boolean addEntity(final Entity akn) {
|
|
if (akn.removed) {
|
|
ServerLevel.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(akn.getType()));
|
|
return false;
|
|
}
|
|
if (this.isUUIDUsed(akn)) {
|
|
return false;
|
|
}
|
|
final ChunkAccess bzv3 = this.getChunk(Mth.floor(akn.getX() / 16.0), Mth.floor(akn.getZ() / 16.0), ChunkStatus.FULL, akn.forcedLoading);
|
|
if (!(bzv3 instanceof LevelChunk)) {
|
|
return false;
|
|
}
|
|
bzv3.addEntity(akn);
|
|
this.add(akn);
|
|
return true;
|
|
}
|
|
|
|
public boolean loadFromChunk(final Entity akn) {
|
|
if (this.isUUIDUsed(akn)) {
|
|
return false;
|
|
}
|
|
this.add(akn);
|
|
return true;
|
|
}
|
|
|
|
private boolean isUUIDUsed(final Entity akn) {
|
|
final Entity akn2 = this.entitiesByUuid.get(akn.getUUID());
|
|
if (akn2 == null) {
|
|
return false;
|
|
}
|
|
ServerLevel.LOGGER.warn("Keeping entity {} that already exists with UUID {}", EntityType.getKey(akn2.getType()), akn.getUUID().toString());
|
|
return true;
|
|
}
|
|
|
|
public void unload(final LevelChunk cai) {
|
|
this.blockEntitiesToUnload.addAll(cai.getBlockEntities().values());
|
|
for (final ClassInstanceMultiMap<Entity> abe6 : cai.getEntitySections()) {
|
|
for (final Entity akn8 : abe6) {
|
|
if (akn8 instanceof ServerPlayer) {
|
|
continue;
|
|
}
|
|
if (this.tickingEntities) {
|
|
throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
|
|
}
|
|
this.entitiesById.remove(akn8.getId());
|
|
this.onEntityRemoved(akn8);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void onEntityRemoved(final Entity akn) {
|
|
if (akn instanceof EnderDragon) {
|
|
for (final EnderDragonPart aun6 : ((EnderDragon)akn).getSubEntities()) {
|
|
aun6.remove();
|
|
}
|
|
}
|
|
this.entitiesByUuid.remove(akn.getUUID());
|
|
this.getChunkSource().removeEntity(akn);
|
|
if (akn instanceof ServerPlayer) {
|
|
final ServerPlayer xe3 = (ServerPlayer)akn;
|
|
this.players.remove(xe3);
|
|
}
|
|
this.getScoreboard().entityRemoved(akn);
|
|
if (akn instanceof Mob) {
|
|
this.navigations.remove(((Mob)akn).getNavigation());
|
|
}
|
|
}
|
|
|
|
private void add(final Entity akn) {
|
|
if (this.tickingEntities) {
|
|
this.toAddAfterTick.add(akn);
|
|
}
|
|
else {
|
|
this.entitiesById.put(akn.getId(), akn);
|
|
if (akn instanceof EnderDragon) {
|
|
for (final EnderDragonPart aun6 : ((EnderDragon)akn).getSubEntities()) {
|
|
this.entitiesById.put(aun6.getId(), aun6);
|
|
}
|
|
}
|
|
this.entitiesByUuid.put(akn.getUUID(), akn);
|
|
this.getChunkSource().addEntity(akn);
|
|
if (akn instanceof Mob) {
|
|
this.navigations.add(((Mob)akn).getNavigation());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void despawn(final Entity akn) {
|
|
if (this.tickingEntities) {
|
|
throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
|
|
}
|
|
this.removeFromChunk(akn);
|
|
this.entitiesById.remove(akn.getId());
|
|
this.onEntityRemoved(akn);
|
|
}
|
|
|
|
private void removeFromChunk(final Entity akn) {
|
|
final ChunkAccess bzv3 = this.getChunk(akn.xChunk, akn.zChunk, ChunkStatus.FULL, false);
|
|
if (bzv3 instanceof LevelChunk) {
|
|
((LevelChunk)bzv3).removeEntity(akn);
|
|
}
|
|
}
|
|
|
|
public void removePlayerImmediately(final ServerPlayer xe) {
|
|
xe.remove();
|
|
this.despawn(xe);
|
|
this.updateSleepingPlayerList();
|
|
}
|
|
|
|
public void addGlobalEntity(final LightningBolt avv) {
|
|
this.globalEntities.add(avv);
|
|
this.server.getPlayerList().broadcast(null, avv.getX(), avv.getY(), avv.getZ(), 512.0, this.dimension.getType(), new ClientboundAddGlobalEntityPacket(avv));
|
|
}
|
|
|
|
@Override
|
|
public void destroyBlockProgress(final int integer1, final BlockPos fk, final int integer3) {
|
|
for (final ServerPlayer xe6 : this.server.getPlayerList().getPlayers()) {
|
|
if (xe6 != null && xe6.level == this) {
|
|
if (xe6.getId() == integer1) {
|
|
continue;
|
|
}
|
|
final double double7 = fk.getX() - xe6.getX();
|
|
final double double8 = fk.getY() - xe6.getY();
|
|
final double double9 = fk.getZ() - xe6.getZ();
|
|
if (double7 * double7 + double8 * double8 + double9 * double9 >= 1024.0) {
|
|
continue;
|
|
}
|
|
xe6.connection.send(new ClientboundBlockDestructionPacket(integer1, fk, integer3));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playSound(@Nullable final Player ayg, final double double2, final double double3, final double double4, final SoundEvent aah, final SoundSource aaj, final float float7, final float float8) {
|
|
this.server.getPlayerList().broadcast(ayg, double2, double3, double4, (float7 > 1.0f) ? ((double)(16.0f * float7)) : 16.0, this.dimension.getType(), new ClientboundSoundPacket(aah, aaj, double2, double3, double4, float7, float8));
|
|
}
|
|
|
|
@Override
|
|
public void playSound(@Nullable final Player ayg, final Entity akn, final SoundEvent aah, final SoundSource aaj, final float float5, final float float6) {
|
|
this.server.getPlayerList().broadcast(ayg, akn.getX(), akn.getY(), akn.getZ(), (float5 > 1.0f) ? ((double)(16.0f * float5)) : 16.0, this.dimension.getType(), new ClientboundSoundEntityPacket(aah, aaj, akn, float5, float6));
|
|
}
|
|
|
|
@Override
|
|
public void globalLevelEvent(final int integer1, final BlockPos fk, final int integer3) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(integer1, fk, integer3, true));
|
|
}
|
|
|
|
@Override
|
|
public void levelEvent(@Nullable final Player ayg, final int integer2, final BlockPos fk, final int integer4) {
|
|
this.server.getPlayerList().broadcast(ayg, fk.getX(), fk.getY(), fk.getZ(), 64.0, this.dimension.getType(), new ClientboundLevelEventPacket(integer2, fk, integer4, false));
|
|
}
|
|
|
|
@Override
|
|
public void sendBlockUpdated(final BlockPos fk, final BlockState byg2, final BlockState byg3, final int integer) {
|
|
this.getChunkSource().blockChanged(fk);
|
|
final VoxelShape cwc6 = byg2.getCollisionShape(this, fk);
|
|
final VoxelShape cwc7 = byg3.getCollisionShape(this, fk);
|
|
if (!Shapes.joinIsNotEmpty(cwc6, cwc7, BooleanOp.NOT_SAME)) {
|
|
return;
|
|
}
|
|
for (final PathNavigation aro9 : this.navigations) {
|
|
if (aro9.hasDelayedRecomputation()) {
|
|
continue;
|
|
}
|
|
aro9.recomputePath(fk);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void broadcastEntityEvent(final Entity akn, final byte byte2) {
|
|
this.getChunkSource().broadcastAndSend(akn, new ClientboundEntityEventPacket(akn, byte2));
|
|
}
|
|
|
|
@Override
|
|
public ServerChunkCache getChunkSource() {
|
|
return (ServerChunkCache)super.getChunkSource();
|
|
}
|
|
|
|
@Override
|
|
public Explosion explode(@Nullable final Entity akn, @Nullable final DamageSource ajw, final double double3, final double double4, final double double5, final float float6, final boolean boolean7, final Explosion.BlockInteraction a) {
|
|
final Explosion bjm13 = new Explosion(this, akn, double3, double4, double5, float6, boolean7, a);
|
|
if (ajw != null) {
|
|
bjm13.setDamageSource(ajw);
|
|
}
|
|
bjm13.explode();
|
|
bjm13.finalizeExplosion(false);
|
|
if (a == Explosion.BlockInteraction.NONE) {
|
|
bjm13.clearToBlow();
|
|
}
|
|
for (final ServerPlayer xe15 : this.players) {
|
|
if (xe15.distanceToSqr(double3, double4, double5) < 4096.0) {
|
|
xe15.connection.send(new ClientboundExplodePacket(double3, double4, double5, float6, bjm13.getToBlow(), bjm13.getHitPlayers().get(xe15)));
|
|
}
|
|
}
|
|
return bjm13;
|
|
}
|
|
|
|
@Override
|
|
public void blockEvent(final BlockPos fk, final Block bpe, final int integer3, final int integer4) {
|
|
this.blockEvents.add(new BlockEventData(fk, bpe, integer3, integer4));
|
|
}
|
|
|
|
private void runBlockEvents() {
|
|
while (!this.blockEvents.isEmpty()) {
|
|
final BlockEventData bjc2 = (BlockEventData)this.blockEvents.removeFirst();
|
|
if (this.doBlockEvent(bjc2)) {
|
|
this.server.getPlayerList().broadcast(null, bjc2.getPos().getX(), bjc2.getPos().getY(), bjc2.getPos().getZ(), 64.0, this.dimension.getType(), new ClientboundBlockEventPacket(bjc2.getPos(), bjc2.getBlock(), bjc2.getParamA(), bjc2.getParamB()));
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean doBlockEvent(final BlockEventData bjc) {
|
|
final BlockState byg3 = this.getBlockState(bjc.getPos());
|
|
return byg3.getBlock() == bjc.getBlock() && byg3.triggerEvent(this, bjc.getPos(), bjc.getParamA(), bjc.getParamB());
|
|
}
|
|
|
|
@Override
|
|
public ServerTickList<Block> getBlockTicks() {
|
|
return this.blockTicks;
|
|
}
|
|
|
|
@Override
|
|
public ServerTickList<Fluid> getLiquidTicks() {
|
|
return this.liquidTicks;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public MinecraftServer getServer() {
|
|
return this.server;
|
|
}
|
|
|
|
public PortalForcer getPortalForcer() {
|
|
return this.portalForcer;
|
|
}
|
|
|
|
public StructureManager getStructureManager() {
|
|
return this.levelStorage.getStructureManager();
|
|
}
|
|
|
|
public <T extends ParticleOptions> int sendParticles(final T gt, final double double2, final double double3, final double double4, final int integer, final double double6, final double double7, final double double8, final double double9) {
|
|
final ClientboundLevelParticlesPacket nh18 = new ClientboundLevelParticlesPacket((T)gt, false, double2, double3, double4, (float)double6, (float)double7, (float)double8, (float)double9, integer);
|
|
int integer2 = 0;
|
|
for (int integer3 = 0; integer3 < this.players.size(); ++integer3) {
|
|
final ServerPlayer xe21 = this.players.get(integer3);
|
|
if (this.sendParticles(xe21, false, double2, double3, double4, nh18)) {
|
|
++integer2;
|
|
}
|
|
}
|
|
return integer2;
|
|
}
|
|
|
|
public <T extends ParticleOptions> boolean sendParticles(final ServerPlayer xe, final T gt, final boolean boolean3, final double double4, final double double5, final double double6, final int integer, final double double8, final double double9, final double double10, final double double11) {
|
|
final Packet<?> lt20 = new ClientboundLevelParticlesPacket((T)gt, boolean3, double4, double5, double6, (float)double8, (float)double9, (float)double10, (float)double11, integer);
|
|
return this.sendParticles(xe, boolean3, double4, double5, double6, lt20);
|
|
}
|
|
|
|
private boolean sendParticles(final ServerPlayer xe, final boolean boolean2, final double double3, final double double4, final double double5, final Packet<?> lt) {
|
|
if (xe.getLevel() != this) {
|
|
return false;
|
|
}
|
|
final BlockPos fk11 = xe.getCommandSenderBlockPosition();
|
|
if (fk11.closerThan(new Vec3(double3, double4, double5), boolean2 ? 512.0 : 32.0)) {
|
|
xe.connection.send(lt);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Entity getEntity(final int integer) {
|
|
return (Entity)this.entitiesById.get(integer);
|
|
}
|
|
|
|
@Nullable
|
|
public Entity getEntity(final UUID uUID) {
|
|
return this.entitiesByUuid.get(uUID);
|
|
}
|
|
|
|
@Nullable
|
|
public BlockPos findNearestMapFeature(final String string, final BlockPos fk, final int integer, final boolean boolean4) {
|
|
return this.getChunkSource().getGenerator().findNearestMapFeature(this, string, fk, integer, boolean4);
|
|
}
|
|
|
|
@Override
|
|
public RecipeManager getRecipeManager() {
|
|
return this.server.getRecipeManager();
|
|
}
|
|
|
|
@Override
|
|
public TagManager getTagManager() {
|
|
return this.server.getTags();
|
|
}
|
|
|
|
@Override
|
|
public void setGameTime(final long long1) {
|
|
super.setGameTime(long1);
|
|
this.levelData.getScheduledEvents().tick(this.server, long1);
|
|
}
|
|
|
|
@Override
|
|
public boolean noSave() {
|
|
return this.noSave;
|
|
}
|
|
|
|
public void checkSession() throws LevelConflictException {
|
|
this.levelStorage.checkSession();
|
|
}
|
|
|
|
public LevelStorage getLevelStorage() {
|
|
return this.levelStorage;
|
|
}
|
|
|
|
public DimensionDataStorage getDataStorage() {
|
|
return this.getChunkSource().getDataStorage();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public MapItemSavedData getMapData(final String string) {
|
|
return this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().<MapItemSavedData>get(() -> new MapItemSavedData(string), string);
|
|
}
|
|
|
|
@Override
|
|
public void setMapData(final MapItemSavedData crd) {
|
|
this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().set(crd);
|
|
}
|
|
|
|
@Override
|
|
public int getFreeMapId() {
|
|
return this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().<MapIndex>computeIfAbsent(MapIndex::new, "idcounts").getFreeAuxValueForMap();
|
|
}
|
|
|
|
@Override
|
|
public void setSpawnPos(final BlockPos fk) {
|
|
final ChunkPos bje3 = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
|
|
super.setSpawnPos(fk);
|
|
this.getChunkSource().<Unit>removeRegionTicket(TicketType.START, bje3, 11, Unit.INSTANCE);
|
|
this.getChunkSource().<Unit>addRegionTicket(TicketType.START, new ChunkPos(fk), 11, Unit.INSTANCE);
|
|
}
|
|
|
|
public LongSet getForcedChunks() {
|
|
final ForcedChunksSavedData bjo2 = this.getDataStorage().<ForcedChunksSavedData>get(ForcedChunksSavedData::new, "chunks");
|
|
return (LongSet)((bjo2 != null) ? LongSets.unmodifiable(bjo2.getChunks()) : LongSets.EMPTY_SET);
|
|
}
|
|
|
|
public boolean setChunkForced(final int integer1, final int integer2, final boolean boolean3) {
|
|
final ForcedChunksSavedData bjo5 = this.getDataStorage().<ForcedChunksSavedData>computeIfAbsent(ForcedChunksSavedData::new, "chunks");
|
|
final ChunkPos bje6 = new ChunkPos(integer1, integer2);
|
|
final long long7 = bje6.toLong();
|
|
boolean boolean4;
|
|
if (boolean3) {
|
|
boolean4 = bjo5.getChunks().add(long7);
|
|
if (boolean4) {
|
|
this.getChunk(integer1, integer2);
|
|
}
|
|
}
|
|
else {
|
|
boolean4 = bjo5.getChunks().remove(long7);
|
|
}
|
|
bjo5.setDirty(boolean4);
|
|
if (boolean4) {
|
|
this.getChunkSource().updateChunkForced(bje6, boolean3);
|
|
}
|
|
return boolean4;
|
|
}
|
|
|
|
@Override
|
|
public List<ServerPlayer> players() {
|
|
return this.players;
|
|
}
|
|
|
|
@Override
|
|
public void onBlockStateChange(final BlockPos fk, final BlockState byg2, final BlockState byg3) {
|
|
final Optional<PoiType> optional5 = PoiType.forState(byg2);
|
|
final Optional<PoiType> optional6 = PoiType.forState(byg3);
|
|
if (Objects.equals(optional5, optional6)) {
|
|
return;
|
|
}
|
|
final BlockPos fk2 = fk.immutable();
|
|
final BlockPos blockPos;
|
|
optional5.ifPresent(asr -> this.getServer().execute(() -> {
|
|
this.getPoiManager().remove(blockPos);
|
|
DebugPackets.sendPoiRemovedPacket(this, blockPos);
|
|
}));
|
|
final BlockPos blockPos2;
|
|
optional6.ifPresent(asr -> this.getServer().execute(() -> {
|
|
this.getPoiManager().add(blockPos2, asr);
|
|
DebugPackets.sendPoiAddedPacket(this, blockPos2);
|
|
}));
|
|
}
|
|
|
|
public PoiManager getPoiManager() {
|
|
return this.getChunkSource().getPoiManager();
|
|
}
|
|
|
|
public boolean isVillage(final BlockPos fk) {
|
|
return this.closeToVillage(fk, 1);
|
|
}
|
|
|
|
public boolean isVillage(final SectionPos gd) {
|
|
return this.isVillage(gd.center());
|
|
}
|
|
|
|
public boolean closeToVillage(final BlockPos fk, final int integer) {
|
|
return integer <= 6 && this.sectionsToVillage(SectionPos.of(fk)) <= integer;
|
|
}
|
|
|
|
public int sectionsToVillage(final SectionPos gd) {
|
|
return this.getPoiManager().sectionsToVillage(gd);
|
|
}
|
|
|
|
public Raids getRaids() {
|
|
return this.raids;
|
|
}
|
|
|
|
@Nullable
|
|
public Raid getRaidAt(final BlockPos fk) {
|
|
return this.raids.getNearbyRaid(fk, 9216);
|
|
}
|
|
|
|
public boolean isRaided(final BlockPos fk) {
|
|
return this.getRaidAt(fk) != null;
|
|
}
|
|
|
|
public void onReputationEvent(final ReputationEventType asl, final Entity akn, final ReputationEventHandler ali) {
|
|
ali.onReputationEventFrom(asl, akn);
|
|
}
|
|
|
|
public void saveDebugReport(final Path path) throws IOException {
|
|
final ChunkMap wp3 = this.getChunkSource().chunkMap;
|
|
try (final Writer writer4 = Files.newBufferedWriter(path.resolve("stats.txt"))) {
|
|
writer4.write(String.format("spawning_chunks: %d\n", wp3.getDistanceManager().getNaturalSpawnChunkCount()));
|
|
for (final Object2IntMap.Entry<MobCategory> entry7 : this.getMobCategoryCounts().object2IntEntrySet()) {
|
|
writer4.write(String.format("spawn_count.%s: %d\n", ((MobCategory)entry7.getKey()).getName(), entry7.getIntValue()));
|
|
}
|
|
writer4.write(String.format("entities: %d\n", this.entitiesById.size()));
|
|
writer4.write(String.format("block_entities: %d\n", this.blockEntityList.size()));
|
|
writer4.write(String.format("block_ticks: %d\n", this.getBlockTicks().size()));
|
|
writer4.write(String.format("fluid_ticks: %d\n", this.getLiquidTicks().size()));
|
|
writer4.write("distance_manager: " + wp3.getDistanceManager().getDebugStatus() + "\n");
|
|
writer4.write(String.format("pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
|
|
}
|
|
final CrashReport h4 = new CrashReport("Level dump", new Exception("dummy"));
|
|
this.fillReportDetails(h4);
|
|
try (final Writer writer5 = Files.newBufferedWriter(path.resolve("example_crash.txt"))) {
|
|
writer5.write(h4.getFriendlyReport());
|
|
}
|
|
final Path path2 = path.resolve("chunks.csv");
|
|
try (final Writer writer6 = Files.newBufferedWriter(path2)) {
|
|
wp3.dumpChunks(writer6);
|
|
}
|
|
final Path path3 = path.resolve("entities.csv");
|
|
try (final Writer writer7 = Files.newBufferedWriter(path3)) {
|
|
dumpEntities(writer7, (Iterable<Entity>)this.entitiesById.values());
|
|
}
|
|
final Path path4 = path.resolve("global_entities.csv");
|
|
try (final Writer writer8 = Files.newBufferedWriter(path4)) {
|
|
dumpEntities(writer8, this.globalEntities);
|
|
}
|
|
final Path path5 = path.resolve("block_entities.csv");
|
|
try (final Writer writer9 = Files.newBufferedWriter(path5)) {
|
|
this.dumpBlockEntities(writer9);
|
|
}
|
|
}
|
|
|
|
private static void dumpEntities(final Writer writer, final Iterable<Entity> iterable) throws IOException {
|
|
final CsvOutput abh3 = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("uuid").addColumn("type").addColumn("alive").addColumn("display_name").addColumn("custom_name").build(writer);
|
|
for (final Entity akn5 : iterable) {
|
|
final Component lf6 = akn5.getCustomName();
|
|
final Component lf7 = akn5.getDisplayName();
|
|
abh3.writeRow(akn5.getX(), akn5.getY(), akn5.getZ(), akn5.getUUID(), Registry.ENTITY_TYPE.getKey(akn5.getType()), akn5.isAlive(), lf7.getString(), (lf6 != null) ? lf6.getString() : null);
|
|
}
|
|
}
|
|
|
|
private void dumpBlockEntities(final Writer writer) throws IOException {
|
|
final CsvOutput abh3 = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(writer);
|
|
for (final BlockEntity bwi5 : this.blockEntityList) {
|
|
final BlockPos fk6 = bwi5.getBlockPos();
|
|
abh3.writeRow(fk6.getX(), fk6.getY(), fk6.getZ(), Registry.BLOCK_ENTITY_TYPE.getKey(bwi5.getType()));
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void clearBlockEvents(final BoundingBox cky) {
|
|
this.blockEvents.removeIf(bjc -> cky.isInside(bjc.getPos()));
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
}
|
|
}
|