From f4607ae15eb6f5fd7ed6dc029fd0d39a370460e5 Mon Sep 17 00:00:00 2001 From: luk Date: Tue, 27 Jan 2026 21:20:28 +0000 Subject: [PATCH] src: update 6 files --- .../system/ChunkBlockTickSystem.java | 209 +-- .../hytale/builtin/fluid/FluidSystems.java | 645 +++---- .../event/events/ecs/GatherBlockEvent.java | 1 + .../interaction/BlockHarvestUtils.java | 1547 +++++++++-------- .../world/chunk/systems/ChunkSystems.java | 689 ++++---- .../IndexedStorageChunkStorageProvider.java | 70 +- 6 files changed, 1585 insertions(+), 1576 deletions(-) diff --git a/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java b/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java index 5ed3448c..06d0f41e 100644 --- a/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java +++ b/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java @@ -21,122 +21,123 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; + +import javax.annotation.Nonnull; import java.time.Instant; import java.util.Set; import java.util.logging.Level; -import javax.annotation.Nonnull; public class ChunkBlockTickSystem { - protected static final HytaleLogger LOGGER = BlockTickPlugin.get().getLogger(); + protected static final HytaleLogger LOGGER = BlockTickPlugin.get().getLogger(); - public ChunkBlockTickSystem() { - } + public ChunkBlockTickSystem() { + } - public static class PreTick extends EntityTickingSystem { - private static final ComponentType COMPONENT_TYPE = BlockChunk.getComponentType(); + public static class PreTick extends EntityTickingSystem { + private static final ComponentType COMPONENT_TYPE = BlockChunk.getComponentType(); - public PreTick() { - } + public PreTick() { + } - @Override - public Query getQuery() { - return COMPONENT_TYPE; - } + @Override + public Query getQuery() { + return COMPONENT_TYPE; + } - @Override - public boolean isParallel(int archetypeChunkSize, int taskCount) { - return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); - } + @Override + public boolean isParallel(int archetypeChunkSize, int taskCount) { + return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); + } - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - Instant time = commandBuffer.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime(); - BlockChunk chunk = archetypeChunk.getComponent(index, COMPONENT_TYPE); + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + Instant time = commandBuffer.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime(); + BlockChunk chunk = archetypeChunk.getComponent(index, COMPONENT_TYPE); - assert chunk != null; + assert chunk != null; - try { - chunk.preTick(time); - } catch (Throwable var9) { - ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to pre-tick chunk: %s", chunk); - } - } - } - - public static class Ticking extends EntityTickingSystem { - private static final ComponentType COMPONENT_TYPE = WorldChunk.getComponentType(); - private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class)); - - public Ticking() { - } - - @Override - public Query getQuery() { - return COMPONENT_TYPE; - } - - @Nonnull - @Override - public Set> getDependencies() { - return DEPENDENCIES; - } - - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - Ref reference = archetypeChunk.getReferenceTo(index); - WorldChunk worldChunk = archetypeChunk.getComponent(index, COMPONENT_TYPE); - - try { - tick(reference, worldChunk); - } catch (Throwable var9) { - ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to tick chunk: %s", worldChunk); - } - } - - protected static void tick(Ref ref, @Nonnull WorldChunk worldChunk) { - int ticked = worldChunk.getBlockChunk().forEachTicking(ref, worldChunk, (r, c, localX, localY, localZ, blockId) -> { - World world = c.getWorld(); - int blockX = c.getX() << 5 | localX; - int blockZ = c.getZ() << 5 | localZ; - return tickProcedure(world, c, blockX, localY, blockZ, blockId); - }); - if (ticked > 0) { - ChunkBlockTickSystem.LOGGER.at(Level.FINER).log("Ticked %d blocks in chunk (%d, %d)", ticked, worldChunk.getX(), worldChunk.getZ()); - } - } - - protected static BlockTickStrategy tickProcedure(@Nonnull World world, @Nonnull WorldChunk chunk, int blockX, int blockY, int blockZ, int blockId) { - if (world.getWorldConfig().isBlockTicking() && BlockTickManager.hasBlockTickProvider()) { - TickProcedure procedure = BlockTickPlugin.get().getTickProcedure(blockId); - if (procedure == null) { - return BlockTickStrategy.IGNORED; - } else { - try { - return procedure.onTick(world, chunk, blockX, blockY, blockZ, blockId); - } catch (Throwable var9) { - BlockType blockType = BlockType.getAssetMap().getAsset(blockId); - ChunkBlockTickSystem.LOGGER - .at(Level.WARNING) - .withCause(var9) - .log("Failed to tick block at (%d, %d, %d) ID %s in world %s:", blockX, blockY, blockZ, blockType.getId(), world.getName()); - return BlockTickStrategy.SLEEP; - } + try { + chunk.preTick(time); + } catch (Throwable var9) { + ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to pre-tick chunk: %s", chunk); } - } else { - return BlockTickStrategy.IGNORED; - } - } - } + } + } + + public static class Ticking extends EntityTickingSystem { + private static final ComponentType COMPONENT_TYPE = WorldChunk.getComponentType(); + private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class)); + + public Ticking() { + } + + @Override + public Query getQuery() { + return COMPONENT_TYPE; + } + + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + Ref reference = archetypeChunk.getReferenceTo(index); + WorldChunk worldChunk = archetypeChunk.getComponent(index, COMPONENT_TYPE); + + try { + tick(reference, worldChunk); + } catch (Throwable var9) { + ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to tick chunk: %s", worldChunk); + } + } + + protected static void tick(Ref ref, @Nonnull WorldChunk worldChunk) { + int ticked = worldChunk.getBlockChunk().forEachTicking(ref, worldChunk, (r, c, localX, localY, localZ, blockId) -> { + World world = c.getWorld(); + int blockX = c.getX() << 5 | localX; + int blockZ = c.getZ() << 5 | localZ; + return tickProcedure(world, c, blockX, localY, blockZ, blockId); + }); + if (ticked > 0) { + ChunkBlockTickSystem.LOGGER.at(Level.FINER).log("Ticked %d blocks in chunk (%d, %d)", ticked, worldChunk.getX(), worldChunk.getZ()); + } + } + + protected static BlockTickStrategy tickProcedure(@Nonnull World world, @Nonnull WorldChunk chunk, int blockX, int blockY, int blockZ, int blockId) { + if (world.getWorldConfig().isBlockTicking() && BlockTickManager.hasBlockTickProvider()) { + TickProcedure procedure = BlockTickPlugin.get().getTickProcedure(blockId); + if (procedure == null) { + return BlockTickStrategy.IGNORED; + } else { + try { + return procedure.onTick(world, chunk, blockX, blockY, blockZ, blockId); + } catch (Throwable var9) { + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + ChunkBlockTickSystem.LOGGER + .at(Level.WARNING) + .withCause(var9) + .log("Failed to tick block at (%d, %d, %d) ID %s in world %s:", blockX, blockY, blockZ, blockType.getId(), world.getName()); + return BlockTickStrategy.SLEEP; + } + } + } else { + return BlockTickStrategy.IGNORED; + } + } + } } diff --git a/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java b/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java index 2865c7d9..9a5d16d8 100644 --- a/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java +++ b/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java @@ -41,373 +41,374 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import javax.annotation.Nonnull; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.logging.Level; -import javax.annotation.Nonnull; public class FluidSystems { - @Nonnull - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private static final int MAX_CHANGES_PER_PACKET = 1024; + @Nonnull + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final int MAX_CHANGES_PER_PACKET = 1024; - public FluidSystems() { - } + public FluidSystems() { + } - public static class EnsureFluidSection extends HolderSystem { - @Nonnull - private static final Query QUERY = Query.and(ChunkSection.getComponentType(), Query.not(FluidSection.getComponentType())); + public static class EnsureFluidSection extends HolderSystem { + @Nonnull + private static final Query QUERY = Query.and(ChunkSection.getComponentType(), Query.not(FluidSection.getComponentType())); - public EnsureFluidSection() { - } + public EnsureFluidSection() { + } - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.addComponent(FluidSection.getComponentType(), new FluidSection()); - } + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + holder.addComponent(FluidSection.getComponentType(), new FluidSection()); + } - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } + @Nonnull + @Override + public Set> getDependencies() { + return RootDependency.firstSet(); + } + } - public static class LoadPacketGenerator extends ChunkStore.LoadFuturePacketDataQuerySystem { - public LoadPacketGenerator() { - } + public static class LoadPacketGenerator extends ChunkStore.LoadFuturePacketDataQuerySystem { + public LoadPacketGenerator() { + } - public void fetch( - int index, - @Nonnull ArchetypeChunk archetypeChunk, - Store store, - @Nonnull CommandBuffer commandBuffer, - PlayerRef query, - @Nonnull List> results - ) { - ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, ChunkColumn.getComponentType()); + public void fetch( + int index, + @Nonnull ArchetypeChunk archetypeChunk, + Store store, + @Nonnull CommandBuffer commandBuffer, + PlayerRef query, + @Nonnull List> results + ) { + ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, ChunkColumn.getComponentType()); - assert chunkColumnComponent != null; + assert chunkColumnComponent != null; - for (Ref sectionRef : chunkColumnComponent.getSections()) { - FluidSection fluidSectionComponent = commandBuffer.getComponent(sectionRef, FluidSection.getComponentType()); - if (fluidSectionComponent != null) { - results.add(fluidSectionComponent.getCachedPacket().exceptionally(throwable -> { - if (throwable != null) { - FluidSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); - } + for (Ref sectionRef : chunkColumnComponent.getSections()) { + FluidSection fluidSectionComponent = commandBuffer.getComponent(sectionRef, FluidSection.getComponentType()); + if (fluidSectionComponent != null) { + results.add(fluidSectionComponent.getCachedPacket().exceptionally(throwable -> { + if (throwable != null) { + FluidSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); + } - return null; - }).thenApply(Function.identity())); + return null; + }).thenApply(Function.identity())); + } } - } - } + } - @Override - public Query getQuery() { - return ChunkColumn.getComponentType(); - } - } + @Override + public Query getQuery() { + return ChunkColumn.getComponentType(); + } + } - public static class MigrateFromColumn extends ChunkColumnMigrationSystem { - @Nonnull - private final Query QUERY = Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType()); - @Nonnull - private final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.BEFORE, LegacyModule.MigrateLegacySections.class)); + public static class MigrateFromColumn extends ChunkColumnMigrationSystem { + @Nonnull + private final Query QUERY = Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType()); + @Nonnull + private final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.BEFORE, LegacyModule.MigrateLegacySections.class)); - public MigrateFromColumn() { - } + public MigrateFromColumn() { + } - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType()); + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType()); - assert chunkColumnComponent != null; + assert chunkColumnComponent != null; - BlockChunk blockChunkComponent = holder.getComponent(BlockChunk.getComponentType()); + BlockChunk blockChunkComponent = holder.getComponent(BlockChunk.getComponentType()); - assert blockChunkComponent != null; + assert blockChunkComponent != null; - Holder[] sections = chunkColumnComponent.getSectionHolders(); - BlockSection[] legacySections = blockChunkComponent.getMigratedSections(); - if (legacySections != null) { - for (int i = 0; i < sections.length; i++) { - Holder section = sections[i]; - BlockSection paletteSection = legacySections[i]; - if (section != null && paletteSection != null) { - FluidSection fluid = paletteSection.takeMigratedFluid(); - if (fluid != null) { - section.putComponent(FluidSection.getComponentType(), fluid); - blockChunkComponent.markNeedsSaving(); - } - } + Holder[] sections = chunkColumnComponent.getSectionHolders(); + BlockSection[] legacySections = blockChunkComponent.getMigratedSections(); + if (legacySections != null) { + for (int i = 0; i < sections.length; i++) { + Holder section = sections[i]; + BlockSection paletteSection = legacySections[i]; + if (section != null && paletteSection != null) { + FluidSection fluid = paletteSection.takeMigratedFluid(); + if (fluid != null) { + section.putComponent(FluidSection.getComponentType(), fluid); + blockChunkComponent.markNeedsSaving(); + } + } + } } - } - } + } - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } - @Nonnull - @Override - public Query getQuery() { - return this.QUERY; - } + @Nonnull + @Override + public Query getQuery() { + return this.QUERY; + } - @Nonnull - @Override - public Set> getDependencies() { - return this.DEPENDENCIES; - } - } + @Nonnull + @Override + public Set> getDependencies() { + return this.DEPENDENCIES; + } + } - public static class ReplicateChanges extends EntityTickingSystem implements RunWhenPausedSystem { - @Nonnull - private static final Query QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.getComponentType()); + public static class ReplicateChanges extends EntityTickingSystem implements RunWhenPausedSystem { + @Nonnull + private static final Query QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.getComponentType()); - public ReplicateChanges() { - } + public ReplicateChanges() { + } - @Override - public boolean isParallel(int archetypeChunkSize, int taskCount) { - return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); - } + @Override + public boolean isParallel(int archetypeChunkSize, int taskCount) { + return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); + } - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType()); + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType()); - assert fluidSectionComponent != null; + assert fluidSectionComponent != null; - IntOpenHashSet changes = fluidSectionComponent.getAndClearChangedPositions(); - if (!changes.isEmpty()) { + IntOpenHashSet changes = fluidSectionComponent.getAndClearChangedPositions(); + if (!changes.isEmpty()) { + ChunkSection chunkSectionComponent = archetypeChunk.getComponent(index, ChunkSection.getComponentType()); + + assert chunkSectionComponent != null; + + World world = commandBuffer.getExternalData().getWorld(); + WorldChunk worldChunkComponent = commandBuffer.getComponent(chunkSectionComponent.getChunkColumnReference(), WorldChunk.getComponentType()); + int sectionY = chunkSectionComponent.getY(); + world.execute(() -> { + if (worldChunkComponent != null && worldChunkComponent.getWorld() != null) { + worldChunkComponent.getWorld().getChunkLighting().invalidateLightInChunkSection(worldChunkComponent, sectionY); + } + }); + Collection playerRefs = store.getExternalData().getWorld().getPlayerRefs(); + if (playerRefs.isEmpty()) { + changes.clear(); + } else { + long chunkIndex = ChunkUtil.indexChunk(fluidSectionComponent.getX(), fluidSectionComponent.getZ()); + if (changes.size() >= 1024) { + ObjectArrayList playersCopy = new ObjectArrayList<>(playerRefs); + fluidSectionComponent.getCachedPacket().whenComplete((packetx, throwable) -> { + if (throwable != null) { + FluidSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); + } else { + for (PlayerRef playerRefx : playersCopy) { + Ref refx = playerRefx.getReference(); + if (refx != null && refx.isValid()) { + ChunkTracker trackerx = playerRefx.getChunkTracker(); + if (trackerx.isLoaded(chunkIndex)) { + playerRefx.getPacketHandler().writeNoCache(packetx); + } + } + } + } + }); + changes.clear(); + } else { + if (changes.size() == 1) { + int change = changes.iterator().nextInt(); + int x = ChunkUtil.minBlock(fluidSectionComponent.getX()) + ChunkUtil.xFromIndex(change); + int y = ChunkUtil.minBlock(fluidSectionComponent.getY()) + ChunkUtil.yFromIndex(change); + int z = ChunkUtil.minBlock(fluidSectionComponent.getZ()) + ChunkUtil.zFromIndex(change); + int fluid = fluidSectionComponent.getFluidId(change); + byte level = fluidSectionComponent.getFluidLevel(change); + ServerSetFluid packet = new ServerSetFluid(x, y, z, fluid, level); + + for (PlayerRef playerRef : playerRefs) { + Ref ref = playerRef.getReference(); + if (ref != null && ref.isValid()) { + ChunkTracker tracker = playerRef.getChunkTracker(); + if (tracker.isLoaded(chunkIndex)) { + playerRef.getPacketHandler().writeNoCache(packet); + } + } + } + } else { + SetFluidCmd[] cmds = new SetFluidCmd[changes.size()]; + IntIterator iter = changes.intIterator(); + int i = 0; + + while (iter.hasNext()) { + int change = iter.nextInt(); + int fluid = fluidSectionComponent.getFluidId(change); + byte level = fluidSectionComponent.getFluidLevel(change); + cmds[i++] = new SetFluidCmd((short) change, fluid, level); + } + + ServerSetFluids packet = new ServerSetFluids( + fluidSectionComponent.getX(), fluidSectionComponent.getY(), fluidSectionComponent.getZ(), cmds + ); + + for (PlayerRef playerRefx : playerRefs) { + Ref ref = playerRefx.getReference(); + if (ref != null && ref.isValid()) { + ChunkTracker tracker = playerRefx.getChunkTracker(); + if (tracker.isLoaded(chunkIndex)) { + playerRefx.getPacketHandler().writeNoCache(packet); + } + } + } + } + + changes.clear(); + } + } + } + } + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Nonnull + @Override + public Set> getDependencies() { + return RootDependency.lastSet(); + } + } + + public static class SetupSection extends HolderSystem { + @Nonnull + private static final Query QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.getComponentType()); + @Nonnull + private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, FluidSystems.MigrateFromColumn.class)); + + public SetupSection() { + } + + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + ChunkSection chunkSectionComponent = holder.getComponent(ChunkSection.getComponentType()); + + assert chunkSectionComponent != null; + + FluidSection fluidSectionComponent = holder.getComponent(FluidSection.getComponentType()); + + assert fluidSectionComponent != null; + + fluidSectionComponent.load(chunkSectionComponent.getX(), chunkSectionComponent.getY(), chunkSectionComponent.getZ()); + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + } + + public static class Ticking extends EntityTickingSystem { + @Nonnull + private static final Query QUERY = Query.and(FluidSection.getComponentType(), ChunkSection.getComponentType()); + @Nonnull + private static final Set> DEPENDENCIES = Set.of( + new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class), new SystemDependency<>(Order.BEFORE, ChunkBlockTickSystem.Ticking.class) + ); + + public Ticking() { + } + + @Override + public boolean isParallel(int archetypeChunkSize, int taskCount) { + return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { ChunkSection chunkSectionComponent = archetypeChunk.getComponent(index, ChunkSection.getComponentType()); assert chunkSectionComponent != null; - World world = commandBuffer.getExternalData().getWorld(); - WorldChunk worldChunkComponent = commandBuffer.getComponent(chunkSectionComponent.getChunkColumnReference(), WorldChunk.getComponentType()); - int sectionY = chunkSectionComponent.getY(); - world.execute(() -> { - if (worldChunkComponent != null && worldChunkComponent.getWorld() != null) { - worldChunkComponent.getWorld().getChunkLighting().invalidateLightInChunkSection(worldChunkComponent, sectionY); - } - }); - Collection playerRefs = store.getExternalData().getWorld().getPlayerRefs(); - if (playerRefs.isEmpty()) { - changes.clear(); - } else { - long chunkIndex = ChunkUtil.indexChunk(fluidSectionComponent.getX(), fluidSectionComponent.getZ()); - if (changes.size() >= 1024) { - ObjectArrayList playersCopy = new ObjectArrayList<>(playerRefs); - fluidSectionComponent.getCachedPacket().whenComplete((packetx, throwable) -> { - if (throwable != null) { - FluidSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); - } else { - for (PlayerRef playerRefx : playersCopy) { - Ref refx = playerRefx.getReference(); - if (refx != null && refx.isValid()) { - ChunkTracker trackerx = playerRefx.getChunkTracker(); - if (trackerx.isLoaded(chunkIndex)) { - playerRefx.getPacketHandler().writeNoCache(packetx); - } - } + FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType()); + + assert fluidSectionComponent != null; + + Ref chunkRef = chunkSectionComponent.getChunkColumnReference(); + BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtIndex(fluidSectionComponent.getY()); + if (blockSection != null) { + if (blockSection.getTickingBlocksCountCopy() != 0) { + FluidTicker.CachedAccessor accessor = FluidTicker.CachedAccessor.of(commandBuffer, fluidSectionComponent, blockSection, 5); + blockSection.forEachTicking(accessor, commandBuffer, fluidSectionComponent.getY(), (accessor1, commandBuffer1, x, y, z, block) -> { + FluidSection fluidSection1 = accessor1.selfFluidSection; + BlockSection blockSection1 = accessor1.selfBlockSection; + int fluidId = fluidSection1.getFluidId(x, y, z); + if (fluidId == 0) { + return BlockTickStrategy.IGNORED; + } else { + Fluid fluid = Fluid.getAssetMap().getAsset(fluidId); + int blockX = fluidSection1.getX() << 5 | x; + int blockZ = fluidSection1.getZ() << 5 | z; + return fluid.getTicker().tick(commandBuffer1, accessor1, fluidSection1, blockSection1, fluid, fluidId, blockX, y, blockZ); } - } - }); - changes.clear(); - } else { - if (changes.size() == 1) { - int change = changes.iterator().nextInt(); - int x = ChunkUtil.minBlock(fluidSectionComponent.getX()) + ChunkUtil.xFromIndex(change); - int y = ChunkUtil.minBlock(fluidSectionComponent.getY()) + ChunkUtil.yFromIndex(change); - int z = ChunkUtil.minBlock(fluidSectionComponent.getZ()) + ChunkUtil.zFromIndex(change); - int fluid = fluidSectionComponent.getFluidId(change); - byte level = fluidSectionComponent.getFluidLevel(change); - ServerSetFluid packet = new ServerSetFluid(x, y, z, fluid, level); - - for (PlayerRef playerRef : playerRefs) { - Ref ref = playerRef.getReference(); - if (ref != null && ref.isValid()) { - ChunkTracker tracker = playerRef.getChunkTracker(); - if (tracker.isLoaded(chunkIndex)) { - playerRef.getPacketHandler().writeNoCache(packet); - } - } - } - } else { - SetFluidCmd[] cmds = new SetFluidCmd[changes.size()]; - IntIterator iter = changes.intIterator(); - int i = 0; - - while (iter.hasNext()) { - int change = iter.nextInt(); - int fluid = fluidSectionComponent.getFluidId(change); - byte level = fluidSectionComponent.getFluidLevel(change); - cmds[i++] = new SetFluidCmd((short)change, fluid, level); - } - - ServerSetFluids packet = new ServerSetFluids( - fluidSectionComponent.getX(), fluidSectionComponent.getY(), fluidSectionComponent.getZ(), cmds - ); - - for (PlayerRef playerRefx : playerRefs) { - Ref ref = playerRefx.getReference(); - if (ref != null && ref.isValid()) { - ChunkTracker tracker = playerRefx.getChunkTracker(); - if (tracker.isLoaded(chunkIndex)) { - playerRefx.getPacketHandler().writeNoCache(packet); - } - } - } - } - - changes.clear(); - } + }); + } } - } - } + } - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.lastSet(); - } - } - - public static class SetupSection extends HolderSystem { - @Nonnull - private static final Query QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.getComponentType()); - @Nonnull - private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, FluidSystems.MigrateFromColumn.class)); - - public SetupSection() { - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - ChunkSection chunkSectionComponent = holder.getComponent(ChunkSection.getComponentType()); - - assert chunkSectionComponent != null; - - FluidSection fluidSectionComponent = holder.getComponent(FluidSection.getComponentType()); - - assert fluidSectionComponent != null; - - fluidSectionComponent.load(chunkSectionComponent.getX(), chunkSectionComponent.getY(), chunkSectionComponent.getZ()); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return DEPENDENCIES; - } - } - - public static class Ticking extends EntityTickingSystem { - @Nonnull - private static final Query QUERY = Query.and(FluidSection.getComponentType(), ChunkSection.getComponentType()); - @Nonnull - private static final Set> DEPENDENCIES = Set.of( - new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class), new SystemDependency<>(Order.BEFORE, ChunkBlockTickSystem.Ticking.class) - ); - - public Ticking() { - } - - @Override - public boolean isParallel(int archetypeChunkSize, int taskCount) { - return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); - } - - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - ChunkSection chunkSectionComponent = archetypeChunk.getComponent(index, ChunkSection.getComponentType()); - - assert chunkSectionComponent != null; - - FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType()); - - assert fluidSectionComponent != null; - - Ref chunkRef = chunkSectionComponent.getChunkColumnReference(); - BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - BlockSection blockSection = blockChunkComponent.getSectionAtIndex(fluidSectionComponent.getY()); - if (blockSection != null) { - if (blockSection.getTickingBlocksCountCopy() != 0) { - FluidTicker.CachedAccessor accessor = FluidTicker.CachedAccessor.of(commandBuffer, fluidSectionComponent, blockSection, 5); - blockSection.forEachTicking(accessor, commandBuffer, fluidSectionComponent.getY(), (accessor1, commandBuffer1, x, y, z, block) -> { - FluidSection fluidSection1 = accessor1.selfFluidSection; - BlockSection blockSection1 = accessor1.selfBlockSection; - int fluidId = fluidSection1.getFluidId(x, y, z); - if (fluidId == 0) { - return BlockTickStrategy.IGNORED; - } else { - Fluid fluid = Fluid.getAssetMap().getAsset(fluidId); - int blockX = fluidSection1.getX() << 5 | x; - int blockZ = fluidSection1.getZ() << 5 | z; - return fluid.getTicker().tick(commandBuffer1, accessor1, fluidSection1, blockSection1, fluid, fluidId, blockX, y, blockZ); - } - }); - } - } - } - - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return DEPENDENCIES; - } - } + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + } } diff --git a/src/com/hypixel/hytale/server/core/event/events/ecs/GatherBlockEvent.java b/src/com/hypixel/hytale/server/core/event/events/ecs/GatherBlockEvent.java index 9b0b8cda..67390cb3 100644 --- a/src/com/hypixel/hytale/server/core/event/events/ecs/GatherBlockEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/ecs/GatherBlockEvent.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.server.core.event.events.ecs; import com.hypixel.hytale.component.system.CancellableEcsEvent; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; + import javax.annotation.Nonnull; /** diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java b/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java index 0ac1b3c9..18d3d4a2 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java @@ -59,834 +59,835 @@ import com.hypixel.hytale.server.core.util.FillerBlockUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectLists; -import java.util.List; -import java.util.Objects; + import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; public class BlockHarvestUtils { - public BlockHarvestUtils() { - } + public BlockHarvestUtils() { + } - @Nullable - public static ItemToolSpec getSpecPowerDamageBlock(@Nullable Item item, @Nullable BlockType blockType, @Nullable ItemTool tool) { - if (blockType == null) { - return null; - } else { - BlockGathering gathering = blockType.getGathering(); - if (gathering == null) { + @Nullable + public static ItemToolSpec getSpecPowerDamageBlock(@Nullable Item item, @Nullable BlockType blockType, @Nullable ItemTool tool) { + if (blockType == null) { return null; - } else { - BlockBreakingDropType breaking = gathering.getBreaking(); - if (breaking == null) { - return null; + } else { + BlockGathering gathering = blockType.getGathering(); + if (gathering == null) { + return null; } else { - String gatherType = breaking.getGatherType(); - if (gatherType == null) { - return null; - } else if (item == null || item.getWeapon() == null && item.getBuilderToolData() == null) { - int requiredQuality = breaking.getQuality(); - if (tool == null) { - ItemToolSpec defaultSpec = ItemToolSpec.getAssetMap().getAsset(gatherType); - return defaultSpec != null && defaultSpec.getQuality() < requiredQuality ? null : defaultSpec; - } else { - if (tool.getSpecs() != null) { - for (ItemToolSpec spec : tool.getSpecs()) { - if (Objects.equals(spec.getGatherType(), gatherType)) { - if (spec.getQuality() < requiredQuality) { - return null; - } - - return spec; - } - } - } - - return null; - } - } else { - return null; - } - } - } - } - } - - public static double calculateDurabilityUse(@Nonnull Item item, @Nullable BlockType blockType) { - if (blockType == null) { - return 0.0; - } else if (blockType.getGathering().isSoft()) { - return 0.0; - } else if (item.getTool() == null) { - return 0.0; - } else { - ItemTool itemTool = item.getTool(); - ItemTool.DurabilityLossBlockTypes[] durabilityLossBlockTypes = itemTool.getDurabilityLossBlockTypes(); - if (durabilityLossBlockTypes == null) { - return item.getDurabilityLossOnHit(); - } else { - String hitBlockTypeId = blockType.getId(); - int hitBlockTypeIndex = BlockType.getAssetMap().getIndex(hitBlockTypeId); - if (hitBlockTypeIndex == Integer.MIN_VALUE) { - throw new IllegalArgumentException("Unknown key! " + hitBlockTypeId); - } else { - BlockSetModule blockSetModule = BlockSetModule.getInstance(); - - for (ItemTool.DurabilityLossBlockTypes durabilityLossBlockType : durabilityLossBlockTypes) { - int[] blockTypeIndexes = durabilityLossBlockType.getBlockTypeIndexes(); - if (blockTypeIndexes != null) { - for (int blockTypeIndex : blockTypeIndexes) { - if (blockTypeIndex == hitBlockTypeIndex) { - return durabilityLossBlockType.getDurabilityLossOnHit(); - } - } - } - - int[] blockSetIndexes = durabilityLossBlockType.getBlockSetIndexes(); - if (blockSetIndexes != null) { - for (int blockSetIndex : blockSetIndexes) { - if (blockSetModule.blockInSet(blockSetIndex, hitBlockTypeId)) { - return durabilityLossBlockType.getDurabilityLossOnHit(); - } - } - } - } - - return item.getDurabilityLossOnHit(); - } - } - } - } - - public static boolean performBlockDamage( - @Nonnull Vector3i targetBlock, - @Nullable ItemStack itemStack, - @Nullable ItemTool tool, - float damageScale, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull CommandBuffer commandBuffer, - @Nonnull ComponentAccessor chunkStore - ) { - return performBlockDamage(null, null, targetBlock, itemStack, tool, null, false, damageScale, setBlockSettings, chunkReference, commandBuffer, chunkStore); - } - - public static boolean performBlockDamage( - @Nullable LivingEntity entity, - @Nullable Ref ref, - @Nonnull Vector3i targetBlockPos, - @Nullable ItemStack itemStack, - @Nullable ItemTool tool, - @Nullable String toolId, - boolean matchTool, - float damageScale, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - World world = entityStore.getExternalData().getWorld(); - GameplayConfig gameplayConfig = world.getGameplayConfig(); - WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); - if (worldChunkComponent == null) { - return false; - } else { - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - BlockSection targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y); - int targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - boolean brokeBlock = false; - int environmentId = blockChunkComponent.getEnvironment(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - Environment environmentAsset = Environment.getAssetMap().getAsset(environmentId); - SpatialResource, EntityStore> playerSpatialResource = entityStore.getResource(EntityModule.get().getPlayerSpatialResourceType()); - if (environmentAsset != null && !environmentAsset.isBlockModificationAllowed()) { - targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - return false; - } else { - BlockType targetBlockType = worldChunkComponent.getBlockType(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - if (targetBlockType == null) { - return false; - } else { - BlockGathering blockGathering = targetBlockType.getGathering(); - if (blockGathering == null) { - return false; - } else if (matchTool && !blockGathering.getToolData().containsKey(toolId)) { - return false; - } else { - Vector3d targetBlockCenterPos = new Vector3d(); - targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); - targetBlockCenterPos.add(targetBlockPos); - Vector3i originBlock = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - if (!targetBlockType.isUnknown()) { - int filler = targetSection.getFiller(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - int fillerX = FillerBlockUtil.unpackX(filler); - int fillerY = FillerBlockUtil.unpackY(filler); - int fillerZ = FillerBlockUtil.unpackZ(filler); - if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - originBlock = originBlock.clone().subtract(fillerX, fillerY, fillerZ); - String oldBlockTypeKey = targetBlockType.getId(); - targetBlockType = world.getBlockType(originBlock.getX(), originBlock.getY(), originBlock.getZ()); - if (targetBlockType == null) { - return false; - } - - if (!oldBlockTypeKey.equals(targetBlockType.getId())) { - worldChunkComponent.breakBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - return true; - } - - blockGathering = targetBlockType.getGathering(); - if (blockGathering == null) { - return false; - } - } - } - - Item heldItem = itemStack != null ? itemStack.getItem() : null; - if (tool == null && heldItem != null) { - tool = heldItem.getTool(); - } - - ItemToolSpec itemToolSpec = getSpecPowerDamageBlock(heldItem, targetBlockType, tool); - float specPower = itemToolSpec != null ? itemToolSpec.getPower() : 0.0F; - boolean canApplyItemStackPenalties = entity != null && entity.canApplyItemStackPenalties(ref, entityStore); - if (specPower != 0.0F && heldItem != null && heldItem.getTool() != null && itemStack.isBroken() && canApplyItemStackPenalties) { - BrokenPenalties brokenPenalties = gameplayConfig.getItemDurabilityConfig().getBrokenPenalties(); - specPower *= 1.0F - (float)brokenPenalties.getTool(0.0); - } - - int dropQuantity = 1; - String itemId; - String dropListId; - float damage; - if (specPower != 0.0F) { - BlockBreakingDropType breaking = blockGathering.getBreaking(); - damage = specPower; - dropQuantity = breaking.getQuantity(); - itemId = breaking.getItemId(); - dropListId = breaking.getDropListId(); - } else { - if (!blockGathering.isSoft()) { - if (heldItem != null && heldItem.getWeapon() == null) { - if (ref != null && entity != null) { - GatheringEffectsConfig unbreakableBlockConfig = gameplayConfig.getGatheringConfig().getUnbreakableBlockConfig(); - if ((setBlockSettings & 4) == 0) { - String particleSystemId = unbreakableBlockConfig.getParticleSystemId(); - if (particleSystemId != null) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); - playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); - ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); - } - } - - if ((setBlockSettings & 1024) == 0) { - int soundEventIndex = unbreakableBlockConfig.getSoundEventIndex(); - if (soundEventIndex != 0) { - SoundUtil.playSoundEvent3d(ref, soundEventIndex, targetBlockCenterPos, entityStore); - } - - if (heldItem.getTool() != null) { - int hitSoundEventLayerIndex = heldItem.getTool().getIncorrectMaterialSoundLayerIndex(); - if (hitSoundEventLayerIndex != 0) { - SoundUtil.playSoundEvent3d(ref, hitSoundEventLayerIndex, targetBlockCenterPos, entityStore); - } - } - } - } - - return false; - } - - return false; - } - - SoftBlockDropType soft = blockGathering.getSoft(); - if (!soft.isWeaponBreakable() && heldItem != null && heldItem.getWeapon() != null) { - return false; - } - - damage = 1.0F; - itemId = soft.getItemId(); - dropListId = soft.getDropListId(); - damageScale = 1.0F; - } - - damage *= damageScale; - ChunkColumn chunkColumnComponent = chunkStore.getComponent(chunkReference, ChunkColumn.getComponentType()); - Ref chunkSectionRef = chunkColumnComponent != null - ? chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(targetBlockPos.y)) - : null; - if (targetBlockType.getGathering().shouldUseDefaultDropWhenPlaced()) { - BlockPhysics decoBlocks = chunkSectionRef != null ? chunkStore.getComponent(chunkSectionRef, BlockPhysics.getComponentType()) : null; - boolean isDeco = decoBlocks != null && decoBlocks.isDeco(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - if (isDeco) { - itemId = null; - dropListId = null; - } - } - - TimeResource timeResource = entityStore.getResource(TimeResource.getResourceType()); - BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, BlockHealthModule.get().getBlockHealthChunkComponentType()); - - assert blockHealthComponent != null; - - float current = blockHealthComponent.getBlockHealth(originBlock); - DamageBlockEvent event = new DamageBlockEvent(itemStack, originBlock, targetBlockType, current, damage); - if (ref != null) { - entityStore.invoke(ref, event); - } else { - entityStore.invoke(event); - } - - if (event.isCancelled()) { - targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - return false; - } else { - damage = event.getDamage(); - targetBlockType = event.getBlockType(); - targetBlockPos = event.getTargetBlock(); - targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y); - targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); - targetBlockCenterPos.add(targetBlockPos); - BlockHealth blockDamage = blockHealthComponent.damageBlock(timeResource.getNow(), world, targetBlockPos, damage); - if (blockHealthComponent.isBlockFragile(targetBlockPos) || blockDamage.isDestroyed()) { - BlockGathering.BlockToolData requiredTool = blockGathering.getToolData().get(toolId); - boolean toolsMatch = requiredTool != null; - if (!toolsMatch) { - performBlockBreak( - world, - targetBlockPos, - targetBlockType, - itemStack, - dropQuantity, - itemId, - dropListId, - setBlockSettings, - ref, - chunkReference, - entityStore, - chunkStore - ); - brokeBlock = true; + BlockBreakingDropType breaking = gathering.getBreaking(); + if (breaking == null) { + return null; + } else { + String gatherType = breaking.getGatherType(); + if (gatherType == null) { + return null; + } else if (item == null || item.getWeapon() == null && item.getBuilderToolData() == null) { + int requiredQuality = breaking.getQuality(); + if (tool == null) { + ItemToolSpec defaultSpec = ItemToolSpec.getAssetMap().getAsset(gatherType); + return defaultSpec != null && defaultSpec.getQuality() < requiredQuality ? null : defaultSpec; } else { - String toolStateId = requiredTool.getStateId(); - BlockType newBlockType = toolStateId != null ? targetBlockType.getBlockForState(toolStateId) : null; - boolean shouldChangeState = newBlockType != null && !targetBlockType.getId().equals(newBlockType.getId()); - if (shouldChangeState) { - blockDamage.setHealth(1.0F); - worldChunkComponent.setBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z, newBlockType); - if ((setBlockSettings & 1024) == 0) { - BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex()); - if (soundSet != null) { - int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0); - if (soundEventIndexx != 0) { - SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore); + if (tool.getSpecs() != null) { + for (ItemToolSpec spec : tool.getSpecs()) { + if (Objects.equals(spec.getGatherType(), gatherType)) { + if (spec.getQuality() < requiredQuality) { + return null; + } + + return spec; } - } - } + } + } - if ((setBlockSettings & 2048) == 0) { - List itemStacks = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId()); - Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } - } else { - performBlockBreak( - world, - targetBlockPos, - targetBlockType, - itemStack, - dropQuantity, - itemId, - dropListId, - setBlockSettings | 2048, - ref, - chunkReference, - entityStore, - chunkStore - ); - brokeBlock = true; - if ((setBlockSettings & 2048) == 0) { - List toolDrops = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId()); - if (!toolDrops.isEmpty()) { - Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops( - entityStore, toolDrops, dropPosition, Vector3f.ZERO - ); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } - } - } + return null; } - } else if ((setBlockSettings & 1024) == 0) { - BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex()); - if (soundSet != null) { - int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Hit, 0); - if (soundEventIndexx != 0) { - SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore); - } - } - } - - if (ref != null && entity != null) { - if ((setBlockSettings & 1024) == 0 && !targetBlockCenterPos.equals(Vector3d.MAX)) { - int hitSoundEventLayerIndex = 0; - if (itemToolSpec != null) { - hitSoundEventLayerIndex = itemToolSpec.getHitSoundLayerIndex(); - } - - if (hitSoundEventLayerIndex == 0 && heldItem != null && heldItem.getTool() != null) { - hitSoundEventLayerIndex = heldItem.getTool().getHitSoundLayerIndex(); - } - - if (hitSoundEventLayerIndex != 0) { - SoundUtil.playSoundEvent3d( - ref, - hitSoundEventLayerIndex, - targetBlockCenterPos.getX(), - targetBlockCenterPos.getY(), - targetBlockCenterPos.getZ(), - entityStore - ); - } - } - - if (itemToolSpec != null && itemToolSpec.isIncorrect()) { - GatheringEffectsConfig incorrectToolConfig = gameplayConfig.getGatheringConfig().getIncorrectToolConfig(); - if (incorrectToolConfig != null) { - if ((setBlockSettings & 4) == 0) { - String particleSystemId = incorrectToolConfig.getParticleSystemId(); - if (particleSystemId != null) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); - playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); - ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); - } - } - - if ((setBlockSettings & 1024) == 0) { - int soundEventIndexx = incorrectToolConfig.getSoundEventIndex(); - if (soundEventIndexx != 0) { - SoundUtil.playSoundEvent3d(ref, soundEventIndexx, targetBlockCenterPos, entityStore); - } - } - } - } - } - - if (entity != null - && ref != null - && entity.canDecreaseItemStackDurability(ref, entityStore) - && itemStack != null - && !itemStack.isUnbreakable()) { - byte activeHotbarSlot = entity.getInventory().getActiveHotbarSlot(); - if (activeHotbarSlot != -1) { - double durability = calculateDurabilityUse(heldItem, targetBlockType); - ItemContainer hotbar = entity.getInventory().getHotbar(); - entity.updateItemStackDurability(ref, itemStack, hotbar, activeHotbarSlot, -durability, entityStore); - } - } - - return brokeBlock; - } - } + } else { + return null; + } + } } - } - } - } + } + } - public static void performBlockBreak( - @Nullable Ref ref, - @Nullable ItemStack heldItemStack, - @Nonnull Vector3i targetBlock, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - performBlockBreak(ref, heldItemStack, targetBlock, 0, chunkReference, entityStore, chunkStore); - } + public static double calculateDurabilityUse(@Nonnull Item item, @Nullable BlockType blockType) { + if (blockType == null) { + return 0.0; + } else if (blockType.getGathering().isSoft()) { + return 0.0; + } else if (item.getTool() == null) { + return 0.0; + } else { + ItemTool itemTool = item.getTool(); + ItemTool.DurabilityLossBlockTypes[] durabilityLossBlockTypes = itemTool.getDurabilityLossBlockTypes(); + if (durabilityLossBlockTypes == null) { + return item.getDurabilityLossOnHit(); + } else { + String hitBlockTypeId = blockType.getId(); + int hitBlockTypeIndex = BlockType.getAssetMap().getIndex(hitBlockTypeId); + if (hitBlockTypeIndex == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + hitBlockTypeId); + } else { + BlockSetModule blockSetModule = BlockSetModule.getInstance(); - public static void performBlockBreak( - @Nullable Ref ref, - @Nullable ItemStack heldItemStack, - @Nonnull Vector3i targetBlock, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - World world = chunkStore.getExternalData().getWorld(); - int targetBlockX = targetBlock.getX(); - int targetBlockY = targetBlock.getY(); - int targetBlockZ = targetBlock.getZ(); - WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); + for (ItemTool.DurabilityLossBlockTypes durabilityLossBlockType : durabilityLossBlockTypes) { + int[] blockTypeIndexes = durabilityLossBlockType.getBlockTypeIndexes(); + if (blockTypeIndexes != null) { + for (int blockTypeIndex : blockTypeIndexes) { + if (blockTypeIndex == hitBlockTypeIndex) { + return durabilityLossBlockType.getDurabilityLossOnHit(); + } + } + } - assert worldChunkComponent != null; + int[] blockSetIndexes = durabilityLossBlockType.getBlockSetIndexes(); + if (blockSetIndexes != null) { + for (int blockSetIndex : blockSetIndexes) { + if (blockSetModule.blockInSet(blockSetIndex, hitBlockTypeId)) { + return durabilityLossBlockType.getDurabilityLossOnHit(); + } + } + } + } - int targetBlockTypeIndex = worldChunkComponent.getBlock(targetBlockX, targetBlockY, targetBlockZ); - BlockType targetBlockTypeAsset = BlockType.getAssetMap().getAsset(targetBlockTypeIndex); - if (targetBlockTypeAsset != null) { - Vector3i affectedBlock = targetBlock; - if (!targetBlockTypeAsset.isUnknown()) { + return item.getDurabilityLossOnHit(); + } + } + } + } + + public static boolean performBlockDamage( + @Nonnull Vector3i targetBlock, + @Nullable ItemStack itemStack, + @Nullable ItemTool tool, + float damageScale, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull CommandBuffer commandBuffer, + @Nonnull ComponentAccessor chunkStore + ) { + return performBlockDamage(null, null, targetBlock, itemStack, tool, null, false, damageScale, setBlockSettings, chunkReference, commandBuffer, chunkStore); + } + + public static boolean performBlockDamage( + @Nullable LivingEntity entity, + @Nullable Ref ref, + @Nonnull Vector3i targetBlockPos, + @Nullable ItemStack itemStack, + @Nullable ItemTool tool, + @Nullable String toolId, + boolean matchTool, + float damageScale, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + World world = entityStore.getExternalData().getWorld(); + GameplayConfig gameplayConfig = world.getGameplayConfig(); + WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); + if (worldChunkComponent == null) { + return false; + } else { BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); assert blockChunkComponent != null; - BlockSection targetBlockSection = blockChunkComponent.getSectionAtBlockY(targetBlockY); - int filler = targetBlockSection.getFiller(targetBlockX, targetBlockY, targetBlockZ); - int fillerX = FillerBlockUtil.unpackX(filler); - int fillerY = FillerBlockUtil.unpackY(filler); - int fillerZ = FillerBlockUtil.unpackZ(filler); - if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); - BlockType originBlock = world.getBlockType(affectedBlock); - if (originBlock != null && !targetBlockTypeAsset.getId().equals(originBlock.getId())) { - world.breakBlock(targetBlockX, targetBlockY, targetBlockZ, setBlockSettings); - return; - } + BlockSection targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y); + int targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + boolean brokeBlock = false; + int environmentId = blockChunkComponent.getEnvironment(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + Environment environmentAsset = Environment.getAssetMap().getAsset(environmentId); + SpatialResource, EntityStore> playerSpatialResource = entityStore.getResource(EntityModule.get().getPlayerSpatialResourceType()); + if (environmentAsset != null && !environmentAsset.isBlockModificationAllowed()) { + targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + return false; + } else { + BlockType targetBlockType = worldChunkComponent.getBlockType(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + if (targetBlockType == null) { + return false; + } else { + BlockGathering blockGathering = targetBlockType.getGathering(); + if (blockGathering == null) { + return false; + } else if (matchTool && !blockGathering.getToolData().containsKey(toolId)) { + return false; + } else { + Vector3d targetBlockCenterPos = new Vector3d(); + targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); + targetBlockCenterPos.add(targetBlockPos); + Vector3i originBlock = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + if (!targetBlockType.isUnknown()) { + int filler = targetSection.getFiller(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + int fillerX = FillerBlockUtil.unpackX(filler); + int fillerY = FillerBlockUtil.unpackY(filler); + int fillerZ = FillerBlockUtil.unpackZ(filler); + if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { + originBlock = originBlock.clone().subtract(fillerX, fillerY, fillerZ); + String oldBlockTypeKey = targetBlockType.getId(); + targetBlockType = world.getBlockType(originBlock.getX(), originBlock.getY(), originBlock.getZ()); + if (targetBlockType == null) { + return false; + } + + if (!oldBlockTypeKey.equals(targetBlockType.getId())) { + worldChunkComponent.breakBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + return true; + } + + blockGathering = targetBlockType.getGathering(); + if (blockGathering == null) { + return false; + } + } + } + + Item heldItem = itemStack != null ? itemStack.getItem() : null; + if (tool == null && heldItem != null) { + tool = heldItem.getTool(); + } + + ItemToolSpec itemToolSpec = getSpecPowerDamageBlock(heldItem, targetBlockType, tool); + float specPower = itemToolSpec != null ? itemToolSpec.getPower() : 0.0F; + boolean canApplyItemStackPenalties = entity != null && entity.canApplyItemStackPenalties(ref, entityStore); + if (specPower != 0.0F && heldItem != null && heldItem.getTool() != null && itemStack.isBroken() && canApplyItemStackPenalties) { + BrokenPenalties brokenPenalties = gameplayConfig.getItemDurabilityConfig().getBrokenPenalties(); + specPower *= 1.0F - (float) brokenPenalties.getTool(0.0); + } + + int dropQuantity = 1; + String itemId; + String dropListId; + float damage; + if (specPower != 0.0F) { + BlockBreakingDropType breaking = blockGathering.getBreaking(); + damage = specPower; + dropQuantity = breaking.getQuantity(); + itemId = breaking.getItemId(); + dropListId = breaking.getDropListId(); + } else { + if (!blockGathering.isSoft()) { + if (heldItem != null && heldItem.getWeapon() == null) { + if (ref != null && entity != null) { + GatheringEffectsConfig unbreakableBlockConfig = gameplayConfig.getGatheringConfig().getUnbreakableBlockConfig(); + if ((setBlockSettings & 4) == 0) { + String particleSystemId = unbreakableBlockConfig.getParticleSystemId(); + if (particleSystemId != null) { + ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); + ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); + } + } + + if ((setBlockSettings & 1024) == 0) { + int soundEventIndex = unbreakableBlockConfig.getSoundEventIndex(); + if (soundEventIndex != 0) { + SoundUtil.playSoundEvent3d(ref, soundEventIndex, targetBlockCenterPos, entityStore); + } + + if (heldItem.getTool() != null) { + int hitSoundEventLayerIndex = heldItem.getTool().getIncorrectMaterialSoundLayerIndex(); + if (hitSoundEventLayerIndex != 0) { + SoundUtil.playSoundEvent3d(ref, hitSoundEventLayerIndex, targetBlockCenterPos, entityStore); + } + } + } + } + + return false; + } + + return false; + } + + SoftBlockDropType soft = blockGathering.getSoft(); + if (!soft.isWeaponBreakable() && heldItem != null && heldItem.getWeapon() != null) { + return false; + } + + damage = 1.0F; + itemId = soft.getItemId(); + dropListId = soft.getDropListId(); + damageScale = 1.0F; + } + + damage *= damageScale; + ChunkColumn chunkColumnComponent = chunkStore.getComponent(chunkReference, ChunkColumn.getComponentType()); + Ref chunkSectionRef = chunkColumnComponent != null + ? chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(targetBlockPos.y)) + : null; + if (targetBlockType.getGathering().shouldUseDefaultDropWhenPlaced()) { + BlockPhysics decoBlocks = chunkSectionRef != null ? chunkStore.getComponent(chunkSectionRef, BlockPhysics.getComponentType()) : null; + boolean isDeco = decoBlocks != null && decoBlocks.isDeco(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + if (isDeco) { + itemId = null; + dropListId = null; + } + } + + TimeResource timeResource = entityStore.getResource(TimeResource.getResourceType()); + BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, BlockHealthModule.get().getBlockHealthChunkComponentType()); + + assert blockHealthComponent != null; + + float current = blockHealthComponent.getBlockHealth(originBlock); + DamageBlockEvent event = new DamageBlockEvent(itemStack, originBlock, targetBlockType, current, damage); + if (ref != null) { + entityStore.invoke(ref, event); + } else { + entityStore.invoke(event); + } + + if (event.isCancelled()) { + targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + return false; + } else { + damage = event.getDamage(); + targetBlockType = event.getBlockType(); + targetBlockPos = event.getTargetBlock(); + targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y); + targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); + targetBlockCenterPos.add(targetBlockPos); + BlockHealth blockDamage = blockHealthComponent.damageBlock(timeResource.getNow(), world, targetBlockPos, damage); + if (blockHealthComponent.isBlockFragile(targetBlockPos) || blockDamage.isDestroyed()) { + BlockGathering.BlockToolData requiredTool = blockGathering.getToolData().get(toolId); + boolean toolsMatch = requiredTool != null; + if (!toolsMatch) { + performBlockBreak( + world, + targetBlockPos, + targetBlockType, + itemStack, + dropQuantity, + itemId, + dropListId, + setBlockSettings, + ref, + chunkReference, + entityStore, + chunkStore + ); + brokeBlock = true; + } else { + String toolStateId = requiredTool.getStateId(); + BlockType newBlockType = toolStateId != null ? targetBlockType.getBlockForState(toolStateId) : null; + boolean shouldChangeState = newBlockType != null && !targetBlockType.getId().equals(newBlockType.getId()); + if (shouldChangeState) { + blockDamage.setHealth(1.0F); + worldChunkComponent.setBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z, newBlockType); + if ((setBlockSettings & 1024) == 0) { + BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex()); + if (soundSet != null) { + int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0); + if (soundEventIndexx != 0) { + SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore); + } + } + } + + if ((setBlockSettings & 2048) == 0) { + List itemStacks = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId()); + Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); + entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); + } + } else { + performBlockBreak( + world, + targetBlockPos, + targetBlockType, + itemStack, + dropQuantity, + itemId, + dropListId, + setBlockSettings | 2048, + ref, + chunkReference, + entityStore, + chunkStore + ); + brokeBlock = true; + if ((setBlockSettings & 2048) == 0) { + List toolDrops = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId()); + if (!toolDrops.isEmpty()) { + Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops( + entityStore, toolDrops, dropPosition, Vector3f.ZERO + ); + entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); + } + } + } + } + } else if ((setBlockSettings & 1024) == 0) { + BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex()); + if (soundSet != null) { + int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Hit, 0); + if (soundEventIndexx != 0) { + SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore); + } + } + } + + if (ref != null && entity != null) { + if ((setBlockSettings & 1024) == 0 && !targetBlockCenterPos.equals(Vector3d.MAX)) { + int hitSoundEventLayerIndex = 0; + if (itemToolSpec != null) { + hitSoundEventLayerIndex = itemToolSpec.getHitSoundLayerIndex(); + } + + if (hitSoundEventLayerIndex == 0 && heldItem != null && heldItem.getTool() != null) { + hitSoundEventLayerIndex = heldItem.getTool().getHitSoundLayerIndex(); + } + + if (hitSoundEventLayerIndex != 0) { + SoundUtil.playSoundEvent3d( + ref, + hitSoundEventLayerIndex, + targetBlockCenterPos.getX(), + targetBlockCenterPos.getY(), + targetBlockCenterPos.getZ(), + entityStore + ); + } + } + + if (itemToolSpec != null && itemToolSpec.isIncorrect()) { + GatheringEffectsConfig incorrectToolConfig = gameplayConfig.getGatheringConfig().getIncorrectToolConfig(); + if (incorrectToolConfig != null) { + if ((setBlockSettings & 4) == 0) { + String particleSystemId = incorrectToolConfig.getParticleSystemId(); + if (particleSystemId != null) { + ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); + ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); + } + } + + if ((setBlockSettings & 1024) == 0) { + int soundEventIndexx = incorrectToolConfig.getSoundEventIndex(); + if (soundEventIndexx != 0) { + SoundUtil.playSoundEvent3d(ref, soundEventIndexx, targetBlockCenterPos, entityStore); + } + } + } + } + } + + if (entity != null + && ref != null + && entity.canDecreaseItemStackDurability(ref, entityStore) + && itemStack != null + && !itemStack.isUnbreakable()) { + byte activeHotbarSlot = entity.getInventory().getActiveHotbarSlot(); + if (activeHotbarSlot != -1) { + double durability = calculateDurabilityUse(heldItem, targetBlockType); + ItemContainer hotbar = entity.getInventory().getHotbar(); + entity.updateItemStackDurability(ref, itemStack, hotbar, activeHotbarSlot, -durability, entityStore); + } + } + + return brokeBlock; + } + } + } } - } + } + } - performBlockBreak( - world, affectedBlock, targetBlockTypeAsset, heldItemStack, 0, null, null, setBlockSettings, ref, chunkReference, entityStore, chunkStore - ); - } - } + public static void performBlockBreak( + @Nullable Ref ref, + @Nullable ItemStack heldItemStack, + @Nonnull Vector3i targetBlock, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + performBlockBreak(ref, heldItemStack, targetBlock, 0, chunkReference, entityStore, chunkStore); + } - public static void performBlockBreak( - @Nonnull World world, - @Nonnull Vector3i blockPosition, - @Nonnull BlockType targetBlockTypeKey, - @Nullable ItemStack heldItemStack, - int dropQuantity, - @Nullable String dropItemId, - @Nullable String dropListId, - int setBlockSettings, - @Nullable Ref ref, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - Vector3i targetBlockPosition = blockPosition; - Ref targetChunkReference = chunkReference; - ComponentAccessor targetChunkStore = chunkStore; - if (ref != null) { - BreakBlockEvent event = new BreakBlockEvent(heldItemStack, blockPosition, targetBlockTypeKey); - entityStore.invoke(ref, event); - if (event.isCancelled()) { + public static void performBlockBreak( + @Nullable Ref ref, + @Nullable ItemStack heldItemStack, + @Nonnull Vector3i targetBlock, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + World world = chunkStore.getExternalData().getWorld(); + int targetBlockX = targetBlock.getX(); + int targetBlockY = targetBlock.getY(); + int targetBlockZ = targetBlock.getZ(); + WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); + + assert worldChunkComponent != null; + + int targetBlockTypeIndex = worldChunkComponent.getBlock(targetBlockX, targetBlockY, targetBlockZ); + BlockType targetBlockTypeAsset = BlockType.getAssetMap().getAsset(targetBlockTypeIndex); + if (targetBlockTypeAsset != null) { + Vector3i affectedBlock = targetBlock; + if (!targetBlockTypeAsset.isUnknown()) { + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection targetBlockSection = blockChunkComponent.getSectionAtBlockY(targetBlockY); + int filler = targetBlockSection.getFiller(targetBlockX, targetBlockY, targetBlockZ); + int fillerX = FillerBlockUtil.unpackX(filler); + int fillerY = FillerBlockUtil.unpackY(filler); + int fillerZ = FillerBlockUtil.unpackZ(filler); + if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { + affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); + BlockType originBlock = world.getBlockType(affectedBlock); + if (originBlock != null && !targetBlockTypeAsset.getId().equals(originBlock.getId())) { + world.breakBlock(targetBlockX, targetBlockY, targetBlockZ, setBlockSettings); + return; + } + } + } + + performBlockBreak( + world, affectedBlock, targetBlockTypeAsset, heldItemStack, 0, null, null, setBlockSettings, ref, chunkReference, entityStore, chunkStore + ); + } + } + + public static void performBlockBreak( + @Nonnull World world, + @Nonnull Vector3i blockPosition, + @Nonnull BlockType targetBlockTypeKey, + @Nullable ItemStack heldItemStack, + int dropQuantity, + @Nullable String dropItemId, + @Nullable String dropListId, + int setBlockSettings, + @Nullable Ref ref, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + Vector3i targetBlockPosition = blockPosition; + Ref targetChunkReference = chunkReference; + ComponentAccessor targetChunkStore = chunkStore; + if (ref != null) { + BreakBlockEvent event = new BreakBlockEvent(heldItemStack, blockPosition, targetBlockTypeKey); + entityStore.invoke(ref, event); + if (event.isCancelled()) { + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); + blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + return; + } + + targetBlockPosition = event.getTargetBlock(); + targetChunkStore = world.getChunkStore().getStore(); + long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPosition.x, targetBlockPosition.z); + targetChunkReference = targetChunkStore.getExternalData().getChunkReference(chunkIndex); + if (targetChunkReference == null || !targetChunkReference.isValid()) { + return; + } + } + + if (!targetBlockPosition.equals(blockPosition) || !world.equals(world)) { BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); assert blockChunkComponent != null; BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + } + + int x = blockPosition.getX(); + int y = blockPosition.getY(); + int z = blockPosition.getZ(); + BlockChunk blockChunkComponent = chunkStore.getComponent(targetChunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); + int filler = blockSection.getFiller(x, y, z); + int blockTypeIndex = blockSection.get(x, y, z); + BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeIndex); + boolean isNaturalBlockBreak = BlockInteractionUtils.isNaturalAction(ref, entityStore); + setBlockSettings |= 256; + if (!isNaturalBlockBreak) { + setBlockSettings |= 2048; + } + + naturallyRemoveBlock( + targetBlockPosition, + blockTypeAsset, + filler, + dropQuantity, + dropItemId, + dropListId, + setBlockSettings, + targetChunkReference, + entityStore, + targetChunkStore + ); + } + + @Deprecated + public static void naturallyRemoveBlockByPhysics( + @Nonnull Vector3i blockPosition, + @Nonnull BlockType blockType, + int filler, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + int quantity = 1; + String itemId = null; + String dropListId = null; + BlockGathering blockGathering = blockType.getGathering(); + if (blockGathering != null) { + PhysicsDropType physics = blockGathering.getPhysics(); + BlockBreakingDropType breaking = blockGathering.getBreaking(); + SoftBlockDropType soft = blockGathering.getSoft(); + HarvestingDropType harvest = blockGathering.getHarvest(); + if (physics != null) { + itemId = physics.getItemId(); + dropListId = physics.getDropListId(); + } else if (breaking != null) { + quantity = breaking.getQuantity(); + itemId = breaking.getItemId(); + dropListId = breaking.getDropListId(); + } else if (soft != null) { + itemId = soft.getItemId(); + dropListId = soft.getDropListId(); + } else if (harvest != null) { + itemId = harvest.getItemId(); + dropListId = harvest.getDropListId(); + } + } + + setBlockSettings |= 32; + naturallyRemoveBlock(blockPosition, blockType, filler, quantity, itemId, dropListId, setBlockSettings, chunkReference, entityStore, chunkStore); + } + + public static void naturallyRemoveBlock( + @Nonnull Vector3i blockPosition, + @Nullable BlockType blockType, + int filler, + int quantity, + String itemId, + String dropListId, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + if (blockType != null) { + WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); + + assert worldChunkComponent != null; + + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + Vector3i affectedBlock = blockPosition; + if (!blockType.isUnknown()) { + int fillerX = FillerBlockUtil.unpackX(filler); + int fillerY = FillerBlockUtil.unpackY(filler); + int fillerZ = FillerBlockUtil.unpackZ(filler); + if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { + affectedBlock = blockPosition.clone().subtract(fillerX, fillerY, fillerZ); + String oldBlockTypeKey = blockType.getId(); + blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); + if (blockType == null) { + throw new IllegalStateException("Null block type fetched for " + affectedBlock + " during block break"); + } + + if (!oldBlockTypeKey.equals(blockType.getId())) { + worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); + return; + } + } + } + + if ((setBlockSettings & 1024) == 0) { + BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(blockType.getBlockSoundSetIndex()); + if (soundSet != null) { + int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0); + if (soundEventIndex != 0) { + BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); + int rotationIndex = section.getRotationIndex(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + Vector3d centerPosition = new Vector3d(); + blockType.getBlockCenter(rotationIndex, centerPosition); + centerPosition.add(blockPosition); + SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore); + } + } + } + + removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore); + if ((setBlockSettings & 2048) == 0 && quantity > 0) { + Vector3d dropPosition = blockPosition.toVector3d().add(0.5, 0.0, 0.5); + List itemStacks = getDrops(blockType, quantity, itemId, dropListId); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); + entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); + } + } + } + + public static boolean shouldPickupByInteraction(@Nullable BlockType blockType) { + return blockType != null && blockType.getGathering() != null && blockType.getGathering().isHarvestable(); + } + + public static void performPickupByInteraction( + @Nonnull Ref ref, + @Nonnull Vector3i targetBlock, + @Nonnull BlockType blockType, + int filler, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor entityStore, + @Nonnull ComponentAccessor chunkStore + ) { + // Fire GatherBlockEvent before proceeding - allows cancellation + GatherBlockEvent gatherEvent = new GatherBlockEvent(targetBlock, blockType); + entityStore.invoke(ref, gatherEvent); + if (gatherEvent.isCancelled()) { + // Invalidate block to sync client state + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + if (blockChunkComponent != null) { + BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); + section.invalidateBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); + } return; - } + } - targetBlockPosition = event.getTargetBlock(); - targetChunkStore = world.getChunkStore().getStore(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPosition.x, targetBlockPosition.z); - targetChunkReference = targetChunkStore.getExternalData().getChunkReference(chunkIndex); - if (targetChunkReference == null || !targetChunkReference.isValid()) { - return; - } - } + WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); - if (!targetBlockPosition.equals(blockPosition) || !world.equals(world)) { - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + assert worldChunkComponent != null; - assert blockChunkComponent != null; + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); - blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); - } + assert blockChunkComponent != null; - int x = blockPosition.getX(); - int y = blockPosition.getY(); - int z = blockPosition.getZ(); - BlockChunk blockChunkComponent = chunkStore.getComponent(targetChunkReference, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); - int filler = blockSection.getFiller(x, y, z); - int blockTypeIndex = blockSection.get(x, y, z); - BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeIndex); - boolean isNaturalBlockBreak = BlockInteractionUtils.isNaturalAction(ref, entityStore); - setBlockSettings |= 256; - if (!isNaturalBlockBreak) { - setBlockSettings |= 2048; - } - - naturallyRemoveBlock( - targetBlockPosition, - blockTypeAsset, - filler, - dropQuantity, - dropItemId, - dropListId, - setBlockSettings, - targetChunkReference, - entityStore, - targetChunkStore - ); - } - - @Deprecated - public static void naturallyRemoveBlockByPhysics( - @Nonnull Vector3i blockPosition, - @Nonnull BlockType blockType, - int filler, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - int quantity = 1; - String itemId = null; - String dropListId = null; - BlockGathering blockGathering = blockType.getGathering(); - if (blockGathering != null) { - PhysicsDropType physics = blockGathering.getPhysics(); - BlockBreakingDropType breaking = blockGathering.getBreaking(); - SoftBlockDropType soft = blockGathering.getSoft(); - HarvestingDropType harvest = blockGathering.getHarvest(); - if (physics != null) { - itemId = physics.getItemId(); - dropListId = physics.getDropListId(); - } else if (breaking != null) { - quantity = breaking.getQuantity(); - itemId = breaking.getItemId(); - dropListId = breaking.getDropListId(); - } else if (soft != null) { - itemId = soft.getItemId(); - dropListId = soft.getDropListId(); - } else if (harvest != null) { - itemId = harvest.getItemId(); - dropListId = harvest.getDropListId(); - } - } - - setBlockSettings |= 32; - naturallyRemoveBlock(blockPosition, blockType, filler, quantity, itemId, dropListId, setBlockSettings, chunkReference, entityStore, chunkStore); - } - - public static void naturallyRemoveBlock( - @Nonnull Vector3i blockPosition, - @Nullable BlockType blockType, - int filler, - int quantity, - String itemId, - String dropListId, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - if (blockType != null) { - WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); - - assert worldChunkComponent != null; - - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - Vector3i affectedBlock = blockPosition; - if (!blockType.isUnknown()) { + Vector3i affectedBlock = targetBlock; + if (!blockType.isUnknown()) { int fillerX = FillerBlockUtil.unpackX(filler); int fillerY = FillerBlockUtil.unpackY(filler); int fillerZ = FillerBlockUtil.unpackZ(filler); if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = blockPosition.clone().subtract(fillerX, fillerY, fillerZ); - String oldBlockTypeKey = blockType.getId(); - blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); - if (blockType == null) { - throw new IllegalStateException("Null block type fetched for " + affectedBlock + " during block break"); - } + affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); + String oldBlockTypeKey = blockType.getId(); + blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); + if (blockType == null) { + return; + } - if (!oldBlockTypeKey.equals(blockType.getId())) { - worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); - return; - } + if (!oldBlockTypeKey.equals(blockType.getId())) { + worldChunkComponent.breakBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); + return; + } } - } + } - if ((setBlockSettings & 1024) == 0) { + BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); + Vector3d centerPosition = new Vector3d(); + int rotationIndex = section.getRotationIndex(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); + blockType.getBlockCenter(rotationIndex, centerPosition); + centerPosition.add(targetBlock); + int setBlockSettings = 0; + setBlockSettings |= 256; + if (!BlockInteractionUtils.isNaturalAction(ref, entityStore)) { + setBlockSettings |= 2048; + } + + removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore); + HarvestingDropType harvest = blockType.getGathering().getHarvest(); + String itemId = harvest.getItemId(); + String dropListId = harvest.getDropListId(); + + for (ItemStack itemStack : getDrops(blockType, 1, itemId, dropListId)) { + ItemUtils.interactivelyPickupItem(ref, itemStack, centerPosition, entityStore); + } + + if ((setBlockSettings & 1024) == 0) { BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(blockType.getBlockSoundSetIndex()); if (soundSet != null) { - int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0); - if (soundEventIndex != 0) { - BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); - int rotationIndex = section.getRotationIndex(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); - Vector3d centerPosition = new Vector3d(); - blockType.getBlockCenter(rotationIndex, centerPosition); - centerPosition.add(blockPosition); - SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore); - } + int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Harvest, 0); + if (soundEventIndex != 0) { + SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore); + } } - } + } + } - removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore); - if ((setBlockSettings & 2048) == 0 && quantity > 0) { - Vector3d dropPosition = blockPosition.toVector3d().add(0.5, 0.0, 0.5); - List itemStacks = getDrops(blockType, quantity, itemId, dropListId); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } - } - } + protected static void removeBlock( + @Nonnull Vector3i blockPosition, + @Nonnull BlockType blockType, + int setBlockSettings, + @Nonnull Ref chunkReference, + @Nonnull ComponentAccessor chunkStore + ) { + World world = chunkStore.getExternalData().getWorld(); + ComponentType blockHealthComponentType = BlockHealthModule.get().getBlockHealthChunkComponentType(); + BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, blockHealthComponentType); - public static boolean shouldPickupByInteraction(@Nullable BlockType blockType) { - return blockType != null && blockType.getGathering() != null && blockType.getGathering().isHarvestable(); - } + assert blockHealthComponent != null; - public static void performPickupByInteraction( - @Nonnull Ref ref, - @Nonnull Vector3i targetBlock, - @Nonnull BlockType blockType, - int filler, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor entityStore, - @Nonnull ComponentAccessor chunkStore - ) { - // Fire GatherBlockEvent before proceeding - allows cancellation - GatherBlockEvent gatherEvent = new GatherBlockEvent(targetBlock, blockType); - entityStore.invoke(ref, gatherEvent); - if (gatherEvent.isCancelled()) { - // Invalidate block to sync client state - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - if (blockChunkComponent != null) { - BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); - section.invalidateBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); - } - return; - } + blockHealthComponent.removeBlock(world, blockPosition); + WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); - WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); + assert worldChunkComponent != null; - assert worldChunkComponent != null; + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + assert blockChunkComponent != null; - assert blockChunkComponent != null; + worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); + if ((setBlockSettings & 256) != 0) { + BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.y); + int rotationIndex = section.getRotationIndex(blockPosition.x, blockPosition.y, blockPosition.z); + BlockBoundingBoxes hitBoxType = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); + if (hitBoxType != null) { + FillerBlockUtil.forEachFillerBlock( + hitBoxType.get(rotationIndex), + (x, y, z) -> world.performBlockUpdate(blockPosition.getX() + x, blockPosition.getY() + y, blockPosition.getZ() + z, false) + ); + } + } - Vector3i affectedBlock = targetBlock; - if (!blockType.isUnknown()) { - int fillerX = FillerBlockUtil.unpackX(filler); - int fillerY = FillerBlockUtil.unpackY(filler); - int fillerZ = FillerBlockUtil.unpackZ(filler); - if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); - String oldBlockTypeKey = blockType.getId(); - blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); - if (blockType == null) { - return; + ConnectedBlocksUtil.setConnectedBlockAndNotifyNeighbors( + BlockType.getAssetMap().getIndex("Empty"), RotationTuple.NONE, Vector3i.ZERO, blockPosition, worldChunkComponent, blockChunkComponent + ); + } + + @Nonnull + public static List getDrops(@Nonnull BlockType blockType, int quantity, @Nullable String itemId, @Nullable String dropListId) { + if (dropListId == null && itemId == null) { + Item item = blockType.getItem(); + return item == null ? ObjectLists.emptyList() : ObjectLists.singleton(new ItemStack(item.getId(), quantity)); + } else { + List randomItemDrops = new ObjectArrayList<>(); + if (dropListId != null) { + ItemModule itemModule = ItemModule.get(); + if (itemModule.isEnabled()) { + for (int i = 0; i < quantity; i++) { + List randomItemsToDrop = itemModule.getRandomItemDrops(dropListId); + randomItemDrops.addAll(randomItemsToDrop); + } + } } - if (!oldBlockTypeKey.equals(blockType.getId())) { - worldChunkComponent.breakBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); - return; + if (itemId != null) { + randomItemDrops.add(new ItemStack(itemId, quantity)); } - } - } - BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); - Vector3d centerPosition = new Vector3d(); - int rotationIndex = section.getRotationIndex(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); - blockType.getBlockCenter(rotationIndex, centerPosition); - centerPosition.add(targetBlock); - int setBlockSettings = 0; - setBlockSettings |= 256; - if (!BlockInteractionUtils.isNaturalAction(ref, entityStore)) { - setBlockSettings |= 2048; - } - - removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore); - HarvestingDropType harvest = blockType.getGathering().getHarvest(); - String itemId = harvest.getItemId(); - String dropListId = harvest.getDropListId(); - - for (ItemStack itemStack : getDrops(blockType, 1, itemId, dropListId)) { - ItemUtils.interactivelyPickupItem(ref, itemStack, centerPosition, entityStore); - } - - if ((setBlockSettings & 1024) == 0) { - BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(blockType.getBlockSoundSetIndex()); - if (soundSet != null) { - int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Harvest, 0); - if (soundEventIndex != 0) { - SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore); - } - } - } - } - - protected static void removeBlock( - @Nonnull Vector3i blockPosition, - @Nonnull BlockType blockType, - int setBlockSettings, - @Nonnull Ref chunkReference, - @Nonnull ComponentAccessor chunkStore - ) { - World world = chunkStore.getExternalData().getWorld(); - ComponentType blockHealthComponentType = BlockHealthModule.get().getBlockHealthChunkComponentType(); - BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, blockHealthComponentType); - - assert blockHealthComponent != null; - - blockHealthComponent.removeBlock(world, blockPosition); - WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); - - assert worldChunkComponent != null; - - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); - if ((setBlockSettings & 256) != 0) { - BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.y); - int rotationIndex = section.getRotationIndex(blockPosition.x, blockPosition.y, blockPosition.z); - BlockBoundingBoxes hitBoxType = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); - if (hitBoxType != null) { - FillerBlockUtil.forEachFillerBlock( - hitBoxType.get(rotationIndex), - (x, y, z) -> world.performBlockUpdate(blockPosition.getX() + x, blockPosition.getY() + y, blockPosition.getZ() + z, false) - ); - } - } - - ConnectedBlocksUtil.setConnectedBlockAndNotifyNeighbors( - BlockType.getAssetMap().getIndex("Empty"), RotationTuple.NONE, Vector3i.ZERO, blockPosition, worldChunkComponent, blockChunkComponent - ); - } - - @Nonnull - public static List getDrops(@Nonnull BlockType blockType, int quantity, @Nullable String itemId, @Nullable String dropListId) { - if (dropListId == null && itemId == null) { - Item item = blockType.getItem(); - return item == null ? ObjectLists.emptyList() : ObjectLists.singleton(new ItemStack(item.getId(), quantity)); - } else { - List randomItemDrops = new ObjectArrayList<>(); - if (dropListId != null) { - ItemModule itemModule = ItemModule.get(); - if (itemModule.isEnabled()) { - for (int i = 0; i < quantity; i++) { - List randomItemsToDrop = itemModule.getRandomItemDrops(dropListId); - randomItemDrops.addAll(randomItemsToDrop); - } - } - } - - if (itemId != null) { - randomItemDrops.add(new ItemStack(itemId, quantity)); - } - - return randomItemDrops; - } - } + return randomItemDrops; + } + } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java index caaf8c41..8bfb21b4 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java @@ -39,376 +39,377 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class ChunkSystems { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private static final int MAX_CHANGES_PER_PACKET = 1024; + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final int MAX_CHANGES_PER_PACKET = 1024; - public ChunkSystems() { - } + public ChunkSystems() { + } - public static class EnsureBlockSection extends HolderSystem { - private static final Query QUERY = Query.and(ChunkSection.getComponentType(), Query.not(BlockSection.getComponentType())); + public static class EnsureBlockSection extends HolderSystem { + private static final Query QUERY = Query.and(ChunkSection.getComponentType(), Query.not(BlockSection.getComponentType())); - public EnsureBlockSection() { - } + public EnsureBlockSection() { + } - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(BlockSection.getComponentType()); - } + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + holder.ensureComponent(BlockSection.getComponentType()); + } - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } + @Nonnull + @Override + public Set> getDependencies() { + return RootDependency.firstSet(); + } + } - public static class LoadBlockSection extends HolderSystem { - public LoadBlockSection() { - } + public static class LoadBlockSection extends HolderSystem { + public LoadBlockSection() { + } - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - BlockSection section = holder.getComponent(BlockSection.getComponentType()); - - assert section != null; - - section.loaded = true; - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Override - public Query getQuery() { - return BlockSection.getComponentType(); - } - } - - public static class OnChunkLoad extends RefSystem { - private static final Query QUERY = Query.and(ChunkColumn.getComponentType(), WorldChunk.getComponentType()); - private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class)); - - public OnChunkLoad() { - } - - @Override - public void onEntityAdded( - @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - - assert chunk != null; - - WorldChunk worldChunk = commandBuffer.getComponent(ref, WorldChunk.getComponentType()); - - assert worldChunk != null; - - Ref[] sections = chunk.getSections(); - Holder[] sectionHolders = chunk.takeSectionHolders(); - boolean isNonTicking = commandBuffer.getArchetype(ref).contains(ChunkStore.REGISTRY.getNonTickingComponentType()); - if (sectionHolders != null && sectionHolders.length > 0 && sectionHolders[0] != null) { - for (int i = 0; i < sectionHolders.length; i++) { - if (isNonTicking) { - sectionHolders[i].ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } else { - sectionHolders[i].tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } - - ChunkSection section = sectionHolders[i].getComponent(ChunkSection.getComponentType()); - if (section == null) { - sectionHolders[i].addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); - } else { - section.load(ref, worldChunk.getX(), i, worldChunk.getZ()); - } - } - - commandBuffer.addEntities(sectionHolders, 0, sections, 0, sections.length, AddReason.LOAD); - } - - for (int i = 0; i < sections.length; i++) { - if (sections[i] == null) { - Holder newSection = ChunkStore.REGISTRY.newHolder(); - if (isNonTicking) { - newSection.ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } else { - newSection.tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } - - newSection.addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); - sections[i] = commandBuffer.addEntity(newSection, AddReason.SPAWN); - } - } - } - - @Override - public void onEntityRemove( - @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - - assert chunk != null; - - Ref[] sections = chunk.getSections(); - Holder[] holders = new Holder[sections.length]; - - for (int i = 0; i < sections.length; i++) { - Ref section = sections[i]; - holders[i] = ChunkStore.REGISTRY.newHolder(); - commandBuffer.removeEntity(section, holders[i], reason); - } - - chunk.putSectionHolders(holders); - Arrays.fill(sections, null); - } - - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return DEPENDENCIES; - } - } - - public static class OnNewChunk extends ChunkColumnMigrationSystem { - private static final Query QUERY = Query.and(WorldChunk.getComponentType(), Query.not(ChunkColumn.getComponentType())); - - public OnNewChunk() { - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - Holder[] sectionHolders = new Holder[10]; - - for (int i = 0; i < sectionHolders.length; i++) { - sectionHolders[i] = ChunkStore.REGISTRY.newHolder(); - } - - holder.addComponent(ChunkColumn.getComponentType(), new ChunkColumn(sectionHolders)); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } - - public static class OnNonTicking extends RefChangeSystem> { - private final Archetype archetype = Archetype.of(WorldChunk.getComponentType(), ChunkColumn.getComponentType()); - - public OnNonTicking() { - } - - @Nonnull - @Override - public ComponentType> componentType() { - return ChunkStore.REGISTRY.getNonTickingComponentType(); - } - - public void onComponentAdded( - @Nonnull Ref ref, - @Nonnull NonTicking component, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - - assert column != null; - - Ref[] sections = column.getSections(); - - for (int i = 0; i < sections.length; i++) { - Ref section = sections[i]; - commandBuffer.ensureComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType()); - } - } - - public void onComponentSet( - @Nonnull Ref ref, - @Nullable NonTicking oldComponent, - @Nonnull NonTicking newComponent, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - } - - public void onComponentRemoved( - @Nonnull Ref ref, - @Nonnull NonTicking component, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - - assert column != null; - - Ref[] sections = column.getSections(); - - for (int i = 0; i < sections.length; i++) { - Ref section = sections[i]; - commandBuffer.tryRemoveComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType()); - } - } - - @Override - public Query getQuery() { - return this.archetype; - } - } - - public static class ReplicateChanges extends EntityTickingSystem implements RunWhenPausedSystem { - private static final Query QUERY = Query.and(ChunkSection.getComponentType(), BlockSection.getComponentType()); - - public ReplicateChanges() { - } - - @Override - public boolean isParallel(int archetypeChunkSize, int taskCount) { - return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); - } - - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - BlockSection blockSection = archetypeChunk.getComponent(index, BlockSection.getComponentType()); - - assert blockSection != null; - - IntOpenHashSet changes = blockSection.getAndClearChangedPositions(); - if (!changes.isEmpty()) { - ChunkSection section = archetypeChunk.getComponent(index, ChunkSection.getComponentType()); + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + BlockSection section = holder.getComponent(BlockSection.getComponentType()); assert section != null; - Collection players = store.getExternalData().getWorld().getPlayerRefs(); - if (players.isEmpty()) { - changes.clear(); - } else { - long chunkIndex = ChunkUtil.indexChunk(section.getX(), section.getZ()); - if (changes.size() >= 1024) { - ObjectArrayList playersCopy = new ObjectArrayList<>(players); - CompletableFuture> set = blockSection.getCachedChunkPacket(section.getX(), section.getY(), section.getZ()); - set.thenAccept(s -> { - for (PlayerRef playerx : playersCopy) { - Ref refx = playerx.getReference(); - if (refx != null) { - ChunkTracker trackerx = playerx.getChunkTracker(); - if (trackerx != null && trackerx.isLoaded(chunkIndex)) { - playerx.getPacketHandler().writeNoCache(s); - } - } - } - }).exceptionally(throwable -> { - if (throwable != null) { - ChunkSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); - } + section.loaded = true; + } - return null; - }); - changes.clear(); - } else { - if (changes.size() == 1) { - int change = changes.iterator().nextInt(); - int x = ChunkUtil.minBlock(section.getX()) + ChunkUtil.xFromIndex(change); - int y = ChunkUtil.minBlock(section.getY()) + ChunkUtil.yFromIndex(change); - int z = ChunkUtil.minBlock(section.getZ()) + ChunkUtil.zFromIndex(change); - int blockId = blockSection.get(change); - int filler = blockSection.getFiller(change); - int rotation = blockSection.getRotationIndex(change); - ServerSetBlock packet = new ServerSetBlock(x, y, z, blockId, (short)filler, (byte)rotation); + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } - for (PlayerRef player : players) { - Ref ref = player.getReference(); - if (ref != null) { - ChunkTracker tracker = player.getChunkTracker(); - if (tracker != null && tracker.isLoaded(chunkIndex)) { - player.getPacketHandler().writeNoCache(packet); - } - } - } - } else { - SetBlockCmd[] cmds = new SetBlockCmd[changes.size()]; - IntIterator iter = changes.intIterator(); - int i = 0; + @Override + public Query getQuery() { + return BlockSection.getComponentType(); + } + } - while (iter.hasNext()) { - int change = iter.nextInt(); - int blockId = blockSection.get(change); - int filler = blockSection.getFiller(change); - int rotation = blockSection.getRotationIndex(change); - cmds[i++] = new SetBlockCmd((short)change, blockId, (short)filler, (byte)rotation); - } + public static class OnChunkLoad extends RefSystem { + private static final Query QUERY = Query.and(ChunkColumn.getComponentType(), WorldChunk.getComponentType()); + private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class)); - ServerSetBlocks packet = new ServerSetBlocks(section.getX(), section.getY(), section.getZ(), cmds); + public OnChunkLoad() { + } - for (PlayerRef playerx : players) { - Ref ref = playerx.getReference(); - if (ref != null) { - ChunkTracker tracker = playerx.getChunkTracker(); - if (tracker != null && tracker.isLoaded(chunkIndex)) { - playerx.getPacketHandler().writeNoCache(packet); - } - } - } - } + @Override + public void onEntityAdded( + @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - changes.clear(); - } + assert chunk != null; + + WorldChunk worldChunk = commandBuffer.getComponent(ref, WorldChunk.getComponentType()); + + assert worldChunk != null; + + Ref[] sections = chunk.getSections(); + Holder[] sectionHolders = chunk.takeSectionHolders(); + boolean isNonTicking = commandBuffer.getArchetype(ref).contains(ChunkStore.REGISTRY.getNonTickingComponentType()); + if (sectionHolders != null && sectionHolders.length > 0 && sectionHolders[0] != null) { + for (int i = 0; i < sectionHolders.length; i++) { + if (isNonTicking) { + sectionHolders[i].ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); + } else { + sectionHolders[i].tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); + } + + ChunkSection section = sectionHolders[i].getComponent(ChunkSection.getComponentType()); + if (section == null) { + sectionHolders[i].addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); + } else { + section.load(ref, worldChunk.getX(), i, worldChunk.getZ()); + } + } + + commandBuffer.addEntities(sectionHolders, 0, sections, 0, sections.length, AddReason.LOAD); } - } - } - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } + for (int i = 0; i < sections.length; i++) { + if (sections[i] == null) { + Holder newSection = ChunkStore.REGISTRY.newHolder(); + if (isNonTicking) { + newSection.ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); + } else { + newSection.tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); + } - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.lastSet(); - } - } + newSection.addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); + sections[i] = commandBuffer.addEntity(newSection, AddReason.SPAWN); + } + } + } + + @Override + public void onEntityRemove( + @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); + + assert chunk != null; + + Ref[] sections = chunk.getSections(); + Holder[] holders = new Holder[sections.length]; + + for (int i = 0; i < sections.length; i++) { + Ref section = sections[i]; + holders[i] = ChunkStore.REGISTRY.newHolder(); + commandBuffer.removeEntity(section, holders[i], reason); + } + + chunk.putSectionHolders(holders); + Arrays.fill(sections, null); + } + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + } + + public static class OnNewChunk extends ChunkColumnMigrationSystem { + private static final Query QUERY = Query.and(WorldChunk.getComponentType(), Query.not(ChunkColumn.getComponentType())); + + public OnNewChunk() { + } + + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + Holder[] sectionHolders = new Holder[10]; + + for (int i = 0; i < sectionHolders.length; i++) { + sectionHolders[i] = ChunkStore.REGISTRY.newHolder(); + } + + holder.addComponent(ChunkColumn.getComponentType(), new ChunkColumn(sectionHolders)); + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Nonnull + @Override + public Set> getDependencies() { + return RootDependency.firstSet(); + } + } + + public static class OnNonTicking extends RefChangeSystem> { + private final Archetype archetype = Archetype.of(WorldChunk.getComponentType(), ChunkColumn.getComponentType()); + + public OnNonTicking() { + } + + @Nonnull + @Override + public ComponentType> componentType() { + return ChunkStore.REGISTRY.getNonTickingComponentType(); + } + + public void onComponentAdded( + @Nonnull Ref ref, + @Nonnull NonTicking component, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); + + assert column != null; + + Ref[] sections = column.getSections(); + + for (int i = 0; i < sections.length; i++) { + Ref section = sections[i]; + commandBuffer.ensureComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType()); + } + } + + public void onComponentSet( + @Nonnull Ref ref, + @Nullable NonTicking oldComponent, + @Nonnull NonTicking newComponent, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + } + + public void onComponentRemoved( + @Nonnull Ref ref, + @Nonnull NonTicking component, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); + + assert column != null; + + Ref[] sections = column.getSections(); + + for (int i = 0; i < sections.length; i++) { + Ref section = sections[i]; + commandBuffer.tryRemoveComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType()); + } + } + + @Override + public Query getQuery() { + return this.archetype; + } + } + + public static class ReplicateChanges extends EntityTickingSystem implements RunWhenPausedSystem { + private static final Query QUERY = Query.and(ChunkSection.getComponentType(), BlockSection.getComponentType()); + + public ReplicateChanges() { + } + + @Override + public boolean isParallel(int archetypeChunkSize, int taskCount) { + return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount); + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + BlockSection blockSection = archetypeChunk.getComponent(index, BlockSection.getComponentType()); + + assert blockSection != null; + + IntOpenHashSet changes = blockSection.getAndClearChangedPositions(); + if (!changes.isEmpty()) { + ChunkSection section = archetypeChunk.getComponent(index, ChunkSection.getComponentType()); + + assert section != null; + + Collection players = store.getExternalData().getWorld().getPlayerRefs(); + if (players.isEmpty()) { + changes.clear(); + } else { + long chunkIndex = ChunkUtil.indexChunk(section.getX(), section.getZ()); + if (changes.size() >= 1024) { + ObjectArrayList playersCopy = new ObjectArrayList<>(players); + CompletableFuture> set = blockSection.getCachedChunkPacket(section.getX(), section.getY(), section.getZ()); + set.thenAccept(s -> { + for (PlayerRef playerx : playersCopy) { + Ref refx = playerx.getReference(); + if (refx != null) { + ChunkTracker trackerx = playerx.getChunkTracker(); + if (trackerx != null && trackerx.isLoaded(chunkIndex)) { + playerx.getPacketHandler().writeNoCache(s); + } + } + } + }).exceptionally(throwable -> { + if (throwable != null) { + ChunkSystems.LOGGER.at(Level.SEVERE).withCause(throwable).log("Exception when compressing chunk fluids:"); + } + + return null; + }); + changes.clear(); + } else { + if (changes.size() == 1) { + int change = changes.iterator().nextInt(); + int x = ChunkUtil.minBlock(section.getX()) + ChunkUtil.xFromIndex(change); + int y = ChunkUtil.minBlock(section.getY()) + ChunkUtil.yFromIndex(change); + int z = ChunkUtil.minBlock(section.getZ()) + ChunkUtil.zFromIndex(change); + int blockId = blockSection.get(change); + int filler = blockSection.getFiller(change); + int rotation = blockSection.getRotationIndex(change); + ServerSetBlock packet = new ServerSetBlock(x, y, z, blockId, (short) filler, (byte) rotation); + + for (PlayerRef player : players) { + Ref ref = player.getReference(); + if (ref != null) { + ChunkTracker tracker = player.getChunkTracker(); + if (tracker != null && tracker.isLoaded(chunkIndex)) { + player.getPacketHandler().writeNoCache(packet); + } + } + } + } else { + SetBlockCmd[] cmds = new SetBlockCmd[changes.size()]; + IntIterator iter = changes.intIterator(); + int i = 0; + + while (iter.hasNext()) { + int change = iter.nextInt(); + int blockId = blockSection.get(change); + int filler = blockSection.getFiller(change); + int rotation = blockSection.getRotationIndex(change); + cmds[i++] = new SetBlockCmd((short) change, blockId, (short) filler, (byte) rotation); + } + + ServerSetBlocks packet = new ServerSetBlocks(section.getX(), section.getY(), section.getZ(), cmds); + + for (PlayerRef playerx : players) { + Ref ref = playerx.getReference(); + if (ref != null) { + ChunkTracker tracker = playerx.getChunkTracker(); + if (tracker != null && tracker.isLoaded(chunkIndex)) { + playerx.getPacketHandler().writeNoCache(packet); + } + } + } + } + + changes.clear(); + } + } + } + } + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Nonnull + @Override + public Set> getDependencies() { + return RootDependency.lastSet(); + } + } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java index ca3b70d1..45feb45d 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.storage.provider; import com.hypixel.fastutil.longs.Long2ObjectConcurrentHashMap; -import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -11,6 +10,7 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemGroup; import com.hypixel.hytale.component.system.StoreSystem; +import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; @@ -126,14 +126,18 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider IndexedStorageChunkStorageProvider.IndexedStorageCache.CacheEntryMetricData[]::new ) ); - + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - - /** How long a region file can be idle before being closed (in milliseconds) */ + + /** + * How long a region file can be idle before being closed (in milliseconds) + */ private static final long IDLE_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5); - /** How often to check for idle region files (in milliseconds) */ + /** + * How often to check for idle region files (in milliseconds) + */ private static final long CLEANUP_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1); - + /** * Wrapper for IndexedStorageFile that tracks usage for async-safe cleanup. */ @@ -142,16 +146,16 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider final AtomicInteger activeOps = new AtomicInteger(0); volatile long lastAccessTime; volatile boolean markedForClose = false; - + CachedFile(IndexedStorageFile file) { this.file = file; this.lastAccessTime = System.currentTimeMillis(); } - + void recordAccess() { this.lastAccessTime = System.currentTimeMillis(); } - + /** * Acquire a reference for an operation. Returns false if file is marked for close. */ @@ -168,16 +172,16 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider recordAccess(); return true; } - + void release() { activeOps.decrementAndGet(); } - + boolean isIdle(long threshold) { return lastAccessTime < threshold && activeOps.get() == 0; } } - + private final Long2ObjectConcurrentHashMap cache = new Long2ObjectConcurrentHashMap<>(true, ChunkUtil.NOT_FOUND); private final Map trackedFiles = new ConcurrentHashMap<>(); private Path path; @@ -185,13 +189,13 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider public IndexedStorageCache() { } - + private void startCleanupTask() { if (cleanupExecutor != null) { return; } - LOGGER.at(Level.INFO).log("Starting region file cleanup task (idle timeout: %ds, interval: %ds)", - IDLE_TIMEOUT_MS / 1000, CLEANUP_INTERVAL_MS / 1000); + LOGGER.at(Level.INFO).log("Starting region file cleanup task (idle timeout: %ds, interval: %ds)", + IDLE_TIMEOUT_MS / 1000, CLEANUP_INTERVAL_MS / 1000); cleanupExecutor = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, "IndexedStorageCache-Cleanup"); t.setDaemon(true); @@ -205,38 +209,38 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider } }, CLEANUP_INTERVAL_MS, CLEANUP_INTERVAL_MS, TimeUnit.MILLISECONDS); } - + private void cleanupIdleFiles() { long now = System.currentTimeMillis(); long threshold = now - IDLE_TIMEOUT_MS; int totalFiles = trackedFiles.size(); int idleCount = 0; - + LOGGER.at(Level.INFO).log("Running region cleanup check: %d tracked files", totalFiles); - + for (Map.Entry entry : trackedFiles.entrySet()) { long regionKey = entry.getKey(); CachedFile cached = entry.getValue(); - + long idleTimeMs = now - cached.lastAccessTime; int activeOps = cached.activeOps.get(); - + if (cached.isIdle(threshold)) { idleCount++; // Mark for close first - prevents new acquisitions cached.markedForClose = true; - + // Double-check no operations started between isIdle check and marking if (cached.activeOps.get() > 0) { cached.markedForClose = false; LOGGER.at(Level.INFO).log("Region cleanup skipped - ops started during close"); continue; } - + // Safe to close now cache.remove(regionKey); trackedFiles.remove(regionKey); - + int regionX = ChunkUtil.xOfChunkIndex(regionKey); int regionZ = ChunkUtil.zOfChunkIndex(regionKey); try { @@ -247,26 +251,26 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider } } else { LOGGER.at(Level.FINE).log("Region %d not idle: lastAccess=%dms ago, activeOps=%d, threshold=%dms", - regionKey, idleTimeMs, activeOps, IDLE_TIMEOUT_MS); + regionKey, idleTimeMs, activeOps, IDLE_TIMEOUT_MS); } } - + if (totalFiles > 0) { LOGGER.at(Level.INFO).log("Region cleanup complete: %d/%d files were idle", idleCount, totalFiles); } } - + private void trackFile(long regionKey, IndexedStorageFile file) { trackedFiles.computeIfAbsent(regionKey, k -> new CachedFile(file)); } - + private void recordAccess(long regionKey) { CachedFile cached = trackedFiles.get(regionKey); if (cached != null) { cached.recordAccess(); } } - + /** * Acquire a file for use. Must call releaseFile() when done. * Returns null if file doesn't exist or is being closed. @@ -278,16 +282,16 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider if (file == null) { return null; } - + CachedFile cached = trackedFiles.get(regionKey); if (cached != null && cached.acquire()) { return file; } - + // File is being closed, retry to get a fresh one return getOrTryOpen(regionX, regionZ, flushOnWrite); } - + /** * Release a file after use. */ @@ -320,7 +324,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider } cleanupExecutor = null; } - + IOException exception = null; Iterator iterator = this.cache.values().iterator(); @@ -336,7 +340,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider exception.addSuppressed(var4); } } - + trackedFiles.clear(); if (exception != null) {