1776 lines
70 KiB
Java
1776 lines
70 KiB
Java
package net.minecraft.server.level;
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.datafixers.DataFixer;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import com.mojang.logging.LogUtils;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
|
|
import java.io.IOException;
|
|
import java.io.Writer;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.function.BooleanSupplier;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportType;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.HolderSet;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundDamageEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
|
|
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
|
import net.minecraft.network.protocol.game.DebugPackets;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.server.ServerScoreboard;
|
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
import net.minecraft.server.players.SleepStatus;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.tags.TagKey;
|
|
import net.minecraft.util.AbortableIterationConsumer;
|
|
import net.minecraft.util.CsvOutput;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.ProgressListener;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.datafix.DataFixTypes;
|
|
import net.minecraft.util.profiling.Profiler;
|
|
import net.minecraft.util.profiling.ProfilerFiller;
|
|
import net.minecraft.util.valueproviders.IntProvider;
|
|
import net.minecraft.util.valueproviders.UniformInt;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.RandomSequences;
|
|
import net.minecraft.world.TickRateManager;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LightningBolt;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.MobCategory;
|
|
import net.minecraft.world.entity.ReputationEventHandler;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.entity.ai.village.ReputationEventType;
|
|
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
|
import net.minecraft.world.entity.ai.village.poi.PoiType;
|
|
import net.minecraft.world.entity.ai.village.poi.PoiTypes;
|
|
import net.minecraft.world.entity.animal.horse.SkeletonHorse;
|
|
import net.minecraft.world.entity.boss.EnderDragonPart;
|
|
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.raid.Raid;
|
|
import net.minecraft.world.entity.raid.Raids;
|
|
import net.minecraft.world.flag.FeatureFlagSet;
|
|
import net.minecraft.world.item.alchemy.PotionBrewing;
|
|
import net.minecraft.world.item.crafting.RecipeManager;
|
|
import net.minecraft.world.level.BlockEventData;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.CustomSpawner;
|
|
import net.minecraft.world.level.Explosion;
|
|
import net.minecraft.world.level.ExplosionDamageCalculator;
|
|
import net.minecraft.world.level.GameRules;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.NaturalSpawner;
|
|
import net.minecraft.world.level.ServerExplosion;
|
|
import net.minecraft.world.level.StructureManager;
|
|
import net.minecraft.world.level.WorldGenLevel;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.SnowLayerBlock;
|
|
import net.minecraft.world.level.block.entity.FuelValues;
|
|
import net.minecraft.world.level.block.entity.TickingBlockEntity;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
import net.minecraft.world.level.chunk.storage.EntityStorage;
|
|
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
|
import net.minecraft.world.level.chunk.storage.SimpleRegionStorage;
|
|
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
|
|
import net.minecraft.world.level.dimension.LevelStem;
|
|
import net.minecraft.world.level.dimension.end.EndDragonFight;
|
|
import net.minecraft.world.level.entity.EntityPersistentStorage;
|
|
import net.minecraft.world.level.entity.EntityTickList;
|
|
import net.minecraft.world.level.entity.EntityTypeTest;
|
|
import net.minecraft.world.level.entity.LevelCallback;
|
|
import net.minecraft.world.level.entity.LevelEntityGetter;
|
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
|
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
import net.minecraft.world.level.material.Fluid;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.pathfinder.PathTypeCache;
|
|
import net.minecraft.world.level.portal.PortalForcer;
|
|
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
|
|
import net.minecraft.world.level.redstone.Orientation;
|
|
import net.minecraft.world.level.saveddata.maps.MapId;
|
|
import net.minecraft.world.level.saveddata.maps.MapIndex;
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
import net.minecraft.world.level.storage.ServerLevelData;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import net.minecraft.world.ticks.LevelTicks;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLevel {
|
|
public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0);
|
|
public static final IntProvider RAIN_DELAY = UniformInt.of(12000, 180000);
|
|
public static final IntProvider RAIN_DURATION = UniformInt.of(12000, 24000);
|
|
private static final IntProvider THUNDER_DELAY = UniformInt.of(12000, 180000);
|
|
public static final IntProvider THUNDER_DURATION = UniformInt.of(3600, 15600);
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final int EMPTY_TIME_NO_TICK = 300;
|
|
private static final int MAX_SCHEDULED_TICKS_PER_TICK = 65536;
|
|
final List<ServerPlayer> players = Lists.newArrayList();
|
|
private final ServerChunkCache chunkSource;
|
|
private final MinecraftServer server;
|
|
private final ServerLevelData serverLevelData;
|
|
private int lastSpawnChunkRadius;
|
|
final EntityTickList entityTickList = new EntityTickList();
|
|
private final PersistentEntitySectionManager<Entity> entityManager;
|
|
private final GameEventDispatcher gameEventDispatcher;
|
|
public boolean noSave;
|
|
private final SleepStatus sleepStatus;
|
|
private int emptyTime;
|
|
private final PortalForcer portalForcer;
|
|
private final LevelTicks<Block> blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
|
|
private final LevelTicks<Fluid> fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
|
|
private final PathTypeCache pathTypesByPosCache = new PathTypeCache();
|
|
final Set<Mob> navigatingMobs = new ObjectOpenHashSet<>();
|
|
volatile boolean isUpdatingNavigations;
|
|
protected final Raids raids;
|
|
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
|
|
private final List<BlockEventData> blockEventsToReschedule = new ArrayList<>(64);
|
|
private boolean handlingTick;
|
|
private final List<CustomSpawner> customSpawners;
|
|
@Nullable
|
|
private EndDragonFight dragonFight;
|
|
final Int2ObjectMap<EnderDragonPart> dragonParts = new Int2ObjectOpenHashMap<>();
|
|
private final StructureManager structureManager;
|
|
private final StructureCheck structureCheck;
|
|
private final boolean tickTime;
|
|
private final RandomSequences randomSequences;
|
|
|
|
public ServerLevel(
|
|
MinecraftServer p_214999_,
|
|
Executor p_215000_,
|
|
LevelStorageSource.LevelStorageAccess p_215001_,
|
|
ServerLevelData p_215002_,
|
|
ResourceKey<Level> p_215003_,
|
|
LevelStem p_215004_,
|
|
ChunkProgressListener p_215005_,
|
|
boolean p_215006_,
|
|
long p_215007_,
|
|
List<CustomSpawner> p_215008_,
|
|
boolean p_215009_,
|
|
@Nullable RandomSequences p_288977_
|
|
) {
|
|
super(p_215002_, p_215003_, p_214999_.registryAccess(), p_215004_.type(), false, p_215006_, p_215007_, p_214999_.getMaxChainedNeighborUpdates());
|
|
this.tickTime = p_215009_;
|
|
this.server = p_214999_;
|
|
this.customSpawners = p_215008_;
|
|
this.serverLevelData = p_215002_;
|
|
ChunkGenerator chunkgenerator = p_215004_.generator();
|
|
boolean flag = p_214999_.forceSynchronousWrites();
|
|
DataFixer datafixer = p_214999_.getFixerUpper();
|
|
EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(
|
|
new SimpleRegionStorage(
|
|
new RegionStorageInfo(p_215001_.getLevelId(), p_215003_, "entities"),
|
|
p_215001_.getDimensionPath(p_215003_).resolve("entities"),
|
|
datafixer,
|
|
flag,
|
|
DataFixTypes.ENTITY_CHUNK
|
|
),
|
|
this,
|
|
p_214999_
|
|
);
|
|
this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage);
|
|
this.chunkSource = new ServerChunkCache(
|
|
this,
|
|
p_215001_,
|
|
datafixer,
|
|
p_214999_.getStructureManager(),
|
|
p_215000_,
|
|
chunkgenerator,
|
|
p_214999_.getPlayerList().getViewDistance(),
|
|
p_214999_.getPlayerList().getSimulationDistance(),
|
|
flag,
|
|
p_215005_,
|
|
this.entityManager::updateChunkStatus,
|
|
() -> p_214999_.overworld().getDataStorage()
|
|
);
|
|
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
|
this.portalForcer = new PortalForcer(this);
|
|
this.updateSkyBrightness();
|
|
this.prepareWeather();
|
|
this.getWorldBorder().setAbsoluteMaxSize(p_214999_.getAbsoluteMaxWorldSize());
|
|
this.raids = this.getDataStorage().computeIfAbsent(Raids.getType(this.dimensionTypeRegistration()));
|
|
if (!p_214999_.isSingleplayer()) {
|
|
p_215002_.setGameType(p_214999_.getDefaultGameType());
|
|
}
|
|
|
|
long i = p_214999_.getWorldData().worldGenOptions().seed();
|
|
this.structureCheck = new StructureCheck(
|
|
this.chunkSource.chunkScanner(),
|
|
this.registryAccess(),
|
|
p_214999_.getStructureManager(),
|
|
p_215003_,
|
|
chunkgenerator,
|
|
this.chunkSource.randomState(),
|
|
this,
|
|
chunkgenerator.getBiomeSource(),
|
|
i,
|
|
datafixer
|
|
);
|
|
this.structureManager = new StructureManager(this, p_214999_.getWorldData().worldGenOptions(), this.structureCheck);
|
|
if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) {
|
|
this.dragonFight = new EndDragonFight(this, i, p_214999_.getWorldData().endDragonFightData());
|
|
} else {
|
|
this.dragonFight = null;
|
|
}
|
|
|
|
this.sleepStatus = new SleepStatus();
|
|
this.gameEventDispatcher = new GameEventDispatcher(this);
|
|
this.randomSequences = Objects.requireNonNullElseGet(p_288977_, () -> this.getDataStorage().computeIfAbsent(RandomSequences.TYPE));
|
|
}
|
|
|
|
@Deprecated
|
|
@VisibleForTesting
|
|
public void setDragonFight(@Nullable EndDragonFight p_287779_) {
|
|
this.dragonFight = p_287779_;
|
|
}
|
|
|
|
public void setWeatherParameters(int p_8607_, int p_8608_, boolean p_8609_, boolean p_8610_) {
|
|
this.serverLevelData.setClearWeatherTime(p_8607_);
|
|
this.serverLevelData.setRainTime(p_8608_);
|
|
this.serverLevelData.setThunderTime(p_8608_);
|
|
this.serverLevelData.setRaining(p_8609_);
|
|
this.serverLevelData.setThundering(p_8610_);
|
|
}
|
|
|
|
@Override
|
|
public Holder<Biome> getUncachedNoiseBiome(int p_203775_, int p_203776_, int p_203777_) {
|
|
return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(p_203775_, p_203776_, p_203777_, this.getChunkSource().randomState().sampler());
|
|
}
|
|
|
|
public StructureManager structureManager() {
|
|
return this.structureManager;
|
|
}
|
|
|
|
public void tick(BooleanSupplier p_8794_) {
|
|
ProfilerFiller profilerfiller = Profiler.get();
|
|
this.handlingTick = true;
|
|
TickRateManager tickratemanager = this.tickRateManager();
|
|
boolean flag = tickratemanager.runsNormally();
|
|
if (flag) {
|
|
profilerfiller.push("world border");
|
|
this.getWorldBorder().tick();
|
|
profilerfiller.popPush("weather");
|
|
this.advanceWeatherCycle();
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) {
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
long j = this.levelData.getDayTime() + 24000L;
|
|
this.setDayTime(j - j % 24000L);
|
|
}
|
|
|
|
this.wakeUpAllPlayers();
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE) && this.isRaining()) {
|
|
this.resetWeatherCycle();
|
|
}
|
|
}
|
|
|
|
this.updateSkyBrightness();
|
|
if (flag) {
|
|
this.tickTime();
|
|
}
|
|
|
|
profilerfiller.push("tickPending");
|
|
if (!this.isDebug() && flag) {
|
|
long k = this.getGameTime();
|
|
profilerfiller.push("blockTicks");
|
|
this.blockTicks.tick(k, 65536, this::tickBlock);
|
|
profilerfiller.popPush("fluidTicks");
|
|
this.fluidTicks.tick(k, 65536, this::tickFluid);
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
profilerfiller.popPush("raid");
|
|
if (flag) {
|
|
this.raids.tick(this);
|
|
}
|
|
|
|
profilerfiller.popPush("chunkSource");
|
|
this.getChunkSource().tick(p_8794_, true);
|
|
profilerfiller.popPush("blockEvents");
|
|
if (flag) {
|
|
this.runBlockEvents();
|
|
}
|
|
|
|
this.handlingTick = false;
|
|
profilerfiller.pop();
|
|
boolean flag1 = !this.players.isEmpty() || !this.getForceLoadedChunks().isEmpty();
|
|
if (flag1) {
|
|
this.resetEmptyTime();
|
|
}
|
|
|
|
if (flag1 || this.emptyTime++ < 300) {
|
|
profilerfiller.push("entities");
|
|
if (this.dragonFight != null && flag) {
|
|
profilerfiller.push("dragonFight");
|
|
this.dragonFight.tick();
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
this.entityTickList.forEach(p_358707_ -> {
|
|
if (!p_358707_.isRemoved()) {
|
|
if (!tickratemanager.isEntityFrozen(p_358707_)) {
|
|
profilerfiller.push("checkDespawn");
|
|
p_358707_.checkDespawn();
|
|
profilerfiller.pop();
|
|
if (p_358707_ instanceof ServerPlayer || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(p_358707_.chunkPosition().toLong())) {
|
|
Entity entity = p_358707_.getVehicle();
|
|
if (entity != null) {
|
|
if (!entity.isRemoved() && entity.hasPassenger(p_358707_)) {
|
|
return;
|
|
}
|
|
|
|
p_358707_.stopRiding();
|
|
}
|
|
|
|
profilerfiller.push("tick");
|
|
this.guardEntityTick(this::tickNonPassenger, p_358707_);
|
|
profilerfiller.pop();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
profilerfiller.pop();
|
|
this.tickBlockEntities();
|
|
}
|
|
|
|
profilerfiller.push("entityManagement");
|
|
this.entityManager.tick();
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldTickBlocksAt(long p_184059_) {
|
|
return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(p_184059_);
|
|
}
|
|
|
|
protected void tickTime() {
|
|
if (this.tickTime) {
|
|
long i = this.levelData.getGameTime() + 1L;
|
|
this.serverLevelData.setGameTime(i);
|
|
Profiler.get().push("scheduledFunctions");
|
|
this.serverLevelData.getScheduledEvents().tick(this.server, i);
|
|
Profiler.get().pop();
|
|
if (this.serverLevelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
|
this.setDayTime(this.levelData.getDayTime() + 1L);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setDayTime(long p_8616_) {
|
|
this.serverLevelData.setDayTime(p_8616_);
|
|
}
|
|
|
|
public void tickCustomSpawners(boolean p_8800_, boolean p_8801_) {
|
|
for (CustomSpawner customspawner : this.customSpawners) {
|
|
customspawner.tick(this, p_8800_, p_8801_);
|
|
}
|
|
}
|
|
|
|
private void wakeUpAllPlayers() {
|
|
this.sleepStatus.removeAllSleepers();
|
|
this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(p_184116_ -> p_184116_.stopSleepInBed(false, false));
|
|
}
|
|
|
|
public void tickChunk(LevelChunk p_8715_, int p_8716_) {
|
|
ChunkPos chunkpos = p_8715_.getPos();
|
|
int i = chunkpos.getMinBlockX();
|
|
int j = chunkpos.getMinBlockZ();
|
|
ProfilerFiller profilerfiller = Profiler.get();
|
|
profilerfiller.push("iceandsnow");
|
|
|
|
for (int k = 0; k < p_8716_; k++) {
|
|
if (this.random.nextInt(48) == 0) {
|
|
this.tickPrecipitation(this.getBlockRandomPos(i, 0, j, 15));
|
|
}
|
|
}
|
|
|
|
profilerfiller.popPush("tickBlocks");
|
|
if (p_8716_ > 0) {
|
|
LevelChunkSection[] alevelchunksection = p_8715_.getSections();
|
|
|
|
for (int l = 0; l < alevelchunksection.length; l++) {
|
|
LevelChunkSection levelchunksection = alevelchunksection[l];
|
|
if (levelchunksection.isRandomlyTicking()) {
|
|
int i1 = p_8715_.getSectionYFromSectionIndex(l);
|
|
int j1 = SectionPos.sectionToBlockCoord(i1);
|
|
|
|
for (int k1 = 0; k1 < p_8716_; k1++) {
|
|
BlockPos blockpos = this.getBlockRandomPos(i, j1, j, 15);
|
|
profilerfiller.push("randomTick");
|
|
BlockState blockstate = levelchunksection.getBlockState(blockpos.getX() - i, blockpos.getY() - j1, blockpos.getZ() - j);
|
|
if (blockstate.isRandomlyTicking()) {
|
|
blockstate.randomTick(this, blockpos, this.random);
|
|
}
|
|
|
|
FluidState fluidstate = blockstate.getFluidState();
|
|
if (fluidstate.isRandomlyTicking()) {
|
|
fluidstate.randomTick(this, blockpos, this.random);
|
|
}
|
|
|
|
profilerfiller.pop();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
public void tickThunder(LevelChunk p_394632_) {
|
|
ChunkPos chunkpos = p_394632_.getPos();
|
|
boolean flag = this.isRaining();
|
|
int i = chunkpos.getMinBlockX();
|
|
int j = chunkpos.getMinBlockZ();
|
|
ProfilerFiller profilerfiller = Profiler.get();
|
|
profilerfiller.push("thunder");
|
|
if (flag && this.isThundering() && this.random.nextInt(100000) == 0) {
|
|
BlockPos blockpos = this.findLightningTargetAround(this.getBlockRandomPos(i, 0, j, 15));
|
|
if (this.isRainingAt(blockpos)) {
|
|
DifficultyInstance difficultyinstance = this.getCurrentDifficultyAt(blockpos);
|
|
boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)
|
|
&& this.random.nextDouble() < difficultyinstance.getEffectiveDifficulty() * 0.01
|
|
&& !this.getBlockState(blockpos.below()).is(Blocks.LIGHTNING_ROD);
|
|
if (flag1) {
|
|
SkeletonHorse skeletonhorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
|
|
if (skeletonhorse != null) {
|
|
skeletonhorse.setTrap(true);
|
|
skeletonhorse.setAge(0);
|
|
skeletonhorse.setPos(blockpos.getX(), blockpos.getY(), blockpos.getZ());
|
|
this.addFreshEntity(skeletonhorse);
|
|
}
|
|
}
|
|
|
|
LightningBolt lightningbolt = EntityType.LIGHTNING_BOLT.create(this, EntitySpawnReason.EVENT);
|
|
if (lightningbolt != null) {
|
|
lightningbolt.snapTo(Vec3.atBottomCenterOf(blockpos));
|
|
lightningbolt.setVisualOnly(flag1);
|
|
this.addFreshEntity(lightningbolt);
|
|
}
|
|
}
|
|
}
|
|
|
|
profilerfiller.pop();
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void tickPrecipitation(BlockPos p_300602_) {
|
|
BlockPos blockpos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, p_300602_);
|
|
BlockPos blockpos1 = blockpos.below();
|
|
Biome biome = this.getBiome(blockpos).value();
|
|
if (biome.shouldFreeze(this, blockpos1)) {
|
|
this.setBlockAndUpdate(blockpos1, Blocks.ICE.defaultBlockState());
|
|
}
|
|
|
|
if (this.isRaining()) {
|
|
int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT);
|
|
if (i > 0 && biome.shouldSnow(this, blockpos)) {
|
|
BlockState blockstate = this.getBlockState(blockpos);
|
|
if (blockstate.is(Blocks.SNOW)) {
|
|
int j = blockstate.getValue(SnowLayerBlock.LAYERS);
|
|
if (j < Math.min(i, 8)) {
|
|
BlockState blockstate1 = blockstate.setValue(SnowLayerBlock.LAYERS, j + 1);
|
|
Block.pushEntitiesUp(blockstate, blockstate1, this, blockpos);
|
|
this.setBlockAndUpdate(blockpos, blockstate1);
|
|
}
|
|
} else {
|
|
this.setBlockAndUpdate(blockpos, Blocks.SNOW.defaultBlockState());
|
|
}
|
|
}
|
|
|
|
Biome.Precipitation biome$precipitation = biome.getPrecipitationAt(blockpos1, this.getSeaLevel());
|
|
if (biome$precipitation != Biome.Precipitation.NONE) {
|
|
BlockState blockstate2 = this.getBlockState(blockpos1);
|
|
blockstate2.getBlock().handlePrecipitation(blockstate2, this, blockpos1, biome$precipitation);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Optional<BlockPos> findLightningRod(BlockPos p_143249_) {
|
|
Optional<BlockPos> optional = this.getPoiManager()
|
|
.findClosest(
|
|
p_215059_ -> p_215059_.is(PoiTypes.LIGHTNING_ROD),
|
|
p_184055_ -> p_184055_.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, p_184055_.getX(), p_184055_.getZ()) - 1,
|
|
p_143249_,
|
|
128,
|
|
PoiManager.Occupancy.ANY
|
|
);
|
|
return optional.map(p_184053_ -> p_184053_.above(1));
|
|
}
|
|
|
|
protected BlockPos findLightningTargetAround(BlockPos p_143289_) {
|
|
BlockPos blockpos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, p_143289_);
|
|
Optional<BlockPos> optional = this.findLightningRod(blockpos);
|
|
if (optional.isPresent()) {
|
|
return optional.get();
|
|
} else {
|
|
AABB aabb = AABB.encapsulatingFullBlocks(blockpos, blockpos.atY(this.getMaxY() + 1)).inflate(3.0);
|
|
List<LivingEntity> list = this.getEntitiesOfClass(
|
|
LivingEntity.class, aabb, p_374883_ -> p_374883_ != null && p_374883_.isAlive() && this.canSeeSky(p_374883_.blockPosition())
|
|
);
|
|
if (!list.isEmpty()) {
|
|
return list.get(this.random.nextInt(list.size())).blockPosition();
|
|
} else {
|
|
if (blockpos.getY() == this.getMinY() - 1) {
|
|
blockpos = blockpos.above(2);
|
|
}
|
|
|
|
return blockpos;
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isHandlingTick() {
|
|
return this.handlingTick;
|
|
}
|
|
|
|
public boolean canSleepThroughNights() {
|
|
return this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE) <= 100;
|
|
}
|
|
|
|
private void announceSleepStatus() {
|
|
if (this.canSleepThroughNights()) {
|
|
if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
|
|
int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
|
|
Component component;
|
|
if (this.sleepStatus.areEnoughSleeping(i)) {
|
|
component = Component.translatable("sleep.skipping_night");
|
|
} else {
|
|
component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i));
|
|
}
|
|
|
|
for (ServerPlayer serverplayer : this.players) {
|
|
serverplayer.displayClientMessage(component, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateSleepingPlayerList() {
|
|
if (!this.players.isEmpty() && this.sleepStatus.update(this.players)) {
|
|
this.announceSleepStatus();
|
|
}
|
|
}
|
|
|
|
public ServerScoreboard getScoreboard() {
|
|
return this.server.getScoreboard();
|
|
}
|
|
|
|
private void advanceWeatherCycle() {
|
|
boolean flag = this.isRaining();
|
|
if (this.dimensionType().hasSkyLight()) {
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
|
|
int i = this.serverLevelData.getClearWeatherTime();
|
|
int j = this.serverLevelData.getThunderTime();
|
|
int k = this.serverLevelData.getRainTime();
|
|
boolean flag1 = this.levelData.isThundering();
|
|
boolean flag2 = this.levelData.isRaining();
|
|
if (i > 0) {
|
|
i--;
|
|
j = flag1 ? 0 : 1;
|
|
k = flag2 ? 0 : 1;
|
|
flag1 = false;
|
|
flag2 = false;
|
|
} else {
|
|
if (j > 0) {
|
|
if (--j == 0) {
|
|
flag1 = !flag1;
|
|
}
|
|
} else if (flag1) {
|
|
j = THUNDER_DURATION.sample(this.random);
|
|
} else {
|
|
j = THUNDER_DELAY.sample(this.random);
|
|
}
|
|
|
|
if (k > 0) {
|
|
if (--k == 0) {
|
|
flag2 = !flag2;
|
|
}
|
|
} else if (flag2) {
|
|
k = RAIN_DURATION.sample(this.random);
|
|
} else {
|
|
k = RAIN_DELAY.sample(this.random);
|
|
}
|
|
}
|
|
|
|
this.serverLevelData.setThunderTime(j);
|
|
this.serverLevelData.setRainTime(k);
|
|
this.serverLevelData.setClearWeatherTime(i);
|
|
this.serverLevelData.setThundering(flag1);
|
|
this.serverLevelData.setRaining(flag2);
|
|
}
|
|
|
|
this.oThunderLevel = this.thunderLevel;
|
|
if (this.levelData.isThundering()) {
|
|
this.thunderLevel += 0.01F;
|
|
} else {
|
|
this.thunderLevel -= 0.01F;
|
|
}
|
|
|
|
this.thunderLevel = Mth.clamp(this.thunderLevel, 0.0F, 1.0F);
|
|
this.oRainLevel = this.rainLevel;
|
|
if (this.levelData.isRaining()) {
|
|
this.rainLevel += 0.01F;
|
|
} else {
|
|
this.rainLevel -= 0.01F;
|
|
}
|
|
|
|
this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F);
|
|
}
|
|
|
|
if (this.oRainLevel != this.rainLevel) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension());
|
|
}
|
|
|
|
if (this.oThunderLevel != this.thunderLevel) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension());
|
|
}
|
|
|
|
if (flag != this.isRaining()) {
|
|
if (flag) {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0F));
|
|
} else {
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
|
|
}
|
|
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel));
|
|
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel));
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void resetWeatherCycle() {
|
|
this.serverLevelData.setRainTime(0);
|
|
this.serverLevelData.setRaining(false);
|
|
this.serverLevelData.setThunderTime(0);
|
|
this.serverLevelData.setThundering(false);
|
|
}
|
|
|
|
public void resetEmptyTime() {
|
|
this.emptyTime = 0;
|
|
}
|
|
|
|
private void tickFluid(BlockPos p_184077_, Fluid p_184078_) {
|
|
BlockState blockstate = this.getBlockState(p_184077_);
|
|
FluidState fluidstate = blockstate.getFluidState();
|
|
if (fluidstate.is(p_184078_)) {
|
|
fluidstate.tick(this, p_184077_, blockstate);
|
|
}
|
|
}
|
|
|
|
private void tickBlock(BlockPos p_184113_, Block p_184114_) {
|
|
BlockState blockstate = this.getBlockState(p_184113_);
|
|
if (blockstate.is(p_184114_)) {
|
|
blockstate.tick(this, p_184113_, this.random);
|
|
}
|
|
}
|
|
|
|
public void tickNonPassenger(Entity p_8648_) {
|
|
p_8648_.setOldPosAndRot();
|
|
ProfilerFiller profilerfiller = Profiler.get();
|
|
p_8648_.tickCount++;
|
|
profilerfiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_8648_.getType()).toString());
|
|
profilerfiller.incrementCounter("tickNonPassenger");
|
|
p_8648_.tick();
|
|
profilerfiller.pop();
|
|
|
|
for (Entity entity : p_8648_.getPassengers()) {
|
|
this.tickPassenger(p_8648_, entity);
|
|
}
|
|
}
|
|
|
|
private void tickPassenger(Entity p_8663_, Entity p_8664_) {
|
|
if (p_8664_.isRemoved() || p_8664_.getVehicle() != p_8663_) {
|
|
p_8664_.stopRiding();
|
|
} else if (p_8664_ instanceof Player || this.entityTickList.contains(p_8664_)) {
|
|
p_8664_.setOldPosAndRot();
|
|
p_8664_.tickCount++;
|
|
ProfilerFiller profilerfiller = Profiler.get();
|
|
profilerfiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_8664_.getType()).toString());
|
|
profilerfiller.incrementCounter("tickPassenger");
|
|
p_8664_.rideTick();
|
|
profilerfiller.pop();
|
|
|
|
for (Entity entity : p_8664_.getPassengers()) {
|
|
this.tickPassenger(p_8664_, entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean mayInteract(Entity p_396695_, BlockPos p_8697_) {
|
|
return !(p_396695_ instanceof Player player && (this.server.isUnderSpawnProtection(this, p_8697_, player) || !this.getWorldBorder().isWithinBounds(p_8697_)));
|
|
}
|
|
|
|
public void save(@Nullable ProgressListener p_8644_, boolean p_8645_, boolean p_8646_) {
|
|
ServerChunkCache serverchunkcache = this.getChunkSource();
|
|
if (!p_8646_) {
|
|
if (p_8644_ != null) {
|
|
p_8644_.progressStartNoAbort(Component.translatable("menu.savingLevel"));
|
|
}
|
|
|
|
this.saveLevelData(p_8645_);
|
|
if (p_8644_ != null) {
|
|
p_8644_.progressStage(Component.translatable("menu.savingChunks"));
|
|
}
|
|
|
|
serverchunkcache.save(p_8645_);
|
|
if (p_8645_) {
|
|
this.entityManager.saveAll();
|
|
} else {
|
|
this.entityManager.autoSave();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveLevelData(boolean p_365704_) {
|
|
if (this.dragonFight != null) {
|
|
this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData());
|
|
}
|
|
|
|
DimensionDataStorage dimensiondatastorage = this.getChunkSource().getDataStorage();
|
|
if (p_365704_) {
|
|
dimensiondatastorage.saveAndJoin();
|
|
} else {
|
|
dimensiondatastorage.scheduleSave();
|
|
}
|
|
}
|
|
|
|
public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> p_143281_, Predicate<? super T> p_143282_) {
|
|
List<T> list = Lists.newArrayList();
|
|
this.getEntities(p_143281_, p_143282_, list);
|
|
return list;
|
|
}
|
|
|
|
public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> p_262152_, Predicate<? super T> p_261808_, List<? super T> p_261583_) {
|
|
this.getEntities(p_262152_, p_261808_, p_261583_, Integer.MAX_VALUE);
|
|
}
|
|
|
|
public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> p_261842_, Predicate<? super T> p_262091_, List<? super T> p_261703_, int p_261907_) {
|
|
this.getEntities().get(p_261842_, p_261428_ -> {
|
|
if (p_262091_.test(p_261428_)) {
|
|
p_261703_.add(p_261428_);
|
|
if (p_261703_.size() >= p_261907_) {
|
|
return AbortableIterationConsumer.Continuation.ABORT;
|
|
}
|
|
}
|
|
|
|
return AbortableIterationConsumer.Continuation.CONTINUE;
|
|
});
|
|
}
|
|
|
|
public List<? extends EnderDragon> getDragons() {
|
|
return this.getEntities(EntityType.ENDER_DRAGON, LivingEntity::isAlive);
|
|
}
|
|
|
|
public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> p_8796_) {
|
|
return this.getPlayers(p_8796_, Integer.MAX_VALUE);
|
|
}
|
|
|
|
public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> p_261698_, int p_262035_) {
|
|
List<ServerPlayer> list = Lists.newArrayList();
|
|
|
|
for (ServerPlayer serverplayer : this.players) {
|
|
if (p_261698_.test(serverplayer)) {
|
|
list.add(serverplayer);
|
|
if (list.size() >= p_262035_) {
|
|
return list;
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
@Nullable
|
|
public ServerPlayer getRandomPlayer() {
|
|
List<ServerPlayer> list = this.getPlayers(LivingEntity::isAlive);
|
|
return list.isEmpty() ? null : list.get(this.random.nextInt(list.size()));
|
|
}
|
|
|
|
@Override
|
|
public boolean addFreshEntity(Entity p_8837_) {
|
|
return this.addEntity(p_8837_);
|
|
}
|
|
|
|
public boolean addWithUUID(Entity p_8848_) {
|
|
return this.addEntity(p_8848_);
|
|
}
|
|
|
|
public void addDuringTeleport(Entity p_143335_) {
|
|
if (p_143335_ instanceof ServerPlayer serverplayer) {
|
|
this.addPlayer(serverplayer);
|
|
} else {
|
|
this.addEntity(p_143335_);
|
|
}
|
|
}
|
|
|
|
public void addNewPlayer(ServerPlayer p_8835_) {
|
|
this.addPlayer(p_8835_);
|
|
}
|
|
|
|
public void addRespawnedPlayer(ServerPlayer p_8846_) {
|
|
this.addPlayer(p_8846_);
|
|
}
|
|
|
|
private void addPlayer(ServerPlayer p_8854_) {
|
|
Entity entity = this.getEntity(p_8854_.getUUID());
|
|
if (entity != null) {
|
|
LOGGER.warn("Force-added player with duplicate UUID {}", p_8854_.getUUID());
|
|
entity.unRide();
|
|
this.removePlayerImmediately((ServerPlayer)entity, Entity.RemovalReason.DISCARDED);
|
|
}
|
|
|
|
this.entityManager.addNewEntity(p_8854_);
|
|
}
|
|
|
|
private boolean addEntity(Entity p_8873_) {
|
|
if (p_8873_.isRemoved()) {
|
|
LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(p_8873_.getType()));
|
|
return false;
|
|
} else {
|
|
return this.entityManager.addNewEntity(p_8873_);
|
|
}
|
|
}
|
|
|
|
public boolean tryAddFreshEntityWithPassengers(Entity p_8861_) {
|
|
if (p_8861_.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) {
|
|
return false;
|
|
} else {
|
|
this.addFreshEntityWithPassengers(p_8861_);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void unload(LevelChunk p_8713_) {
|
|
p_8713_.clearAllBlockEntities();
|
|
p_8713_.unregisterTickContainerFromLevel(this);
|
|
}
|
|
|
|
public void removePlayerImmediately(ServerPlayer p_143262_, Entity.RemovalReason p_143263_) {
|
|
p_143262_.remove(p_143263_);
|
|
}
|
|
|
|
@Override
|
|
public void destroyBlockProgress(int p_8612_, BlockPos p_8613_, int p_8614_) {
|
|
for (ServerPlayer serverplayer : this.server.getPlayerList().getPlayers()) {
|
|
if (serverplayer != null && serverplayer.level() == this && serverplayer.getId() != p_8612_) {
|
|
double d0 = p_8613_.getX() - serverplayer.getX();
|
|
double d1 = p_8613_.getY() - serverplayer.getY();
|
|
double d2 = p_8613_.getZ() - serverplayer.getZ();
|
|
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0) {
|
|
serverplayer.connection.send(new ClientboundBlockDestructionPacket(p_8612_, p_8613_, p_8614_));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playSeededSound(
|
|
@Nullable Entity p_263544_,
|
|
double p_393402_,
|
|
double p_397340_,
|
|
double p_394643_,
|
|
Holder<SoundEvent> p_263491_,
|
|
SoundSource p_263542_,
|
|
float p_263530_,
|
|
float p_263520_,
|
|
long p_263490_
|
|
) {
|
|
this.server
|
|
.getPlayerList()
|
|
.broadcast(
|
|
p_263544_ instanceof Player player ? player : null,
|
|
p_393402_,
|
|
p_397340_,
|
|
p_394643_,
|
|
p_263491_.value().getRange(p_263530_),
|
|
this.dimension(),
|
|
new ClientboundSoundPacket(p_263491_, p_263542_, p_393402_, p_397340_, p_394643_, p_263530_, p_263520_, p_263490_)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public void playSeededSound(
|
|
@Nullable Entity p_392657_, Entity p_394285_, Holder<SoundEvent> p_263412_, SoundSource p_263338_, float p_263352_, float p_263390_, long p_263403_
|
|
) {
|
|
this.server
|
|
.getPlayerList()
|
|
.broadcast(
|
|
p_392657_ instanceof Player player ? player : null,
|
|
p_394285_.getX(),
|
|
p_394285_.getY(),
|
|
p_394285_.getZ(),
|
|
p_263412_.value().getRange(p_263352_),
|
|
this.dimension(),
|
|
new ClientboundSoundEntityPacket(p_263412_, p_263338_, p_394285_, p_263352_, p_263390_, p_263403_)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public void globalLevelEvent(int p_8811_, BlockPos p_8812_, int p_8813_) {
|
|
if (this.getGameRules().getBoolean(GameRules.RULE_GLOBAL_SOUND_EVENTS)) {
|
|
this.server.getPlayerList().getPlayers().forEach(p_358704_ -> {
|
|
Vec3 vec3;
|
|
if (p_358704_.level() == this) {
|
|
Vec3 vec31 = Vec3.atCenterOf(p_8812_);
|
|
if (p_358704_.distanceToSqr(vec31) < Mth.square(32)) {
|
|
vec3 = vec31;
|
|
} else {
|
|
Vec3 vec32 = vec31.subtract(p_358704_.position()).normalize();
|
|
vec3 = p_358704_.position().add(vec32.scale(32.0));
|
|
}
|
|
} else {
|
|
vec3 = p_358704_.position();
|
|
}
|
|
|
|
p_358704_.connection.send(new ClientboundLevelEventPacket(p_8811_, BlockPos.containing(vec3), p_8813_, true));
|
|
});
|
|
} else {
|
|
this.levelEvent(null, p_8811_, p_8812_, p_8813_);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void levelEvent(@Nullable Entity p_392499_, int p_8685_, BlockPos p_8686_, int p_8687_) {
|
|
this.server
|
|
.getPlayerList()
|
|
.broadcast(
|
|
p_392499_ instanceof Player player ? player : null,
|
|
p_8686_.getX(),
|
|
p_8686_.getY(),
|
|
p_8686_.getZ(),
|
|
64.0,
|
|
this.dimension(),
|
|
new ClientboundLevelEventPacket(p_8685_, p_8686_, p_8687_, false)
|
|
);
|
|
}
|
|
|
|
public int getLogicalHeight() {
|
|
return this.dimensionType().logicalHeight();
|
|
}
|
|
|
|
@Override
|
|
public void gameEvent(Holder<GameEvent> p_334475_, Vec3 p_215042_, GameEvent.Context p_215043_) {
|
|
this.gameEventDispatcher.post(p_334475_, p_215042_, p_215043_);
|
|
}
|
|
|
|
@Override
|
|
public void sendBlockUpdated(BlockPos p_8755_, BlockState p_8756_, BlockState p_8757_, int p_8758_) {
|
|
if (this.isUpdatingNavigations) {
|
|
String s = "recursive call to sendBlockUpdated";
|
|
Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated"));
|
|
}
|
|
|
|
this.getChunkSource().blockChanged(p_8755_);
|
|
this.pathTypesByPosCache.invalidate(p_8755_);
|
|
VoxelShape voxelshape1 = p_8756_.getCollisionShape(this, p_8755_);
|
|
VoxelShape voxelshape = p_8757_.getCollisionShape(this, p_8755_);
|
|
if (Shapes.joinIsNotEmpty(voxelshape1, voxelshape, BooleanOp.NOT_SAME)) {
|
|
List<PathNavigation> list = new ObjectArrayList<>();
|
|
|
|
for (Mob mob : this.navigatingMobs) {
|
|
PathNavigation pathnavigation = mob.getNavigation();
|
|
if (pathnavigation.shouldRecomputePath(p_8755_)) {
|
|
list.add(pathnavigation);
|
|
}
|
|
}
|
|
|
|
try {
|
|
this.isUpdatingNavigations = true;
|
|
|
|
for (PathNavigation pathnavigation1 : list) {
|
|
pathnavigation1.recomputePath();
|
|
}
|
|
} finally {
|
|
this.isUpdatingNavigations = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void updateNeighborsAt(BlockPos p_215045_, Block p_215046_) {
|
|
this.updateNeighborsAt(p_215045_, p_215046_, ExperimentalRedstoneUtils.initialOrientation(this, null, null));
|
|
}
|
|
|
|
@Override
|
|
public void updateNeighborsAt(BlockPos p_362530_, Block p_362552_, @Nullable Orientation p_367766_) {
|
|
this.neighborUpdater.updateNeighborsAtExceptFromFacing(p_362530_, p_362552_, null, p_367766_);
|
|
}
|
|
|
|
@Override
|
|
public void updateNeighborsAtExceptFromFacing(BlockPos p_215052_, Block p_215053_, Direction p_215054_, @Nullable Orientation p_370072_) {
|
|
this.neighborUpdater.updateNeighborsAtExceptFromFacing(p_215052_, p_215053_, p_215054_, p_370072_);
|
|
}
|
|
|
|
@Override
|
|
public void neighborChanged(BlockPos p_215048_, Block p_215049_, @Nullable Orientation p_368434_) {
|
|
this.neighborUpdater.neighborChanged(p_215048_, p_215049_, p_368434_);
|
|
}
|
|
|
|
@Override
|
|
public void neighborChanged(BlockState p_215035_, BlockPos p_215036_, Block p_215037_, @Nullable Orientation p_369243_, boolean p_215039_) {
|
|
this.neighborUpdater.neighborChanged(p_215035_, p_215036_, p_215037_, p_369243_, p_215039_);
|
|
}
|
|
|
|
@Override
|
|
public void broadcastEntityEvent(Entity p_8650_, byte p_8651_) {
|
|
this.getChunkSource().broadcastAndSend(p_8650_, new ClientboundEntityEventPacket(p_8650_, p_8651_));
|
|
}
|
|
|
|
@Override
|
|
public void broadcastDamageEvent(Entity p_270420_, DamageSource p_270311_) {
|
|
this.getChunkSource().broadcastAndSend(p_270420_, new ClientboundDamageEventPacket(p_270420_, p_270311_));
|
|
}
|
|
|
|
public ServerChunkCache getChunkSource() {
|
|
return this.chunkSource;
|
|
}
|
|
|
|
@Override
|
|
public void explode(
|
|
@Nullable Entity p_256039_,
|
|
@Nullable DamageSource p_255778_,
|
|
@Nullable ExplosionDamageCalculator p_256002_,
|
|
double p_256067_,
|
|
double p_256370_,
|
|
double p_256153_,
|
|
float p_256045_,
|
|
boolean p_255686_,
|
|
Level.ExplosionInteraction p_255827_,
|
|
ParticleOptions p_310962_,
|
|
ParticleOptions p_310322_,
|
|
Holder<SoundEvent> p_333158_
|
|
) {
|
|
Explosion.BlockInteraction explosion$blockinteraction = switch (p_255827_) {
|
|
case NONE -> Explosion.BlockInteraction.KEEP;
|
|
case BLOCK -> this.getDestroyType(GameRules.RULE_BLOCK_EXPLOSION_DROP_DECAY);
|
|
case MOB -> this.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? this.getDestroyType(GameRules.RULE_MOB_EXPLOSION_DROP_DECAY) : Explosion.BlockInteraction.KEEP;
|
|
case TNT -> this.getDestroyType(GameRules.RULE_TNT_EXPLOSION_DROP_DECAY);
|
|
case TRIGGER -> Explosion.BlockInteraction.TRIGGER_BLOCK;
|
|
};
|
|
Vec3 vec3 = new Vec3(p_256067_, p_256370_, p_256153_);
|
|
ServerExplosion serverexplosion = new ServerExplosion(this, p_256039_, p_255778_, p_256002_, vec3, p_256045_, p_255686_, explosion$blockinteraction);
|
|
serverexplosion.explode();
|
|
ParticleOptions particleoptions = serverexplosion.isSmall() ? p_310962_ : p_310322_;
|
|
|
|
for (ServerPlayer serverplayer : this.players) {
|
|
if (serverplayer.distanceToSqr(vec3) < 4096.0) {
|
|
Optional<Vec3> optional = Optional.ofNullable(serverexplosion.getHitPlayers().get(serverplayer));
|
|
serverplayer.connection.send(new ClientboundExplodePacket(vec3, optional, particleoptions, p_333158_));
|
|
}
|
|
}
|
|
}
|
|
|
|
private Explosion.BlockInteraction getDestroyType(GameRules.Key<GameRules.BooleanValue> p_368408_) {
|
|
return this.getGameRules().getBoolean(p_368408_) ? Explosion.BlockInteraction.DESTROY_WITH_DECAY : Explosion.BlockInteraction.DESTROY;
|
|
}
|
|
|
|
@Override
|
|
public void blockEvent(BlockPos p_8746_, Block p_8747_, int p_8748_, int p_8749_) {
|
|
this.blockEvents.add(new BlockEventData(p_8746_, p_8747_, p_8748_, p_8749_));
|
|
}
|
|
|
|
private void runBlockEvents() {
|
|
this.blockEventsToReschedule.clear();
|
|
|
|
while (!this.blockEvents.isEmpty()) {
|
|
BlockEventData blockeventdata = this.blockEvents.removeFirst();
|
|
if (this.shouldTickBlocksAt(blockeventdata.pos())) {
|
|
if (this.doBlockEvent(blockeventdata)) {
|
|
this.server
|
|
.getPlayerList()
|
|
.broadcast(
|
|
null,
|
|
blockeventdata.pos().getX(),
|
|
blockeventdata.pos().getY(),
|
|
blockeventdata.pos().getZ(),
|
|
64.0,
|
|
this.dimension(),
|
|
new ClientboundBlockEventPacket(
|
|
blockeventdata.pos(), blockeventdata.block(), blockeventdata.paramA(), blockeventdata.paramB()
|
|
)
|
|
);
|
|
}
|
|
} else {
|
|
this.blockEventsToReschedule.add(blockeventdata);
|
|
}
|
|
}
|
|
|
|
this.blockEvents.addAll(this.blockEventsToReschedule);
|
|
}
|
|
|
|
private boolean doBlockEvent(BlockEventData p_8699_) {
|
|
BlockState blockstate = this.getBlockState(p_8699_.pos());
|
|
return blockstate.is(p_8699_.block()) ? blockstate.triggerEvent(this, p_8699_.pos(), p_8699_.paramA(), p_8699_.paramB()) : false;
|
|
}
|
|
|
|
public LevelTicks<Block> getBlockTicks() {
|
|
return this.blockTicks;
|
|
}
|
|
|
|
public LevelTicks<Fluid> getFluidTicks() {
|
|
return this.fluidTicks;
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public MinecraftServer getServer() {
|
|
return this.server;
|
|
}
|
|
|
|
public PortalForcer getPortalForcer() {
|
|
return this.portalForcer;
|
|
}
|
|
|
|
public StructureTemplateManager getStructureManager() {
|
|
return this.server.getStructureManager();
|
|
}
|
|
|
|
public <T extends ParticleOptions> int sendParticles(
|
|
T p_8768_, double p_8769_, double p_8770_, double p_8771_, int p_8772_, double p_8773_, double p_8774_, double p_8775_, double p_8776_
|
|
) {
|
|
return this.sendParticles(p_8768_, false, false, p_8769_, p_8770_, p_8771_, p_8772_, p_8773_, p_8774_, p_8775_, p_8776_);
|
|
}
|
|
|
|
public <T extends ParticleOptions> int sendParticles(
|
|
T p_8626_,
|
|
boolean p_8627_,
|
|
boolean p_378427_,
|
|
double p_8628_,
|
|
double p_8629_,
|
|
double p_8630_,
|
|
int p_8631_,
|
|
double p_8632_,
|
|
double p_8633_,
|
|
double p_8634_,
|
|
double p_8635_
|
|
) {
|
|
ClientboundLevelParticlesPacket clientboundlevelparticlespacket = new ClientboundLevelParticlesPacket(
|
|
p_8626_, p_8627_, p_378427_, p_8628_, p_8629_, p_8630_, (float)p_8632_, (float)p_8633_, (float)p_8634_, (float)p_8635_, p_8631_
|
|
);
|
|
int i = 0;
|
|
|
|
for (int j = 0; j < this.players.size(); j++) {
|
|
ServerPlayer serverplayer = this.players.get(j);
|
|
if (this.sendParticles(serverplayer, p_8627_, p_8628_, p_8629_, p_8630_, clientboundlevelparticlespacket)) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public <T extends ParticleOptions> boolean sendParticles(
|
|
ServerPlayer p_376717_,
|
|
T p_375887_,
|
|
boolean p_376188_,
|
|
boolean p_378707_,
|
|
double p_378284_,
|
|
double p_377685_,
|
|
double p_375756_,
|
|
int p_377117_,
|
|
double p_378090_,
|
|
double p_376820_,
|
|
double p_376920_,
|
|
double p_377557_
|
|
) {
|
|
Packet<?> packet = new ClientboundLevelParticlesPacket(
|
|
p_375887_, p_376188_, p_378707_, p_378284_, p_377685_, p_375756_, (float)p_378090_, (float)p_376820_, (float)p_376920_, (float)p_377557_, p_377117_
|
|
);
|
|
return this.sendParticles(p_376717_, p_376188_, p_378284_, p_377685_, p_375756_, packet);
|
|
}
|
|
|
|
private boolean sendParticles(ServerPlayer p_8637_, boolean p_8638_, double p_8639_, double p_8640_, double p_8641_, Packet<?> p_8642_) {
|
|
if (p_8637_.level() != this) {
|
|
return false;
|
|
} else {
|
|
BlockPos blockpos = p_8637_.blockPosition();
|
|
if (blockpos.closerToCenterThan(new Vec3(p_8639_, p_8640_, p_8641_), p_8638_ ? 512.0 : 32.0)) {
|
|
p_8637_.connection.send(p_8642_);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Entity getEntity(int p_8597_) {
|
|
return this.getEntities().get(p_8597_);
|
|
}
|
|
|
|
@Deprecated
|
|
@Nullable
|
|
public Entity getEntityOrPart(int p_143318_) {
|
|
Entity entity = this.getEntities().get(p_143318_);
|
|
return entity != null ? entity : this.dragonParts.get(p_143318_);
|
|
}
|
|
|
|
@Override
|
|
public Collection<EnderDragonPart> dragonParts() {
|
|
return this.dragonParts.values();
|
|
}
|
|
|
|
@Nullable
|
|
public BlockPos findNearestMapStructure(TagKey<Structure> p_215012_, BlockPos p_215013_, int p_215014_, boolean p_215015_) {
|
|
if (!this.server.getWorldData().worldGenOptions().generateStructures()) {
|
|
return null;
|
|
} else {
|
|
Optional<HolderSet.Named<Structure>> optional = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(p_215012_);
|
|
if (optional.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
Pair<BlockPos, Holder<Structure>> pair = this.getChunkSource().getGenerator().findNearestMapStructure(this, optional.get(), p_215013_, p_215014_, p_215015_);
|
|
return pair != null ? pair.getFirst() : null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(Predicate<Holder<Biome>> p_215070_, BlockPos p_215071_, int p_215072_, int p_215073_, int p_215074_) {
|
|
return this.getChunkSource()
|
|
.getGenerator()
|
|
.getBiomeSource()
|
|
.findClosestBiome3d(p_215071_, p_215072_, p_215073_, p_215074_, p_215070_, this.getChunkSource().randomState().sampler(), this);
|
|
}
|
|
|
|
public RecipeManager recipeAccess() {
|
|
return this.server.getRecipeManager();
|
|
}
|
|
|
|
@Override
|
|
public TickRateManager tickRateManager() {
|
|
return this.server.tickRateManager();
|
|
}
|
|
|
|
@Override
|
|
public boolean noSave() {
|
|
return this.noSave;
|
|
}
|
|
|
|
public DimensionDataStorage getDataStorage() {
|
|
return this.getChunkSource().getDataStorage();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public MapItemSavedData getMapData(MapId p_330550_) {
|
|
return this.getServer().overworld().getDataStorage().get(MapItemSavedData.type(p_330550_));
|
|
}
|
|
|
|
public void setMapData(MapId p_331590_, MapItemSavedData p_143306_) {
|
|
this.getServer().overworld().getDataStorage().set(MapItemSavedData.type(p_331590_), p_143306_);
|
|
}
|
|
|
|
public MapId getFreeMapId() {
|
|
return this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.TYPE).getNextMapId();
|
|
}
|
|
|
|
public void setDefaultSpawnPos(BlockPos p_8734_, float p_8735_) {
|
|
BlockPos blockpos = this.levelData.getSpawnPos();
|
|
float f = this.levelData.getSpawnAngle();
|
|
if (!blockpos.equals(p_8734_) || f != p_8735_) {
|
|
this.levelData.setSpawn(p_8734_, p_8735_);
|
|
this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(p_8734_, p_8735_));
|
|
}
|
|
|
|
if (this.lastSpawnChunkRadius > 1) {
|
|
this.getChunkSource().removeTicketWithRadius(TicketType.START, new ChunkPos(blockpos), this.lastSpawnChunkRadius);
|
|
}
|
|
|
|
int i = this.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS) + 1;
|
|
if (i > 1) {
|
|
this.getChunkSource().addTicketWithRadius(TicketType.START, new ChunkPos(p_8734_), i);
|
|
}
|
|
|
|
this.lastSpawnChunkRadius = i;
|
|
}
|
|
|
|
public LongSet getForceLoadedChunks() {
|
|
return this.chunkSource.getForceLoadedChunks();
|
|
}
|
|
|
|
public boolean setChunkForced(int p_8603_, int p_8604_, boolean p_8605_) {
|
|
boolean flag = this.chunkSource.updateChunkForced(new ChunkPos(p_8603_, p_8604_), p_8605_);
|
|
if (p_8605_ && flag) {
|
|
this.getChunk(p_8603_, p_8604_);
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
@Override
|
|
public List<ServerPlayer> players() {
|
|
return this.players;
|
|
}
|
|
|
|
@Override
|
|
public void updatePOIOnBlockStateChange(BlockPos p_8751_, BlockState p_8752_, BlockState p_8753_) {
|
|
Optional<Holder<PoiType>> optional = PoiTypes.forState(p_8752_);
|
|
Optional<Holder<PoiType>> optional1 = PoiTypes.forState(p_8753_);
|
|
if (!Objects.equals(optional, optional1)) {
|
|
BlockPos blockpos = p_8751_.immutable();
|
|
optional.ifPresent(p_215081_ -> this.getServer().execute(() -> {
|
|
this.getPoiManager().remove(blockpos);
|
|
DebugPackets.sendPoiRemovedPacket(this, blockpos);
|
|
}));
|
|
optional1.ifPresent(p_215057_ -> this.getServer().execute(() -> {
|
|
this.getPoiManager().add(blockpos, (Holder<PoiType>)p_215057_);
|
|
DebugPackets.sendPoiAddedPacket(this, blockpos);
|
|
}));
|
|
}
|
|
}
|
|
|
|
public PoiManager getPoiManager() {
|
|
return this.getChunkSource().getPoiManager();
|
|
}
|
|
|
|
public boolean isVillage(BlockPos p_8803_) {
|
|
return this.isCloseToVillage(p_8803_, 1);
|
|
}
|
|
|
|
public boolean isVillage(SectionPos p_8763_) {
|
|
return this.isVillage(p_8763_.center());
|
|
}
|
|
|
|
public boolean isCloseToVillage(BlockPos p_8737_, int p_8738_) {
|
|
return p_8738_ > 6 ? false : this.sectionsToVillage(SectionPos.of(p_8737_)) <= p_8738_;
|
|
}
|
|
|
|
public int sectionsToVillage(SectionPos p_8829_) {
|
|
return this.getPoiManager().sectionsToVillage(p_8829_);
|
|
}
|
|
|
|
public Raids getRaids() {
|
|
return this.raids;
|
|
}
|
|
|
|
@Nullable
|
|
public Raid getRaidAt(BlockPos p_8833_) {
|
|
return this.raids.getNearbyRaid(p_8833_, 9216);
|
|
}
|
|
|
|
public boolean isRaided(BlockPos p_8844_) {
|
|
return this.getRaidAt(p_8844_) != null;
|
|
}
|
|
|
|
public void onReputationEvent(ReputationEventType p_8671_, Entity p_8672_, ReputationEventHandler p_8673_) {
|
|
p_8673_.onReputationEventFrom(p_8671_, p_8672_);
|
|
}
|
|
|
|
public void saveDebugReport(Path p_8787_) throws IOException {
|
|
ChunkMap chunkmap = this.getChunkSource().chunkMap;
|
|
|
|
try (Writer writer = Files.newBufferedWriter(p_8787_.resolve("stats.txt"))) {
|
|
writer.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", chunkmap.getDistanceManager().getNaturalSpawnChunkCount()));
|
|
NaturalSpawner.SpawnState naturalspawner$spawnstate = this.getChunkSource().getLastSpawnState();
|
|
if (naturalspawner$spawnstate != null) {
|
|
for (Entry<MobCategory> entry : naturalspawner$spawnstate.getMobCategoryCounts().object2IntEntrySet()) {
|
|
writer.write(String.format(Locale.ROOT, "spawn_count.%s: %d\n", entry.getKey().getName(), entry.getIntValue()));
|
|
}
|
|
}
|
|
|
|
writer.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats()));
|
|
writer.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
|
|
writer.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
|
|
writer.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
|
|
writer.write("distance_manager: " + chunkmap.getDistanceManager().getDebugStatus() + "\n");
|
|
writer.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
|
|
}
|
|
|
|
CrashReport crashreport = new CrashReport("Level dump", new Exception("dummy"));
|
|
this.fillReportDetails(crashreport);
|
|
|
|
try (Writer writer3 = Files.newBufferedWriter(p_8787_.resolve("example_crash.txt"))) {
|
|
writer3.write(crashreport.getFriendlyReport(ReportType.TEST));
|
|
}
|
|
|
|
Path path = p_8787_.resolve("chunks.csv");
|
|
|
|
try (Writer writer4 = Files.newBufferedWriter(path)) {
|
|
chunkmap.dumpChunks(writer4);
|
|
}
|
|
|
|
Path path1 = p_8787_.resolve("entity_chunks.csv");
|
|
|
|
try (Writer writer5 = Files.newBufferedWriter(path1)) {
|
|
this.entityManager.dumpSections(writer5);
|
|
}
|
|
|
|
Path path2 = p_8787_.resolve("entities.csv");
|
|
|
|
try (Writer writer1 = Files.newBufferedWriter(path2)) {
|
|
dumpEntities(writer1, this.getEntities().getAll());
|
|
}
|
|
|
|
Path path3 = p_8787_.resolve("block_entities.csv");
|
|
|
|
try (Writer writer2 = Files.newBufferedWriter(path3)) {
|
|
this.dumpBlockEntityTickers(writer2);
|
|
}
|
|
}
|
|
|
|
private static void dumpEntities(Writer p_8782_, Iterable<Entity> p_8783_) throws IOException {
|
|
CsvOutput csvoutput = CsvOutput.builder()
|
|
.addColumn("x")
|
|
.addColumn("y")
|
|
.addColumn("z")
|
|
.addColumn("uuid")
|
|
.addColumn("type")
|
|
.addColumn("alive")
|
|
.addColumn("display_name")
|
|
.addColumn("custom_name")
|
|
.build(p_8782_);
|
|
|
|
for (Entity entity : p_8783_) {
|
|
Component component = entity.getCustomName();
|
|
Component component1 = entity.getDisplayName();
|
|
csvoutput.writeRow(
|
|
entity.getX(),
|
|
entity.getY(),
|
|
entity.getZ(),
|
|
entity.getUUID(),
|
|
BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()),
|
|
entity.isAlive(),
|
|
component1.getString(),
|
|
component != null ? component.getString() : null
|
|
);
|
|
}
|
|
}
|
|
|
|
private void dumpBlockEntityTickers(Writer p_143300_) throws IOException {
|
|
CsvOutput csvoutput = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(p_143300_);
|
|
|
|
for (TickingBlockEntity tickingblockentity : this.blockEntityTickers) {
|
|
BlockPos blockpos = tickingblockentity.getPos();
|
|
csvoutput.writeRow(blockpos.getX(), blockpos.getY(), blockpos.getZ(), tickingblockentity.getType());
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void clearBlockEvents(BoundingBox p_8723_) {
|
|
this.blockEvents.removeIf(p_207568_ -> p_8723_.isInside(p_207568_.pos()));
|
|
}
|
|
|
|
@Override
|
|
public float getShade(Direction p_8760_, boolean p_8761_) {
|
|
return 1.0F;
|
|
}
|
|
|
|
public Iterable<Entity> getAllEntities() {
|
|
return this.getEntities().getAll();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ServerLevel[" + this.serverLevelData.getLevelName() + "]";
|
|
}
|
|
|
|
public boolean isFlat() {
|
|
return this.server.getWorldData().isFlatWorld();
|
|
}
|
|
|
|
@Override
|
|
public long getSeed() {
|
|
return this.server.getWorldData().worldGenOptions().seed();
|
|
}
|
|
|
|
@Nullable
|
|
public EndDragonFight getDragonFight() {
|
|
return this.dragonFight;
|
|
}
|
|
|
|
@Override
|
|
public ServerLevel getLevel() {
|
|
return this;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public String getWatchdogStats() {
|
|
return String.format(
|
|
Locale.ROOT,
|
|
"players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s",
|
|
this.players.size(),
|
|
this.entityManager.gatherStats(),
|
|
getTypeCount(this.entityManager.getEntityGetter().getAll(), p_258244_ -> BuiltInRegistries.ENTITY_TYPE.getKey(p_258244_.getType()).toString()),
|
|
this.blockEntityTickers.size(),
|
|
getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType),
|
|
this.getBlockTicks().count(),
|
|
this.getFluidTicks().count(),
|
|
this.gatherChunkSourceStats()
|
|
);
|
|
}
|
|
|
|
private static <T> String getTypeCount(Iterable<T> p_143302_, Function<T, String> p_143303_) {
|
|
try {
|
|
Object2IntOpenHashMap<String> object2intopenhashmap = new Object2IntOpenHashMap<>();
|
|
|
|
for (T t : p_143302_) {
|
|
String s = p_143303_.apply(t);
|
|
object2intopenhashmap.addTo(s, 1);
|
|
}
|
|
|
|
return object2intopenhashmap.object2IntEntrySet()
|
|
.stream()
|
|
.sorted(Comparator.<Entry<String>, Integer>comparing(Entry::getIntValue).reversed())
|
|
.limit(5L)
|
|
.map(p_207570_ -> p_207570_.getKey() + ":" + p_207570_.getIntValue())
|
|
.collect(Collectors.joining(","));
|
|
} catch (Exception exception) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected LevelEntityGetter<Entity> getEntities() {
|
|
return this.entityManager.getEntityGetter();
|
|
}
|
|
|
|
public void addLegacyChunkEntities(Stream<Entity> p_143312_) {
|
|
this.entityManager.addLegacyChunkEntities(p_143312_);
|
|
}
|
|
|
|
public void addWorldGenChunkEntities(Stream<Entity> p_143328_) {
|
|
this.entityManager.addWorldGenChunkEntities(p_143328_);
|
|
}
|
|
|
|
public void startTickingChunk(LevelChunk p_184103_) {
|
|
p_184103_.unpackTicks(this.getLevelData().getGameTime());
|
|
}
|
|
|
|
public void onStructureStartsAvailable(ChunkAccess p_196558_) {
|
|
this.server.execute(() -> this.structureCheck.onStructureLoad(p_196558_.getPos(), p_196558_.getAllStarts()));
|
|
}
|
|
|
|
public PathTypeCache getPathTypeCache() {
|
|
return this.pathTypesByPosCache;
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
super.close();
|
|
this.entityManager.close();
|
|
}
|
|
|
|
@Override
|
|
public String gatherChunkSourceStats() {
|
|
return "Chunks[S] W: " + this.chunkSource.gatherStats() + " E: " + this.entityManager.gatherStats();
|
|
}
|
|
|
|
public boolean areEntitiesLoaded(long p_143320_) {
|
|
return this.entityManager.areEntitiesLoaded(p_143320_);
|
|
}
|
|
|
|
public boolean isPositionTickingWithEntitiesLoaded(long p_184111_) {
|
|
return this.areEntitiesLoaded(p_184111_) && this.chunkSource.isPositionTicking(p_184111_);
|
|
}
|
|
|
|
public boolean isPositionEntityTicking(BlockPos p_143341_) {
|
|
return this.entityManager.canPositionTick(p_143341_) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(p_143341_));
|
|
}
|
|
|
|
public boolean areEntitiesActuallyLoadedAndTicking(ChunkPos p_391873_) {
|
|
return this.entityManager.isTicking(p_391873_) && this.entityManager.areEntitiesLoaded(p_391873_.toLong());
|
|
}
|
|
|
|
public boolean anyPlayerCloseEnoughForSpawning(BlockPos p_393845_) {
|
|
return this.anyPlayerCloseEnoughForSpawning(new ChunkPos(p_393845_));
|
|
}
|
|
|
|
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos p_395852_) {
|
|
return this.chunkSource.chunkMap.anyPlayerCloseEnoughForSpawning(p_395852_);
|
|
}
|
|
|
|
public boolean canSpawnEntitiesInChunk(ChunkPos p_395526_) {
|
|
return this.entityManager.canPositionTick(p_395526_) && this.getWorldBorder().isWithinBounds(p_395526_);
|
|
}
|
|
|
|
@Override
|
|
public FeatureFlagSet enabledFeatures() {
|
|
return this.server.getWorldData().enabledFeatures();
|
|
}
|
|
|
|
@Override
|
|
public PotionBrewing potionBrewing() {
|
|
return this.server.potionBrewing();
|
|
}
|
|
|
|
@Override
|
|
public FuelValues fuelValues() {
|
|
return this.server.fuelValues();
|
|
}
|
|
|
|
public RandomSource getRandomSequence(ResourceLocation p_287689_) {
|
|
return this.randomSequences.get(p_287689_);
|
|
}
|
|
|
|
public RandomSequences getRandomSequences() {
|
|
return this.randomSequences;
|
|
}
|
|
|
|
public GameRules getGameRules() {
|
|
return this.serverLevelData.getGameRules();
|
|
}
|
|
|
|
@Override
|
|
public CrashReportCategory fillReportDetails(CrashReport p_311549_) {
|
|
CrashReportCategory crashreportcategory = super.fillReportDetails(p_311549_);
|
|
crashreportcategory.setDetail("Loaded entity count", () -> String.valueOf(this.entityManager.count()));
|
|
return crashreportcategory;
|
|
}
|
|
|
|
@Override
|
|
public int getSeaLevel() {
|
|
return this.chunkSource.getGenerator().getSeaLevel();
|
|
}
|
|
|
|
final class EntityCallbacks implements LevelCallback<Entity> {
|
|
public void onCreated(Entity p_143355_) {
|
|
}
|
|
|
|
public void onDestroyed(Entity p_143359_) {
|
|
ServerLevel.this.getScoreboard().entityRemoved(p_143359_);
|
|
}
|
|
|
|
public void onTickingStart(Entity p_143363_) {
|
|
ServerLevel.this.entityTickList.add(p_143363_);
|
|
}
|
|
|
|
public void onTickingEnd(Entity p_143367_) {
|
|
ServerLevel.this.entityTickList.remove(p_143367_);
|
|
}
|
|
|
|
public void onTrackingStart(Entity p_143371_) {
|
|
ServerLevel.this.getChunkSource().addEntity(p_143371_);
|
|
if (p_143371_ instanceof ServerPlayer serverplayer) {
|
|
ServerLevel.this.players.add(serverplayer);
|
|
ServerLevel.this.updateSleepingPlayerList();
|
|
}
|
|
|
|
if (p_143371_ instanceof Mob mob) {
|
|
if (ServerLevel.this.isUpdatingNavigations) {
|
|
String s = "onTrackingStart called during navigation iteration";
|
|
Util.logAndPauseIfInIde(
|
|
"onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
|
|
);
|
|
}
|
|
|
|
ServerLevel.this.navigatingMobs.add(mob);
|
|
}
|
|
|
|
if (p_143371_ instanceof EnderDragon enderdragon) {
|
|
for (EnderDragonPart enderdragonpart : enderdragon.getSubEntities()) {
|
|
ServerLevel.this.dragonParts.put(enderdragonpart.getId(), enderdragonpart);
|
|
}
|
|
}
|
|
|
|
p_143371_.updateDynamicGameEventListener(DynamicGameEventListener::add);
|
|
}
|
|
|
|
public void onTrackingEnd(Entity p_143375_) {
|
|
ServerLevel.this.getChunkSource().removeEntity(p_143375_);
|
|
if (p_143375_ instanceof ServerPlayer serverplayer) {
|
|
ServerLevel.this.players.remove(serverplayer);
|
|
ServerLevel.this.updateSleepingPlayerList();
|
|
}
|
|
|
|
if (p_143375_ instanceof Mob mob) {
|
|
if (ServerLevel.this.isUpdatingNavigations) {
|
|
String s = "onTrackingStart called during navigation iteration";
|
|
Util.logAndPauseIfInIde(
|
|
"onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
|
|
);
|
|
}
|
|
|
|
ServerLevel.this.navigatingMobs.remove(mob);
|
|
}
|
|
|
|
if (p_143375_ instanceof EnderDragon enderdragon) {
|
|
for (EnderDragonPart enderdragonpart : enderdragon.getSubEntities()) {
|
|
ServerLevel.this.dragonParts.remove(enderdragonpart.getId());
|
|
}
|
|
}
|
|
|
|
p_143375_.updateDynamicGameEventListener(DynamicGameEventListener::remove);
|
|
}
|
|
|
|
public void onSectionChange(Entity p_215086_) {
|
|
p_215086_.updateDynamicGameEventListener(DynamicGameEventListener::move);
|
|
}
|
|
}
|
|
}
|