9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00
Files
LeavesMC/patches/server/0045-PCA-sync-protocol.patch
2023-03-05 16:23:40 +08:00

760 lines
36 KiB
Diff

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/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index bc2dba4a2efe56b0ca2e6c2a8881fe5d036e4166..2e762d02bc3f5d768e08a41f281b504ceb8d6996 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -241,6 +241,7 @@ import org.bukkit.inventory.CraftingInventory;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.SmithingInventory;
+import top.leavesmc.leaves.util.ProtocolUtils;
// CraftBukkit end
public class ServerGamePacketListenerImpl implements ServerPlayerConnection, TickablePacketListener, ServerGamePacketListener {
@@ -3551,6 +3552,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
this.disconnect("Invalid payload UNREGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
}
} else {
+ // Leaves start - leaves extra protocol
+ try {
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol && ProtocolUtils.isNamespacePacket(packet, top.leavesmc.leaves.protocol.PcaSyncProtocol.PROTOCOL_ID)) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.handlePacket(server, player, packet);
+ }
+ } catch (Exception ex) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex);
+ this.disconnect("Invalid custom payload!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD);
+ }
+ // Leaves end - leaves extra protocol
try {
byte[] data = new byte[packet.data.readableBytes()];
packet.data.readBytes(data);
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index a057f6fa87f76707cdef2615d5d49e32fc181740..70493b62ad4a7722b8823d8bdc3443d3367ac959 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -334,6 +334,7 @@ public abstract class PlayerList {
bot1.render(playerconnection, true,player.getBukkitEntity().getWorld() == bot1.getBukkitEntity().getWorld())); // Leaves - render bot
}
// Leaves end - bot support
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.onJoin(player); // Leaves - pca
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
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 72d660cd2ade39335024897cffb8b8a151a7cb71..228abbb9ecca77694ccec405342e67c19195163b 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
@@ -346,6 +346,11 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
@Override
public void containerChanged(Container sender) {
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncEntityToClient(this);
+ }
+ // Leaves end - pca
boolean flag = this.isSaddled();
this.updateContainerEquipment();
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 ca96b893e22de3ae7c11d5cded51edf70bdcb6f2..a0380713ff1e3faa285c76ea45f75c9c480caafe 100644
--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
@@ -65,6 +65,15 @@ public abstract class AbstractVillager extends AgeableMob implements InventoryCa
super(type, world);
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 16.0F);
this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, -1.0F);
+ // Leaves start - pca
+ if (!this.level.isClientSide()) {
+ this.inventory.addListener(inventory -> {
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 cc74eeb45913fab03e85969957215d2811252a83..69a8436a343d7b0738e74700a01966111e37a17a 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
@@ -129,7 +129,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
}
@Override
- public void setChanged() {}
+ public void setChanged() {
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 cac2768fe520b591990c7bc943ae7e95f49efb31..59938c323ad2597af03a310e930d1a61f1a456c7 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
@@ -568,6 +568,16 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
public boolean stillValid(net.minecraft.world.entity.player.Player player) {
return this.level.getBlockEntity(this.worldPosition) != this ? false : player.distanceToSqr((double) this.worldPosition.getX() + 0.5D, (double) this.worldPosition.getY() + 0.5D, (double) this.worldPosition.getZ() + 0.5D) <= 64.0D;
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 416aa989ebb18a8741cc9d605a1180ab830f6643..213bc3c11ff4ed9bc761e8153aa669d1e2301960 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
@@ -131,6 +131,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
this.items = list;
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 41c9f074203915c31c1ae7a160ce509c13383f84..4c9e84dcd154a4306659ef41ab40127ea8452835 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
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -127,6 +128,11 @@ public class BeehiveBlockEntity extends BlockEntity {
super.setChanged();
}
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ // Leaves end - pca
return list;
}
@@ -188,6 +194,12 @@ public class BeehiveBlockEntity extends BlockEntity {
this.level.gameEvent(GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, this.getBlockState()));
}
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ // Leaves end - pca
+
entity.discard();
super.setChanged();
}
@@ -334,6 +346,11 @@ public class BeehiveBlockEntity extends BlockEntity {
if (BeehiveBlockEntity.releaseOccupant(world, pos, state, tileentitybeehive_hivebee, (List) null, tileentitybeehive_releasestatus, flowerPos)) {
flag = true;
iterator.remove();
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(Objects.requireNonNull(world.getBlockEntity(pos)));
+ }
+ // Leaves end - pca
// CraftBukkit start
} else {
tileentitybeehive_hivebee.exitTickCounter = tileentitybeehive_hivebee.minOccupationTicks / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable // Paper - use exitTickCounter to keep actual bee life
@@ -385,6 +402,11 @@ public class BeehiveBlockEntity extends BlockEntity {
this.maxBees = nbt.getInt("Bukkit.MaxEntities");
}
// CraftBukkit end
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 0bab2693b91d5bab222c7db8bc6965ccde954003..99044c31cf703f7942a89dd57cdc65ddf3a58d75 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
@@ -333,6 +333,16 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
public boolean stillValid(Player player) {
return this.level.getBlockEntity(this.worldPosition) != this ? false : player.distanceToSqr((double) this.worldPosition.getX() + 0.5D, (double) this.worldPosition.getY() + 0.5D, (double) this.worldPosition.getZ() + 0.5D) <= 64.0D;
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 a71414397bd45ee7bcacfeef0041d80dfa25f114..1d6c7a04b8246a60d80325e1615e50573b17a6bb 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
@@ -190,6 +190,16 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
this.items = list;
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 9b2c162c362fcf6093a3bf6da715ae8f18176c82..84b877a712a883b868b139d29da7e09c9552a1e5 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
@@ -23,6 +23,16 @@ public class ComparatorBlockEntity extends BlockEntity {
this.output = nbt.getInt("OutputSignal");
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 881379681c39230a00b3a1f11cd87498984396c7..e01eb2025458cf311348c58a1530854053af78f4 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
@@ -92,6 +92,16 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity {
return -1;
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 d76603c4172aa10889949c6c2acff05fee02a13d..d24a1e3a19dccf0c672d064d44afe5c525dc696d 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
@@ -127,6 +127,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
protected Component getDefaultName() {
return Component.translatable("container.hopper");
@@ -167,6 +177,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (flag) {
blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
setChanged(world, pos, state);
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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 b7686fd63b7c5d88c3a12ec4ee9bc01a17f997e0..25a9c38c60d183bb65b14f4d7550ab98b431c218 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
@@ -259,6 +259,16 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
this.itemStacks = list;
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.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/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index ba4a1f12c29cb28441ae316ff5e1f23358e6af02..8e9fb3731edc302ae472599f8e2d953d485a265e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -386,6 +386,7 @@ public final class CraftServer implements Server {
MapPalette.setMapColorCache(new CraftMapColorCache(this.logger));
}
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.init(); // Leaves - pca
}
public boolean getCommandBlockOverride(String command) {
@@ -962,6 +963,13 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
this.console.paperConfigurations.reloadConfigs(this.console);
top.leavesmc.leaves.LeavesConfig.init((File) console.options.valueOf("leaves-settings")); // Leaves - Server Config
+ // Leaves start - pca
+ if (top.leavesmc.leaves.LeavesConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.enablePcaSyncProtocolGlobal();
+ } else {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.disablePcaSyncProtocolGlobal();
+ }
+ // Leaves end - pca
for (ServerLevel world : this.console.getAllLevels()) {
// world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
diff --git a/src/main/java/top/leavesmc/leaves/LeavesConfig.java b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
index 03f618c9ba4f4f4c8ce4f3187be7e7fed8b6f8f8..29acea996719de0e1333e51df310678a7025267a 100644
--- a/src/main/java/top/leavesmc/leaves/LeavesConfig.java
+++ b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
@@ -386,6 +386,21 @@ public final class LeavesConfig {
simplerVanillaShapelessRecipes = getBoolean("settings.performance.simpler-vanilla-shapeless-recipes", simplerVanillaShapelessRecipes);
}
+ public static boolean pcaSyncProtocol = false;
+ private static void pcaSyncProtocol() {
+ pcaSyncProtocol = getBoolean("settings.protocol.pca-sync-protocol", pcaSyncProtocol);
+ }
+
+ public static String pcaSyncPlayerEntity = "OPS";
+ private static final List<String> pcaSyncPlayerEntityList = List.of("NOBODY", "BOT", "OPS", "OPS_AND_SELF", "EVERYONE");
+ private static void pcaSyncPlayerEntity() {
+ pcaSyncPlayerEntity = getString("settings.protocol.pca-sync-player-entity", pcaSyncPlayerEntity);
+ if (!pcaSyncPlayerEntityList.contains(pcaSyncPlayerEntity)) {
+ pcaSyncPlayerEntity = "OPS";
+ LeavesLogger.LOGGER.severe("pca-sync-player-entity value error, reset to OPS");
+ }
+ }
+
public static final class WorldConfig {
public final String worldName;
diff --git a/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8f84c38aaae14a093bfe8e901a3be802617fb12
--- /dev/null
+++ b/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java
@@ -0,0 +1,357 @@
+package top.leavesmc.leaves.protocol;
+
+import io.netty.buffer.Unpooled;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
+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.apache.logging.log4j.util.TriConsumer;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import top.leavesmc.leaves.LeavesConfig;
+import top.leavesmc.leaves.LeavesLogger;
+import top.leavesmc.leaves.bot.ServerBot;
+import top.leavesmc.leaves.util.ProtocolUtils;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+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 ResourceLocation UPDATE_ENTITY = id("update_entity");
+ private static final ResourceLocation UPDATE_BLOCK_ENTITY = id("update_block_entity");
+ // call
+ private static final ResourceLocation SYNC_BLOCK_ENTITY = id("sync_block_entity");
+ private static final ResourceLocation SYNC_ENTITY = id("sync_entity");
+ private static final ResourceLocation CANCEL_SYNC_BLOCK_ENTITY = id("cancel_sync_block_entity");
+ private static final ResourceLocation CANCEL_SYNC_ENTITY = id("cancel_sync_entity");
+ private static final Map<ServerPlayer, Pair<ResourceLocation, BlockPos>> playerWatchBlockPos = new HashMap<>();
+ private static final Map<ServerPlayer, Pair<ResourceLocation, Entity>> playerWatchEntity = new HashMap<>();
+ private static final Map<Pair<ResourceLocation, BlockPos>, Set<ServerPlayer>> blockPosWatchPlayerSet = new HashMap<>();
+ private static final Map<Pair<ResourceLocation, Entity>, Set<ServerPlayer>> entityWatchPlayerSet = new HashMap<>();
+ private static final MutablePair<ResourceLocation, Entity> ResourceLocationEntityPair = new MutablePair<>();
+ private static final MutablePair<ResourceLocation, BlockPos> ResourceLocationBlockPosPair = new MutablePair<>();
+
+ @Contract("_ -> new")
+ public static @NotNull ResourceLocation id(String path) {
+ return new ResourceLocation(PROTOCOL_ID, path);
+ }
+
+ public static void enablePcaSyncProtocol(@NotNull ServerPlayer player) {
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ ProtocolUtils.sendPayloadPacket(player, ENABLE_PCA_SYNC_PROTOCOL, buf);
+ lock.lock();
+ lock.unlock();
+ }
+
+ public static void disablePcaSyncProtocol(@NotNull ServerPlayer player) {
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ ProtocolUtils.sendPayloadPacket(player, DISABLE_PCA_SYNC_PROTOCOL, buf);
+ }
+
+ public static void updateEntity(@NotNull ServerPlayer player, @NotNull Entity entity) {
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ buf.writeResourceLocation(entity.getLevel().dimension().location());
+ buf.writeInt(entity.getId());
+ buf.writeNbt(entity.saveWithoutId(new CompoundTag()));
+ ProtocolUtils.sendPayloadPacket(player, UPDATE_ENTITY, buf);
+ }
+
+ public static void updateBlockEntity(@NotNull ServerPlayer player, @NotNull BlockEntity blockEntity) {
+ Level world = blockEntity.getLevel();
+
+ if (world == null) {
+ return;
+ }
+
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ buf.writeResourceLocation(world.dimension().location());
+ buf.writeBlockPos(blockEntity.getBlockPos());
+ buf.writeNbt(blockEntity.saveWithId());
+ ProtocolUtils.sendPayloadPacket(player, UPDATE_BLOCK_ENTITY, buf);
+ }
+
+ private static final Map<ResourceLocation, TriConsumer<MinecraftServer, ServerPlayer, FriendlyByteBuf>> globalReceivers = new HashMap<>();
+
+ private static void registerGlobalReceiver(ResourceLocation identifier, TriConsumer<MinecraftServer, ServerPlayer, FriendlyByteBuf> consumer) {
+ globalReceivers.put(identifier, consumer);
+ }
+
+ public static void handlePacket(MinecraftServer server, ServerPlayer player, @NotNull ServerboundCustomPayloadPacket packet) {
+ globalReceivers.getOrDefault(
+ packet.identifier, ((server1, player1, buf) -> LeavesLogger.LOGGER.severe("NullPotion"))
+ ).accept(server, player, packet.data);
+ }
+
+ public static void init() {
+ registerGlobalReceiver(SYNC_BLOCK_ENTITY, PcaSyncProtocol::syncBlockEntityHandler);
+ registerGlobalReceiver(SYNC_ENTITY, PcaSyncProtocol::syncEntityHandler);
+ registerGlobalReceiver(CANCEL_SYNC_BLOCK_ENTITY, PcaSyncProtocol::cancelSyncBlockEntityHandler);
+ registerGlobalReceiver(CANCEL_SYNC_ENTITY, PcaSyncProtocol::cancelSyncEntityHandler);
+ }
+
+ public static void onJoin(ServerPlayer player) {
+ if (LeavesConfig.pcaSyncProtocol) {
+ enablePcaSyncProtocol(player);
+ }
+ }
+
+ private static void cancelSyncBlockEntityHandler(MinecraftServer server, ServerPlayer player,
+ FriendlyByteBuf buf) {
+ if (!LeavesConfig.pcaSyncProtocol) {
+ return;
+ }
+ PcaSyncProtocol.clearPlayerWatchBlock(player);
+ }
+
+ private static void cancelSyncEntityHandler(MinecraftServer server, ServerPlayer player,
+ FriendlyByteBuf buf) {
+ if (!LeavesConfig.pcaSyncProtocol) {
+ return;
+ }
+ PcaSyncProtocol.clearPlayerWatchEntity(player);
+ }
+
+ private static void syncBlockEntityHandler(MinecraftServer server, ServerPlayer player,
+ FriendlyByteBuf buf) {
+ if (!LeavesConfig.pcaSyncProtocol) {
+ return;
+ }
+ BlockPos pos = buf.readBlockPos();
+ ServerLevel world = player.getLevel();
+ 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.offset(ChestBlock.getConnectedDirection(blockState).getNormal());
+ // 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<ResourceLocation, BlockPos> pair = new ImmutablePair<>(player.getLevel().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();
+ }
+
+ private static void syncEntityHandler(MinecraftServer server, ServerPlayer player,
+ FriendlyByteBuf buf) {
+ if (!LeavesConfig.pcaSyncProtocol) {
+ return;
+ }
+ int entityId = buf.readInt();
+ ServerLevel world = player.getLevel();
+ Entity entity = world.getEntity(entityId);
+ if (entity != null) {
+ clearPlayerWatchData(player);
+ if (entity instanceof Player) {
+ if (LeavesConfig.pcaSyncPlayerEntity.equals("NOBODY")) {
+ return;
+ } else if (LeavesConfig.pcaSyncPlayerEntity.equals("BOT")) {
+ if (!(entity instanceof ServerBot)) {
+ return;
+ }
+ } else if (LeavesConfig.pcaSyncPlayerEntity.equals("OPS")) {
+ if (!(entity instanceof ServerBot) && server.getProfilePermissions(player.getGameProfile()) < 2) {
+ return;
+ }
+ } else if (LeavesConfig.pcaSyncPlayerEntity.equals("OPS_AND_SELF")) {
+ if (!(entity instanceof ServerBot) &&
+ server.getProfilePermissions(player.getGameProfile()) < 2 &&
+ entity != player) {
+ return;
+ }
+ } else if (!LeavesConfig.pcaSyncPlayerEntity.equals("EVERYONE")) {
+ // wtf????
+ LeavesLogger.LOGGER.warning("pcaSyncPlayerEntity wtf???");
+ return;
+ }
+ }
+ updateEntity(player, entity);
+
+ Pair<ResourceLocation, Entity> pair = new ImmutablePair<>(entity.getLevel().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();
+ }
+ }
+
+ private static MutablePair<ResourceLocation, Entity> getResourceLocationEntityPair(ResourceLocation ResourceLocation, Entity entity) {
+ pairLock.lock();
+ ResourceLocationEntityPair.setLeft(ResourceLocation);
+ ResourceLocationEntityPair.setRight(entity);
+ pairLock.unlock();
+ return ResourceLocationEntityPair;
+ }
+
+ private static MutablePair<ResourceLocation, BlockPos> getResourceLocationBlockPosPair(ResourceLocation ResourceLocation, BlockPos pos) {
+ pairLock.lock();
+ ResourceLocationBlockPosPair.setLeft(ResourceLocation);
+ ResourceLocationBlockPosPair.setRight(pos);
+ pairLock.unlock();
+ return ResourceLocationBlockPosPair;
+ }
+
+ private static @Nullable Set<ServerPlayer> getWatchPlayerList(@NotNull Entity entity) {
+ return entityWatchPlayerSet.get(getResourceLocationEntityPair(entity.getLevel().dimension().location(), entity));
+ }
+
+ private static @Nullable Set<ServerPlayer> getWatchPlayerList(@NotNull Level world, @NotNull BlockPos blockPos) {
+ return blockPosWatchPlayerSet.get(getResourceLocationBlockPosPair(world.dimension().location(), blockPos));
+ }
+
+ public static boolean syncEntityToClient(@NotNull Entity entity) {
+ if (entity.getLevel().isClientSide()) {
+ return false;
+ }
+ lock.lock();
+ Set<ServerPlayer> 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<ServerPlayer> playerList = getWatchPlayerList(world, blockEntity.getBlockPos());
+
+ Set<ServerPlayer> playerListAdj = null;
+
+ if (blockState.getBlock() instanceof ChestBlock) {
+ if (blockState.getValue(ChestBlock.TYPE) != ChestType.SINGLE) {
+ BlockPos posAdj = pos.offset(ChestBlock.getConnectedDirection(blockState).getNormal());
+ 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<ResourceLocation, Entity> pair = playerWatchEntity.get(player);
+ if (pair != null) {
+ Set<ServerPlayer> 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<ResourceLocation, BlockPos> pair = playerWatchBlockPos.get(player);
+ if (pair != null) {
+ Set<ServerPlayer> 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);
+ }
+}