From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:34:15 +0800 Subject: [PATCH] PCA sync protocol This patch is Powered by plusls-carpet-addition(https://github.com/plusls/plusls-carpet-addition) diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java index 8aed30cdbbfdd42c20dcd4c8773c8a0ee21a980d..b2c83534446f95503b9fd8bd09be90575622c395 100644 --- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java +++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java @@ -449,6 +449,11 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, @Override public void containerChanged(Container sender) { + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncEntityToClient(this); + } + // Leaves end - pca boolean flag = this.isSaddled(); this.syncSaddleToClients(); diff --git a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java index 5f656fc726a1dc5f42657095a2f2b7cf85b92d7c..6c0cece094d36ddb2ae8d67d3c847a2c8faa3da8 100644 --- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java +++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java @@ -69,6 +69,15 @@ public abstract class AbstractVillager extends AgeableMob implements InventoryCa super(type, world); this.setPathfindingMalus(PathType.DANGER_FIRE, 16.0F); this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0F); + // Leaves start - pca + if (!this.level().isClientSide()) { + this.inventory.addListener(inventory -> { + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncEntityToClient(this); + } + }); + } + // Leaves end - pca } @Override diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java index d528e8e4aea266c495377365f01e314001eb1970..2472eb9f8f0b6872f7b98cb2da511c6a2527b8fc 100644 --- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java @@ -122,7 +122,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme } @Override - public void setChanged() {} + public void setChanged() { + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncEntityToClient(this); + } + // Leaves end - pca + } @Override public boolean stillValid(Player player) { diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java index 15e0861486a2bda3e2f4049b1b5a299c870acd31..82e93a63ea64adbf648ea7b8a844a376bbe63597 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java @@ -440,6 +440,16 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit } } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override public boolean canPlaceItem(int slot, ItemStack stack) { if (slot == 2) { diff --git a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java index 618552afbdacc919c33b30a6bf4834fb71ab3d5b..c14e7805e849e56912d50b61d5f2f3d802ac9bde 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java @@ -132,6 +132,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { this.items = inventory; } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override protected Component getDefaultName() { return Component.translatable("container.barrel"); diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java index 83ad45aed0894e90825d22e078632352c3a06816..58319df10af098db7cb74f08651880dab64ae4e1 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java @@ -144,6 +144,11 @@ public class BeehiveBlockEntity extends BlockEntity { super.setChanged(); } + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + // Leaves end - pca return list; } @@ -198,6 +203,12 @@ public class BeehiveBlockEntity extends BlockEntity { this.level.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, this.getBlockState())); } + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + // Leaves end - pca + entity.discard(EntityRemoveEvent.Cause.ENTER_BLOCK); // CraftBukkit - add Bukkit remove cause super.setChanged(); } @@ -313,6 +324,11 @@ public class BeehiveBlockEntity extends BlockEntity { if (BeehiveBlockEntity.releaseOccupant(world, pos, state, tileentitybeehive_hivebee.toOccupant(), (List) null, tileentitybeehive_releasestatus, flowerPos)) { flag = true; iterator.remove(); + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(Objects.requireNonNull(world.getBlockEntity(pos))); + } + // Leaves end - pca // CraftBukkit start } else { tileentitybeehive_hivebee.exitTickCounter = tileentitybeehive_hivebee.occupant.minTicksInHive / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable // Paper - Fix bees aging inside hives; use exitTickCounter to keep actual bee life @@ -358,6 +374,11 @@ public class BeehiveBlockEntity extends BlockEntity { this.maxBees = nbt.getInt("Bukkit.MaxEntities"); } // CraftBukkit end + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + // Leaves end - pca } @Override diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java index 02fc9ce21c7d367055da350d21be4870d4242f3a..974a7fbec9ec347ea16a07d7c857a27e3b25f76d 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java @@ -345,6 +345,16 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements return this.canPlaceItem(slot, stack); } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override public boolean canTakeItemThroughFace(int slot, ItemStack stack, Direction dir) { return slot == 3 ? stack.is(Items.GLASS_BOTTLE) : true; diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java index 8847617f6a23e6d2fe9bf7444a2072dc53f741b8..ef91101fb9e44b5db58272e4c2fdada1fa9e1686 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java @@ -191,6 +191,16 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement this.items = inventory; } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override public float getOpenNess(float tickDelta) { return this.chestLidController.getOpenness(tickDelta); diff --git a/src/main/java/net/minecraft/world/level/block/entity/ComparatorBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ComparatorBlockEntity.java index 9db5826420d693628ad74614f4cee79e1ebd88d9..0e14b15b262f1ca5e12e31d2f18bc90bb94baf79 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/ComparatorBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/ComparatorBlockEntity.java @@ -24,6 +24,16 @@ public class ComparatorBlockEntity extends BlockEntity { this.output = nbt.getInt("OutputSignal"); } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + public int getOutputSignal() { return this.output; } diff --git a/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java index c7f1937b0f171eee967388ab4699703dcdcfbd2b..c3d41360b2c5dab04f519459bd855045e8bec7c1 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java @@ -109,6 +109,16 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity { return stack; } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override protected Component getDefaultName() { return Component.translatable("container.dispenser"); diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java index 5ebbdb94d9b91c442ff60eb6872f740ebd790fa0..02bda85189fd57bd3f6d18cfd573553b2e607300 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -134,6 +134,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen this.facing = (Direction) state.getValue(HopperBlock.FACING); } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override protected Component getDefaultName() { return Component.translatable("container.hopper"); @@ -212,6 +222,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen if (flag) { blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Spigot setChanged(world, pos, state); + // Leaves start - pca + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(blockEntity); + } + // Leaves end - pca return true; } } diff --git a/src/main/java/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java index 6291ebf3426bddb0e6d13159ce20936b6e9ba6bd..3784e4c14bb59073f941b966f616ddac3f80a497 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java @@ -270,6 +270,16 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl this.itemStacks = inventory; } + // Leaves start - pca + @Override + public void setChanged() { + super.setChanged(); + if (org.leavesmc.leaves.LeavesConfig.protocol.pca.enable) { + org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); + } + } + // Leaves end - pca + @Override public int[] getSlotsForFace(Direction side) { return ShulkerBoxBlockEntity.SLOTS; diff --git a/src/main/java/org/leavesmc/leaves/protocol/PcaSyncProtocol.java b/src/main/java/org/leavesmc/leaves/protocol/PcaSyncProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..a38318506aeb1632ba7903671bcfad1e871c8fc7 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/protocol/PcaSyncProtocol.java @@ -0,0 +1,432 @@ +package org.leavesmc.leaves.protocol; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.ChestType; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.LeavesConfig; +import org.leavesmc.leaves.LeavesLogger; +import org.leavesmc.leaves.bot.ServerBot; +import org.leavesmc.leaves.protocol.core.LeavesCustomPayload; +import org.leavesmc.leaves.protocol.core.LeavesProtocol; +import org.leavesmc.leaves.protocol.core.ProtocolHandler; +import org.leavesmc.leaves.protocol.core.ProtocolUtils; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +import static org.leavesmc.leaves.protocol.core.LeavesProtocolManager.EmptyPayload; + +@LeavesProtocol(namespace = "pca") +public class PcaSyncProtocol { + + public static final String PROTOCOL_ID = "pca"; + + public static final ReentrantLock lock = new ReentrantLock(true); + public static final ReentrantLock pairLock = new ReentrantLock(true); + + // send + private static final ResourceLocation ENABLE_PCA_SYNC_PROTOCOL = id("enable_pca_sync_protocol"); + private static final ResourceLocation DISABLE_PCA_SYNC_PROTOCOL = id("disable_pca_sync_protocol"); + + private static final Map> playerWatchBlockPos = new HashMap<>(); + private static final Map> playerWatchEntity = new HashMap<>(); + private static final Map, Set> blockPosWatchPlayerSet = new HashMap<>(); + private static final Map, Set> entityWatchPlayerSet = new HashMap<>(); + private static final MutablePair ResourceLocationEntityPair = new MutablePair<>(); + private static final MutablePair ResourceLocationBlockPosPair = new MutablePair<>(); + + @Contract("_ -> new") + public static @NotNull ResourceLocation id(String path) { + return new ResourceLocation(PROTOCOL_ID, path); + } + + @ProtocolHandler.PlayerJoin + private static void onJoin(ServerPlayer player) { + if (LeavesConfig.protocol.pca.enable) { + enablePcaSyncProtocol(player); + } + } + + @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_block_entity") + private static void cancelSyncBlockEntityHandler(ServerPlayer player, EmptyPayload payload) { + if (!LeavesConfig.protocol.pca.enable) { + return; + } + PcaSyncProtocol.clearPlayerWatchBlock(player); + } + + @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_entity") + private static void cancelSyncEntityHandler(ServerPlayer player, EmptyPayload payload) { + if (!LeavesConfig.protocol.pca.enable) { + return; + } + PcaSyncProtocol.clearPlayerWatchEntity(player); + } + + @ProtocolHandler.PayloadReceiver(payload = SyncBlockEntityPayload.class, payloadId = "sync_block_entity") + private static void syncBlockEntityHandler(ServerPlayer player, SyncBlockEntityPayload payload) { + if (!LeavesConfig.protocol.pca.enable) { + return; + } + + MinecraftServer server = MinecraftServer.getServer(); + BlockPos pos = payload.pos; + ServerLevel world = player.serverLevel(); + + server.execute(() -> { + BlockState blockState = world.getBlockState(pos); + clearPlayerWatchData(player); + + BlockEntity blockEntityAdj = null; + if (blockState.getBlock() instanceof ChestBlock) { + if (blockState.getValue(ChestBlock.TYPE) != ChestType.SINGLE) { + BlockPos posAdj = pos.relative(ChestBlock.getConnectedDirection(blockState)); + // The method in World now checks that the caller is from the same thread... + blockEntityAdj = world.getChunk(posAdj).getBlockEntity(posAdj); + } + } + + if (blockEntityAdj != null) { + updateBlockEntity(player, blockEntityAdj); + } + + // The method in World now checks that the caller is from the same thread... + BlockEntity blockEntity = world.getChunk(pos).getBlockEntity(pos); + if (blockEntity != null) { + updateBlockEntity(player, blockEntity); + } + + Pair pair = new ImmutablePair<>(player.level().dimension().location(), pos); + lock.lock(); + playerWatchBlockPos.put(player, pair); + if (!blockPosWatchPlayerSet.containsKey(pair)) { + blockPosWatchPlayerSet.put(pair, new HashSet<>()); + } + blockPosWatchPlayerSet.get(pair).add(player); + lock.unlock(); + }); + } + + @ProtocolHandler.PayloadReceiver(payload = SyncEntityPayload.class, payloadId = "sync_entity") + private static void syncEntityHandler(ServerPlayer player, SyncEntityPayload payload) { + if (!LeavesConfig.protocol.pca.enable) { + return; + } + + MinecraftServer server = MinecraftServer.getServer(); + int entityId = payload.entityId; + ServerLevel world = player.serverLevel(); + + server.execute(() -> { + Entity entity = world.getEntity(entityId); + + if (entity != null) { + clearPlayerWatchData(player); + + if (entity instanceof Player) { + switch (LeavesConfig.protocol.pca.syncPlayerEntity) { + case NOBODY -> { + return; + } + case BOT -> { + if (!(entity instanceof ServerBot)) { + return; + } + } + case OPS -> { + if (!(entity instanceof ServerBot) && server.getPlayerList().isOp(player.gameProfile)) { + return; + } + } + case OPS_AND_SELF -> { + if (!(entity instanceof ServerBot) && server.getPlayerList().isOp(player.gameProfile) && entity != player) { + return; + } + } + case EVERYONE -> {} + case null -> LeavesLogger.LOGGER.warning("pcaSyncPlayerEntity wtf???"); + } + } + updateEntity(player, entity); + + Pair pair = new ImmutablePair<>(entity.level().dimension().location(), entity); + lock.lock(); + playerWatchEntity.put(player, pair); + if (!entityWatchPlayerSet.containsKey(pair)) { + entityWatchPlayerSet.put(pair, new HashSet<>()); + } + entityWatchPlayerSet.get(pair).add(player); + lock.unlock(); + } + }); + } + + public static void onConfigModify(boolean enable) { + if (enable) { + enablePcaSyncProtocolGlobal(); + } else { + disablePcaSyncProtocolGlobal(); + } + } + + public static void enablePcaSyncProtocol(@NotNull ServerPlayer player) { + ProtocolUtils.sendEmptyPayloadPacket(player, ENABLE_PCA_SYNC_PROTOCOL); + lock.lock(); + lock.unlock(); + } + + public static void disablePcaSyncProtocol(@NotNull ServerPlayer player) { + ProtocolUtils.sendEmptyPayloadPacket(player, DISABLE_PCA_SYNC_PROTOCOL); + } + + public static void updateEntity(@NotNull ServerPlayer player, @NotNull Entity entity) { + CompoundTag nbt = entity.saveWithoutId(new CompoundTag()); + ProtocolUtils.sendPayloadPacket(player, new UpdateEntityPayload(entity.level().dimension().location(), entity.getId(), nbt)); + } + + public static void updateBlockEntity(@NotNull ServerPlayer player, @NotNull BlockEntity blockEntity) { + Level world = blockEntity.getLevel(); + + if (world == null) { + return; + } + + ProtocolUtils.sendPayloadPacket(player, new UpdateBlockEntityPayload(world.dimension().location(), blockEntity.getBlockPos(), blockEntity.saveWithoutMetadata(world.registryAccess()))); + } + + private static MutablePair getResourceLocationEntityPair(ResourceLocation ResourceLocation, Entity entity) { + pairLock.lock(); + ResourceLocationEntityPair.setLeft(ResourceLocation); + ResourceLocationEntityPair.setRight(entity); + pairLock.unlock(); + return ResourceLocationEntityPair; + } + + private static MutablePair getResourceLocationBlockPosPair(ResourceLocation ResourceLocation, BlockPos pos) { + pairLock.lock(); + ResourceLocationBlockPosPair.setLeft(ResourceLocation); + ResourceLocationBlockPosPair.setRight(pos); + pairLock.unlock(); + return ResourceLocationBlockPosPair; + } + + private static @Nullable Set getWatchPlayerList(@NotNull Entity entity) { + return entityWatchPlayerSet.get(getResourceLocationEntityPair(entity.level().dimension().location(), entity)); + } + + private static @Nullable Set getWatchPlayerList(@NotNull Level world, @NotNull BlockPos blockPos) { + return blockPosWatchPlayerSet.get(getResourceLocationBlockPosPair(world.dimension().location(), blockPos)); + } + + public static boolean syncEntityToClient(@NotNull Entity entity) { + if (entity.level().isClientSide()) { + return false; + } + lock.lock(); + Set playerList = getWatchPlayerList(entity); + boolean ret = false; + if (playerList != null) { + for (ServerPlayer player : playerList) { + updateEntity(player, entity); + ret = true; + } + } + lock.unlock(); + return ret; + } + + public static boolean syncBlockEntityToClient(@NotNull BlockEntity blockEntity) { + boolean ret = false; + Level world = blockEntity.getLevel(); + BlockPos pos = blockEntity.getBlockPos(); + if (world != null) { + if (world.isClientSide()) { + return false; + } + BlockState blockState = world.getBlockState(pos); + lock.lock(); + Set playerList = getWatchPlayerList(world, blockEntity.getBlockPos()); + + Set playerListAdj = null; + + if (blockState.getBlock() instanceof ChestBlock) { + if (blockState.getValue(ChestBlock.TYPE) != ChestType.SINGLE) { + BlockPos posAdj = pos.relative(ChestBlock.getConnectedDirection(blockState)); + playerListAdj = getWatchPlayerList(world, posAdj); + } + } + if (playerListAdj != null) { + if (playerList == null) { + playerList = playerListAdj; + } else { + playerList.addAll(playerListAdj); + } + } + + if (playerList != null) { + for (ServerPlayer player : playerList) { + updateBlockEntity(player, blockEntity); + ret = true; + } + } + lock.unlock(); + } + return ret; + } + + private static void clearPlayerWatchEntity(ServerPlayer player) { + lock.lock(); + Pair pair = playerWatchEntity.get(player); + if (pair != null) { + Set playerSet = entityWatchPlayerSet.get(pair); + playerSet.remove(player); + if (playerSet.isEmpty()) { + entityWatchPlayerSet.remove(pair); + } + playerWatchEntity.remove(player); + } + lock.unlock(); + } + + private static void clearPlayerWatchBlock(ServerPlayer player) { + lock.lock(); + Pair pair = playerWatchBlockPos.get(player); + if (pair != null) { + Set playerSet = blockPosWatchPlayerSet.get(pair); + playerSet.remove(player); + if (playerSet.isEmpty()) { + blockPosWatchPlayerSet.remove(pair); + } + playerWatchBlockPos.remove(player); + } + lock.unlock(); + } + + public static void disablePcaSyncProtocolGlobal() { + lock.lock(); + playerWatchBlockPos.clear(); + playerWatchEntity.clear(); + blockPosWatchPlayerSet.clear(); + entityWatchPlayerSet.clear(); + lock.unlock(); + for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) { + disablePcaSyncProtocol(player); + } + } + + public static void enablePcaSyncProtocolGlobal() { + for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) { + enablePcaSyncProtocol(player); + } + } + + + public static void clearPlayerWatchData(ServerPlayer player) { + PcaSyncProtocol.clearPlayerWatchBlock(player); + PcaSyncProtocol.clearPlayerWatchEntity(player); + } + + public record UpdateEntityPayload(ResourceLocation dimension, int entityId, CompoundTag tag) implements LeavesCustomPayload { + + public static final ResourceLocation UPDATE_ENTITY = PcaSyncProtocol.id("update_entity"); + + @New + public UpdateEntityPayload(ResourceLocation location, FriendlyByteBuf byteBuf) { + this(byteBuf.readResourceLocation(), byteBuf.readInt(), byteBuf.readNbt()); + } + + @Override + public void write(@NotNull FriendlyByteBuf buf) { + buf.writeResourceLocation(this.dimension); + buf.writeInt(this.entityId); + buf.writeNbt(this.tag); + } + + @Override + public ResourceLocation id() { + return UPDATE_ENTITY; + } + } + + public record UpdateBlockEntityPayload(ResourceLocation dimension, BlockPos blockPos, CompoundTag tag) implements LeavesCustomPayload { + + private static final ResourceLocation UPDATE_BLOCK_ENTITY = PcaSyncProtocol.id("update_block_entity"); + + @New + public UpdateBlockEntityPayload(ResourceLocation location, @NotNull FriendlyByteBuf byteBuf) { + this(byteBuf.readResourceLocation(), byteBuf.readBlockPos(), byteBuf.readNbt()); + } + + @Override + public void write(@NotNull FriendlyByteBuf buf) { + buf.writeResourceLocation(this.dimension); + buf.writeBlockPos(this.blockPos); + buf.writeNbt(this.tag); + } + + @Override + public ResourceLocation id() { + return UPDATE_BLOCK_ENTITY; + } + } + + public record SyncBlockEntityPayload(BlockPos pos) implements LeavesCustomPayload { + + public static final ResourceLocation SYNC_BLOCK_ENTITY = PcaSyncProtocol.id("sync_block_entity"); + + @New + public SyncBlockEntityPayload(ResourceLocation id, @NotNull FriendlyByteBuf buf) { + this(buf.readBlockPos()); + } + + @Override + public void write(@NotNull FriendlyByteBuf buf) { + buf.writeBlockPos(pos); + } + + @Override + public @NotNull ResourceLocation id() { + return SYNC_BLOCK_ENTITY; + } + } + + public record SyncEntityPayload(int entityId) implements LeavesCustomPayload { + + public static final ResourceLocation SYNC_ENTITY = PcaSyncProtocol.id("sync_entity"); + + @New + public SyncEntityPayload(ResourceLocation id, @NotNull FriendlyByteBuf buf) { + this(buf.readInt()); + } + + @Override + public void write(@NotNull FriendlyByteBuf buf) { + buf.writeInt(entityId); + } + + @Override + public @NotNull ResourceLocation id() { + return SYNC_ENTITY; + } + } +}