2025-11-24 22:52:51 +03:00

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;
}
}
}