From 50b28854dc82f15d37ba99c5fee4f4f6e158524b Mon Sep 17 00:00:00 2001 From: Jeremiah Winsley Date: Mon, 27 Dec 2021 13:41:18 -0500 Subject: [PATCH] Add range support to expulsion pylon --- .../resources/assets/pylons/lang/en_us.json | 1 + .../java/net/permutated/pylons/Pylons.java | 2 + .../client/gui/AbstractPylonScreen.java | 36 +++++++- .../pylons/data/client/Languages.java | 1 + .../container/AbstractPylonContainer.java | 47 ++++++---- .../container/ExpulsionPylonContainer.java | 5 ++ .../pylons/network/NetworkDispatcher.java | 25 ++++++ .../pylons/network/PacketButtonClicked.java | 44 ++++++++++ .../pylons/network/package-info.java | 7 ++ .../pylons/tile/AbstractPylonTile.java | 27 +++++- .../pylons/tile/ExpulsionPylonTile.java | 87 +++++++++---------- .../net/permutated/pylons/util/Constants.java | 4 + .../net/permutated/pylons/util/Range.java | 40 +++++++++ 13 files changed, 258 insertions(+), 68 deletions(-) create mode 100644 src/main/java/net/permutated/pylons/network/NetworkDispatcher.java create mode 100644 src/main/java/net/permutated/pylons/network/PacketButtonClicked.java create mode 100644 src/main/java/net/permutated/pylons/network/package-info.java create mode 100644 src/main/java/net/permutated/pylons/util/Range.java diff --git a/src/generated/resources/assets/pylons/lang/en_us.json b/src/generated/resources/assets/pylons/lang/en_us.json index 72d5f91..98ea0bd 100644 --- a/src/generated/resources/assets/pylons/lang/en_us.json +++ b/src/generated/resources/assets/pylons/lang/en_us.json @@ -7,6 +7,7 @@ "gui.pylons.noOwner": "Owner not found. Pylon disabled.", "gui.pylons.owner": "Owner: %s", "gui.pylons.whitelist": "Add players to whitelist:", + "gui.pylons.workArea": "Work area (in chunks)", "gui.pylons.wrongDimension": "This dimension is disabled.", "item.pylons.player_filter": "Player Filter", "item.pylons.potion_filter": "Potion Filter", diff --git a/src/main/java/net/permutated/pylons/Pylons.java b/src/main/java/net/permutated/pylons/Pylons.java index b9cc7e3..032548b 100644 --- a/src/main/java/net/permutated/pylons/Pylons.java +++ b/src/main/java/net/permutated/pylons/Pylons.java @@ -13,6 +13,7 @@ import net.permutated.pylons.block.AbstractPylonBlock; import net.permutated.pylons.client.ClientSetup; import net.permutated.pylons.item.PlayerFilterCard; +import net.permutated.pylons.network.NetworkDispatcher; import net.permutated.pylons.util.ChunkManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,6 +31,7 @@ public Pylons() { LOGGER.info("Registering mod: {}", MODID); ModRegistry.register(); + NetworkDispatcher.register(); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ConfigManager.COMMON_SPEC); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onCommonSetupEvent); diff --git a/src/main/java/net/permutated/pylons/client/gui/AbstractPylonScreen.java b/src/main/java/net/permutated/pylons/client/gui/AbstractPylonScreen.java index fd9f4bf..1c5804e 100644 --- a/src/main/java/net/permutated/pylons/client/gui/AbstractPylonScreen.java +++ b/src/main/java/net/permutated/pylons/client/gui/AbstractPylonScreen.java @@ -1,13 +1,14 @@ package net.permutated.pylons.client.gui; -import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.resources.ResourceLocation; import net.minecraft.network.chat.Component; -import net.minecraft.ChatFormatting; import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; import net.permutated.pylons.inventory.container.AbstractPylonContainer; import net.permutated.pylons.util.Constants; import net.permutated.pylons.util.ResourceUtil; @@ -15,6 +16,7 @@ public abstract class AbstractPylonScreen extends AbstractContainerScreen { protected final ResourceLocation gui; + protected Button rangeButton; protected AbstractPylonScreen(T container, Inventory inv, Component name, String pylonType) { super(container, inv, name); @@ -23,6 +25,32 @@ protected AbstractPylonScreen(T container, Inventory inv, Component name, String this.imageHeight = 166; } + @Override + protected void init() { + super.init(); + // x, y, width, height + rangeButton = new Button(this.leftPos + 140, this.height / 2 - 77, 30, 20, + this.menu.getRangeComponent(), this.menu::sendRangePacket, this::buttonTooltip); + if (this.menu.shouldRenderRange()) { + addRenderableWidget(rangeButton); + updateMessages(); + } + } + + private void buttonTooltip(Button button, PoseStack poseStack, int p_169460_, int p_169461_) { + this.renderTooltip(poseStack, translate("workArea"), p_169460_, p_169461_); + } + + public void updateMessages() { + this.rangeButton.setMessage(this.menu.getRangeComponent()); + } + + @Override + protected void containerTick() { + updateMessages(); + super.containerTick(); + } + @Override public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { this.renderBackground(matrixStack); diff --git a/src/main/java/net/permutated/pylons/data/client/Languages.java b/src/main/java/net/permutated/pylons/data/client/Languages.java index e84ffbe..962c638 100644 --- a/src/main/java/net/permutated/pylons/data/client/Languages.java +++ b/src/main/java/net/permutated/pylons/data/client/Languages.java @@ -32,6 +32,7 @@ protected void addTranslations() { add(gui("insideWorldSpawn"), "Too close to world spawn."); add(gui("whitelist"), "Add players to whitelist:"); add(gui("effects"), "Active potion effects:"); + add(gui("workArea"), "Work area (in chunks)"); add(tab(), "Pylons"); add(chat("expelled"), "You have been expelled from %s's chunk!"); diff --git a/src/main/java/net/permutated/pylons/inventory/container/AbstractPylonContainer.java b/src/main/java/net/permutated/pylons/inventory/container/AbstractPylonContainer.java index ed0c9cb..336cf78 100644 --- a/src/main/java/net/permutated/pylons/inventory/container/AbstractPylonContainer.java +++ b/src/main/java/net/permutated/pylons/inventory/container/AbstractPylonContainer.java @@ -1,21 +1,23 @@ package net.permutated.pylons.inventory.container; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.entity.player.Player; +import net.minecraft.client.gui.components.Button; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.inventory.Slot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.inventory.ContainerLevelAccess; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; -import net.minecraftforge.registries.RegistryObject; +import net.minecraft.world.level.block.Block; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.SlotItemHandler; import net.minecraftforge.items.wrapper.InvWrapper; +import net.minecraftforge.registries.RegistryObject; +import net.permutated.pylons.network.NetworkDispatcher; +import net.permutated.pylons.network.PacketButtonClicked; import net.permutated.pylons.tile.AbstractPylonTile; import javax.annotation.Nullable; @@ -25,14 +27,15 @@ public abstract class AbstractPylonContainer extends AbstractContainerMenu { @Nullable // should only be accessed from server private final AbstractPylonTile tileEntity; protected final String ownerName; + protected final BlockPos blockPos; protected AbstractPylonContainer(@Nullable MenuType containerType, int windowId, Inventory playerInventory, FriendlyByteBuf packetBuffer) { super(containerType, windowId); - BlockPos pos = packetBuffer.readBlockPos(); + blockPos = packetBuffer.readBlockPos(); Level world = playerInventory.player.getCommandSenderWorld(); - tileEntity = (AbstractPylonTile) world.getBlockEntity(pos); + tileEntity = (AbstractPylonTile) world.getBlockEntity(blockPos); IItemHandler wrappedInventory = new InvWrapper(playerInventory); if (tileEntity != null) { @@ -59,6 +62,19 @@ public String getOwnerName() { return ownerName; } + public boolean shouldRenderRange() { + return false; + } + + public Component getRangeComponent() { + var range = tileEntity != null ? tileEntity.getSelectedRange() : 0; + return new TextComponent(String.format("%dx%d", range, range)); + } + + public void sendRangePacket(Button button) { + NetworkDispatcher.INSTANCE.sendToServer(new PacketButtonClicked(blockPos)); + } + @Override public boolean stillValid(Player playerEntity) { if (tileEntity != null) { @@ -100,16 +116,13 @@ public ItemStack quickMoveStack(Player playerIn, int index) { } public void registerPlayerSlots(IItemHandler wrappedInventory) { - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 9; j++) - { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 9; j++) { addSlot(new SlotItemHandler(wrappedInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); } } - for (int i = 0; i < 9; i++) - { + for (int i = 0; i < 9; i++) { addSlot(new SlotItemHandler(wrappedInventory, i, 8 + i * 18, 142)); } } diff --git a/src/main/java/net/permutated/pylons/inventory/container/ExpulsionPylonContainer.java b/src/main/java/net/permutated/pylons/inventory/container/ExpulsionPylonContainer.java index f7ee54a..082fae7 100644 --- a/src/main/java/net/permutated/pylons/inventory/container/ExpulsionPylonContainer.java +++ b/src/main/java/net/permutated/pylons/inventory/container/ExpulsionPylonContainer.java @@ -29,4 +29,9 @@ public boolean isAllowedDimension() { public boolean isAllowedLocation() { return allowedLocation; } + + @Override + public boolean shouldRenderRange() { + return true; + } } diff --git a/src/main/java/net/permutated/pylons/network/NetworkDispatcher.java b/src/main/java/net/permutated/pylons/network/NetworkDispatcher.java new file mode 100644 index 0000000..5663ddb --- /dev/null +++ b/src/main/java/net/permutated/pylons/network/NetworkDispatcher.java @@ -0,0 +1,25 @@ +package net.permutated.pylons.network; + +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.simple.SimpleChannel; +import net.permutated.pylons.Pylons; + + +public class NetworkDispatcher { + private NetworkDispatcher() { + // nothing to do + } + + private static final String PROTOCOL_VERSION = "1"; + + public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(new ResourceLocation(Pylons.MODID, "main"), + () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); + + public static void register() { + int packetIndex = 0; + INSTANCE.registerMessage(packetIndex++, PacketButtonClicked.class, PacketButtonClicked::toBytes, PacketButtonClicked::new, PacketButtonClicked::handle); + + Pylons.LOGGER.info("Registered {} network packets", packetIndex); + } +} diff --git a/src/main/java/net/permutated/pylons/network/PacketButtonClicked.java b/src/main/java/net/permutated/pylons/network/PacketButtonClicked.java new file mode 100644 index 0000000..d4c2374 --- /dev/null +++ b/src/main/java/net/permutated/pylons/network/PacketButtonClicked.java @@ -0,0 +1,44 @@ +package net.permutated.pylons.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraftforge.network.NetworkEvent; +import net.permutated.pylons.tile.AbstractPylonTile; + +import java.util.Optional; +import java.util.function.Supplier; + +public class PacketButtonClicked { + private final BlockPos blockPos; + + public PacketButtonClicked(BlockPos blockPos) { + this.blockPos = blockPos; + } + + public PacketButtonClicked(FriendlyByteBuf buffer) { + blockPos = buffer.readBlockPos(); + } + + public void toBytes(FriendlyByteBuf buffer) { + buffer.writeBlockPos(this.blockPos); + } + + @SuppressWarnings("java:S1172") + public static void handle(PacketButtonClicked event, Supplier ctx) { + ctx.get().enqueueWork(() -> { + Optional player = Optional.ofNullable(ctx.get().getSender()); + Optional world = player.map(Player::getCommandSenderWorld); + + if (player.isPresent() && world.get() instanceof ServerLevel serverLevel && serverLevel.isLoaded(event.blockPos)) { + var be = serverLevel.getBlockEntity(event.blockPos); + if (be instanceof AbstractPylonTile pylonTile) { + pylonTile.handleRangePacket(); + } + } + }); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/net/permutated/pylons/network/package-info.java b/src/main/java/net/permutated/pylons/network/package-info.java new file mode 100644 index 0000000..dea0c64 --- /dev/null +++ b/src/main/java/net/permutated/pylons/network/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package net.permutated.pylons.network; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/net/permutated/pylons/tile/AbstractPylonTile.java b/src/main/java/net/permutated/pylons/tile/AbstractPylonTile.java index 9303f50..56c34e8 100644 --- a/src/main/java/net/permutated/pylons/tile/AbstractPylonTile.java +++ b/src/main/java/net/permutated/pylons/tile/AbstractPylonTile.java @@ -22,6 +22,7 @@ import net.minecraftforge.items.ItemStackHandler; import net.permutated.pylons.util.ChunkManager; import net.permutated.pylons.util.Constants; +import net.permutated.pylons.util.Range; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nonnull; @@ -128,8 +129,9 @@ public String getOwnerName() { /** * Serialize data to be sent to the GUI on the client. - * + *

* Overrides MUST call the super method first to ensure correct deserialization. + * * @param packetBuffer the packet ready to be filled */ public void updateContainer(FriendlyByteBuf packetBuffer) { @@ -144,6 +146,7 @@ public void updateContainer(FriendlyByteBuf packetBuffer) { @Override protected void saveAdditional(CompoundTag tag) { tag.put(Constants.NBT.INV, itemStackHandler.serializeNBT()); + tag.put(Constants.NBT.RANGE, range.serializeNBT()); writeOwner(tag); } @@ -158,6 +161,7 @@ private void writeOwner(CompoundTag tag) { @Override public void load(CompoundTag tag) { itemStackHandler.deserializeNBT(tag.getCompound(Constants.NBT.INV)); + range.deserializeNBT(tag.getCompound(Constants.NBT.RANGE)); readOwner(tag); super.load(tag); } @@ -173,12 +177,16 @@ private void readOwner(@Nullable CompoundTag tag) { @Override public CompoundTag getUpdateTag() { CompoundTag tag = super.getUpdateTag(); + tag.put(Constants.NBT.RANGE, range.serializeNBT()); writeOwner(tag); return tag; } @Override public void handleUpdateTag(@Nullable CompoundTag tag) { + if (tag != null) { + range.deserializeNBT(tag.getCompound(Constants.NBT.RANGE)); + } readOwner(tag); } @@ -205,4 +213,21 @@ protected void onContentsChanged(int slot) { setChanged(); } } + + protected final Range range = new Range(getRange()); + + protected byte[] getRange() { + return new byte[]{1}; + } + + public int getSelectedRange() { + return range.get(); + } + + public void handleRangePacket() { + if (getRange().length > 1 && this.level != null) { + this.range.next(); + this.level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 2); + } + } } diff --git a/src/main/java/net/permutated/pylons/tile/ExpulsionPylonTile.java b/src/main/java/net/permutated/pylons/tile/ExpulsionPylonTile.java index 07fdb93..6035adf 100644 --- a/src/main/java/net/permutated/pylons/tile/ExpulsionPylonTile.java +++ b/src/main/java/net/permutated/pylons/tile/ExpulsionPylonTile.java @@ -20,8 +20,6 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.permutated.pylons.ConfigManager; @@ -44,37 +42,47 @@ public ExpulsionPylonTile(BlockPos pos, BlockState state) { super(ModRegistry.EXPULSION_PYLON_TILE.get(), pos, state); } + @Override + protected byte[] getRange() { + return new byte[]{1, 3, 5}; + } + @Override protected boolean isItemValid(ItemStack stack) { return stack.getItem() instanceof PlayerFilterCard; } + protected AABB getBoundingBox(ServerLevel level) { + + var chunkPos = level.getChunkAt(worldPosition).getPos(); + var aabb = new AABB( + chunkPos.getMinBlockX(), + level.getMinBuildHeight(), + chunkPos.getMinBlockZ(), + chunkPos.getMaxBlockX() + 1D, + level.getMaxBuildHeight() + 1D, + chunkPos.getMaxBlockZ() + 1D + ); + + var selected = range.get() - 1; // center chunk is already included + if (selected > 0) { + return aabb.inflate(selected * 8D); // range is diameter, inflate is radius + } + return aabb; + } + @Override public void tick() { - if (level != null && !level.isClientSide && canTick(10) && owner != null && isAllowedDimension() && isAllowedLocation()) { - LevelChunk chunk = level.getChunkAt(worldPosition); - - var chunkPos = chunk.getPos(); - var aabb = new AABB( - chunkPos.getMinBlockX(), - level.getMinBuildHeight(), - chunkPos.getMinBlockZ(), - chunkPos.getMaxBlockX() + 1D, - level.getMaxBuildHeight() + 1D, - chunkPos.getMaxBlockZ() + 1D - ); - - var players = level.getEntitiesOfClass(ServerPlayer.class, aabb); - - List allowed = allowedPlayers(); - - MinecraftServer server = level.getServer(); - for (ServerPlayer player : players) { - if (server != null - && !player.hasPermissions(2) - && !player.getUUID().equals(owner) - && !allowed.contains(player.getUUID())) { - doRespawn(server, player); + if (level instanceof ServerLevel serverLevel && canTick(10) && owner != null && isAllowedDimension() && isAllowedLocation()) { + var aabb = getBoundingBox(serverLevel); + var players = serverLevel.getEntitiesOfClass(ServerPlayer.class, aabb); + + if (!players.isEmpty()) { + List allowed = allowedPlayers(); + for (ServerPlayer player : players) { + if (!this.canAccess(player) && !allowed.contains(player.getUUID())) { + doRespawn(serverLevel.getServer(), player); + } } } } @@ -107,10 +115,11 @@ public boolean isAllowedLocation() { int spawnRadius = serverLevel.getGameRules().getInt(GameRules.RULE_SPAWN_RADIUS); int configRadius = ConfigManager.COMMON.expulsionWorldSpawnRadius.get(); - var bb = new BoundingBox(serverLevel.getSharedSpawnPos()); - var area = bb.inflatedBy(Math.max(configRadius, spawnRadius)); + var bb = new AABB(serverLevel.getSharedSpawnPos()); + var area = bb.inflate(Math.max(configRadius, spawnRadius)); - return !area.intersects(getBlockPos().getX(), getBlockPos().getZ(), getBlockPos().getX(), getBlockPos().getZ()); + var workArea = getBoundingBox(serverLevel); + return !area.intersects(workArea); } else { return false; } @@ -166,8 +175,9 @@ private void doRespawn(MinecraftServer server, ServerPlayer player) { dummyPlayer.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, actualAngle, 0.0F); - // player has a spawn position, is the pylon in the same chunk? - if (sameChunk(actualLevel, dummyPlayer.blockPosition())) { + // player has a spawn position, is the spawn position in the pylon's work area? + var spawnPosition = Vec3.atBottomCenterOf(dummyPlayer.blockPosition()); + if (getBoundingBox(actualLevel).contains(spawnPosition)) { return; } } @@ -179,19 +189,4 @@ private void doRespawn(MinecraftServer server, ServerPlayer player) { player.teleportTo(actualLevel, dummyPlayer.getX(), dummyPlayer.getY(), dummyPlayer.getZ(), dummyPlayer.getYRot(), dummyPlayer.getXRot()); player.sendMessage(new TranslatableComponent(TranslationKey.chat("expelled"), getOwnerName()).withStyle(ChatFormatting.RED), player.getUUID()); } - - - - private boolean sameChunk(Level world, BlockPos target) { - if (level != null && level.dimension() == world.dimension()) { - int thisX = worldPosition.getX() >> 4; - int thisZ = worldPosition.getZ() >> 4; - - int thatX = target.getX() >> 4; - int thatZ = target.getZ() >> 4; - - return thisX == thatX && thisZ == thatZ; - } - return false; - } } diff --git a/src/main/java/net/permutated/pylons/util/Constants.java b/src/main/java/net/permutated/pylons/util/Constants.java index c031d8b..6e387d8 100644 --- a/src/main/java/net/permutated/pylons/util/Constants.java +++ b/src/main/java/net/permutated/pylons/util/Constants.java @@ -25,5 +25,9 @@ private NBT() { public static final String EFFECT = "effect"; public static final String DURATION = "duration"; public static final String AMPLIFIER = "amplifier"; + + public static final String RANGE = "range"; + public static final String CONTENTS = "contents"; + public static final String POSITION = "position"; } } diff --git a/src/main/java/net/permutated/pylons/util/Range.java b/src/main/java/net/permutated/pylons/util/Range.java new file mode 100644 index 0000000..c0f7fd6 --- /dev/null +++ b/src/main/java/net/permutated/pylons/util/Range.java @@ -0,0 +1,40 @@ +package net.permutated.pylons.util; + +import net.minecraft.nbt.CompoundTag; + +public class Range { + private byte[] contents; + private byte position = 0; + + public Range(byte[] contents) { + this.contents = contents; + } + + public void next() { + this.position = (byte) (++this.position % contents.length); + } + + public byte get() { + return contents[position]; + } + + public byte get(byte at) { + return contents[at % contents.length]; + } + + public CompoundTag serializeNBT() { + var tag = new CompoundTag(); + tag.putByteArray(Constants.NBT.CONTENTS, this.contents); + tag.putByte(Constants.NBT.POSITION, this.position); + return tag; + } + + public void deserializeNBT(CompoundTag tag) { + contents = getOrDefault(tag.getByteArray(Constants.NBT.CONTENTS)); + position = tag.getByte(Constants.NBT.POSITION); + } + + private static byte[] getOrDefault(byte[] test) { + return test.length > 0 ? test : new byte[]{1}; + } +}