package com.hypixel.hytale.server.core.universe.world.chunk;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.map.Int2ObjectMapCodec;
import com.hypixel.hytale.codec.store.StoredCodec;
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.Component;
import com.hypixel.hytale.component.ComponentRegistry;
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.query.Query;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.server.core.modules.LegacyModule;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.meta.BlockState;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectCollection;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class BlockComponentChunk implements Component<ChunkStore> {
public static final BuilderCodec<BlockComponentChunk> CODEC;
@Nonnull
private final Int2ObjectMap<Holder<ChunkStore>> entityHolders;
@Nonnull
private final Int2ObjectMap<Ref<ChunkStore>> entityReferences;
@Nonnull
private final Int2ObjectMap<Holder<ChunkStore>> entityHoldersUnmodifiable;
@Nonnull
private final Int2ObjectMap<Ref<ChunkStore>> entityReferencesUnmodifiable;
private boolean needsSaving;
public static ComponentType<ChunkStore, BlockComponentChunk> getComponentType() {
return LegacyModule.get().getBlockComponentChunkComponentType();
}
public BlockComponentChunk() {
this.entityHolders = new Int2ObjectOpenHashMap<Holder<ChunkStore>>();
this.entityReferences = new Int2ObjectOpenHashMap<Ref<ChunkStore>>();
this.entityHoldersUnmodifiable = Int2ObjectMaps.<Holder<ChunkStore>>unmodifiable(this.entityHolders);
this.entityReferencesUnmodifiable = Int2ObjectMaps.<Ref<ChunkStore>>unmodifiable(this.entityReferences);
}
public BlockComponentChunk(@Nonnull Int2ObjectMap<Holder<ChunkStore>> entityHolders, @Nonnull Int2ObjectMap<Ref<ChunkStore>> entityReferences) {
this.entityHolders = entityHolders;
this.entityReferences = entityReferences;
this.entityHoldersUnmodifiable = Int2ObjectMaps.<Holder<ChunkStore>>unmodifiable(entityHolders);
this.entityReferencesUnmodifiable = Int2ObjectMaps.<Ref<ChunkStore>>unmodifiable(entityReferences);
}
@Nonnull
public Component<ChunkStore> clone() {
Int2ObjectOpenHashMap<Holder<ChunkStore>> entityHoldersClone = new Int2ObjectOpenHashMap<Holder<ChunkStore>>(this.entityHolders.size() + this.entityReferences.size());
for(Int2ObjectMap.Entry<Holder<ChunkStore>> entry : this.entityHolders.int2ObjectEntrySet()) {
entityHoldersClone.put(entry.getIntKey(), ((Holder)entry.getValue()).clone());
}
for(Int2ObjectMap.Entry<Ref<ChunkStore>> entry : this.entityReferences.int2ObjectEntrySet()) {
Ref<ChunkStore> reference = (Ref)entry.getValue();
entityHoldersClone.put(entry.getIntKey(), reference.getStore().copyEntity(reference));
}
return new BlockComponentChunk(entityHoldersClone, new Int2ObjectOpenHashMap());
}
@Nonnull
public Component<ChunkStore> cloneSerializable() {
ComponentRegistry.Data<ChunkStore> data = ChunkStore.REGISTRY.getData();
Int2ObjectOpenHashMap<Holder<ChunkStore>> entityHoldersClone = new Int2ObjectOpenHashMap<Holder<ChunkStore>>(this.entityHolders.size() + this.entityReferences.size());
for(Int2ObjectMap.Entry<Holder<ChunkStore>> entry : this.entityHolders.int2ObjectEntrySet()) {
Holder<ChunkStore> holder = (Holder)entry.getValue();
if (holder.getArchetype().hasSerializableComponents(data)) {
entityHoldersClone.put(entry.getIntKey(), holder.cloneSerializable(data));
}
}
for(Int2ObjectMap.Entry<Ref<ChunkStore>> entry : this.entityReferences.int2ObjectEntrySet()) {
Ref<ChunkStore> reference = (Ref)entry.getValue();
Store<ChunkStore> store = reference.getStore();
if (store.getArchetype(reference).hasSerializableComponents(data)) {
entityHoldersClone.put(entry.getIntKey(), store.copySerializableEntity(reference));
}
}
return new BlockComponentChunk(entityHoldersClone, new Int2ObjectOpenHashMap());
}
@Nonnull
public Int2ObjectMap<Holder<ChunkStore>> getEntityHolders() {
return this.entityHoldersUnmodifiable;
}
@Nullable
public Holder<ChunkStore> getEntityHolder(int index) {
return (Holder)this.entityHolders.get(index);
}
public void addEntityHolder(int index, @Nonnull Holder<ChunkStore> holder) {
if (this.entityReferences.containsKey(index)) {
throw new IllegalArgumentException("Duplicate block components at: " + index);
} else if (this.entityHolders.putIfAbsent(index, (Holder)Objects.requireNonNull(holder)) != null) {
throw new IllegalArgumentException("Duplicate block components (entity holder) at: " + index);
} else {
this.markNeedsSaving();
}
}
public void storeEntityHolder(int index, @Nonnull Holder<ChunkStore> holder) {
if (this.entityHolders.putIfAbsent(index, (Holder)Objects.requireNonNull(holder)) != null) {
throw new IllegalArgumentException("Duplicate block components (entity holder) at: " + index);
}
}
@Nullable
public Holder<ChunkStore> removeEntityHolder(int index) {
Holder<ChunkStore> reference = (Holder)this.entityHolders.remove(index);
if (reference != null) {
this.markNeedsSaving();
}
return reference;
}
@Nonnull
public Int2ObjectMap<Ref<ChunkStore>> getEntityReferences() {
return this.entityReferencesUnmodifiable;
}
@Nullable
public Ref<ChunkStore> getEntityReference(int index) {
return (Ref)this.entityReferences.get(index);
}
public void addEntityReference(int index, @Nonnull Ref<ChunkStore> reference) {
reference.validate();
if (this.entityHolders.containsKey(index)) {
throw new IllegalArgumentException("Duplicate block components at: " + index);
} else if (this.entityReferences.putIfAbsent(index, (Ref)Objects.requireNonNull(reference)) != null) {
throw new IllegalArgumentException("Duplicate block components (entity reference) at: " + index);
} else {
this.markNeedsSaving();
}
}
public void loadEntityReference(int index, @Nonnull Ref<ChunkStore> reference) {
reference.validate();
if (this.entityHolders.containsKey(index)) {
throw new IllegalArgumentException("Duplicate block components at: " + index);
} else if (this.entityReferences.putIfAbsent(index, (Ref)Objects.requireNonNull(reference)) != null) {
throw new IllegalArgumentException("Duplicate block components (entity reference) at: " + index);
}
}
public void removeEntityReference(int index, Ref<ChunkStore> reference) {
if (this.entityReferences.remove(index, reference)) {
this.markNeedsSaving();
}
}
public void unloadEntityReference(int index, Ref<ChunkStore> reference) {
this.entityReferences.remove(index, reference);
}
@Nullable
public Int2ObjectMap<Holder<ChunkStore>> takeEntityHolders() {
if (this.entityHolders.isEmpty()) {
return null;
} else {
Int2ObjectOpenHashMap<Holder<ChunkStore>> holders = new Int2ObjectOpenHashMap<Holder<ChunkStore>>(this.entityHolders);
this.entityHolders.clear();
return holders;
}
}
@Nullable
public Int2ObjectMap<Ref<ChunkStore>> takeEntityReferences() {
if (this.entityReferences.isEmpty()) {
return null;
} else {
Int2ObjectOpenHashMap<Ref<ChunkStore>> holders = new Int2ObjectOpenHashMap<Ref<ChunkStore>>(this.entityReferences);
this.entityReferences.clear();
return holders;
}
}
@Nullable
public <T extends Component<ChunkStore>> T getComponent(int index, @Nonnull ComponentType<ChunkStore, T> componentType) {
Ref<ChunkStore> reference = (Ref)this.entityReferences.get(index);
if (reference != null) {
return (T)reference.getStore().getComponent(reference, componentType);
} else {
Holder<ChunkStore> holder = (Holder)this.entityHolders.get(index);
return (T)(holder != null ? holder.getComponent(componentType) : null);
}
}
public boolean hasComponents(int index) {
return this.entityReferences.containsKey(index) || this.entityHolders.containsKey(index);
}
public boolean getNeedsSaving() {
return this.needsSaving;
}
public void markNeedsSaving() {
this.needsSaving = true;
}
public boolean consumeNeedsSaving() {
boolean out = this.needsSaving;
this.needsSaving = false;
return out;
}
static {
CODEC = ((BuilderCodec.Builder)BuilderCodec.builder(BlockComponentChunk.class, BlockComponentChunk::new).addField(new KeyedCodec("BlockComponents", new Int2ObjectMapCodec(new StoredCodec(ChunkStore.HOLDER_CODEC_KEY), Int2ObjectOpenHashMap::new)), (entityChunk, map) -> {
entityChunk.entityHolders.clear();
entityChunk.entityHolders.putAll(map);
}, (entityChunk) -> {
if (entityChunk.entityReferences.isEmpty()) {
return entityChunk.entityHolders;
} else {
Int2ObjectMap<Holder<ChunkStore>> map = new Int2ObjectOpenHashMap<Holder<ChunkStore>>(entityChunk.entityHolders.size() + entityChunk.entityReferences.size());
map.putAll(entityChunk.entityHolders);
for(Int2ObjectMap.Entry<Ref<ChunkStore>> entry : entityChunk.entityReferences.int2ObjectEntrySet()) {
Ref<ChunkStore> reference = (Ref)entry.getValue();
Store<ChunkStore> store = reference.getStore();
if (store.getArchetype(reference).hasSerializableComponents(store.getRegistry().getData())) {
map.put(entry.getIntKey(), store.copySerializableEntity(reference));
}
}
return map;
}
})).build();
}
public static class BlockComponentChunkLoadingSystem extends RefChangeSystem<ChunkStore, NonTicking<ChunkStore>> {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private final Archetype<ChunkStore> archetype = Archetype.of(WorldChunk.getComponentType(), BlockComponentChunk.getComponentType());
public BlockComponentChunkLoadingSystem() {
}
public Query<ChunkStore> getQuery() {
return this.archetype;
}
@Nonnull
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) {
BlockComponentChunk blockComponentChunk = (BlockComponentChunk)store.getComponent(ref, BlockComponentChunk.getComponentType());
Int2ObjectMap<Ref<ChunkStore>> entityReferences = blockComponentChunk.takeEntityReferences();
if (entityReferences != null) {
int size = entityReferences.size();
int[] indexes = new int[size];
Ref<ChunkStore>[] references = new Ref[size];
int j = 0;
for(Int2ObjectMap.Entry<Ref<ChunkStore>> entry : entityReferences.int2ObjectEntrySet()) {
indexes[j] = entry.getIntKey();
references[j] = (Ref)entry.getValue();
++j;
}
ComponentRegistry.Data<ChunkStore> data = ChunkStore.REGISTRY.getData();
for(int i = 0; i < size; ++i) {
if (store.getArchetype(references[i]).hasSerializableComponents(data)) {
Holder<ChunkStore> holder = ChunkStore.REGISTRY.newHolder();
commandBuffer.removeEntity(references[i], holder, RemoveReason.UNLOAD);
blockComponentChunk.storeEntityHolder(indexes[i], holder);
} else {
commandBuffer.removeEntity(references[i], RemoveReason.UNLOAD);
}
}
}
}
public void onComponentSet(@Nonnull Ref<ChunkStore> ref, 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) {
WorldChunk chunk = (WorldChunk)store.getComponent(ref, WorldChunk.getComponentType());
BlockComponentChunk blockComponentChunk = (BlockComponentChunk)store.getComponent(ref, BlockComponentChunk.getComponentType());
Int2ObjectMap<Holder<ChunkStore>> entityHolders = blockComponentChunk.takeEntityHolders();
if (entityHolders != null) {
int holderCount = entityHolders.size();
int[] indexes = new int[holderCount];
Holder<ChunkStore>[] holders = new Holder[holderCount];
int j = 0;
for(Int2ObjectMap.Entry<Holder<ChunkStore>> entry : entityHolders.int2ObjectEntrySet()) {
indexes[j] = entry.getIntKey();
holders[j] = (Holder)entry.getValue();
++j;
}
for(int i = holderCount - 1; i >= 0; --i) {
Holder<ChunkStore> holder = holders[i];
if (holder.getArchetype().isEmpty()) {
LOGGER.at(Level.SEVERE).log("Empty archetype entity holder: %s (#%d)", holder, i);
--holderCount;
holders[i] = holders[holderCount];
holders[holderCount] = holder;
chunk.markNeedsSaving();
} else {
int index = indexes[i];
int x = ChunkUtil.xFromBlockInColumn(index);
int y = ChunkUtil.yFromBlockInColumn(index);
int z = ChunkUtil.zFromBlockInColumn(index);
holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, ref));
BlockState state = BlockState.getBlockState(holder);
if (state != null) {
state.setPosition(chunk, new Vector3i(x, y, z));
}
}
}
commandBuffer.addEntities(holders, AddReason.LOAD);
}
}
}
public static class LoadBlockComponentPacketSystem extends ChunkStore.LoadPacketDataQuerySystem {
private final ComponentType<ChunkStore, BlockComponentChunk> componentType;
public LoadBlockComponentPacketSystem(ComponentType<ChunkStore, BlockComponentChunk> blockComponentChunkComponentType) {
this.componentType = blockComponentChunkComponentType;
}
public Query<ChunkStore> getQuery() {
return this.componentType;
}
public void fetch(int index, @Nonnull ArchetypeChunk<ChunkStore> archetypeChunk, @Nonnull Store<ChunkStore> store, CommandBuffer<ChunkStore> commandBuffer, PlayerRef player, @Nonnull List<Packet> results) {
BlockComponentChunk component = (BlockComponentChunk)archetypeChunk.getComponent(index, this.componentType);
ObjectCollection<Ref<ChunkStore>> references = component.entityReferences.values();
Store<ChunkStore> componentStore = ((ChunkStore)store.getExternalData()).getWorld().getChunkStore().getStore();
componentStore.fetch(references, ChunkStore.LOAD_PACKETS_DATA_QUERY_SYSTEM_TYPE, player, results);
}
}
public static class UnloadBlockComponentPacketSystem extends ChunkStore.UnloadPacketDataQuerySystem {
private final ComponentType<ChunkStore, BlockComponentChunk> componentType;
public UnloadBlockComponentPacketSystem(ComponentType<ChunkStore, BlockComponentChunk> blockComponentChunkComponentType) {
this.componentType = blockComponentChunkComponentType;
}
public Query<ChunkStore> getQuery() {
return this.componentType;
}
public void fetch(int index, @Nonnull ArchetypeChunk<ChunkStore> archetypeChunk, @Nonnull Store<ChunkStore> store, CommandBuffer<ChunkStore> commandBuffer, PlayerRef player, @Nonnull List<Packet> results) {
BlockComponentChunk component = (BlockComponentChunk)archetypeChunk.getComponent(index, this.componentType);
ObjectCollection<Ref<ChunkStore>> references = component.entityReferences.values();
Store<ChunkStore> componentStore = ((ChunkStore)store.getExternalData()).getWorld().getChunkStore().getStore();
componentStore.fetch(references, ChunkStore.UNLOAD_PACKETS_DATA_QUERY_SYSTEM_TYPE, player, results);
}
}
}