package com.hypixel.hytale.builtin.blockspawner; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.builtin.blockspawner.command.BlockSpawnerCommand; import com.hypixel.hytale.builtin.blockspawner.state.BlockSpawner; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentType; 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.data.unknown.UnknownComponents; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.blocktype.config.VariantRotation; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.world.WorldConfig; import com.hypixel.hytale.server.core.universe.world.chunk.BlockRotationUtil; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BlockSpawnerPlugin extends JavaPlugin { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private ComponentType blockSpawnerComponentType; private static BlockSpawnerPlugin INSTANCE; public static BlockSpawnerPlugin get() { return INSTANCE; } public BlockSpawnerPlugin(@Nonnull JavaPluginInit init) { super(init); INSTANCE = this; } @Override protected void setup() { this.getCommandRegistry().registerCommand(new BlockSpawnerCommand()); AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( BlockSpawnerTable.class, new DefaultAssetMap() ) .setPath("Item/Block/Spawners")) .setCodec(BlockSpawnerTable.CODEC)) .setKeyFunction(BlockSpawnerTable::getId)) .loadsAfter(Item.class, BlockType.class)) .build() ); this.blockSpawnerComponentType = this.getChunkStoreRegistry().registerComponent(BlockSpawner.class, "BlockSpawner", BlockSpawner.CODEC); this.getChunkStoreRegistry().registerSystem(new BlockSpawnerPlugin.BlockSpawnerSystem()); this.getChunkStoreRegistry().registerSystem(new BlockSpawnerPlugin.MigrateBlockSpawner()); } public ComponentType getBlockSpawnerComponentType() { return this.blockSpawnerComponentType; } private static class BlockSpawnerSystem extends RefSystem { private static final ComponentType COMPONENT_TYPE = BlockSpawner.getComponentType(); private static final ComponentType BLOCK_INFO_COMPONENT_TYPE = BlockModule.BlockStateInfo.getComponentType(); private static final Query QUERY = Query.and(COMPONENT_TYPE, BLOCK_INFO_COMPONENT_TYPE); public BlockSpawnerSystem() { } @Override public Query getQuery() { return QUERY; } @Override public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { WorldConfig worldConfig = store.getExternalData().getWorld().getWorldConfig(); if (worldConfig.getGameMode() != GameMode.Creative) { BlockSpawner state = commandBuffer.getComponent(ref, COMPONENT_TYPE); assert state != null; BlockModule.BlockStateInfo info = commandBuffer.getComponent(ref, BLOCK_INFO_COMPONENT_TYPE); assert info != null; String blockSpawnerId = state.getBlockSpawnerId(); if (blockSpawnerId != null) { BlockSpawnerTable table = BlockSpawnerTable.getAssetMap().getAsset(blockSpawnerId); if (table == null) { BlockSpawnerPlugin.LOGGER.at(Level.WARNING).log("Failed to find BlockSpawner Asset by name: %s", blockSpawnerId); } else { Ref chunk = info.getChunkRef(); if (chunk != null) { WorldChunk wc = commandBuffer.getComponent(chunk, WorldChunk.getComponentType()); int x = ChunkUtil.worldCoordFromLocalCoord(wc.getX(), ChunkUtil.xFromBlockInColumn(info.getIndex())); int y = ChunkUtil.yFromBlockInColumn(info.getIndex()); int z = ChunkUtil.worldCoordFromLocalCoord(wc.getZ(), ChunkUtil.zFromBlockInColumn(info.getIndex())); long seed = worldConfig.getSeed(); double randomRnd = HashUtil.random(x, y, z, seed + -1699164769L); BlockSpawnerEntry entry = table.getEntries().get(randomRnd); if (entry != null) { String blockKey = entry.getBlockName(); RotationTuple rotation = switch (entry.getRotationMode()) { case NONE -> RotationTuple.NONE; case RANDOM -> { String key = entry.getBlockName(); VariantRotation variantRotation = BlockType.getAssetMap().getAsset(key).getVariantRotation(); if (variantRotation == VariantRotation.None) { yield RotationTuple.NONE; } else { int randomHash = (int)HashUtil.rehash(x, y, z, seed + -1699164769L); Rotation rotationYaw = Rotation.NORMAL[(randomHash & 65535) % Rotation.NORMAL.length]; yield BlockRotationUtil.getRotated(RotationTuple.NONE, Axis.Y, rotationYaw, variantRotation); } } case INHERIT -> { String key = entry.getBlockName(); VariantRotation variantRotation = BlockType.getAssetMap().getAsset(key).getVariantRotation(); if (variantRotation == VariantRotation.None) { yield RotationTuple.NONE; } else { RotationTuple spawnerRotation = RotationTuple.get(wc.getRotationIndex(x, y, z)); Rotation spawnerYaw = spawnerRotation.yaw(); yield BlockRotationUtil.getRotated(RotationTuple.NONE, Axis.Y, spawnerYaw, variantRotation); } } }; Holder holder = entry.getBlockComponents(); commandBuffer.removeEntity(ref, RemoveReason.REMOVE); commandBuffer.run(_store -> { int flags = 4; if (holder != null) { flags |= 2; } int blockId = BlockType.getAssetMap().getIndex(blockKey); BlockType blockType = BlockType.getAssetMap().getAsset(blockId); wc.setBlock(x, y, z, blockId, blockType, rotation.index(), 0, flags); if (holder != null) { wc.setState(x, y, z, holder.clone()); } }); } } } } } } @Override public void onEntityRemove( @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } } @Deprecated(forRemoval = true) public static class MigrateBlockSpawner extends BlockModule.MigrationSystem { @Override public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { UnknownComponents unknown = holder.getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); assert unknown != null; BlockSpawner blockSpawner = unknown.removeComponent("blockspawner", BlockSpawner.CODEC); if (blockSpawner != null) { holder.putComponent(BlockSpawner.getComponentType(), blockSpawner); } } @Override public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { } @Nullable @Override public Query getQuery() { return ChunkStore.REGISTRY.getUnknownComponentType(); } } }