diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java index 2830d7ace..8c5147655 100644 --- a/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java +++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java @@ -214,21 +214,17 @@ public class BlockLogic implements BlockInterface, IItemConvertible { @Override public void onPlacedByWorld(final @NotNull World world, final @NotNull TilePosc tilePos) { - if (world.isClientSide) { - ParticleEmitterRange t = this.getParticleEmitterRange(); - if (t != null) { - world.particleEmitters.add(tilePos, t); - } + ParticleEmitterRange t = this.getParticleEmitterRange(); + if (t != null) { + world.particleEmitters.add(tilePos, t); } } @Override public void onRemoved(final @NotNull World world, final @NotNull TilePosc tilePos, final int data) { - if (world.isClientSide) { - ParticleEmitterRange t = this.getParticleEmitterRange(); - if (t != null) { - world.particleEmitters.remove(tilePos); - } + ParticleEmitterRange t = this.getParticleEmitterRange(); + if (t != null) { + world.particleEmitters.remove(tilePos); } } @@ -250,11 +246,9 @@ public class BlockLogic implements BlockInterface, IItemConvertible { public void onPlacedByMob(final @NotNull World world, final @NotNull TilePosc tilePos, final @NotNull Side side, final @NotNull Mob mob, final double xHit, final double yHit) { this.onPlacedOnSide(world, tilePos, side, xHit, yHit); - if (world.isClientSide) { - ParticleEmitterRange t = this.getParticleEmitterRange(); - if (t != null) { - world.particleEmitters.add(tilePos, t); - } + ParticleEmitterRange t = this.getParticleEmitterRange(); + if (t != null) { + world.particleEmitters.add(tilePos, t); } } diff --git a/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java b/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java index f45c1cad8..63b7c6496 100644 --- a/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java +++ b/game/core/src/main/java/net/minecraft/core/block/piston/TileEntityMovingPistonBlock.java @@ -95,7 +95,7 @@ public class TileEntityMovingPistonBlock extends TileEntity implements IMovingBl this.initCollision(); } - private void initCollision() { + private final void initCollision() { assert this.worldObj != null; assert this.worldObj.isChunkLoaded(new ChunkPos(this.tilePos)); diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java b/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java index 8f2d70dfe..f8628f9ff 100644 --- a/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java +++ b/game/core/src/main/java/net/minecraft/core/world/chunk/Chunk.java @@ -12,9 +12,10 @@ import net.minecraft.core.enums.LightLayer; import net.minecraft.core.util.helper.MathHelper; import net.minecraft.core.world.World; import net.minecraft.core.world.biome.Biome; +import net.minecraft.core.world.particle.ParticleEmitterRange; +import net.minecraft.core.world.pos.ChunkSectionTilePos; import net.minecraft.core.world.pos.ChunkPos; import net.minecraft.core.world.pos.ChunkPosc; -import net.minecraft.core.world.pos.ChunkSectionTilePos; import net.minecraft.core.world.pos.ChunkTilePos; import net.minecraft.core.world.pos.ChunkTilePosc; import net.minecraft.core.world.pos.TilePos; @@ -683,6 +684,42 @@ public class Chunk { for (ChunkSection section : this.sections) { section.onLoad(this.world); } + + if (!this.world.isClientSide) { + this.registerParticleEmittersFromLoadedBlocks(); + } + } + + /** + * Registers particle emitter blocks in this chunk on the server. + */ + private void registerParticleEmittersFromLoadedBlocks() { + final ChunkSectionTilePos sectionTile = new ChunkSectionTilePos(); + for (final ChunkSection section : this.sections) { + if (section.blocks == null) { + continue; + } + final int baseY = section.yPosition * ChunkSection.SECTION_SIZE_Y; + for (sectionTile.y = 0; sectionTile.y < ChunkSection.SECTION_SIZE_Y; sectionTile.y++) { + for (sectionTile.z = 0; sectionTile.z < CHUNK_SIZE_Z; sectionTile.z++) { + for (sectionTile.x = 0; sectionTile.x < CHUNK_SIZE_X; sectionTile.x++) { + final int id = section.getBlockId(sectionTile); + if (id == 0) { + continue; + } + final Block block = Blocks.getBlock(id); + final ParticleEmitterRange range = block.getLogic().getParticleEmitterRange(); + if (range == null) { + continue; + } + final int wx = this.pos.x * CHUNK_SIZE_X + sectionTile.x; + final int wy = baseY + sectionTile.y; + final int wz = this.pos.z * CHUNK_SIZE_Z + sectionTile.z; + this.world.particleEmitters.add(new TilePos(wx, wy, wz), range); + } + } + } + } } public void onUnload() { diff --git a/game/core/src/main/java/net/minecraft/core/world/particle/ParticleEmitterRegistry.java b/game/core/src/main/java/net/minecraft/core/world/particle/ParticleEmitterRegistry.java index 7e7d6b532..14016cc2d 100644 --- a/game/core/src/main/java/net/minecraft/core/world/particle/ParticleEmitterRegistry.java +++ b/game/core/src/main/java/net/minecraft/core/world/particle/ParticleEmitterRegistry.java @@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.ConcurrentLinkedQueue; -public class ParticleEmitterRegistry { +public final class ParticleEmitterRegistry { private record EmitterNode(TilePos pos, ParticleEmitterRange type) { } @@ -51,7 +51,7 @@ public class ParticleEmitterRegistry { this.pendingRemoves.add(getPosHash(pos.x(), pos.y(), pos.z())); } - public void tick(World world, Player player, int renderDistanceChunks) { + private void drainPendingQueues() { while (!this.pendingRemoves.isEmpty()) { this.emitters.remove(this.pendingRemoves.poll().longValue()); } @@ -59,6 +59,10 @@ public class ParticleEmitterRegistry { EmitterNode node = this.pendingAdds.poll(); this.emitters.put(getPosHash(node.pos.x(), node.pos.y(), node.pos.z()), node); } + } + + public void tick(World world, Player player, int renderDistanceChunks) { + this.drainPendingQueues(); if (this.emitters.isEmpty()) return; @@ -100,4 +104,58 @@ public class ParticleEmitterRegistry { } } } + + /** + * runs/ticks emitters if any player is within range. + */ + public void tickServer(World world, Iterable players, int renderDistanceChunks) { + this.drainPendingQueues(); + + if (this.emitters.isEmpty()) return; + + var iterator = this.emitters.long2ObjectEntrySet().fastIterator(); + + while (iterator.hasNext()) { + Long2ObjectMap.Entry entry = iterator.next(); + EmitterNode node = entry.getValue(); + TilePos pos = node.pos; + ParticleEmitterRange type = node.type; + + if (!world.isBlockLoaded(pos)) { + iterator.remove(); + continue; + } + + double maxDistSq = type.getMaxDistanceSq(renderDistanceChunks); + double distSq = Double.POSITIVE_INFINITY; + for (Player player : players) { + double dx = pos.x() - player.x; + double dy = pos.y() - player.y; + double dz = pos.z() - player.z; + double d = dx * dx + dy * dy + dz * dz; + if (d <= maxDistSq && d < distSq) { + distSq = d; + } + } + + if (distSq == Double.POSITIVE_INFINITY) continue; + + int throttle = 4; + if (distSq > 4096) { + throttle = 64; + } else if (distSq > 1024) { + throttle = 16; + } + + if (world.rand.nextInt(throttle) == 0) { + Block block = world.getBlockType(pos); + + if (block.getLogic().getParticleEmitterRange() != null) { + block.animationTick(world, pos, world.rand); + } else { + iterator.remove(); + } + } + } + } } \ No newline at end of file diff --git a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java index 54b837927..c073953af 100644 --- a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java @@ -135,6 +135,7 @@ public class MinecraftServer public int sleepPercentage = 100; public int maxPlayers = 20; public int viewDistance = 10; + public int particleRenderDistance = 10; public Gamemode defaultGamemode; public static String statsToken; public static boolean statsStatus; @@ -243,6 +244,7 @@ public class MinecraftServer NetworkManager.TIMEOUT_TIME = propertyManager.getIntProperty("login-timeout-time", 1800 * 50); maxPlayers = propertyManager.getIntProperty("max-players", 20); viewDistance = propertyManager.getIntProperty("view-distance", 10); + particleRenderDistance = Math.max(1, Math.min(24, propertyManager.getIntProperty("particle-render-distance", 10))); language = propertyManager.getStringProperty("language", "en_US"); disablePhotoMode = propertyManager.getBooleanProperty("disable-photomode", false); name = propertyManager.getStringProperty("name", ""); diff --git a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java index 1cbab820d..8be6bfd6a 100644 --- a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java +++ b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java @@ -279,6 +279,10 @@ public class WorldServer extends World { public void tick() { super.tick(); + if (!this.players.isEmpty()) { + this.particleEmitters.tickServer(this, this.players, this.mcServer.particleRenderDistance); + } + if (System.currentTimeMillis() - lastWeatherSend > 1000) { mcServer.playerList.sendPacketToAllPlayers(new PacketWeatherStatus(dimension.id, getCurrentWeather() != null ? getCurrentWeather().getId() : -1, getWeatherManager().getNextWeather() != null ? getWeatherManager().getNextWeather().getId() : -1, getWeatherManager().getWeatherDuration(), getWeatherManager().getWeatherIntensity(), getWeatherManager().getWeatherPower())); lastWeatherSend = System.currentTimeMillis(); diff --git a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java index 96b7562d4..ef1a9abbe 100644 --- a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java +++ b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java @@ -43,24 +43,12 @@ public class SmeltingGenerator { furnaceRecipe("cobble_gloomstone_to_gloomstone", Blocks.COBBLE_GLOOMSTONE.getDefaultStack(), Blocks.GLOOMSTONE.getDefaultStack()); blastRecipe("cobble_stone_to_slate", Blocks.COBBLE_STONE.getDefaultStack(), Blocks.SLATE.getDefaultStack()); - blastRecipe("cobble_stone_to_stone", Blocks.COBBLE_STONE.getDefaultStack(), Blocks.COBBLE_STONE.getDefaultStack(), Blocks.STONE.getDefaultStack()); - blastRecipe("cobble_basalt_to_olivine", Blocks.COBBLE_BASALT.getDefaultStack(), Items.OLIVINE.getDefaultStack()); - blastRecipe("cobble_basalt_to_basalt", Blocks.COBBLE_BASALT.getDefaultStack(), Blocks.COBBLE_BASALT.getDefaultStack(), Blocks.BASALT.getDefaultStack()); - blastRecipe("cobble_granite_to_quartz", Blocks.COBBLE_GRANITE.getDefaultStack(), Items.QUARTZ.getDefaultStack()); - blastRecipe("cobble_granite_to_granite", Blocks.COBBLE_GRANITE.getDefaultStack(), Blocks.COBBLE_GRANITE.getDefaultStack(), Blocks.GRANITE.getDefaultStack()); - blastRecipe("cobble_limestone_to_marble", Blocks.COBBLE_LIMESTONE.getDefaultStack(), Blocks.MARBLE.getDefaultStack()); - blastRecipe("cobble_limestone_to_limestone", Blocks.COBBLE_LIMESTONE.getDefaultStack(), Blocks.COBBLE_LIMESTONE.getDefaultStack(), Blocks.LIMESTONE.getDefaultStack()); - blastRecipe("cobble_netherrack_to_magma", Blocks.COBBLE_NETHERRACK.getDefaultStack(), Blocks.MAGMA.getDefaultStack()); - blastRecipe("cobble_netherrack_to_netherrack", Blocks.COBBLE_NETHERRACK.getDefaultStack(), Blocks.COBBLE_NETHERRACK.getDefaultStack(), Blocks.NETHERRACK.getDefaultStack()); - blastRecipe("brimstone_to_brimthaw", Blocks.BRIMSTONE.getDefaultStack(), Blocks.BRIMTHAW.getDefaultStack()); - blastRecipe("cobble_permafrost_to_permafrost", Blocks.COBBLE_PERMAFROST.getDefaultStack(), Blocks.PERMAFROST.getDefaultStack()); - blastRecipe("cobble_gloomstone_to_gloomstone", Blocks.COBBLE_GLOOMSTONE.getDefaultStack(), Blocks.GLOOMSTONE.getDefaultStack()); blastRecipe("iron_ingot_and_coal_to_crude_steel", Items.INGOT_IRON.getDefaultStack(), Items.COAL.getDefaultStack(), Items.INGOT_STEEL_CRUDE.getDefaultStack()); blastRecipe("iron_ingot_and_charcoal_to_crude_steel", Items.INGOT_IRON.getDefaultStack(), new ItemStack(Items.COAL, 1, 1), Items.INGOT_STEEL_CRUDE.getDefaultStack());