1084 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1084 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| 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.util.Arrays;
 | |
| import java.util.Deque;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Set;
 | |
| import java.util.function.BooleanSupplier;
 | |
| import java.util.function.Supplier;
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.CrashReport;
 | |
| import net.minecraft.CrashReportCategory;
 | |
| import net.minecraft.CrashReportDetail;
 | |
| import net.minecraft.ReportedException;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.client.Minecraft;
 | |
| import net.minecraft.client.color.block.BlockTintCache;
 | |
| import net.minecraft.client.multiplayer.prediction.BlockStatePredictionHandler;
 | |
| import net.minecraft.client.particle.FireworkParticles;
 | |
| import net.minecraft.client.player.AbstractClientPlayer;
 | |
| import net.minecraft.client.player.LocalPlayer;
 | |
| import net.minecraft.client.renderer.BiomeColors;
 | |
| import net.minecraft.client.renderer.DimensionSpecialEffects;
 | |
| 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.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.particles.BlockParticleOption;
 | |
| 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.network.chat.Component;
 | |
| import net.minecraft.network.protocol.Packet;
 | |
| import net.minecraft.resources.ResourceKey;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.sounds.SoundSource;
 | |
| import net.minecraft.tags.BlockTags;
 | |
| import net.minecraft.util.ARGB;
 | |
| import net.minecraft.util.CubicSampler;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.util.RandomSource;
 | |
| import net.minecraft.util.profiling.Profiler;
 | |
| import net.minecraft.util.profiling.ProfilerFiller;
 | |
| import net.minecraft.util.profiling.Zone;
 | |
| import net.minecraft.world.Difficulty;
 | |
| import net.minecraft.world.TickRateManager;
 | |
| 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.entity.BlockEntity;
 | |
| import net.minecraft.world.level.block.entity.FuelValues;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| 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.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.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| 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;
 | |
| 	final EntityTickList tickingEntities = new EntityTickList();
 | |
| 	private final TransientEntitySectionManager<Entity> entityStorage = new TransientEntitySectionManager<>(Entity.class, new ClientLevel.EntityCallbacks());
 | |
| 	private final ClientPacketListener connection;
 | |
| 	private final LevelRenderer levelRenderer;
 | |
| 	private final LevelEventHandler levelEventHandler;
 | |
| 	private final ClientLevel.ClientLevelData clientLevelData;
 | |
| 	private final DimensionSpecialEffects effects;
 | |
| 	private final TickRateManager tickRateManager;
 | |
| 	private final Minecraft minecraft = Minecraft.getInstance();
 | |
| 	final List<AbstractClientPlayer> players = Lists.<AbstractClientPlayer>newArrayList();
 | |
| 	final List<EnderDragonPart> dragonParts = Lists.<EnderDragonPart>newArrayList();
 | |
| 	private final Map<MapId, MapItemSavedData> mapData = Maps.<MapId, MapItemSavedData>newHashMap();
 | |
| 	private static final int CLOUD_COLOR = -1;
 | |
| 	private int skyFlashTime;
 | |
| 	private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches = Util.make(
 | |
| 		new Object2ObjectArrayMap<>(3),
 | |
| 		object2ObjectArrayMap -> {
 | |
| 			object2ObjectArrayMap.put(
 | |
| 				BiomeColors.GRASS_COLOR_RESOLVER, new BlockTintCache(blockPos -> this.calculateBlockTint(blockPos, BiomeColors.GRASS_COLOR_RESOLVER))
 | |
| 			);
 | |
| 			object2ObjectArrayMap.put(
 | |
| 				BiomeColors.FOLIAGE_COLOR_RESOLVER, new BlockTintCache(blockPos -> this.calculateBlockTint(blockPos, BiomeColors.FOLIAGE_COLOR_RESOLVER))
 | |
| 			);
 | |
| 			object2ObjectArrayMap.put(
 | |
| 				BiomeColors.DRY_FOLIAGE_COLOR_RESOLVER, new BlockTintCache(blockPos -> this.calculateBlockTint(blockPos, BiomeColors.DRY_FOLIAGE_COLOR_RESOLVER))
 | |
| 			);
 | |
| 			object2ObjectArrayMap.put(
 | |
| 				BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache(blockPos -> this.calculateBlockTint(blockPos, BiomeColors.WATER_COLOR_RESOLVER))
 | |
| 			);
 | |
| 		}
 | |
| 	);
 | |
| 	private final ClientChunkCache chunkSource;
 | |
| 	private final Deque<Runnable> lightUpdateQueue = Queues.<Runnable>newArrayDeque();
 | |
| 	private int serverSimulationDistance;
 | |
| 	private final BlockStatePredictionHandler blockStatePredictionHandler = new BlockStatePredictionHandler();
 | |
| 	private final Set<BlockEntity> globallyRenderedBlockEntities = new ReferenceOpenHashSet<>();
 | |
| 	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) {
 | |
| 		this.blockStatePredictionHandler.endPredictionsUpTo(sequence, this);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onBlockEntityAdded(BlockEntity entity) {
 | |
| 		BlockEntityRenderer<BlockEntity> blockEntityRenderer = this.minecraft.getBlockEntityRenderDispatcher().getRenderer(entity);
 | |
| 		if (blockEntityRenderer != null && blockEntityRenderer.shouldRenderOffScreen()) {
 | |
| 			this.globallyRenderedBlockEntities.add(entity);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public Set<BlockEntity> getGloballyRenderedBlockEntities() {
 | |
| 		return this.globallyRenderedBlockEntities;
 | |
| 	}
 | |
| 
 | |
| 	public void setServerVerifiedBlockState(BlockPos pos, BlockState state, int flags) {
 | |
| 		if (!this.blockStatePredictionHandler.updateKnownServerState(pos, state)) {
 | |
| 			super.setBlock(pos, state, flags, 512);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void syncBlockState(BlockPos pos, BlockState state, Vec3 playerPos) {
 | |
| 		BlockState blockState = this.getBlockState(pos);
 | |
| 		if (blockState != state) {
 | |
| 			this.setBlock(pos, state, 19);
 | |
| 			Player 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 state, int flags, int recursionLeft) {
 | |
| 		if (this.blockStatePredictionHandler.isPredicting()) {
 | |
| 			BlockState blockState = this.getBlockState(pos);
 | |
| 			boolean bl = super.setBlock(pos, state, flags, recursionLeft);
 | |
| 			if (bl) {
 | |
| 				this.blockStatePredictionHandler.retainKnownServerState(pos, blockState, this.minecraft.player);
 | |
| 			}
 | |
| 
 | |
| 			return bl;
 | |
| 		} else {
 | |
| 			return super.setBlock(pos, state, flags, recursionLeft);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public ClientLevel(
 | |
| 		ClientPacketListener connection,
 | |
| 		ClientLevel.ClientLevelData levelData,
 | |
| 		ResourceKey<Level> dimension,
 | |
| 		Holder<DimensionType> dimensionTypeRegistration,
 | |
| 		int viewDistance,
 | |
| 		int serverSimulationDistance,
 | |
| 		LevelRenderer levelRenderer,
 | |
| 		boolean isDebug,
 | |
| 		long biomeZoomSeed,
 | |
| 		int seaLevel
 | |
| 	) {
 | |
| 		super(levelData, dimension, connection.registryAccess(), dimensionTypeRegistration, true, isDebug, biomeZoomSeed, 1000000);
 | |
| 		this.connection = connection;
 | |
| 		this.chunkSource = new ClientChunkCache(this, viewDistance);
 | |
| 		this.tickRateManager = new TickRateManager();
 | |
| 		this.clientLevelData = levelData;
 | |
| 		this.levelRenderer = levelRenderer;
 | |
| 		this.seaLevel = seaLevel;
 | |
| 		this.levelEventHandler = new LevelEventHandler(this.minecraft, this, levelRenderer);
 | |
| 		this.effects = DimensionSpecialEffects.forType(dimensionTypeRegistration.value());
 | |
| 		this.setDefaultSpawnPos(new BlockPos(8, 64, 8), 0.0F);
 | |
| 		this.serverSimulationDistance = serverSimulationDistance;
 | |
| 		this.updateSkyBrightness();
 | |
| 		this.prepareWeather();
 | |
| 	}
 | |
| 
 | |
| 	public void queueLightUpdate(Runnable task) {
 | |
| 		this.lightUpdateQueue.add(task);
 | |
| 	}
 | |
| 
 | |
| 	public void pollLightUpdates() {
 | |
| 		int i = this.lightUpdateQueue.size();
 | |
| 		int j = i < 1000 ? Math.max(10, i / 10) : i;
 | |
| 
 | |
| 		for (int k = 0; k < j; k++) {
 | |
| 			Runnable runnable = (Runnable)this.lightUpdateQueue.poll();
 | |
| 			if (runnable == null) {
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			runnable.run();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public DimensionSpecialEffects effects() {
 | |
| 		return this.effects;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Runs a single tick for the world
 | |
| 	 */
 | |
| 	public void tick(BooleanSupplier hasTimeLeft) {
 | |
| 		this.getWorldBorder().tick();
 | |
| 		this.updateSkyBrightness();
 | |
| 		if (this.tickRateManager().runsNormally()) {
 | |
| 			this.tickTime();
 | |
| 		}
 | |
| 
 | |
| 		if (this.skyFlashTime > 0) {
 | |
| 			this.setSkyFlashTime(this.skyFlashTime - 1);
 | |
| 		}
 | |
| 
 | |
| 		try (Zone zone = Profiler.get().zone("blocks")) {
 | |
| 			this.chunkSource.tick(hasTimeLeft, true);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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() {
 | |
| 		ProfilerFiller profilerFiller = Profiler.get();
 | |
| 		profilerFiller.push("entities");
 | |
| 		this.tickingEntities.forEach(entity -> {
 | |
| 			if (!entity.isRemoved() && !entity.isPassenger() && !this.tickRateManager.isEntityFrozen(entity)) {
 | |
| 				this.guardEntityTick(this::tickNonPassenger, entity);
 | |
| 			}
 | |
| 		});
 | |
| 		profilerFiller.pop();
 | |
| 		this.tickBlockEntities();
 | |
| 	}
 | |
| 
 | |
| 	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((Supplier<String>)(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()));
 | |
| 		entity.tick();
 | |
| 		Profiler.get().pop();
 | |
| 
 | |
| 		for (Entity entity2 : entity.getPassengers()) {
 | |
| 			this.tickPassenger(entity, entity2);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void tickPassenger(Entity mount, Entity rider) {
 | |
| 		if (rider.isRemoved() || rider.getVehicle() != mount) {
 | |
| 			rider.stopRiding();
 | |
| 		} else if (rider instanceof Player || this.tickingEntities.contains(rider)) {
 | |
| 			rider.setOldPosAndRot();
 | |
| 			rider.tickCount++;
 | |
| 			rider.rideTick();
 | |
| 
 | |
| 			for (Entity entity : rider.getPassengers()) {
 | |
| 				this.tickPassenger(rider, entity);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void unload(LevelChunk chunk) {
 | |
| 		chunk.clearAllBlockEntities();
 | |
| 		this.chunkSource.getLightEngine().setLightEnabled(chunk.getPos(), false);
 | |
| 		this.entityStorage.stopTicking(chunk.getPos());
 | |
| 	}
 | |
| 
 | |
| 	public void onChunkLoaded(ChunkPos chunkPos) {
 | |
| 		this.tintCaches.forEach((colorResolver, blockTintCache) -> blockTintCache.invalidateForChunk(chunkPos.x, chunkPos.z));
 | |
| 		this.entityStorage.startTicking(chunkPos);
 | |
| 	}
 | |
| 
 | |
| 	public void onSectionBecomingNonEmpty(long sectionPos) {
 | |
| 		this.levelRenderer.onSectionBecomingNonEmpty(sectionPos);
 | |
| 	}
 | |
| 
 | |
| 	public void clearTintCaches() {
 | |
| 		this.tintCaches.forEach((colorResolver, blockTintCache) -> blockTintCache.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 entityId, Entity.RemovalReason reason) {
 | |
| 		Entity entity = this.getEntities().get(entityId);
 | |
| 		if (entity != null) {
 | |
| 			entity.setRemoved(reason);
 | |
| 			entity.onClientRemoval();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public List<Entity> getPushableEntities(Entity entity, AABB boundingBox) {
 | |
| 		LocalPlayer localPlayer = this.minecraft.player;
 | |
| 		return localPlayer != null
 | |
| 				&& localPlayer != entity
 | |
| 				&& localPlayer.getBoundingBox().intersects(boundingBox)
 | |
| 				&& EntitySelector.pushableBy(entity).test(localPlayer)
 | |
| 			? List.of(localPlayer)
 | |
| 			: List.of();
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public Entity getEntity(int id) {
 | |
| 		return this.getEntities().get(id);
 | |
| 	}
 | |
| 
 | |
| 	public void disconnect(Component reason) {
 | |
| 		this.connection.getConnection().disconnect(reason);
 | |
| 	}
 | |
| 
 | |
| 	public void animateTick(int posX, int posY, int posZ) {
 | |
| 		int i = 32;
 | |
| 		RandomSource randomSource = RandomSource.create();
 | |
| 		Block block = this.getMarkerParticleTarget();
 | |
| 		BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
 | |
| 
 | |
| 		for (int j = 0; j < 667; j++) {
 | |
| 			this.doAnimateTick(posX, posY, posZ, 16, randomSource, block, mutableBlockPos);
 | |
| 			this.doAnimateTick(posX, posY, posZ, 32, randomSource, block, mutableBlockPos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	private Block getMarkerParticleTarget() {
 | |
| 		if (this.minecraft.gameMode.getPlayerMode() == GameType.CREATIVE) {
 | |
| 			ItemStack itemStack = this.minecraft.player.getMainHandItem();
 | |
| 			Item item = itemStack.getItem();
 | |
| 			if (MARKER_PARTICLE_ITEMS.contains(item) && item instanceof BlockItem blockItem) {
 | |
| 				return blockItem.getBlock();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 	public void doAnimateTick(int posX, int posY, int posZ, int range, RandomSource random, @Nullable Block block, BlockPos.MutableBlockPos blockPos) {
 | |
| 		int i = posX + this.random.nextInt(range) - this.random.nextInt(range);
 | |
| 		int j = posY + this.random.nextInt(range) - this.random.nextInt(range);
 | |
| 		int k = posZ + this.random.nextInt(range) - this.random.nextInt(range);
 | |
| 		blockPos.set(i, j, k);
 | |
| 		BlockState blockState = this.getBlockState(blockPos);
 | |
| 		blockState.getBlock().animateTick(blockState, this, blockPos, random);
 | |
| 		FluidState fluidState = this.getFluidState(blockPos);
 | |
| 		if (!fluidState.isEmpty()) {
 | |
| 			fluidState.animateTick(this, blockPos, random);
 | |
| 			ParticleOptions particleOptions = fluidState.getDripParticle();
 | |
| 			if (particleOptions != null && this.random.nextInt(10) == 0) {
 | |
| 				boolean bl = blockState.isFaceSturdy(this, blockPos, Direction.DOWN);
 | |
| 				BlockPos blockPos2 = blockPos.below();
 | |
| 				this.trySpawnDripParticles(blockPos2, this.getBlockState(blockPos2), particleOptions, bl);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (block == blockState.getBlock()) {
 | |
| 			this.addParticle(new BlockParticleOption(ParticleTypes.BLOCK_MARKER, blockState), i + 0.5, j + 0.5, k + 0.5, 0.0, 0.0, 0.0);
 | |
| 		}
 | |
| 
 | |
| 		if (!blockState.isCollisionShapeFullBlock(this, blockPos)) {
 | |
| 			this.getBiome(blockPos)
 | |
| 				.value()
 | |
| 				.getAmbientParticle()
 | |
| 				.ifPresent(
 | |
| 					ambientParticleSettings -> {
 | |
| 						if (ambientParticleSettings.canSpawn(this.random)) {
 | |
| 							this.addParticle(
 | |
| 								ambientParticleSettings.getOptions(),
 | |
| 								blockPos.getX() + this.random.nextDouble(),
 | |
| 								blockPos.getY() + this.random.nextDouble(),
 | |
| 								blockPos.getZ() + this.random.nextDouble(),
 | |
| 								0.0,
 | |
| 								0.0,
 | |
| 								0.0
 | |
| 							);
 | |
| 						}
 | |
| 					}
 | |
| 				);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void trySpawnDripParticles(BlockPos blockPos, BlockState blockState, ParticleOptions particleData, boolean shapeDownSolid) {
 | |
| 		if (blockState.getFluidState().isEmpty()) {
 | |
| 			VoxelShape voxelShape = blockState.getCollisionShape(this, blockPos);
 | |
| 			double d = voxelShape.max(Direction.Axis.Y);
 | |
| 			if (d < 1.0) {
 | |
| 				if (shapeDownSolid) {
 | |
| 					this.spawnFluidParticle(blockPos.getX(), blockPos.getX() + 1, blockPos.getZ(), blockPos.getZ() + 1, blockPos.getY() + 1 - 0.05, particleData);
 | |
| 				}
 | |
| 			} else if (!blockState.is(BlockTags.IMPERMEABLE)) {
 | |
| 				double e = voxelShape.min(Direction.Axis.Y);
 | |
| 				if (e > 0.0) {
 | |
| 					this.spawnParticle(blockPos, particleData, voxelShape, blockPos.getY() + e - 0.05);
 | |
| 				} else {
 | |
| 					BlockPos blockPos2 = blockPos.below();
 | |
| 					BlockState blockState2 = this.getBlockState(blockPos2);
 | |
| 					VoxelShape voxelShape2 = blockState2.getCollisionShape(this, blockPos2);
 | |
| 					double f = voxelShape2.max(Direction.Axis.Y);
 | |
| 					if (f < 1.0 && blockState2.getFluidState().isEmpty()) {
 | |
| 						this.spawnParticle(blockPos, particleData, voxelShape, blockPos.getY() - 0.05);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void spawnParticle(BlockPos pos, ParticleOptions particleData, VoxelShape voxelShape, double y) {
 | |
| 		this.spawnFluidParticle(
 | |
| 			pos.getX() + voxelShape.min(Direction.Axis.X),
 | |
| 			pos.getX() + voxelShape.max(Direction.Axis.X),
 | |
| 			pos.getZ() + voxelShape.min(Direction.Axis.Z),
 | |
| 			pos.getZ() + voxelShape.max(Direction.Axis.Z),
 | |
| 			y,
 | |
| 			particleData
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	private void spawnFluidParticle(double xStart, double xEnd, double zStart, double zEnd, double y, ParticleOptions particleData) {
 | |
| 		this.addParticle(particleData, Mth.lerp(this.random.nextDouble(), xStart, xEnd), y, Mth.lerp(this.random.nextDouble(), zStart, zEnd), 0.0, 0.0, 0.0);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public CrashReportCategory fillReportDetails(CrashReport report) {
 | |
| 		CrashReportCategory crashReportCategory = super.fillReportDetails(report);
 | |
| 		crashReportCategory.setDetail("Server brand", (CrashReportDetail<String>)(() -> this.minecraft.player.connection.serverBrand()));
 | |
| 		crashReportCategory.setDetail(
 | |
| 			"Server type",
 | |
| 			(CrashReportDetail<String>)(() -> this.minecraft.getSingleplayerServer() == null ? "Non-integrated multiplayer server" : "Integrated singleplayer server")
 | |
| 		);
 | |
| 		crashReportCategory.setDetail("Tracked entity count", (CrashReportDetail<String>)(() -> String.valueOf(this.getEntityCount())));
 | |
| 		return crashReportCategory;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void playSeededSound(
 | |
| 		@Nullable Entity entity, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
 | |
| 	) {
 | |
| 		if (entity == this.minecraft.player) {
 | |
| 			this.playSound(x, y, z, sound.value(), source, volume, pitch, false, seed);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void playSeededSound(@Nullable Entity entity, Entity sourceEntity, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed) {
 | |
| 		if (entity == this.minecraft.player) {
 | |
| 			this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(sound.value(), source, volume, pitch, sourceEntity, seed));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void playLocalSound(Entity entity, SoundEvent sound, SoundSource source, float volume, float pitch) {
 | |
| 		this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(sound, source, volume, pitch, entity, 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 soundEvent, SoundSource source, float volume, float pitch, boolean distanceDelay, long seed) {
 | |
| 		double d = this.minecraft.gameRenderer.getMainCamera().getPosition().distanceToSqr(x, y, z);
 | |
| 		SimpleSoundInstance simpleSoundInstance = new SimpleSoundInstance(soundEvent, source, volume, pitch, RandomSource.create(seed), x, y, z);
 | |
| 		if (distanceDelay && d > 100.0) {
 | |
| 			double e = Math.sqrt(d) / 40.0;
 | |
| 			this.minecraft.getSoundManager().playDelayed(simpleSoundInstance, (int)(e * 20.0));
 | |
| 		} else {
 | |
| 			this.minecraft.getSoundManager().play(simpleSoundInstance);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void createFireworks(double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, 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, xSpeed, ySpeed, zSpeed, this.minecraft.particleEngine, explosions));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void sendPacketToServer(Packet<?> packet) {
 | |
| 		this.connection.send(packet);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public RecipeAccess recipeAccess() {
 | |
| 		return this.connection.recipes();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public TickRateManager tickRateManager() {
 | |
| 		return this.tickRateManager;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public LevelTickAccess<Block> getBlockTicks() {
 | |
| 		return BlackholeTickAccess.emptyLevelList();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public LevelTickAccess<Fluid> getFluidTicks() {
 | |
| 		return BlackholeTickAccess.emptyLevelList();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the world's chunk provider
 | |
| 	 */
 | |
| 	public ClientChunkCache getChunkSource() {
 | |
| 		return this.chunkSource;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public MapItemSavedData getMapData(MapId mapId) {
 | |
| 		return (MapItemSavedData)this.mapData.get(mapId);
 | |
| 	}
 | |
| 
 | |
| 	public void overrideMapData(MapId mapId, MapItemSavedData mapData) {
 | |
| 		this.mapData.put(mapId, mapData);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Scoreboard getScoreboard() {
 | |
| 		return this.connection.scoreboard();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {
 | |
| 		this.levelRenderer.blockChanged(this, pos, oldState, newState, flags);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setBlocksDirty(BlockPos blockPos, BlockState oldState, BlockState newState) {
 | |
| 		this.levelRenderer.setBlockDirty(blockPos, oldState, newState);
 | |
| 	}
 | |
| 
 | |
| 	public void setSectionDirtyWithNeighbors(int sectionX, int sectionY, int sectionZ) {
 | |
| 		this.levelRenderer.setSectionDirtyWithNeighbors(sectionX, sectionY, sectionZ);
 | |
| 	}
 | |
| 
 | |
| 	public void setSectionRangeDirty(int minY, int minX, int minZ, int maxY, int maxX, int maxZ) {
 | |
| 		this.levelRenderer.setSectionRangeDirty(minY, minX, minZ, maxY, maxX, maxZ);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {
 | |
| 		this.levelRenderer.destroyBlockProgress(breakerId, pos, progress);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void globalLevelEvent(int id, BlockPos pos, int data) {
 | |
| 		this.levelEventHandler.globalLevelEvent(id, pos, data);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void levelEvent(@Nullable Entity entity, int type, BlockPos pos, int data) {
 | |
| 		try {
 | |
| 			this.levelEventHandler.levelEvent(type, pos, data);
 | |
| 		} catch (Throwable var8) {
 | |
| 			CrashReport crashReport = CrashReport.forThrowable(var8, "Playing level event");
 | |
| 			CrashReportCategory crashReportCategory = crashReport.addCategory("Level event being played");
 | |
| 			crashReportCategory.setDetail("Block coordinates", CrashReportCategory.formatLocation(this, pos));
 | |
| 			crashReportCategory.setDetail("Event source", entity);
 | |
| 			crashReportCategory.setDetail("Event type", type);
 | |
| 			crashReportCategory.setDetail("Event data", data);
 | |
| 			throw new ReportedException(crashReport);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void addParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
 | |
| 		this.levelRenderer.addParticle(particle, particle.getType().getOverrideLimiter(), x, y, z, xSpeed, ySpeed, zSpeed);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void addParticle(
 | |
| 		ParticleOptions particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed
 | |
| 	) {
 | |
| 		this.levelRenderer.addParticle(particle, particle.getType().getOverrideLimiter() || overrideLimiter, alwaysShow, x, y, z, xSpeed, ySpeed, zSpeed);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void addAlwaysVisibleParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
 | |
| 		this.levelRenderer.addParticle(particle, false, true, x, y, z, xSpeed, ySpeed, zSpeed);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void addAlwaysVisibleParticle(ParticleOptions particle, boolean ignoreRange, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
 | |
| 		this.levelRenderer.addParticle(particle, particle.getType().getOverrideLimiter() || ignoreRange, true, x, y, z, xSpeed, ySpeed, zSpeed);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public List<AbstractClientPlayer> players() {
 | |
| 		return this.players;
 | |
| 	}
 | |
| 
 | |
| 	public List<EnderDragonPart> dragonParts() {
 | |
| 		return this.dragonParts;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Holder<Biome> getUncachedNoiseBiome(int x, int y, int z) {
 | |
| 		return this.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
 | |
| 	}
 | |
| 
 | |
| 	public float getSkyDarken(float partialTick) {
 | |
| 		float f = this.getTimeOfDay(partialTick);
 | |
| 		float g = 1.0F - (Mth.cos(f * (float) (Math.PI * 2)) * 2.0F + 0.2F);
 | |
| 		g = Mth.clamp(g, 0.0F, 1.0F);
 | |
| 		g = 1.0F - g;
 | |
| 		g *= 1.0F - this.getRainLevel(partialTick) * 5.0F / 16.0F;
 | |
| 		g *= 1.0F - this.getThunderLevel(partialTick) * 5.0F / 16.0F;
 | |
| 		return g * 0.8F + 0.2F;
 | |
| 	}
 | |
| 
 | |
| 	public int getSkyColor(Vec3 cameraPosition, float partialTick) {
 | |
| 		float f = this.getTimeOfDay(partialTick);
 | |
| 		Vec3 vec3 = cameraPosition.subtract(2.0, 2.0, 2.0).scale(0.25);
 | |
| 		Vec3 vec32 = CubicSampler.gaussianSampleVec3(
 | |
| 			vec3, (ix, jx, k) -> Vec3.fromRGB24(this.getBiomeManager().getNoiseBiomeAtQuart(ix, jx, k).value().getSkyColor())
 | |
| 		);
 | |
| 		float g = Mth.cos(f * (float) (Math.PI * 2)) * 2.0F + 0.5F;
 | |
| 		g = Mth.clamp(g, 0.0F, 1.0F);
 | |
| 		vec32 = vec32.scale(g);
 | |
| 		int i = ARGB.color(vec32);
 | |
| 		float h = this.getRainLevel(partialTick);
 | |
| 		if (h > 0.0F) {
 | |
| 			float j = 0.6F;
 | |
| 			float k = h * 0.75F;
 | |
| 			int l = ARGB.scaleRGB(ARGB.greyscale(i), 0.6F);
 | |
| 			i = ARGB.lerp(k, i, l);
 | |
| 		}
 | |
| 
 | |
| 		float j = this.getThunderLevel(partialTick);
 | |
| 		if (j > 0.0F) {
 | |
| 			float k = 0.2F;
 | |
| 			float m = j * 0.75F;
 | |
| 			int n = ARGB.scaleRGB(ARGB.greyscale(i), 0.2F);
 | |
| 			i = ARGB.lerp(m, i, n);
 | |
| 		}
 | |
| 
 | |
| 		int o = this.getSkyFlashTime();
 | |
| 		if (o > 0) {
 | |
| 			float m = Math.min(o - partialTick, 1.0F);
 | |
| 			m *= 0.45F;
 | |
| 			i = ARGB.lerp(m, i, ARGB.color(204, 204, 255));
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	public int getCloudColor(float partialTick) {
 | |
| 		int i = -1;
 | |
| 		float f = this.getRainLevel(partialTick);
 | |
| 		if (f > 0.0F) {
 | |
| 			int j = ARGB.scaleRGB(ARGB.greyscale(i), 0.6F);
 | |
| 			i = ARGB.lerp(f * 0.95F, i, j);
 | |
| 		}
 | |
| 
 | |
| 		float g = this.getTimeOfDay(partialTick);
 | |
| 		float h = Mth.cos(g * (float) (Math.PI * 2)) * 2.0F + 0.5F;
 | |
| 		h = Mth.clamp(h, 0.0F, 1.0F);
 | |
| 		i = ARGB.multiply(i, ARGB.colorFromFloat(1.0F, h * 0.9F + 0.1F, h * 0.9F + 0.1F, h * 0.85F + 0.15F));
 | |
| 		float k = this.getThunderLevel(partialTick);
 | |
| 		if (k > 0.0F) {
 | |
| 			int l = ARGB.scaleRGB(ARGB.greyscale(i), 0.2F);
 | |
| 			i = ARGB.lerp(k * 0.95F, i, l);
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	public float getStarBrightness(float partialTick) {
 | |
| 		float f = this.getTimeOfDay(partialTick);
 | |
| 		float g = 1.0F - (Mth.cos(f * (float) (Math.PI * 2)) * 2.0F + 0.25F);
 | |
| 		g = Mth.clamp(g, 0.0F, 1.0F);
 | |
| 		return g * g * 0.5F;
 | |
| 	}
 | |
| 
 | |
| 	public int getSkyFlashTime() {
 | |
| 		return this.minecraft.options.hideLightningFlash().get() ? 0 : this.skyFlashTime;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setSkyFlashTime(int timeFlash) {
 | |
| 		this.skyFlashTime = timeFlash;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public float getShade(Direction direction, boolean shade) {
 | |
| 		boolean bl = this.effects().constantAmbientLight();
 | |
| 		if (!shade) {
 | |
| 			return bl ? 0.9F : 1.0F;
 | |
| 		} else {
 | |
| 			switch (direction) {
 | |
| 				case DOWN:
 | |
| 					return bl ? 0.9F : 0.5F;
 | |
| 				case UP:
 | |
| 					return bl ? 0.9F : 1.0F;
 | |
| 				case NORTH:
 | |
| 				case SOUTH:
 | |
| 					return 0.8F;
 | |
| 				case WEST:
 | |
| 				case EAST:
 | |
| 					return 0.6F;
 | |
| 				default:
 | |
| 					return 1.0F;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
 | |
| 		BlockTintCache blockTintCache = this.tintCaches.get(colorResolver);
 | |
| 		return blockTintCache.getColor(blockPos);
 | |
| 	}
 | |
| 
 | |
| 	public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
 | |
| 		int i = Minecraft.getInstance().options.biomeBlendRadius().get();
 | |
| 		if (i == 0) {
 | |
| 			return colorResolver.getColor(this.getBiome(blockPos).value(), blockPos.getX(), blockPos.getZ());
 | |
| 		} else {
 | |
| 			int j = (i * 2 + 1) * (i * 2 + 1);
 | |
| 			int k = 0;
 | |
| 			int l = 0;
 | |
| 			int m = 0;
 | |
| 			Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
 | |
| 			BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
 | |
| 
 | |
| 			while (cursor3D.advance()) {
 | |
| 				mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
 | |
| 				int n = colorResolver.getColor(this.getBiome(mutableBlockPos).value(), mutableBlockPos.getX(), mutableBlockPos.getZ());
 | |
| 				k += (n & 0xFF0000) >> 16;
 | |
| 				l += (n & 0xFF00) >> 8;
 | |
| 				m += n & 0xFF;
 | |
| 			}
 | |
| 
 | |
| 			return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void setDefaultSpawnPos(BlockPos spawnPos, float spawnAngle) {
 | |
| 		this.levelData.setSpawn(spawnPos, spawnAngle);
 | |
| 	}
 | |
| 
 | |
| 	public String toString() {
 | |
| 		return "ClientLevel";
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the world's WorldInfo object
 | |
| 	 */
 | |
| 	public ClientLevel.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> map) {
 | |
| 		this.mapData.putAll(map);
 | |
| 	}
 | |
| 
 | |
| 	@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 state) {
 | |
| 		this.minecraft.particleEngine.destroy(pos, state);
 | |
| 	}
 | |
| 
 | |
| 	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 radius,
 | |
| 		boolean fire,
 | |
| 		Level.ExplosionInteraction explosionInteraction,
 | |
| 		ParticleOptions smallExplosionParticles,
 | |
| 		ParticleOptions largeExplosionParticles,
 | |
| 		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, ?> cacheSlot) {
 | |
| 		this.connection.registerForCleaning(cacheSlot);
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public static class ClientLevelData implements WritableLevelData {
 | |
| 		private final boolean hardcore;
 | |
| 		private final boolean isFlat;
 | |
| 		private BlockPos spawnPos;
 | |
| 		private float spawnAngle;
 | |
| 		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 BlockPos getSpawnPos() {
 | |
| 			return this.spawnPos;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public float getSpawnAngle() {
 | |
| 			return this.spawnAngle;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public long getGameTime() {
 | |
| 			return this.gameTime;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public long getDayTime() {
 | |
| 			return this.dayTime;
 | |
| 		}
 | |
| 
 | |
| 		public void setGameTime(long gameTime) {
 | |
| 			this.gameTime = gameTime;
 | |
| 		}
 | |
| 
 | |
| 		public void setDayTime(long dayTime) {
 | |
| 			this.dayTime = dayTime;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public void setSpawn(BlockPos spawnPoint, float spawnAngle) {
 | |
| 			this.spawnPos = spawnPoint.immutable();
 | |
| 			this.spawnAngle = spawnAngle;
 | |
| 		}
 | |
| 
 | |
| 		@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 crashReportCategory, LevelHeightAccessor level) {
 | |
| 			WritableLevelData.super.fillCrashReportCategory(crashReportCategory, level);
 | |
| 		}
 | |
| 
 | |
| 		public void setDifficulty(Difficulty difficulty) {
 | |
| 			this.difficulty = difficulty;
 | |
| 		}
 | |
| 
 | |
| 		public void setDifficultyLocked(boolean difficultyLocked) {
 | |
| 			this.difficultyLocked = difficultyLocked;
 | |
| 		}
 | |
| 
 | |
| 		public double getHorizonHeight(LevelHeightAccessor level) {
 | |
| 			return this.isFlat ? level.getMinY() : 63.0;
 | |
| 		}
 | |
| 
 | |
| 		public float voidDarknessOnsetRange() {
 | |
| 			return this.isFlat ? 1.0F : 32.0F;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	final class EntityCallbacks implements LevelCallback<Entity> {
 | |
| 		public void onCreated(Entity entity) {
 | |
| 		}
 | |
| 
 | |
| 		public void onDestroyed(Entity entity) {
 | |
| 		}
 | |
| 
 | |
| 		public void onTickingStart(Entity entity) {
 | |
| 			ClientLevel.this.tickingEntities.add(entity);
 | |
| 		}
 | |
| 
 | |
| 		public void onTickingEnd(Entity entity) {
 | |
| 			ClientLevel.this.tickingEntities.remove(entity);
 | |
| 		}
 | |
| 
 | |
| 		public void onTrackingStart(Entity entity) {
 | |
| 			switch (entity) {
 | |
| 				case AbstractClientPlayer abstractClientPlayer:
 | |
| 					ClientLevel.this.players.add(abstractClientPlayer);
 | |
| 					break;
 | |
| 				case EnderDragon enderDragon:
 | |
| 					ClientLevel.this.dragonParts.addAll(Arrays.asList(enderDragon.getSubEntities()));
 | |
| 					break;
 | |
| 				default:
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public void onTrackingEnd(Entity entity) {
 | |
| 			entity.unRide();
 | |
| 			switch (entity) {
 | |
| 				case AbstractClientPlayer abstractClientPlayer:
 | |
| 					ClientLevel.this.players.remove(abstractClientPlayer);
 | |
| 					break;
 | |
| 				case EnderDragon enderDragon:
 | |
| 					ClientLevel.this.dragonParts.removeAll(Arrays.asList(enderDragon.getSubEntities()));
 | |
| 					break;
 | |
| 				default:
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public void onSectionChange(Entity entity) {
 | |
| 		}
 | |
| 	}
 | |
| }
 |