src: add 3 files

This commit is contained in:
luk
2026-01-27 20:19:44 +00:00
parent 4675a2afdd
commit d579fee4e0
4 changed files with 972 additions and 1 deletions

2
.gitignore vendored
View File

@@ -52,3 +52,5 @@ Assets
Server
patcher
start.*

View File

@@ -0,0 +1,142 @@
package com.hypixel.hytale.builtin.blocktick.system;
import com.hypixel.hytale.builtin.blocktick.BlockTickPlugin;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickManager;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.TickProcedure;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
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 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();
public ChunkBlockTickSystem() {
}
public static class PreTick extends EntityTickingSystem<ChunkStore> {
private static final ComponentType<ChunkStore, BlockChunk> COMPONENT_TYPE = BlockChunk.getComponentType();
public PreTick() {
}
@Override
public Query<ChunkStore> getQuery() {
return COMPONENT_TYPE;
}
@Override
public boolean isParallel(int archetypeChunkSize, int taskCount) {
return EntityTickingSystem.useParallel(archetypeChunkSize, taskCount);
}
@Override
public void tick(
float dt,
int index,
@Nonnull ArchetypeChunk<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
Instant time = commandBuffer.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime();
BlockChunk chunk = archetypeChunk.getComponent(index, COMPONENT_TYPE);
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<ChunkStore> {
private static final ComponentType<ChunkStore, WorldChunk> COMPONENT_TYPE = WorldChunk.getComponentType();
private static final Set<Dependency<ChunkStore>> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class));
public Ticking() {
}
@Override
public Query<ChunkStore> getQuery() {
return COMPONENT_TYPE;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return DEPENDENCIES;
}
@Override
public void tick(
float dt,
int index,
@Nonnull ArchetypeChunk<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
Ref<ChunkStore> 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<ChunkStore> 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;
}
}
}
}

View File

@@ -0,0 +1,413 @@
package com.hypixel.hytale.builtin.fluid;
import com.hypixel.hytale.builtin.blocktick.system.ChunkBlockTickSystem;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.system.tick.RunWhenPausedSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.world.ServerSetFluid;
import com.hypixel.hytale.protocol.packets.world.ServerSetFluids;
import com.hypixel.hytale.protocol.packets.world.SetFluidCmd;
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker;
import com.hypixel.hytale.server.core.modules.LegacyModule;
import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker;
import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem;
import com.hypixel.hytale.server.core.universe.PlayerRef;
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.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
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 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;
public FluidSystems() {
}
public static class EnsureFluidSection extends HolderSystem<ChunkStore> {
@Nonnull
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(FluidSection.getComponentType()));
public EnsureFluidSection() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
holder.addComponent(FluidSection.getComponentType(), new FluidSection());
}
@Override
public void onEntityRemoved(@Nonnull Holder<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Nonnull
@Override
public Query<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return RootDependency.firstSet();
}
}
public static class LoadPacketGenerator extends ChunkStore.LoadFuturePacketDataQuerySystem {
public LoadPacketGenerator() {
}
public void fetch(
int index,
@Nonnull ArchetypeChunk<ChunkStore> archetypeChunk,
Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer,
PlayerRef query,
@Nonnull List<CompletableFuture<Packet>> results
) {
ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, ChunkColumn.getComponentType());
assert chunkColumnComponent != null;
for (Ref<ChunkStore> 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()));
}
}
}
@Override
public Query<ChunkStore> getQuery() {
return ChunkColumn.getComponentType();
}
}
public static class MigrateFromColumn extends ChunkColumnMigrationSystem {
@Nonnull
private final Query<ChunkStore> QUERY = Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType());
@Nonnull
private final Set<Dependency<ChunkStore>> DEPENDENCIES = Set.of(new SystemDependency<>(Order.BEFORE, LegacyModule.MigrateLegacySections.class));
public MigrateFromColumn() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType());
assert chunkColumnComponent != null;
BlockChunk blockChunkComponent = holder.getComponent(BlockChunk.getComponentType());
assert blockChunkComponent != null;
Holder<ChunkStore>[] sections = chunkColumnComponent.getSectionHolders();
BlockSection[] legacySections = blockChunkComponent.getMigratedSections();
if (legacySections != null) {
for (int i = 0; i < sections.length; i++) {
Holder<ChunkStore> 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<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Nonnull
@Override
public Query<ChunkStore> getQuery() {
return this.QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return this.DEPENDENCIES;
}
}
public static class ReplicateChanges extends EntityTickingSystem<ChunkStore> implements RunWhenPausedSystem<ChunkStore> {
@Nonnull
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.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<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType());
assert fluidSectionComponent != null;
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<PlayerRef> playerRefs = store.getExternalData().getWorld().getPlayerRefs();
if (playerRefs.isEmpty()) {
changes.clear();
} else {
long chunkIndex = ChunkUtil.indexChunk(fluidSectionComponent.getX(), fluidSectionComponent.getZ());
if (changes.size() >= 1024) {
ObjectArrayList<PlayerRef> 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<EntityStore> 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<EntityStore> 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<EntityStore> 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<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return RootDependency.lastSet();
}
}
public static class SetupSection extends HolderSystem<ChunkStore> {
@Nonnull
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), FluidSection.getComponentType());
@Nonnull
private static final Set<Dependency<ChunkStore>> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, FluidSystems.MigrateFromColumn.class));
public SetupSection() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> 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<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Nonnull
@Override
public Query<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return DEPENDENCIES;
}
}
public static class Ticking extends EntityTickingSystem<ChunkStore> {
@Nonnull
private static final Query<ChunkStore> QUERY = Query.and(FluidSection.getComponentType(), ChunkSection.getComponentType());
@Nonnull
private static final Set<Dependency<ChunkStore>> 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<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
ChunkSection chunkSectionComponent = archetypeChunk.getComponent(index, ChunkSection.getComponentType());
assert chunkSectionComponent != null;
FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType());
assert fluidSectionComponent != null;
Ref<ChunkStore> 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<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return DEPENDENCIES;
}
}
}

View File

@@ -0,0 +1,414 @@
package com.hypixel.hytale.server.core.universe.world.chunk.systems;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.NonTicking;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.system.tick.RunWhenPausedSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.CachedPacket;
import com.hypixel.hytale.protocol.packets.world.ServerSetBlock;
import com.hypixel.hytale.protocol.packets.world.ServerSetBlocks;
import com.hypixel.hytale.protocol.packets.world.SetBlockCmd;
import com.hypixel.hytale.protocol.packets.world.SetChunk;
import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker;
import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
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 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;
public ChunkSystems() {
}
public static class EnsureBlockSection extends HolderSystem<ChunkStore> {
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(BlockSection.getComponentType()));
public EnsureBlockSection() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
holder.ensureComponent(BlockSection.getComponentType());
}
@Override
public void onEntityRemoved(@Nonnull Holder<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Nonnull
@Override
public Query<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return RootDependency.firstSet();
}
}
public static class LoadBlockSection extends HolderSystem<ChunkStore> {
public LoadBlockSection() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
BlockSection section = holder.getComponent(BlockSection.getComponentType());
assert section != null;
section.loaded = true;
}
@Override
public void onEntityRemoved(@Nonnull Holder<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Override
public Query<ChunkStore> getQuery() {
return BlockSection.getComponentType();
}
}
public static class OnChunkLoad extends RefSystem<ChunkStore> {
private static final Query<ChunkStore> QUERY = Query.and(ChunkColumn.getComponentType(), WorldChunk.getComponentType());
private static final Set<Dependency<ChunkStore>> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class));
public OnChunkLoad() {
}
@Override
public void onEntityAdded(
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType());
assert chunk != null;
WorldChunk worldChunk = commandBuffer.getComponent(ref, WorldChunk.getComponentType());
assert worldChunk != null;
Ref<ChunkStore>[] sections = chunk.getSections();
Holder<ChunkStore>[] 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<ChunkStore> 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<ChunkStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType());
assert chunk != null;
Ref<ChunkStore>[] sections = chunk.getSections();
Holder<ChunkStore>[] holders = new Holder[sections.length];
for (int i = 0; i < sections.length; i++) {
Ref<ChunkStore> 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<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return DEPENDENCIES;
}
}
public static class OnNewChunk extends ChunkColumnMigrationSystem {
private static final Query<ChunkStore> QUERY = Query.and(WorldChunk.getComponentType(), Query.not(ChunkColumn.getComponentType()));
public OnNewChunk() {
}
@Override
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> 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<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
}
@Nonnull
@Override
public Query<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return RootDependency.firstSet();
}
}
public static class OnNonTicking extends RefChangeSystem<ChunkStore, NonTicking<ChunkStore>> {
private final Archetype<ChunkStore> archetype = Archetype.of(WorldChunk.getComponentType(), ChunkColumn.getComponentType());
public OnNonTicking() {
}
@Nonnull
@Override
public ComponentType<ChunkStore, NonTicking<ChunkStore>> componentType() {
return ChunkStore.REGISTRY.getNonTickingComponentType();
}
public void onComponentAdded(
@Nonnull Ref<ChunkStore> ref,
@Nonnull NonTicking<ChunkStore> component,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType());
assert column != null;
Ref<ChunkStore>[] sections = column.getSections();
for (int i = 0; i < sections.length; i++) {
Ref<ChunkStore> section = sections[i];
commandBuffer.ensureComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType());
}
}
public void onComponentSet(
@Nonnull Ref<ChunkStore> ref,
@Nullable NonTicking<ChunkStore> oldComponent,
@Nonnull NonTicking<ChunkStore> newComponent,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
}
public void onComponentRemoved(
@Nonnull Ref<ChunkStore> ref,
@Nonnull NonTicking<ChunkStore> component,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
ChunkColumn column = commandBuffer.getComponent(ref, ChunkColumn.getComponentType());
assert column != null;
Ref<ChunkStore>[] sections = column.getSections();
for (int i = 0; i < sections.length; i++) {
Ref<ChunkStore> section = sections[i];
commandBuffer.tryRemoveComponent(section, ChunkStore.REGISTRY.getNonTickingComponentType());
}
}
@Override
public Query<ChunkStore> getQuery() {
return this.archetype;
}
}
public static class ReplicateChanges extends EntityTickingSystem<ChunkStore> implements RunWhenPausedSystem<ChunkStore> {
private static final Query<ChunkStore> 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<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> 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<PlayerRef> players = store.getExternalData().getWorld().getPlayerRefs();
if (players.isEmpty()) {
changes.clear();
} else {
long chunkIndex = ChunkUtil.indexChunk(section.getX(), section.getZ());
if (changes.size() >= 1024) {
ObjectArrayList<PlayerRef> playersCopy = new ObjectArrayList<>(players);
CompletableFuture<CachedPacket<SetChunk>> set = blockSection.getCachedChunkPacket(section.getX(), section.getY(), section.getZ());
set.thenAccept(s -> {
for (PlayerRef playerx : playersCopy) {
Ref<EntityStore> 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<EntityStore> 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<EntityStore> 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<ChunkStore> getQuery() {
return QUERY;
}
@Nonnull
@Override
public Set<Dependency<ChunkStore>> getDependencies() {
return RootDependency.lastSet();
}
}
}