src: update 6 files
This commit is contained in:
@@ -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<ChunkStore> {
|
||||
private static final ComponentType<ChunkStore, BlockChunk> COMPONENT_TYPE = BlockChunk.getComponentType();
|
||||
public static class PreTick extends EntityTickingSystem<ChunkStore> {
|
||||
private static final ComponentType<ChunkStore, BlockChunk> COMPONENT_TYPE = BlockChunk.getComponentType();
|
||||
|
||||
public PreTick() {
|
||||
}
|
||||
public PreTick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return COMPONENT_TYPE;
|
||||
}
|
||||
@Override
|
||||
public Query<ChunkStore> 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<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);
|
||||
@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;
|
||||
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;
|
||||
}
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ChunkStore> {
|
||||
@Nonnull
|
||||
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(FluidSection.getComponentType()));
|
||||
public static class EnsureFluidSection extends HolderSystem<ChunkStore> {
|
||||
@Nonnull
|
||||
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(FluidSection.getComponentType()));
|
||||
|
||||
public EnsureFluidSection() {
|
||||
}
|
||||
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 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) {
|
||||
}
|
||||
@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 Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> getDependencies() {
|
||||
return RootDependency.firstSet();
|
||||
}
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> 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<ChunkStore> archetypeChunk,
|
||||
Store<ChunkStore> store,
|
||||
@Nonnull CommandBuffer<ChunkStore> commandBuffer,
|
||||
PlayerRef query,
|
||||
@Nonnull List<CompletableFuture<Packet>> results
|
||||
) {
|
||||
ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, ChunkColumn.getComponentType());
|
||||
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;
|
||||
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:");
|
||||
}
|
||||
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()));
|
||||
return null;
|
||||
}).thenApply(Function.identity()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return ChunkColumn.getComponentType();
|
||||
}
|
||||
}
|
||||
@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 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() {
|
||||
}
|
||||
public MigrateFromColumn() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType());
|
||||
@Override
|
||||
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> 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<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();
|
||||
}
|
||||
}
|
||||
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) {
|
||||
}
|
||||
@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 Query<ChunkStore> getQuery() {
|
||||
return this.QUERY;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> getDependencies() {
|
||||
return this.DEPENDENCIES;
|
||||
}
|
||||
}
|
||||
@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 static class ReplicateChanges extends EntityTickingSystem<ChunkStore> implements RunWhenPausedSystem<ChunkStore> {
|
||||
@Nonnull
|
||||
private static final Query<ChunkStore> 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<ChunkStore> archetypeChunk,
|
||||
@Nonnull Store<ChunkStore> store,
|
||||
@Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
FluidSection fluidSectionComponent = archetypeChunk.getComponent(index, FluidSection.getComponentType());
|
||||
@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;
|
||||
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<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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> getDependencies() {
|
||||
return DEPENDENCIES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(BlockSection.getComponentType()));
|
||||
public static class EnsureBlockSection extends HolderSystem<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(ChunkSection.getComponentType(), Query.not(BlockSection.getComponentType()));
|
||||
|
||||
public EnsureBlockSection() {
|
||||
}
|
||||
public EnsureBlockSection() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
holder.ensureComponent(BlockSection.getComponentType());
|
||||
}
|
||||
@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) {
|
||||
}
|
||||
@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 Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> getDependencies() {
|
||||
return RootDependency.firstSet();
|
||||
}
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> getDependencies() {
|
||||
return RootDependency.firstSet();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoadBlockSection extends HolderSystem<ChunkStore> {
|
||||
public LoadBlockSection() {
|
||||
}
|
||||
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());
|
||||
@Override
|
||||
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
BlockSection section = holder.getComponent(BlockSection.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:");
|
||||
}
|
||||
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<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
}
|
||||
|
||||
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;
|
||||
@Override
|
||||
public Query<ChunkStore> 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<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));
|
||||
|
||||
ServerSetBlocks packet = new ServerSetBlocks(section.getX(), section.getY(), section.getZ(), cmds);
|
||||
public OnChunkLoad() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@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());
|
||||
|
||||
changes.clear();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<ChunkStore>> 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<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IndexedStorageFile> cache = new Long2ObjectConcurrentHashMap<>(true, ChunkUtil.NOT_FOUND);
|
||||
private final Map<Long, CachedFile> 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<Long, CachedFile> 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<IndexedStorageFile> iterator = this.cache.values().iterator();
|
||||
|
||||
@@ -336,7 +340,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider
|
||||
exception.addSuppressed(var4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trackedFiles.clear();
|
||||
|
||||
if (exception != null) {
|
||||
|
||||
Reference in New Issue
Block a user