Files
LuminolMC/patches/server/0045-Leaves-PCA-sync-protocol.patch
2024-01-21 00:28:00 +00:00

710 lines
32 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: M2ke4U <79621885+MrHua269@users.noreply.github.com>
Date: Sun, 17 Dec 2023 21:01:59 +0800
Subject: [PATCH] Leaves PCA sync protocol
diff --git a/src/main/java/me/earthme/luminol/LuminolConfig.java b/src/main/java/me/earthme/luminol/LuminolConfig.java
index a62680768068b611fe723fedeb617d42c643e59e..cfe9a8eb705039ee7e2dc9262e1355c4b0f664bb 100644
--- a/src/main/java/me/earthme/luminol/LuminolConfig.java
+++ b/src/main/java/me/earthme/luminol/LuminolConfig.java
@@ -66,6 +66,10 @@ public class LuminolConfig {
public static boolean enableAsyncMobSpawning = false;
public static boolean useAlternateKeepAlive = false;
+ public static boolean pcaSyncProtocol = false;
+ public static String pcaSyncPlayerEntity = "NOBODY";
+
+
public static void init() throws IOException {
PARENT_FOLDER.mkdir();
@@ -194,6 +198,9 @@ public class LuminolConfig {
enableAsyncMobSpawning = get("optimizations.enable_async_mob_spawning",enableAsyncMobSpawning);
RegionizedWorldData.initMobSpawningExecutor();
useAlternateKeepAlive = get("optimizations.enable_alternative_keep_alive_handling",useAlternateKeepAlive,"Enabling this sends a keepalive packet once per second to a player, and only kicks for timeout if none of them were responded to in 30 seconds. Responding to any of them in any order will keep the player connected. AKA, it won't kick your players because one packet gets dropped somewhere along the lines(From purpur)");
+
+ pcaSyncProtocol = get("gameplay.enable_pca_sync_protocol",pcaSyncProtocol);
+ pcaSyncPlayerEntity = get("gameplay.pca_sync_player_entity",pcaSyncPlayerEntity,"Available values: NOBODY,EVERYBODY,OPS,OPS_AND_SELF");
}
public static <T> T get(String key,T def){
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 a0628b9d74c29d02bfba583edf7ee6f2cde2cff6..698c7bfddb0d45d088c30fd26eccb86b924fd60a 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
@@ -373,6 +373,11 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
@Override
public void containerChanged(Container sender) {
+ // Leaves start - pca
+ if (me.earthme.luminol.LuminolConfig.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 ce728f062794e239d1dfdf842d7d0c725f77fba7..a3200593788525c7f18420036044bfdccfaf40fc 100644
--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
@@ -64,6 +64,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 (me.earthme.luminol.LuminolConfig.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 f64edfdb03f99624daf1e05b5dc86d845c3018b6..e98ea5f93a829e232a61eee4564209d1831f8b8d 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
@@ -138,7 +138,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
}
@Override
- public void setChanged() {}
+ public void setChanged() {
+ // Leaves start - pca
+ if (me.earthme.luminol.LuminolConfig.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 a18aadbf7ae83713e1f2b21553185d8000bc7699..4b174bed02fb98f798f9444e03fc866baf1e1a07 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
@@ -569,6 +569,16 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
public boolean stillValid(net.minecraft.world.entity.player.Player player) {
return Container.stillValidBlockEntity(this, player);
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..b73c3dc28a95279285e911bfed3bc3d3f038e6db 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 (me.earthme.luminol.LuminolConfig.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 cf09525efd2d53bf884cd6ec3b0b9229715895eb..9c7b5fbec4ec1eb6b39f139a9b16483208249b6f 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 (me.earthme.luminol.LuminolConfig.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 (me.earthme.luminol.LuminolConfig.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 (me.earthme.luminol.LuminolConfig.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 (me.earthme.luminol.LuminolConfig.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 526d1bfd5ad0de7bcfd0c2da902515f3dec94c54..66758af67aca2c726b2f5b451f3f7197102af22f 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 (me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
public boolean stillValid(Player player) {
return Container.stillValidBlockEntity(this, player);
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 d66806565770cb03a21794f99e5c4b0f3040b26a..034bc37a8b50a3c9904f4f53c8758b1b02b458b1 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
@@ -220,6 +220,16 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
// Pufferfish end
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (me.earthme.luminol.LuminolConfig.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/DispenserBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java
index 881379681c39230a00b3a1f11cd87498984396c7..51fae4d72eed9e4224a8abe1a0b6520835857ba5 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 (me.earthme.luminol.LuminolConfig.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 c90d578643490709936545ee9cbd41c8671eeb7a..3527a3cc76d3b1ad1319f1eebe31fd4d9e8aa039 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
@@ -155,6 +155,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ top.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this);
+ }
+ }
+ // Leaves end - pca
+
@Override
protected Component getDefaultName() {
return Component.translatable("container.hopper");
@@ -234,6 +244,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (flag) {
blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
setChanged(world, pos, state);
+ // Leaves start - pca
+ if (me.earthme.luminol.LuminolConfig.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 1fa22445a4ecc8c08dbcf0cc6bd39dc5003604c4..c1492fce06cdc00a8e82977f0c474a541efa39e8 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
@@ -269,6 +269,16 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
this.itemStacks = list;
}
+ // Leaves start - pca
+ @Override
+ public void setChanged() {
+ super.setChanged();
+ if (me.earthme.luminol.LuminolConfig.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/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index b23e43f7ac22bc106a0bcde4a8bca9ecba1a9ae4..08bf4f13e0ee6698b18a6946cb78adb4ae65c852 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -45,7 +45,7 @@ import org.bukkit.scheduler.BukkitWorker;
*/
public class CraftScheduler implements BukkitScheduler {
- static Plugin MINECRAFT = new MinecraftInternalPlugin();
+ public static Plugin MINECRAFT = new MinecraftInternalPlugin();
/**
* The start ID for the counter.
*/
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..ebd28033ddf0fe6a354585dc2818a9b481d90ed4
--- /dev/null
+++ b/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java
@@ -0,0 +1,384 @@
+package top.leavesmc.leaves.protocol;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
+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.LogManager;
+import org.bukkit.craftbukkit.scheduler.CraftScheduler;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import top.leavesmc.leaves.protocol.core.LeavesProtocol;
+import top.leavesmc.leaves.protocol.core.ProtocolHandler;
+import top.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.bukkit.craftbukkit.scheduler.CraftScheduler.MINECRAFT;
+import static top.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 ResourceLocation UPDATE_ENTITY = id("update_entity");
+ private static final ResourceLocation UPDATE_BLOCK_ENTITY = id("update_block_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);
+ }
+
+ @ProtocolHandler.PlayerJoin
+ private static void onJoin(ServerPlayer player) {
+ if (me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ enablePcaSyncProtocol(player);
+ }
+ }
+
+ @ProtocolHandler.ReloadServer
+ private static void onServerReload() {
+ if (me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ enablePcaSyncProtocolGlobal();
+ } else {
+ disablePcaSyncProtocolGlobal();
+ }
+ }
+
+ @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_block_entity")
+ private static void cancelSyncBlockEntityHandler(ServerPlayer player, EmptyPayload payload) {
+ if (!me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ return;
+ }
+ PcaSyncProtocol.clearPlayerWatchBlock(player);
+ }
+
+ @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_entity")
+ private static void cancelSyncEntityHandler(ServerPlayer player, EmptyPayload payload) {
+ if (!me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ return;
+ }
+ PcaSyncProtocol.clearPlayerWatchEntity(player);
+ }
+
+ @ProtocolHandler.PayloadReceiver(payload = SyncBlockEntityPayload.class, payloadId = "sync_block_entity")
+ private static void syncBlockEntityHandler(ServerPlayer player, SyncBlockEntityPayload payload) {
+ if (!me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ return;
+ }
+ MinecraftServer server = MinecraftServer.getServer();
+ BlockPos pos = payload.pos;
+ ServerLevel world = player.serverLevel();
+
+ player.getBukkitEntity().getScheduler().execute(MINECRAFT,() -> {
+ 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.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();
+ },null,1);
+ }
+
+ @ProtocolHandler.PayloadReceiver(payload = SyncEntityPayload.class, payloadId = "sync_entity")
+ private static void syncEntityHandler(ServerPlayer player, SyncEntityPayload payload) {
+ if (!me.earthme.luminol.LuminolConfig.pcaSyncProtocol) {
+ return;
+ }
+ MinecraftServer server = MinecraftServer.getServer();
+ int entityId = payload.entityId;
+ ServerLevel world = player.serverLevel();
+ player.getBukkitEntity().getScheduler().execute(MINECRAFT,() -> {
+ Entity entity = world.getEntity(entityId);
+ if (entity != null) {
+ clearPlayerWatchData(player);
+ if (entity instanceof Player) {
+ if (me.earthme.luminol.LuminolConfig.pcaSyncPlayerEntity.equals("NOBODY")) {
+ return;
+ }else if (me.earthme.luminol.LuminolConfig.pcaSyncPlayerEntity.equals("OPS")) {
+ if (server.getProfilePermissions(player.getGameProfile()) < 2) {
+ return;
+ }
+ } else if (me.earthme.luminol.LuminolConfig.pcaSyncPlayerEntity.equals("OPS_AND_SELF")) {
+ if (server.getProfilePermissions(player.getGameProfile()) < 2 &&
+ entity != player) {
+ return;
+ }
+ } else if (!me.earthme.luminol.LuminolConfig.pcaSyncPlayerEntity.equals("EVERYONE")) {
+ // wtf????
+ LogManager.getLogger().warn("pcaSyncPlayerEntity wtf???");
+ return;
+ }
+ }
+ updateEntity(player, entity);
+
+ Pair<ResourceLocation, Entity> 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();
+ }
+ },null,1);
+ }
+
+ public static void enablePcaSyncProtocol(@NotNull ServerPlayer player) {
+ ProtocolUtils.sendEmptyPayloadPacket(player, ENABLE_PCA_SYNC_PROTOCOL);
+ }
+
+ 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, UPDATE_ENTITY, buf -> {
+ buf.writeResourceLocation(entity.level().dimension().location());
+ buf.writeInt(entity.getId());
+ buf.writeNbt(nbt);
+ });
+ }
+
+ public static void updateBlockEntity(@NotNull ServerPlayer player, @NotNull BlockEntity blockEntity) {
+ Level world = blockEntity.getLevel();
+
+ if (world == null) {
+ return;
+ }
+
+ ProtocolUtils.sendPayloadPacket(player, UPDATE_BLOCK_ENTITY, buf -> {
+ buf.writeResourceLocation(world.dimension().location());
+ buf.writeBlockPos(blockEntity.getBlockPos());
+ buf.writeNbt(blockEntity.saveWithId());
+ });
+ }
+
+ 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.level().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.level().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);
+ }
+
+ public record SyncBlockEntityPayload(BlockPos pos) implements CustomPacketPayload {
+
+ public static final ResourceLocation SYNC_BLOCK_ENTITY = PcaSyncProtocol.id("sync_block_entity");
+
+ public SyncBlockEntityPayload(ResourceLocation id, FriendlyByteBuf buf) {
+ this(buf.readBlockPos());
+ }
+
+ @Override
+ public void write(FriendlyByteBuf buf) {
+ buf.writeBlockPos(pos);
+ }
+
+ @Override
+ public @NotNull ResourceLocation id() {
+ return SYNC_BLOCK_ENTITY;
+ }
+ }
+
+ public record SyncEntityPayload(int entityId) implements CustomPacketPayload {
+
+ public static final ResourceLocation SYNC_ENTITY = PcaSyncProtocol.id("sync_entity");
+
+ public SyncEntityPayload(ResourceLocation id, FriendlyByteBuf buf) {
+ this(buf.readInt());
+ }
+
+ @Override
+ public void write(FriendlyByteBuf buf) {
+ buf.writeInt(entityId);
+ }
+
+ @Override
+ public @NotNull ResourceLocation id() {
+ return SYNC_ENTITY;
+ }
+ }
+}