package com.hypixel.hytale.builtin.fluid; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.arguments.types.AssetArgumentType; import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition; import com.hypixel.hytale.server.core.command.system.arguments.types.SingleArgumentType; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; public class FluidCommand extends AbstractCommandCollection { private static final SingleArgumentType FLUID_ARG = new AssetArgumentType("Fluid", Fluid.class, ""); public FluidCommand() { super("fluid", "Fluid debug commands"); this.addSubCommand(new FluidCommand.SetCommand()); this.addSubCommand(new FluidCommand.GetCommand()); this.addSubCommand(new FluidCommand.SetRadiusCommand()); } public static class GetCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock"); @Nonnull private static final Message MESSAGE_COMMANDS_NO_SECTION_COMPONENT = Message.translation("server.commands.noSectionComponent"); @Nonnull private final OptionalArg targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION); public GetCommand() { super("get", "Gets the fluid at the target position"); } @Override protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { RelativeIntPosition offset = this.targetOffset.get(context); Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store); if (blockTarget == null) { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)) .thenAcceptAsync( section -> { Store sectionStore = section.getStore(); FluidSection fluidSection = sectionStore.getComponent((Ref)section, FluidSection.getComponentType()); if (fluidSection == null) { playerRef.sendMessage(MESSAGE_COMMANDS_NO_SECTION_COMPONENT); } else { int index = ChunkUtil.indexBlock(pos.x, pos.y, pos.z); Fluid fluid = fluidSection.getFluid(index); byte level = fluidSection.getFluidLevel(index); playerRef.sendMessage( Message.translation("server.commands.get.success") .param("x", pos.x) .param("y", pos.y) .param("z", pos.z) .param("id", fluid.getId()) .param("level", (int)level) ); } }, world ); } } } public static class SetCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock"); @Nonnull private static final Message MESSAGE_COMMANDS_SET_UNKNOWN_FLUID = Message.translation("server.commands.set.unknownFluid"); @Nonnull private static final Message MESSAGE_COMMANDS_NO_SECTION_COMPONENT = Message.translation("server.commands.noSectionComponent"); @Nonnull private final RequiredArg fluid = this.withRequiredArg("fluid", "", FluidCommand.FLUID_ARG); @Nonnull private final RequiredArg level = this.withRequiredArg("level", "", ArgTypes.INTEGER); @Nonnull private final OptionalArg targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION); public SetCommand() { super("set", "Changes the fluid at the target position"); } @Override protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { RelativeIntPosition offset = this.targetOffset.get(context); Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store); if (blockTarget == null) { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); Fluid fluid = this.fluid.get(context); if (fluid == null) { playerRef.sendMessage(MESSAGE_COMMANDS_SET_UNKNOWN_FLUID); } else { Integer level = this.level.get(context); if (level > fluid.getMaxFluidLevel()) { level = fluid.getMaxFluidLevel(); playerRef.sendMessage(Message.translation("server.commands.set.maxFluidLevelClamped").param("level", fluid.getMaxFluidLevel())); } Integer finalLevel = level; chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)) .thenAcceptAsync( section -> { Store sectionStore = section.getStore(); FluidSection fluidSection = sectionStore.getComponent((Ref)section, FluidSection.getComponentType()); if (fluidSection == null) { playerRef.sendMessage(MESSAGE_COMMANDS_NO_SECTION_COMPONENT); } else { int index = ChunkUtil.indexBlock(pos.x, pos.y, pos.z); fluidSection.setFluid(index, fluid, finalLevel.byteValue()); playerRef.sendMessage( Message.translation("server.commands.set.success") .param("x", pos.x) .param("y", pos.y) .param("z", pos.z) .param("id", fluid.getId()) .param("level", finalLevel) ); ChunkSection chunkSection = sectionStore.getComponent((Ref)section, ChunkSection.getComponentType()); WorldChunk worldChunk = sectionStore.getComponent(chunkSection.getChunkColumnReference(), WorldChunk.getComponentType()); worldChunk.markNeedsSaving(); worldChunk.setTicking(pos.x, pos.y, pos.z, true); } }, world ); } } } } public static class SetRadiusCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_SET_UNKNOWN_FLUID = Message.translation("server.commands.set.unknownFluid"); @Nonnull private static final Message MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK = Message.translation("server.commands.errors.playerNotLookingAtBlock"); @Nonnull private final RequiredArg radius = this.withRequiredArg("radius", "", ArgTypes.INTEGER); @Nonnull private final RequiredArg fluid = this.withRequiredArg("fluid", "", FluidCommand.FLUID_ARG); @Nonnull private final RequiredArg level = this.withRequiredArg("level", "", ArgTypes.INTEGER); @Nonnull private final OptionalArg targetOffset = this.withOptionalArg("offset", "", ArgTypes.RELATIVE_BLOCK_POSITION); public SetRadiusCommand() { super("setradius", "Changes the fluid at the player position in a given radius"); } @Override protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { RelativeIntPosition offset = this.targetOffset.get(context); Vector3i blockTarget = TargetUtil.getTargetBlock(ref, 8.0, store); if (blockTarget == null) { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); Fluid fluid = this.fluid.get(context); if (fluid == null) { playerRef.sendMessage(MESSAGE_COMMANDS_SET_UNKNOWN_FLUID); } else { Integer level = this.level.get(context); if (level > fluid.getMaxFluidLevel()) { level = fluid.getMaxFluidLevel(); playerRef.sendMessage(Message.translation("server.commands.set.maxFluidLevelClamped").param("level", fluid.getMaxFluidLevel())); } Integer radius = this.radius.get(context); int minX = pos.x - radius; int maxX = pos.x + radius; int minY = pos.y - radius; int maxY = pos.y + radius; int minZ = pos.z - radius; int maxZ = pos.z + radius; int minCX = ChunkUtil.chunkCoordinate(minX); int maxCX = ChunkUtil.chunkCoordinate(maxX); int minCY = ChunkUtil.chunkCoordinate(minY); int maxCY = ChunkUtil.chunkCoordinate(maxY); int minCZ = ChunkUtil.chunkCoordinate(minZ); int maxCZ = ChunkUtil.chunkCoordinate(maxZ); Integer finalLevel = level; for (int cx = minCX; cx <= maxCX; cx++) { for (int cz = minCZ; cz <= maxCZ; cz++) { int relMinX = MathUtil.clamp(minX - ChunkUtil.minBlock(cx), 0, 32); int relMaxX = MathUtil.clamp(maxX - ChunkUtil.minBlock(cx), 0, 32); int relMinZ = MathUtil.clamp(minZ - ChunkUtil.minBlock(cz), 0, 32); int relMaxZ = MathUtil.clamp(maxZ - ChunkUtil.minBlock(cz), 0, 32); for (int cy = minCY; cy <= maxCY; cy++) { chunkStore.getChunkSectionReferenceAsync(cx, cy, cz).thenAcceptAsync(section -> { Store sectionStore = section.getStore(); FluidSection fluidSection = sectionStore.getComponent((Ref)section, FluidSection.getComponentType()); if (fluidSection != null) { int relMinY = MathUtil.clamp(minY - ChunkUtil.minBlock(fluidSection.getY()), 0, 32); int relMaxY = MathUtil.clamp(maxY - ChunkUtil.minBlock(fluidSection.getY()), 0, 32); ChunkSection sectionComp = sectionStore.getComponent((Ref)section, ChunkSection.getComponentType()); WorldChunk worldChunk = sectionStore.getComponent(sectionComp.getChunkColumnReference(), WorldChunk.getComponentType()); for (int y = relMinY; y < relMaxY; y++) { for (int z = relMinZ; z < relMaxZ; z++) { for (int x = relMinX; x < relMaxX; x++) { int index = ChunkUtil.indexBlock(x, y, z); fluidSection.setFluid(index, fluid, finalLevel.byteValue()); worldChunk.setTicking(pos.x, pos.y, pos.z, true); } } } worldChunk.markNeedsSaving(); } }, world); } } } } } } } }