1121 lines
46 KiB
Java
1121 lines
46 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.ImmutableMap
|
|
* com.google.common.collect.Lists
|
|
* com.google.common.collect.Maps
|
|
* com.google.common.collect.Queues
|
|
* com.mojang.logging.LogUtils
|
|
* it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
|
* it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.client.multiplayer;
|
|
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import com.google.common.collect.Queues;
|
|
import com.mojang.logging.LogUtils;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
|
import java.lang.runtime.SwitchBootstraps;
|
|
import java.util.Arrays;
|
|
import java.util.Deque;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Set;
|
|
import java.util.function.BooleanSupplier;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportedException;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.client.Camera;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.color.block.BlockTintCache;
|
|
import net.minecraft.client.gui.screens.WinScreen;
|
|
import net.minecraft.client.multiplayer.CacheSlot;
|
|
import net.minecraft.client.multiplayer.ClientChunkCache;
|
|
import net.minecraft.client.multiplayer.ClientExplosionTracker;
|
|
import net.minecraft.client.multiplayer.ClientPacketListener;
|
|
import net.minecraft.client.multiplayer.prediction.BlockStatePredictionHandler;
|
|
import net.minecraft.client.particle.FireworkParticles;
|
|
import net.minecraft.client.particle.TerrainParticle;
|
|
import net.minecraft.client.player.AbstractClientPlayer;
|
|
import net.minecraft.client.player.LocalPlayer;
|
|
import net.minecraft.client.renderer.BiomeColors;
|
|
import net.minecraft.client.renderer.EndFlashState;
|
|
import net.minecraft.client.renderer.LevelEventHandler;
|
|
import net.minecraft.client.renderer.LevelRenderer;
|
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
|
import net.minecraft.client.resources.sounds.DirectionalSoundInstance;
|
|
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance;
|
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Cursor3D;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.core.particles.BlockParticleOption;
|
|
import net.minecraft.core.particles.ExplosionParticleInfo;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ParticleStatus;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.util.ARGB;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.Util;
|
|
import net.minecraft.util.profiling.Profiler;
|
|
import net.minecraft.util.profiling.Zone;
|
|
import net.minecraft.util.profiling.jfr.JvmProfiler;
|
|
import net.minecraft.util.random.WeightedList;
|
|
import net.minecraft.world.Difficulty;
|
|
import net.minecraft.world.TickRateManager;
|
|
import net.minecraft.world.attribute.AmbientParticle;
|
|
import net.minecraft.world.attribute.EnvironmentAttributeSystem;
|
|
import net.minecraft.world.attribute.EnvironmentAttributes;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
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.flag.FeatureFlagSet;
|
|
import net.minecraft.world.item.BlockItem;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.item.alchemy.PotionBrewing;
|
|
import net.minecraft.world.item.component.FireworkExplosion;
|
|
import net.minecraft.world.item.crafting.RecipeAccess;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.ColorResolver;
|
|
import net.minecraft.world.level.ExplosionDamageCalculator;
|
|
import net.minecraft.world.level.GameType;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelHeightAccessor;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.biome.Biomes;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.RenderShape;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.FuelValues;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.border.WorldBorder;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import net.minecraft.world.level.dimension.DimensionType;
|
|
import net.minecraft.world.level.entity.EntityTickList;
|
|
import net.minecraft.world.level.entity.LevelCallback;
|
|
import net.minecraft.world.level.entity.LevelEntityGetter;
|
|
import net.minecraft.world.level.entity.TransientEntitySectionManager;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.material.Fluid;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.saveddata.maps.MapId;
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import net.minecraft.world.level.storage.LevelData;
|
|
import net.minecraft.world.level.storage.WritableLevelData;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import net.minecraft.world.scores.Scoreboard;
|
|
import net.minecraft.world.ticks.BlackholeTickAccess;
|
|
import net.minecraft.world.ticks.LevelTickAccess;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ClientLevel
|
|
extends Level
|
|
implements CacheSlot.Cleaner<ClientLevel> {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final Component DEFAULT_QUIT_MESSAGE = Component.translatable("multiplayer.status.quitting");
|
|
private static final double FLUID_PARTICLE_SPAWN_OFFSET = 0.05;
|
|
private static final int NORMAL_LIGHT_UPDATES_PER_FRAME = 10;
|
|
private static final int LIGHT_UPDATE_QUEUE_SIZE_THRESHOLD = 1000;
|
|
private final EntityTickList tickingEntities = new EntityTickList();
|
|
private final TransientEntitySectionManager<Entity> entityStorage = new TransientEntitySectionManager<Entity>(Entity.class, new EntityCallbacks());
|
|
private final ClientPacketListener connection;
|
|
private final LevelRenderer levelRenderer;
|
|
private final LevelEventHandler levelEventHandler;
|
|
private final ClientLevelData clientLevelData;
|
|
private final TickRateManager tickRateManager;
|
|
private final @Nullable EndFlashState endFlashState;
|
|
private final Minecraft minecraft = Minecraft.getInstance();
|
|
private final List<AbstractClientPlayer> players = Lists.newArrayList();
|
|
private final List<EnderDragonPart> dragonParts = Lists.newArrayList();
|
|
private final Map<MapId, MapItemSavedData> mapData = Maps.newHashMap();
|
|
private int skyFlashTime;
|
|
private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches = Util.make(new Object2ObjectArrayMap(3), cache -> {
|
|
cache.put((Object)BiomeColors.GRASS_COLOR_RESOLVER, (Object)new BlockTintCache(pos -> this.calculateBlockTint((BlockPos)pos, BiomeColors.GRASS_COLOR_RESOLVER)));
|
|
cache.put((Object)BiomeColors.FOLIAGE_COLOR_RESOLVER, (Object)new BlockTintCache(pos -> this.calculateBlockTint((BlockPos)pos, BiomeColors.FOLIAGE_COLOR_RESOLVER)));
|
|
cache.put((Object)BiomeColors.DRY_FOLIAGE_COLOR_RESOLVER, (Object)new BlockTintCache(pos -> this.calculateBlockTint((BlockPos)pos, BiomeColors.DRY_FOLIAGE_COLOR_RESOLVER)));
|
|
cache.put((Object)BiomeColors.WATER_COLOR_RESOLVER, (Object)new BlockTintCache(pos -> this.calculateBlockTint((BlockPos)pos, BiomeColors.WATER_COLOR_RESOLVER)));
|
|
});
|
|
private final ClientChunkCache chunkSource;
|
|
private final Deque<Runnable> lightUpdateQueue = Queues.newArrayDeque();
|
|
private int serverSimulationDistance;
|
|
private final BlockStatePredictionHandler blockStatePredictionHandler = new BlockStatePredictionHandler();
|
|
private final Set<BlockEntity> globallyRenderedBlockEntities = new ReferenceOpenHashSet();
|
|
private final ClientExplosionTracker explosionTracker = new ClientExplosionTracker();
|
|
private final WorldBorder worldBorder = new WorldBorder();
|
|
private final EnvironmentAttributeSystem environmentAttributes;
|
|
private final int seaLevel;
|
|
private boolean tickDayTime;
|
|
private static final Set<Item> MARKER_PARTICLE_ITEMS = Set.of(Items.BARRIER, Items.LIGHT);
|
|
|
|
public void handleBlockChangedAck(int sequence) {
|
|
if (SharedConstants.DEBUG_BLOCK_BREAK) {
|
|
LOGGER.debug("ACK {}", (Object)sequence);
|
|
}
|
|
this.blockStatePredictionHandler.endPredictionsUpTo(sequence, this);
|
|
}
|
|
|
|
@Override
|
|
public void onBlockEntityAdded(BlockEntity blockEntity) {
|
|
BlockEntityRenderer renderer = this.minecraft.getBlockEntityRenderDispatcher().getRenderer(blockEntity);
|
|
if (renderer != null && renderer.shouldRenderOffScreen()) {
|
|
this.globallyRenderedBlockEntities.add(blockEntity);
|
|
}
|
|
}
|
|
|
|
public Set<BlockEntity> getGloballyRenderedBlockEntities() {
|
|
return this.globallyRenderedBlockEntities;
|
|
}
|
|
|
|
public void setServerVerifiedBlockState(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlag) {
|
|
if (!this.blockStatePredictionHandler.updateKnownServerState(pos, blockState)) {
|
|
super.setBlock(pos, blockState, updateFlag, 512);
|
|
}
|
|
}
|
|
|
|
public void syncBlockState(BlockPos pos, BlockState state, Vec3 playerPos) {
|
|
BlockState oldState = this.getBlockState(pos);
|
|
if (oldState != state) {
|
|
this.setBlock(pos, state, 19);
|
|
LocalPlayer player = this.minecraft.player;
|
|
if (this == player.level() && player.isColliding(pos, state)) {
|
|
player.absSnapTo(playerPos.x, playerPos.y, playerPos.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
BlockStatePredictionHandler getBlockStatePredictionHandler() {
|
|
return this.blockStatePredictionHandler;
|
|
}
|
|
|
|
@Override
|
|
public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) {
|
|
if (this.blockStatePredictionHandler.isPredicting()) {
|
|
BlockState oldState = this.getBlockState(pos);
|
|
boolean success = super.setBlock(pos, blockState, updateFlags, updateLimit);
|
|
if (success) {
|
|
this.blockStatePredictionHandler.retainKnownServerState(pos, oldState, this.minecraft.player);
|
|
}
|
|
return success;
|
|
}
|
|
return super.setBlock(pos, blockState, updateFlags, updateLimit);
|
|
}
|
|
|
|
public ClientLevel(ClientPacketListener connection, ClientLevelData levelData, ResourceKey<Level> dimension, Holder<DimensionType> dimensionType, int serverChunkRadius, int serverSimulationDistance, LevelRenderer levelRenderer, boolean isDebug, long biomeZoomSeed, int seaLevel) {
|
|
super(levelData, dimension, connection.registryAccess(), dimensionType, true, isDebug, biomeZoomSeed, 1000000);
|
|
this.connection = connection;
|
|
this.chunkSource = new ClientChunkCache(this, serverChunkRadius);
|
|
this.tickRateManager = new TickRateManager();
|
|
this.clientLevelData = levelData;
|
|
this.levelRenderer = levelRenderer;
|
|
this.seaLevel = seaLevel;
|
|
this.levelEventHandler = new LevelEventHandler(this.minecraft, this);
|
|
this.endFlashState = dimensionType.value().hasEndFlashes() ? new EndFlashState() : null;
|
|
this.setRespawnData(LevelData.RespawnData.of(dimension, new BlockPos(8, 64, 8), 0.0f, 0.0f));
|
|
this.serverSimulationDistance = serverSimulationDistance;
|
|
this.environmentAttributes = this.addEnvironmentAttributeLayers(EnvironmentAttributeSystem.builder()).build();
|
|
this.updateSkyBrightness();
|
|
this.prepareWeather();
|
|
}
|
|
|
|
private EnvironmentAttributeSystem.Builder addEnvironmentAttributeLayers(EnvironmentAttributeSystem.Builder environmentAttributes) {
|
|
environmentAttributes.addDefaultLayers(this);
|
|
int flashColor = ARGB.color(204, 204, 255);
|
|
environmentAttributes.addTimeBasedLayer(EnvironmentAttributes.SKY_COLOR, (skyColor, cacheTickId) -> {
|
|
if (this.getSkyFlashTime() > 0) {
|
|
return ARGB.srgbLerp(0.22f, skyColor, flashColor);
|
|
}
|
|
return skyColor;
|
|
});
|
|
environmentAttributes.addTimeBasedLayer(EnvironmentAttributes.SKY_LIGHT_FACTOR, (skyFactor, cacheTickId) -> Float.valueOf(this.getSkyFlashTime() > 0 ? 1.0f : skyFactor.floatValue()));
|
|
return environmentAttributes;
|
|
}
|
|
|
|
public void queueLightUpdate(Runnable update) {
|
|
this.lightUpdateQueue.add(update);
|
|
}
|
|
|
|
public void pollLightUpdates() {
|
|
Runnable update;
|
|
int size = this.lightUpdateQueue.size();
|
|
int lightUpdatesPerFrame = size < 1000 ? Math.max(10, size / 10) : size;
|
|
for (int i = 0; i < lightUpdatesPerFrame && (update = this.lightUpdateQueue.poll()) != null; ++i) {
|
|
update.run();
|
|
}
|
|
}
|
|
|
|
public @Nullable EndFlashState endFlashState() {
|
|
return this.endFlashState;
|
|
}
|
|
|
|
public void tick(BooleanSupplier haveTime) {
|
|
this.updateSkyBrightness();
|
|
if (this.tickRateManager().runsNormally()) {
|
|
this.getWorldBorder().tick();
|
|
this.tickTime();
|
|
}
|
|
if (this.skyFlashTime > 0) {
|
|
this.setSkyFlashTime(this.skyFlashTime - 1);
|
|
}
|
|
if (this.endFlashState != null) {
|
|
this.endFlashState.tick(this.getGameTime());
|
|
if (this.endFlashState.flashStartedThisTick() && !(this.minecraft.screen instanceof WinScreen)) {
|
|
this.minecraft.getSoundManager().playDelayed(new DirectionalSoundInstance(SoundEvents.WEATHER_END_FLASH, SoundSource.WEATHER, this.random, this.minecraft.gameRenderer.getMainCamera(), this.endFlashState.getXAngle(), this.endFlashState.getYAngle()), 30);
|
|
}
|
|
}
|
|
this.explosionTracker.tick(this);
|
|
try (Zone ignored = Profiler.get().zone("blocks");){
|
|
this.chunkSource.tick(haveTime, true);
|
|
}
|
|
JvmProfiler.INSTANCE.onClientTick(this.minecraft.getFps());
|
|
this.environmentAttributes().invalidateTickCache();
|
|
}
|
|
|
|
private void tickTime() {
|
|
this.clientLevelData.setGameTime(this.clientLevelData.getGameTime() + 1L);
|
|
if (this.tickDayTime) {
|
|
this.clientLevelData.setDayTime(this.clientLevelData.getDayTime() + 1L);
|
|
}
|
|
}
|
|
|
|
public void setTimeFromServer(long gameTime, long dayTime, boolean tickDayTime) {
|
|
this.clientLevelData.setGameTime(gameTime);
|
|
this.clientLevelData.setDayTime(dayTime);
|
|
this.tickDayTime = tickDayTime;
|
|
}
|
|
|
|
public Iterable<Entity> entitiesForRendering() {
|
|
return this.getEntities().getAll();
|
|
}
|
|
|
|
public void tickEntities() {
|
|
this.tickingEntities.forEach(entity -> {
|
|
if (entity.isRemoved() || entity.isPassenger() || this.tickRateManager.isEntityFrozen((Entity)entity)) {
|
|
return;
|
|
}
|
|
this.guardEntityTick(this::tickNonPassenger, entity);
|
|
});
|
|
}
|
|
|
|
public boolean isTickingEntity(Entity entity) {
|
|
return this.tickingEntities.contains(entity);
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldTickDeath(Entity entity) {
|
|
return entity.chunkPosition().getChessboardDistance(this.minecraft.player.chunkPosition()) <= this.serverSimulationDistance;
|
|
}
|
|
|
|
public void tickNonPassenger(Entity entity) {
|
|
entity.setOldPosAndRot();
|
|
++entity.tickCount;
|
|
Profiler.get().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString());
|
|
entity.tick();
|
|
Profiler.get().pop();
|
|
for (Entity passenger : entity.getPassengers()) {
|
|
this.tickPassenger(entity, passenger);
|
|
}
|
|
}
|
|
|
|
private void tickPassenger(Entity vehicle, Entity entity) {
|
|
if (entity.isRemoved() || entity.getVehicle() != vehicle) {
|
|
entity.stopRiding();
|
|
return;
|
|
}
|
|
if (!(entity instanceof Player) && !this.tickingEntities.contains(entity)) {
|
|
return;
|
|
}
|
|
entity.setOldPosAndRot();
|
|
++entity.tickCount;
|
|
entity.rideTick();
|
|
for (Entity passenger : entity.getPassengers()) {
|
|
this.tickPassenger(entity, passenger);
|
|
}
|
|
}
|
|
|
|
public void unload(LevelChunk levelChunk) {
|
|
levelChunk.clearAllBlockEntities();
|
|
this.chunkSource.getLightEngine().setLightEnabled(levelChunk.getPos(), false);
|
|
this.entityStorage.stopTicking(levelChunk.getPos());
|
|
}
|
|
|
|
public void onChunkLoaded(ChunkPos pos) {
|
|
this.tintCaches.forEach((resolver, cache) -> cache.invalidateForChunk(pos.x, pos.z));
|
|
this.entityStorage.startTicking(pos);
|
|
}
|
|
|
|
public void onSectionBecomingNonEmpty(long sectionNode) {
|
|
this.levelRenderer.onSectionBecomingNonEmpty(sectionNode);
|
|
}
|
|
|
|
public void clearTintCaches() {
|
|
this.tintCaches.forEach((resolver, cache) -> cache.invalidateAll());
|
|
}
|
|
|
|
@Override
|
|
public boolean hasChunk(int chunkX, int chunkZ) {
|
|
return true;
|
|
}
|
|
|
|
public int getEntityCount() {
|
|
return this.entityStorage.count();
|
|
}
|
|
|
|
public void addEntity(Entity entity) {
|
|
this.removeEntity(entity.getId(), Entity.RemovalReason.DISCARDED);
|
|
this.entityStorage.addEntity(entity);
|
|
}
|
|
|
|
public void removeEntity(int id, Entity.RemovalReason reason) {
|
|
Entity entity = this.getEntities().get(id);
|
|
if (entity != null) {
|
|
entity.setRemoved(reason);
|
|
entity.onClientRemoval();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<Entity> getPushableEntities(Entity pusher, AABB boundingBox) {
|
|
LocalPlayer player = this.minecraft.player;
|
|
if (player != null && player != pusher && player.getBoundingBox().intersects(boundingBox) && EntitySelector.pushableBy(pusher).test(player)) {
|
|
return List.of(player);
|
|
}
|
|
return List.of();
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Entity getEntity(int id) {
|
|
return this.getEntities().get(id);
|
|
}
|
|
|
|
public void disconnect(Component message) {
|
|
this.connection.getConnection().disconnect(message);
|
|
}
|
|
|
|
public void animateTick(int xt, int yt, int zt) {
|
|
int r = 32;
|
|
RandomSource animateRandom = RandomSource.create();
|
|
Block markerParticleTarget = this.getMarkerParticleTarget();
|
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
|
for (int i = 0; i < 667; ++i) {
|
|
this.doAnimateTick(xt, yt, zt, 16, animateRandom, markerParticleTarget, pos);
|
|
this.doAnimateTick(xt, yt, zt, 32, animateRandom, markerParticleTarget, pos);
|
|
}
|
|
}
|
|
|
|
private @Nullable Block getMarkerParticleTarget() {
|
|
ItemStack carriedItemStack;
|
|
Item carriedItem;
|
|
if (this.minecraft.gameMode.getPlayerMode() == GameType.CREATIVE && MARKER_PARTICLE_ITEMS.contains(carriedItem = (carriedItemStack = this.minecraft.player.getMainHandItem()).getItem()) && carriedItem instanceof BlockItem) {
|
|
BlockItem blockItem = (BlockItem)carriedItem;
|
|
return blockItem.getBlock();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void doAnimateTick(int xt, int yt, int zt, int r, RandomSource animateRandom, @Nullable Block markerParticleTarget, BlockPos.MutableBlockPos pos) {
|
|
int x = xt + this.random.nextInt(r) - this.random.nextInt(r);
|
|
int y = yt + this.random.nextInt(r) - this.random.nextInt(r);
|
|
int z = zt + this.random.nextInt(r) - this.random.nextInt(r);
|
|
pos.set(x, y, z);
|
|
BlockState state = this.getBlockState(pos);
|
|
state.getBlock().animateTick(state, this, pos, animateRandom);
|
|
FluidState fluidState = this.getFluidState(pos);
|
|
if (!fluidState.isEmpty()) {
|
|
fluidState.animateTick(this, pos, animateRandom);
|
|
ParticleOptions dripParticle = fluidState.getDripParticle();
|
|
if (dripParticle != null && this.random.nextInt(10) == 0) {
|
|
boolean hasWatertightBottom = state.isFaceSturdy(this, pos, Direction.DOWN);
|
|
Vec3i below = pos.below();
|
|
this.trySpawnDripParticles((BlockPos)below, this.getBlockState((BlockPos)below), dripParticle, hasWatertightBottom);
|
|
}
|
|
}
|
|
if (markerParticleTarget == state.getBlock()) {
|
|
this.addParticle(new BlockParticleOption(ParticleTypes.BLOCK_MARKER, state), (double)x + 0.5, (double)y + 0.5, (double)z + 0.5, 0.0, 0.0, 0.0);
|
|
}
|
|
if (!state.isCollisionShapeFullBlock(this, pos)) {
|
|
for (AmbientParticle particle : this.environmentAttributes().getValue(EnvironmentAttributes.AMBIENT_PARTICLES, pos)) {
|
|
if (!particle.canSpawn(this.random)) continue;
|
|
this.addParticle(particle.particle(), (double)pos.getX() + this.random.nextDouble(), (double)pos.getY() + this.random.nextDouble(), (double)pos.getZ() + this.random.nextDouble(), 0.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void trySpawnDripParticles(BlockPos pos, BlockState state, ParticleOptions dripParticle, boolean isTopSolid) {
|
|
if (!state.getFluidState().isEmpty()) {
|
|
return;
|
|
}
|
|
VoxelShape collisionShape = state.getCollisionShape(this, pos);
|
|
double topSideHeight = collisionShape.max(Direction.Axis.Y);
|
|
if (topSideHeight < 1.0) {
|
|
if (isTopSolid) {
|
|
this.spawnFluidParticle(pos.getX(), pos.getX() + 1, pos.getZ(), pos.getZ() + 1, (double)(pos.getY() + 1) - 0.05, dripParticle);
|
|
}
|
|
} else if (!state.is(BlockTags.IMPERMEABLE)) {
|
|
double bottomSideHeight = collisionShape.min(Direction.Axis.Y);
|
|
if (bottomSideHeight > 0.0) {
|
|
this.spawnParticle(pos, dripParticle, collisionShape, (double)pos.getY() + bottomSideHeight - 0.05);
|
|
} else {
|
|
BlockPos below = pos.below();
|
|
BlockState belowState = this.getBlockState(below);
|
|
VoxelShape belowShape = belowState.getCollisionShape(this, below);
|
|
double belowTopSideHeight = belowShape.max(Direction.Axis.Y);
|
|
if (belowTopSideHeight < 1.0 && belowState.getFluidState().isEmpty()) {
|
|
this.spawnParticle(pos, dripParticle, collisionShape, (double)pos.getY() - 0.05);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void spawnParticle(BlockPos pos, ParticleOptions dripParticle, VoxelShape dripShape, double height) {
|
|
this.spawnFluidParticle((double)pos.getX() + dripShape.min(Direction.Axis.X), (double)pos.getX() + dripShape.max(Direction.Axis.X), (double)pos.getZ() + dripShape.min(Direction.Axis.Z), (double)pos.getZ() + dripShape.max(Direction.Axis.Z), height, dripParticle);
|
|
}
|
|
|
|
private void spawnFluidParticle(double x1, double x2, double z1, double z2, double y, ParticleOptions dripParticle) {
|
|
this.addParticle(dripParticle, Mth.lerp(this.random.nextDouble(), x1, x2), y, Mth.lerp(this.random.nextDouble(), z1, z2), 0.0, 0.0, 0.0);
|
|
}
|
|
|
|
@Override
|
|
public CrashReportCategory fillReportDetails(CrashReport report) {
|
|
CrashReportCategory category = super.fillReportDetails(report);
|
|
category.setDetail("Server brand", () -> this.minecraft.player.connection.serverBrand());
|
|
category.setDetail("Server type", () -> this.minecraft.getSingleplayerServer() == null ? "Non-integrated multiplayer server" : "Integrated singleplayer server");
|
|
category.setDetail("Tracked entity count", () -> String.valueOf(this.getEntityCount()));
|
|
return category;
|
|
}
|
|
|
|
@Override
|
|
public void playSeededSound(@Nullable Entity except, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed) {
|
|
if (except == this.minecraft.player) {
|
|
this.playSound(x, y, z, sound.value(), source, volume, pitch, false, seed);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playSeededSound(@Nullable Entity except, Entity sourceEntity, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed) {
|
|
if (except == this.minecraft.player) {
|
|
this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(sound.value(), source, volume, pitch, sourceEntity, seed));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playLocalSound(Entity sourceEntity, SoundEvent sound, SoundSource source, float volume, float pitch) {
|
|
this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(sound, source, volume, pitch, sourceEntity, this.random.nextLong()));
|
|
}
|
|
|
|
@Override
|
|
public void playPlayerSound(SoundEvent sound, SoundSource source, float volume, float pitch) {
|
|
if (this.minecraft.player != null) {
|
|
this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(sound, source, volume, pitch, this.minecraft.player, this.random.nextLong()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playLocalSound(double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) {
|
|
this.playSound(x, y, z, sound, source, volume, pitch, distanceDelay, this.random.nextLong());
|
|
}
|
|
|
|
private void playSound(double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay, long seed) {
|
|
double distanceToSqr = this.minecraft.gameRenderer.getMainCamera().position().distanceToSqr(x, y, z);
|
|
SimpleSoundInstance instance = new SimpleSoundInstance(sound, source, volume, pitch, RandomSource.create(seed), x, y, z);
|
|
if (distanceDelay && distanceToSqr > 100.0) {
|
|
double delayInSeconds = Math.sqrt(distanceToSqr) / 40.0;
|
|
this.minecraft.getSoundManager().playDelayed(instance, (int)(delayInSeconds * 20.0));
|
|
} else {
|
|
this.minecraft.getSoundManager().play(instance);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void createFireworks(double x, double y, double z, double xd, double yd, double zd, List<FireworkExplosion> explosions) {
|
|
if (explosions.isEmpty()) {
|
|
for (int i = 0; i < this.random.nextInt(3) + 2; ++i) {
|
|
this.addParticle(ParticleTypes.POOF, x, y, z, this.random.nextGaussian() * 0.05, 0.005, this.random.nextGaussian() * 0.05);
|
|
}
|
|
} else {
|
|
this.minecraft.particleEngine.add(new FireworkParticles.Starter(this, x, y, z, xd, yd, zd, this.minecraft.particleEngine, explosions));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void sendPacketToServer(Packet<?> packet) {
|
|
this.connection.send(packet);
|
|
}
|
|
|
|
@Override
|
|
public WorldBorder getWorldBorder() {
|
|
return this.worldBorder;
|
|
}
|
|
|
|
@Override
|
|
public RecipeAccess recipeAccess() {
|
|
return this.connection.recipes();
|
|
}
|
|
|
|
@Override
|
|
public TickRateManager tickRateManager() {
|
|
return this.tickRateManager;
|
|
}
|
|
|
|
@Override
|
|
public EnvironmentAttributeSystem environmentAttributes() {
|
|
return this.environmentAttributes;
|
|
}
|
|
|
|
@Override
|
|
public LevelTickAccess<Block> getBlockTicks() {
|
|
return BlackholeTickAccess.emptyLevelList();
|
|
}
|
|
|
|
@Override
|
|
public LevelTickAccess<Fluid> getFluidTicks() {
|
|
return BlackholeTickAccess.emptyLevelList();
|
|
}
|
|
|
|
@Override
|
|
public ClientChunkCache getChunkSource() {
|
|
return this.chunkSource;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable MapItemSavedData getMapData(MapId id) {
|
|
return this.mapData.get(id);
|
|
}
|
|
|
|
public void overrideMapData(MapId id, MapItemSavedData data) {
|
|
this.mapData.put(id, data);
|
|
}
|
|
|
|
@Override
|
|
public Scoreboard getScoreboard() {
|
|
return this.connection.scoreboard();
|
|
}
|
|
|
|
@Override
|
|
public void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, int updateFlags) {
|
|
this.levelRenderer.blockChanged(this, pos, old, current, updateFlags);
|
|
}
|
|
|
|
@Override
|
|
public void setBlocksDirty(BlockPos pos, BlockState oldState, BlockState newState) {
|
|
this.levelRenderer.setBlockDirty(pos, oldState, newState);
|
|
}
|
|
|
|
public void setSectionDirtyWithNeighbors(int chunkX, int chunkY, int chunkZ) {
|
|
this.levelRenderer.setSectionDirtyWithNeighbors(chunkX, chunkY, chunkZ);
|
|
}
|
|
|
|
public void setSectionRangeDirty(int minSectionX, int minSectionY, int minSectionZ, int maxSectionX, int maxSectionY, int maxSectionZ) {
|
|
this.levelRenderer.setSectionRangeDirty(minSectionX, minSectionY, minSectionZ, maxSectionX, maxSectionY, maxSectionZ);
|
|
}
|
|
|
|
@Override
|
|
public void destroyBlockProgress(int id, BlockPos blockPos, int progress) {
|
|
this.levelRenderer.destroyBlockProgress(id, blockPos, progress);
|
|
}
|
|
|
|
@Override
|
|
public void globalLevelEvent(int type, BlockPos pos, int data) {
|
|
this.levelEventHandler.globalLevelEvent(type, pos, data);
|
|
}
|
|
|
|
@Override
|
|
public void levelEvent(@Nullable Entity source, int type, BlockPos pos, int data) {
|
|
try {
|
|
this.levelEventHandler.levelEvent(type, pos, data);
|
|
}
|
|
catch (Throwable t) {
|
|
CrashReport report = CrashReport.forThrowable(t, "Playing level event");
|
|
CrashReportCategory category = report.addCategory("Level event being played");
|
|
category.setDetail("Block coordinates", CrashReportCategory.formatLocation(this, pos));
|
|
category.setDetail("Event source", source);
|
|
category.setDetail("Event type", type);
|
|
category.setDetail("Event data", data);
|
|
throw new ReportedException(report);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addParticle(ParticleOptions particle, double x, double y, double z, double xd, double yd, double zd) {
|
|
this.doAddParticle(particle, particle.getType().getOverrideLimiter(), false, x, y, z, xd, yd, zd);
|
|
}
|
|
|
|
@Override
|
|
public void addParticle(ParticleOptions particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, double xd, double yd, double zd) {
|
|
this.doAddParticle(particle, particle.getType().getOverrideLimiter() || overrideLimiter, alwaysShow, x, y, z, xd, yd, zd);
|
|
}
|
|
|
|
@Override
|
|
public void addAlwaysVisibleParticle(ParticleOptions particle, double x, double y, double z, double xd, double yd, double zd) {
|
|
this.doAddParticle(particle, false, true, x, y, z, xd, yd, zd);
|
|
}
|
|
|
|
@Override
|
|
public void addAlwaysVisibleParticle(ParticleOptions particle, boolean overrideLimiter, double x, double y, double z, double xd, double yd, double zd) {
|
|
this.doAddParticle(particle, particle.getType().getOverrideLimiter() || overrideLimiter, true, x, y, z, xd, yd, zd);
|
|
}
|
|
|
|
private void doAddParticle(ParticleOptions particle, boolean overrideLimiter, boolean alwaysShowParticles, double x, double y, double z, double xd, double yd, double zd) {
|
|
try {
|
|
Camera camera = this.minecraft.gameRenderer.getMainCamera();
|
|
ParticleStatus particleLevel = this.calculateParticleLevel(alwaysShowParticles);
|
|
if (overrideLimiter) {
|
|
this.minecraft.particleEngine.createParticle(particle, x, y, z, xd, yd, zd);
|
|
return;
|
|
}
|
|
if (camera.position().distanceToSqr(x, y, z) > 1024.0) {
|
|
return;
|
|
}
|
|
if (particleLevel == ParticleStatus.MINIMAL) {
|
|
return;
|
|
}
|
|
this.minecraft.particleEngine.createParticle(particle, x, y, z, xd, yd, zd);
|
|
}
|
|
catch (Throwable t) {
|
|
CrashReport report = CrashReport.forThrowable(t, "Exception while adding particle");
|
|
CrashReportCategory category = report.addCategory("Particle being added");
|
|
category.setDetail("ID", BuiltInRegistries.PARTICLE_TYPE.getKey(particle.getType()));
|
|
category.setDetail("Parameters", () -> ParticleTypes.CODEC.encodeStart(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), (Object)particle).toString());
|
|
category.setDetail("Position", () -> CrashReportCategory.formatLocation((LevelHeightAccessor)this, x, y, z));
|
|
throw new ReportedException(report);
|
|
}
|
|
}
|
|
|
|
private ParticleStatus calculateParticleLevel(boolean alwaysShowParticles) {
|
|
ParticleStatus particleLevel = this.minecraft.options.particles().get();
|
|
if (alwaysShowParticles && particleLevel == ParticleStatus.MINIMAL && this.random.nextInt(10) == 0) {
|
|
particleLevel = ParticleStatus.DECREASED;
|
|
}
|
|
if (particleLevel == ParticleStatus.DECREASED && this.random.nextInt(3) == 0) {
|
|
particleLevel = ParticleStatus.MINIMAL;
|
|
}
|
|
return particleLevel;
|
|
}
|
|
|
|
public List<AbstractClientPlayer> players() {
|
|
return this.players;
|
|
}
|
|
|
|
public List<EnderDragonPart> dragonParts() {
|
|
return this.dragonParts;
|
|
}
|
|
|
|
@Override
|
|
public Holder<Biome> getUncachedNoiseBiome(int quartX, int quartY, int quartZ) {
|
|
return this.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
|
|
}
|
|
|
|
private int getSkyFlashTime() {
|
|
return this.minecraft.options.hideLightningFlash().get() != false ? 0 : this.skyFlashTime;
|
|
}
|
|
|
|
@Override
|
|
public void setSkyFlashTime(int skyFlashTime) {
|
|
this.skyFlashTime = skyFlashTime;
|
|
}
|
|
|
|
@Override
|
|
public float getShade(Direction direction, boolean shade) {
|
|
DimensionType.CardinalLightType type = this.dimensionType().cardinalLightType();
|
|
if (!shade) {
|
|
return type == DimensionType.CardinalLightType.NETHER ? 0.9f : 1.0f;
|
|
}
|
|
return switch (direction) {
|
|
default -> throw new MatchException(null, null);
|
|
case Direction.DOWN -> {
|
|
if (type == DimensionType.CardinalLightType.NETHER) {
|
|
yield 0.9f;
|
|
}
|
|
yield 0.5f;
|
|
}
|
|
case Direction.UP -> {
|
|
if (type == DimensionType.CardinalLightType.NETHER) {
|
|
yield 0.9f;
|
|
}
|
|
yield 1.0f;
|
|
}
|
|
case Direction.NORTH, Direction.SOUTH -> 0.8f;
|
|
case Direction.WEST, Direction.EAST -> 0.6f;
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public int getBlockTint(BlockPos pos, ColorResolver resolver) {
|
|
BlockTintCache cache = (BlockTintCache)this.tintCaches.get((Object)resolver);
|
|
return cache.getColor(pos);
|
|
}
|
|
|
|
public int calculateBlockTint(BlockPos pos, ColorResolver colorResolver) {
|
|
int dist = Minecraft.getInstance().options.biomeBlendRadius().get();
|
|
if (dist == 0) {
|
|
return colorResolver.getColor(this.getBiome(pos).value(), pos.getX(), pos.getZ());
|
|
}
|
|
int count = (dist * 2 + 1) * (dist * 2 + 1);
|
|
int totalRed = 0;
|
|
int totalGreen = 0;
|
|
int totalBlue = 0;
|
|
Cursor3D cursor = new Cursor3D(pos.getX() - dist, pos.getY(), pos.getZ() - dist, pos.getX() + dist, pos.getY(), pos.getZ() + dist);
|
|
BlockPos.MutableBlockPos nextPos = new BlockPos.MutableBlockPos();
|
|
while (cursor.advance()) {
|
|
nextPos.set(cursor.nextX(), cursor.nextY(), cursor.nextZ());
|
|
int color = colorResolver.getColor(this.getBiome(nextPos).value(), nextPos.getX(), nextPos.getZ());
|
|
totalRed += (color & 0xFF0000) >> 16;
|
|
totalGreen += (color & 0xFF00) >> 8;
|
|
totalBlue += color & 0xFF;
|
|
}
|
|
return (totalRed / count & 0xFF) << 16 | (totalGreen / count & 0xFF) << 8 | totalBlue / count & 0xFF;
|
|
}
|
|
|
|
@Override
|
|
public void setRespawnData(LevelData.RespawnData respawnData) {
|
|
this.levelData.setSpawn(this.getWorldBorderAdjustedRespawnData(respawnData));
|
|
}
|
|
|
|
@Override
|
|
public LevelData.RespawnData getRespawnData() {
|
|
return this.levelData.getRespawnData();
|
|
}
|
|
|
|
public String toString() {
|
|
return "ClientLevel";
|
|
}
|
|
|
|
@Override
|
|
public ClientLevelData getLevelData() {
|
|
return this.clientLevelData;
|
|
}
|
|
|
|
@Override
|
|
public void gameEvent(Holder<GameEvent> gameEvent, Vec3 pos, GameEvent.Context context) {
|
|
}
|
|
|
|
protected Map<MapId, MapItemSavedData> getAllMapData() {
|
|
return ImmutableMap.copyOf(this.mapData);
|
|
}
|
|
|
|
protected void addMapData(Map<MapId, MapItemSavedData> mapData) {
|
|
this.mapData.putAll(mapData);
|
|
}
|
|
|
|
@Override
|
|
protected LevelEntityGetter<Entity> getEntities() {
|
|
return this.entityStorage.getEntityGetter();
|
|
}
|
|
|
|
@Override
|
|
public String gatherChunkSourceStats() {
|
|
return "Chunks[C] W: " + this.chunkSource.gatherStats() + " E: " + this.entityStorage.gatherStats();
|
|
}
|
|
|
|
@Override
|
|
public void addDestroyBlockEffect(BlockPos pos, BlockState blockState) {
|
|
if (blockState.isAir() || !blockState.shouldSpawnTerrainParticles()) {
|
|
return;
|
|
}
|
|
VoxelShape shape = blockState.getShape(this, pos);
|
|
double density = 0.25;
|
|
shape.forAllBoxes((x1, y1, z1, x2, y2, z2) -> {
|
|
double widthX = Math.min(1.0, x2 - x1);
|
|
double widthY = Math.min(1.0, y2 - y1);
|
|
double widthZ = Math.min(1.0, z2 - z1);
|
|
int countX = Math.max(2, Mth.ceil(widthX / 0.25));
|
|
int countY = Math.max(2, Mth.ceil(widthY / 0.25));
|
|
int countZ = Math.max(2, Mth.ceil(widthZ / 0.25));
|
|
for (int xx = 0; xx < countX; ++xx) {
|
|
for (int yy = 0; yy < countY; ++yy) {
|
|
for (int zz = 0; zz < countZ; ++zz) {
|
|
double relX = ((double)xx + 0.5) / (double)countX;
|
|
double relY = ((double)yy + 0.5) / (double)countY;
|
|
double relZ = ((double)zz + 0.5) / (double)countZ;
|
|
double x = relX * widthX + x1;
|
|
double y = relY * widthY + y1;
|
|
double z = relZ * widthZ + z1;
|
|
this.minecraft.particleEngine.add(new TerrainParticle(this, (double)pos.getX() + x, (double)pos.getY() + y, (double)pos.getZ() + z, relX - 0.5, relY - 0.5, relZ - 0.5, blockState, pos));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void addBreakingBlockEffect(BlockPos pos, Direction direction) {
|
|
BlockState blockState = this.getBlockState(pos);
|
|
if (blockState.getRenderShape() == RenderShape.INVISIBLE || !blockState.shouldSpawnTerrainParticles()) {
|
|
return;
|
|
}
|
|
int x = pos.getX();
|
|
int y = pos.getY();
|
|
int z = pos.getZ();
|
|
float r = 0.1f;
|
|
AABB shape = blockState.getShape(this, pos).bounds();
|
|
double xp = (double)x + this.random.nextDouble() * (shape.maxX - shape.minX - (double)0.2f) + (double)0.1f + shape.minX;
|
|
double yp = (double)y + this.random.nextDouble() * (shape.maxY - shape.minY - (double)0.2f) + (double)0.1f + shape.minY;
|
|
double zp = (double)z + this.random.nextDouble() * (shape.maxZ - shape.minZ - (double)0.2f) + (double)0.1f + shape.minZ;
|
|
if (direction == Direction.DOWN) {
|
|
yp = (double)y + shape.minY - (double)0.1f;
|
|
}
|
|
if (direction == Direction.UP) {
|
|
yp = (double)y + shape.maxY + (double)0.1f;
|
|
}
|
|
if (direction == Direction.NORTH) {
|
|
zp = (double)z + shape.minZ - (double)0.1f;
|
|
}
|
|
if (direction == Direction.SOUTH) {
|
|
zp = (double)z + shape.maxZ + (double)0.1f;
|
|
}
|
|
if (direction == Direction.WEST) {
|
|
xp = (double)x + shape.minX - (double)0.1f;
|
|
}
|
|
if (direction == Direction.EAST) {
|
|
xp = (double)x + shape.maxX + (double)0.1f;
|
|
}
|
|
this.minecraft.particleEngine.add(new TerrainParticle(this, xp, yp, zp, 0.0, 0.0, 0.0, blockState, pos).setPower(0.2f).scale(0.6f));
|
|
}
|
|
|
|
public void setServerSimulationDistance(int serverSimulationDistance) {
|
|
this.serverSimulationDistance = serverSimulationDistance;
|
|
}
|
|
|
|
public int getServerSimulationDistance() {
|
|
return this.serverSimulationDistance;
|
|
}
|
|
|
|
@Override
|
|
public FeatureFlagSet enabledFeatures() {
|
|
return this.connection.enabledFeatures();
|
|
}
|
|
|
|
@Override
|
|
public PotionBrewing potionBrewing() {
|
|
return this.connection.potionBrewing();
|
|
}
|
|
|
|
@Override
|
|
public FuelValues fuelValues() {
|
|
return this.connection.fuelValues();
|
|
}
|
|
|
|
@Override
|
|
public void explode(@Nullable Entity source, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator damageCalculator, double x, double y, double z, float r, boolean fire, Level.ExplosionInteraction interactionType, ParticleOptions smallExplosionParticles, ParticleOptions largeExplosionParticles, WeightedList<ExplosionParticleInfo> secondaryParticles, Holder<SoundEvent> explosionSound) {
|
|
}
|
|
|
|
@Override
|
|
public int getSeaLevel() {
|
|
return this.seaLevel;
|
|
}
|
|
|
|
@Override
|
|
public int getClientLeafTintColor(BlockPos pos) {
|
|
return Minecraft.getInstance().getBlockColors().getColor(this.getBlockState(pos), this, pos, 0);
|
|
}
|
|
|
|
@Override
|
|
public void registerForCleaning(CacheSlot<ClientLevel, ?> slot) {
|
|
this.connection.registerForCleaning(slot);
|
|
}
|
|
|
|
public void trackExplosionEffects(Vec3 center, float radius, int blockCount, WeightedList<ExplosionParticleInfo> blockParticles) {
|
|
this.explosionTracker.track(center, radius, blockCount, blockParticles);
|
|
}
|
|
|
|
private final class EntityCallbacks
|
|
implements LevelCallback<Entity> {
|
|
private EntityCallbacks() {
|
|
}
|
|
|
|
@Override
|
|
public void onCreated(Entity entity) {
|
|
}
|
|
|
|
@Override
|
|
public void onDestroyed(Entity entity) {
|
|
}
|
|
|
|
@Override
|
|
public void onTickingStart(Entity entity) {
|
|
ClientLevel.this.tickingEntities.add(entity);
|
|
}
|
|
|
|
@Override
|
|
public void onTickingEnd(Entity entity) {
|
|
ClientLevel.this.tickingEntities.remove(entity);
|
|
}
|
|
|
|
@Override
|
|
public void onTrackingStart(Entity entity) {
|
|
Entity entity2 = entity;
|
|
Objects.requireNonNull(entity2);
|
|
Entity entity3 = entity2;
|
|
int n = 0;
|
|
switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{AbstractClientPlayer.class, EnderDragon.class}, (Object)entity3, n)) {
|
|
case 0: {
|
|
AbstractClientPlayer player = (AbstractClientPlayer)entity3;
|
|
ClientLevel.this.players.add(player);
|
|
break;
|
|
}
|
|
case 1: {
|
|
EnderDragon dragon = (EnderDragon)entity3;
|
|
ClientLevel.this.dragonParts.addAll(Arrays.asList(dragon.getSubEntities()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTrackingEnd(Entity entity) {
|
|
entity.unRide();
|
|
Entity entity2 = entity;
|
|
Objects.requireNonNull(entity2);
|
|
Entity entity3 = entity2;
|
|
int n = 0;
|
|
switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{AbstractClientPlayer.class, EnderDragon.class}, (Object)entity3, n)) {
|
|
case 0: {
|
|
AbstractClientPlayer player = (AbstractClientPlayer)entity3;
|
|
ClientLevel.this.players.remove(player);
|
|
break;
|
|
}
|
|
case 1: {
|
|
EnderDragon dragon = (EnderDragon)entity3;
|
|
ClientLevel.this.dragonParts.removeAll(Arrays.asList(dragon.getSubEntities()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onSectionChange(Entity entity) {
|
|
}
|
|
}
|
|
|
|
public static class ClientLevelData
|
|
implements WritableLevelData {
|
|
private final boolean hardcore;
|
|
private final boolean isFlat;
|
|
private LevelData.RespawnData respawnData;
|
|
private long gameTime;
|
|
private long dayTime;
|
|
private boolean raining;
|
|
private Difficulty difficulty;
|
|
private boolean difficultyLocked;
|
|
|
|
public ClientLevelData(Difficulty difficulty, boolean hardcore, boolean isFlat) {
|
|
this.difficulty = difficulty;
|
|
this.hardcore = hardcore;
|
|
this.isFlat = isFlat;
|
|
}
|
|
|
|
@Override
|
|
public LevelData.RespawnData getRespawnData() {
|
|
return this.respawnData;
|
|
}
|
|
|
|
@Override
|
|
public long getGameTime() {
|
|
return this.gameTime;
|
|
}
|
|
|
|
@Override
|
|
public long getDayTime() {
|
|
return this.dayTime;
|
|
}
|
|
|
|
public void setGameTime(long time) {
|
|
this.gameTime = time;
|
|
}
|
|
|
|
public void setDayTime(long time) {
|
|
this.dayTime = time;
|
|
}
|
|
|
|
@Override
|
|
public void setSpawn(LevelData.RespawnData respawnData) {
|
|
this.respawnData = respawnData;
|
|
}
|
|
|
|
@Override
|
|
public boolean isThundering() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRaining() {
|
|
return this.raining;
|
|
}
|
|
|
|
@Override
|
|
public void setRaining(boolean raining) {
|
|
this.raining = raining;
|
|
}
|
|
|
|
@Override
|
|
public boolean isHardcore() {
|
|
return this.hardcore;
|
|
}
|
|
|
|
@Override
|
|
public Difficulty getDifficulty() {
|
|
return this.difficulty;
|
|
}
|
|
|
|
@Override
|
|
public boolean isDifficultyLocked() {
|
|
return this.difficultyLocked;
|
|
}
|
|
|
|
@Override
|
|
public void fillCrashReportCategory(CrashReportCategory category, LevelHeightAccessor levelHeightAccessor) {
|
|
WritableLevelData.super.fillCrashReportCategory(category, levelHeightAccessor);
|
|
}
|
|
|
|
public void setDifficulty(Difficulty difficulty) {
|
|
this.difficulty = difficulty;
|
|
}
|
|
|
|
public void setDifficultyLocked(boolean locked) {
|
|
this.difficultyLocked = locked;
|
|
}
|
|
|
|
public double getHorizonHeight(LevelHeightAccessor level) {
|
|
if (this.isFlat) {
|
|
return level.getMinY();
|
|
}
|
|
return 63.0;
|
|
}
|
|
|
|
public float voidDarknessOnsetRange() {
|
|
if (this.isFlat) {
|
|
return 1.0f;
|
|
}
|
|
return 32.0f;
|
|
}
|
|
}
|
|
}
|
|
|