From db44d0a8fa46a071c23b436dd4db07e30868cfc8 Mon Sep 17 00:00:00 2001 From: AltronMaxX Date: Tue, 13 Aug 2024 14:07:12 +0400 Subject: [PATCH] rewrite Foldenor command and more patches --- .../server/0008-Add-Foldenor-commands.patch | 193 ++--- .../server/0040-Pufferfish-Entity-TTL.patch | 79 ++ .../0041-Lumina-PCA-sync-protocol.patch | 721 ++++++++++++++++++ ...e-Optimize-CraftServer.getWorld-UUID.patch | 50 ++ ...-which-zone-has-which-players-in-tps.patch | 71 ++ ...timization-entity-activation-check-f.patch | 68 ++ 6 files changed, 1045 insertions(+), 137 deletions(-) create mode 100644 patches/server/0040-Pufferfish-Entity-TTL.patch create mode 100644 patches/server/0041-Lumina-PCA-sync-protocol.patch create mode 100644 patches/server/0042-Divine-Optimize-CraftServer.getWorld-UUID.patch create mode 100644 patches/server/0043-LevelBukkit-Show-which-zone-has-which-players-in-tps.patch create mode 100644 patches/server/0044-ShreddedPaper-Optimization-entity-activation-check-f.patch diff --git a/patches/server/0008-Add-Foldenor-commands.patch b/patches/server/0008-Add-Foldenor-commands.patch index 65f9717..a494a56 100644 --- a/patches/server/0008-Add-Foldenor-commands.patch +++ b/patches/server/0008-Add-Foldenor-commands.patch @@ -4,168 +4,87 @@ Date: Wed, 31 Jul 2024 14:55:42 +0400 Subject: [PATCH] Add-Foldenor-commands -diff --git a/src/main/java/net/edenor/foldenor/command/FoldenorCommands.java b/src/main/java/net/edenor/foldenor/command/FoldenorCommands.java +diff --git a/src/main/java/net/edenor/foldenor/commands/FoldenorCommand.java b/src/main/java/net/edenor/foldenor/commands/FoldenorCommand.java new file mode 100644 -index 0000000000000000000000000000000000000000..0a31c1c7ae98ceca60e8e57a56298e36650b9e68 +index 0000000000000000000000000000000000000000..a4bbdbb119c9e9c15265cd06831ee7a0c1f54622 --- /dev/null -+++ b/src/main/java/net/edenor/foldenor/command/FoldenorCommands.java -@@ -0,0 +1,28 @@ -+package net.edenor.foldenor.command; -+ -+import net.edenor.foldenor.commands.ChunkTPCommand; -+import net.edenor.foldenor.commands.FoldenorReloadConfigCommand; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.command.Command; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.framework.qual.DefaultQualifier; -+ -+import java.util.HashMap; -+import java.util.Map; -+ -+@DefaultQualifier(NonNull.class) -+public final class FoldenorCommands { -+ -+ private FoldenorCommands() { -+ } -+ -+ private static final Map COMMANDS = new HashMap<>(); -+ static { -+ COMMANDS.put("foldenor-reload", new FoldenorReloadConfigCommand("foldenor-reload")); -+ COMMANDS.put("chunktp", new ChunkTPCommand("chunktp")); -+ } -+ -+ public static void registerCommands(final MinecraftServer server) { -+ COMMANDS.forEach((s, command) -> server.server.getCommandMap().register(s, "Foldenor", command)); -+ } -+} -diff --git a/src/main/java/net/edenor/foldenor/commands/ChunkTPCommand.java b/src/main/java/net/edenor/foldenor/commands/ChunkTPCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f343f7921361c4ddb58db4b0f02de978b21dbdb4 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/commands/ChunkTPCommand.java -@@ -0,0 +1,58 @@ ++++ b/src/main/java/net/edenor/foldenor/commands/FoldenorCommand.java +@@ -0,0 +1,66 @@ +package net.edenor.foldenor.commands; + -+ -+import org.bukkit.Bukkit; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.craftbukkit.entity.CraftPlayer; -+import org.bukkit.event.player.PlayerTeleportEvent; -+import org.bukkit.permissions.Permission; -+import org.bukkit.permissions.PermissionDefault; -+import org.bukkit.plugin.PluginManager; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.ArrayList; ++import java.io.File; ++import java.util.Collections; +import java.util.List; -+ -+public class ChunkTPCommand extends Command { -+ public ChunkTPCommand(@NotNull String name) { -+ super(name); -+ this.setPermission("bukkit.command.foldenor.chunktp"); -+ this.description = "Teleport to chunk"; -+ this.usageMessage = "/chunktp [chunkX] [chunkZ]"; -+ } -+ -+ @Override -+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { -+ if (!testPermission(sender)) return true; -+ -+ if (sender instanceof CraftPlayer player) { -+ if (args.length == 2) { -+ if (this.validateArguments(args[0], args[1])) { -+ double x = Integer.parseInt(args[0]) * 16; -+ double z = Integer.parseInt(args[1]) * 16; -+ if (player.teleport(new Location(player.getWorld(), x, 100.0D, z), PlayerTeleportEvent.TeleportCause.PLUGIN)) { -+ player.sendMessage("You were teleported to: " + x + " , 100, " + z); -+ } else { -+ player.sendMessage("You couldn't be teleported there for some reason."); -+ } -+ } -+ } else { -+ player.sendMessage("You must specify chunk coordinates. Example: /chunktp -230 334"); -+ } -+ } -+ -+ return true; -+ } -+ -+ private boolean validateArguments(String string, String string2) { -+ try { -+ Integer.parseInt(string); -+ Integer.parseInt(string2); -+ return true; -+ } catch (NumberFormatException var4) { -+ return false; -+ } -+ } -+} -diff --git a/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java b/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4188e8db6386fe1790192c9efd8c2fe256ce6418 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java -@@ -0,0 +1,49 @@ -+package net.edenor.foldenor.commands; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; + +import net.edenor.foldenor.config.FoldenorConfig; -+import net.minecraft.server.dedicated.DedicatedServer; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.TextColor; ++import net.kyori.adventure.text.format.TextDecoration; ++import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; -+import org.bukkit.permissions.Permission; -+import org.bukkit.permissions.PermissionDefault; -+import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; + -+import java.util.ArrayList; -+import java.util.Collections; -+import java.util.List; -+ -+import static net.kyori.adventure.text.Component.text; -+import static net.kyori.adventure.text.format.NamedTextColor.GREEN; -+import static net.kyori.adventure.text.format.NamedTextColor.RED; ++public class FoldenorCommand extends Command { + -+public class FoldenorReloadConfigCommand extends Command { ++ public static void init() { ++ MinecraftServer.getServer().server.getCommandMap().register("foldenor", "Foldenor", new FoldenorCommand()); ++ } + -+ public FoldenorReloadConfigCommand(@NotNull String name) { -+ super(name); -+ this.setPermission("bukkit.command.foldenor.reload_config"); -+ this.description = "Reload Foldenor config"; -+ this.usageMessage = "/foldenor-reload"; ++ protected FoldenorCommand() { ++ super("foldenor"); ++ this.description = "Foldenor related commands"; ++ this.usageMessage = "/foldenor [reload | version]"; ++ this.setPermission("bukkit.command.foldenor"); + } + + @Override -+ public @NotNull List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { ++ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, String[] args, Location location) throws IllegalArgumentException { ++ if (args.length == 1) { ++ return Stream.of("reload", "version") ++ .filter(arg -> arg.startsWith(args[0].toLowerCase())) ++ .collect(Collectors.toList()); ++ } + return Collections.emptyList(); + } + + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + if (!testPermission(sender)) return true; ++ Component prefix = Component.text("Foldenor ยป ", TextColor.fromHexString("#12fff6"), TextDecoration.BOLD); ++ ++ if (args.length != 1) { ++ sender.sendMessage(prefix.append(Component.text("Usage: " + usageMessage).color(TextColor.fromHexString("#e8f9f9")))); ++ args = new String[]{"version"}; ++ } + -+ try { -+ FoldenorConfig.reload((java.io.File) DedicatedServer.getServer().options.valueOf("foldenor-settings")); -+ Command.broadcastCommandMessage(sender, text("Foldenor config reload complete.", GREEN)); -+ return true; -+ } catch (Exception e) { -+ Command.broadcastCommandMessage(sender, text("Error while reloading Foldenor config \n." + e.toString(), RED)); -+ throw new RuntimeException(e); ++ if (args[0].equalsIgnoreCase("reload")) { ++ MinecraftServer console = MinecraftServer.getServer(); ++ FoldenorConfig.init(new File("foldenor.yml")); ++ console.server.reloadCount++; ++ ++ Command.broadcastCommandMessage(sender, prefix.append(Component.text("Foldenor configuration has been reloaded.").color(TextColor.fromHexString("#e8f9f9")))); ++ } else if (args[0].equalsIgnoreCase("version")) { ++ Command.broadcastCommandMessage(sender, prefix.append(Component.text("This server is running " + Bukkit.getName() + " version " + ++ Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")").color(TextColor.fromHexString("#e8f9f9")))); + } ++ ++ return true; + } +} -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index fc5f7909d85d99b7fdaceb15d2672e36dcb6a426..c19b0a9552a642b1ace5b61109d1be6501b4342f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1149,6 +1149,7 @@ public final class CraftServer implements Server { - this.reloadData(); - org.spigotmc.SpigotConfig.registerCommands(); // Spigot - io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper -+ net.edenor.foldenor.command.FoldenorCommands.registerCommands(this.console); - this.spark.registerCommandBeforePlugins(this); // Paper - spark - this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); - this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index a196839396e9a54c449b7dd5f1953a8a16657ba4..6a140fa920002798f7e88f699416ae04d6a4915b 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -246,6 +246,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + DedicatedServer.LOGGER.error("Unable to load server configuration", e); + return false; + } ++ net.edenor.foldenor.commands.FoldenorCommand.init(); // Foldenor + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); diff --git a/patches/server/0040-Pufferfish-Entity-TTL.patch b/patches/server/0040-Pufferfish-Entity-TTL.patch new file mode 100644 index 0000000..63ba303 --- /dev/null +++ b/patches/server/0040-Pufferfish-Entity-TTL.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Mon, 12 Aug 2024 15:27:53 +0400 +Subject: [PATCH] Pufferfish Entity TTL + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index d4f616a4580e2fc29f9f34b7bbcbf27eecaa27ab..49c5684edf5f605ba862599b0afcb48d8d0bebaa 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -20,6 +20,7 @@ import java.lang.reflect.Method; + import java.lang.reflect.Modifier; + import java.util.Collections; + import java.util.List; ++import java.util.Locale; + import java.util.Map; + import java.util.logging.Level; + +@@ -106,6 +107,8 @@ public class FoldenorConfig { + + readRegionFormatSettings(); + ++ projectileTimeouts(); ++ + try { + readDynamicActivationOfBrains(); + } catch (IOException e) { +@@ -179,6 +182,22 @@ public class FoldenorConfig { + }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); + } + ++ public static Map projectileTimeouts; ++ private static void projectileTimeouts() { ++ // Set some defaults ++ getInt("entity_timeouts.SNOWBALL", -1); ++ getInt("entity_timeouts.LLAMA_SPIT", -1); ++ //"These values define a entity's maximum lifespan. If an", ++ //"entity is in this list and it has survived for longer than", ++ //"that number of ticks, then it will be removed. Setting a value to", ++ //"-1 disables this feature." ++ ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); ++ entityType.ttl = config.getInt("entity_timeouts." + type, -1); ++ } ++ } ++ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 93457c36f3b8dabb860472c8336bca57668a408b..b3b3be71268a39e24b3da69e284885b94c73d0fe 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -862,6 +862,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void tick() { ++ // Pufferfish start - entity TTL ++ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { ++ discard(); ++ return; ++ } ++ // Pufferfish end - entity TTL + this.baseTick(); + } + +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index 280b65d9ea6cde9f417057632c305645de10c1ed..a4e49c4097cc77686ea56fe1cf4a01a2c739c8db 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -317,6 +317,7 @@ public class EntityType implements FeatureElement, EntityTypeT + private final int clientTrackingRange; + private final int updateInterval; + public boolean dabEnabled = false; // Pufferfish ++ public int ttl = -1; // Pufferfish + @Nullable + private String descriptionId; + @Nullable diff --git a/patches/server/0041-Lumina-PCA-sync-protocol.patch b/patches/server/0041-Lumina-PCA-sync-protocol.patch new file mode 100644 index 0000000..05398e5 --- /dev/null +++ b/patches/server/0041-Lumina-PCA-sync-protocol.patch @@ -0,0 +1,721 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Mon, 12 Aug 2024 15:40:15 +0400 +Subject: [PATCH] Lumina PCA-sync-protocol + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 49c5684edf5f605ba862599b0afcb48d8d0bebaa..e3ef04b52ed1c0dfb9e40cc2daedc0659f761fb7 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -109,6 +109,8 @@ public class FoldenorConfig { + + projectileTimeouts(); + ++ pcaSyncProtocol(); ++ + try { + readDynamicActivationOfBrains(); + } catch (IOException e) { +@@ -198,6 +200,21 @@ public class FoldenorConfig { + } + } + ++ public static boolean pcaSyncProtocolEnabled = false; ++ public static SyncOption pcaSyncProtocolOption = SyncOption.OPS; ++ private static void pcaSyncProtocol() { ++ pcaSyncProtocolEnabled = getBoolean("pcaSyncProtocol.enabled", pcaSyncProtocolEnabled); ++ pcaSyncProtocolOption = SyncOption.valueOf(getString("pcaSyncProtocol.option", String.valueOf(pcaSyncProtocolOption))); ++ } ++ ++ public enum SyncOption { ++ NOBODY, ++ BOT, ++ OPS, ++ OPS_AND_SELF, ++ EVERYONE ++ } ++ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); +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 1f5ed236fb7c0c1b0181675747d25d233f534284..f0f521411cdfb7b8016fb22f715b3d28d33628d4 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 +@@ -7,6 +7,8 @@ import java.util.function.DoubleSupplier; + import java.util.function.IntUnaryOperator; + import java.util.function.Predicate; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.advancements.CriteriaTriggers; + import net.minecraft.core.BlockPos; + import net.minecraft.core.Direction; +@@ -436,6 +438,11 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + + @Override + public void containerChanged(Container sender) { ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 5f3104b2a46d4b47cf505012438f848e3b744315..5425f0b39175ebe273c1577de4001a3153775f0a 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java +@@ -6,6 +6,8 @@ import com.mojang.serialization.DataResult; + import java.util.ArrayList; + import java.util.Objects; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.Util; + import net.minecraft.advancements.CriteriaTriggers; + import net.minecraft.core.particles.ParticleOptions; +@@ -71,6 +73,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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 9549eee0d92f322bd5232abd7e695213660c2e22..76b7b1dfbc7ba0eab32ecfb5b00b01fd93c7913a 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +@@ -126,7 +126,13 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme + } + + @Override +- public void setChanged() {} ++ public void setChanged() { ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 730aca233f6e7564d4cb85b5b628d23c4f01d2f4..e345703bd95901ff306380c6c18ebd15ca3828b3 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 +@@ -558,6 +558,16 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + + } + ++ // Leaves start - pca ++ @Override ++ public void setChanged() { ++ super.setChanged(); ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 6186e55014bbb9d5bedaa0e9d196879c55339d42..88bd1ddcfbe03a4bb166691b3c28612c7027204d 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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 f933fa419a4b55b0096ff42caf1b071d027b8e7e..a23f5de13a6a9d832018952e597680b5847ade46 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 +@@ -143,6 +143,11 @@ public class BeehiveBlockEntity extends BlockEntity { + super.setChanged(); + } + ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); ++ } ++ // Leaves end - pca + return list; + } + +@@ -197,6 +202,12 @@ public class BeehiveBlockEntity extends BlockEntity { + this.level.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, this.getBlockState())); + } + ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); ++ } ++ // Leaves end - pca ++ + entity.discard(EntityRemoveEvent.Cause.ENTER_BLOCK); // CraftBukkit - add Bukkit remove cause + super.setChanged(); + } +@@ -312,6 +323,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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 +@@ -357,6 +373,11 @@ public class BeehiveBlockEntity extends BlockEntity { + this.maxBees = nbt.getInt("Bukkit.MaxEntities"); + } + // CraftBukkit end ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 46011ababdadaafd72a8717911e49f6581ab5688..1d70b273e12f7dea799f93ce75bf85e4b39d6104 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 + return this.canPlaceItem(slot, stack); + } + ++ // Leaves start - pca ++ @Override ++ public void setChanged() { ++ super.setChanged(); ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 b88aa184cd06a0485146f58a5b61a56a50911209..7b652017c08a419d652420f3e8ec6836f9e9e628 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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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/DispenserBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/DispenserBlockEntity.java +index 431fb6a658c6aac43b6f9dbd1f578b83f261a4e3..75df0fc2eb581b36713001f1f43912ffc8e3b80d 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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 66d20e4b7f096f0755044a3946e98251385ed18c..516ab11feb7e234ef19c90198a327fc021a2baaf 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 +@@ -144,6 +144,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + this.facing = (Direction) iblockdata.getValue(HopperBlock.FACING); + } + ++ // Leaves start - pca ++ @Override ++ public void setChanged() { ++ super.setChanged(); ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ org.leavesmc.leaves.protocol.PcaSyncProtocol.syncBlockEntityToClient(this); ++ } ++ } ++ // Leaves end - pca ++ + @Override + protected Component getDefaultName() { + return Component.translatable("container.hopper"); +@@ -222,6 +232,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + if (flag) { + blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + setChanged(world, pos, state); ++ // Leaves start - pca ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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 0d68db20f5fbe5e834f12c1e8fd429099a44e4b6..8355c1ab072cbc68517a177a9a298010f5b943f7 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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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..0b29f88c87b453f02c48e3006fca574c7d3cd017 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/PcaSyncProtocol.java +@@ -0,0 +1,389 @@ ++package org.leavesmc.leaves.protocol; ++ ++import com.mojang.logging.LogUtils; ++import net.edenor.foldenor.config.FoldenorConfig; ++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.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 org.slf4j.Logger; ++ ++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 { ++ private static final Logger LOGGER = LogUtils.getClassLogger(); ++ ++ 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> 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 (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ enablePcaSyncProtocol(player); ++ } ++ } ++ ++ @ProtocolHandler.ReloadServer ++ private static void onServerReload() { ++ if (net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ enablePcaSyncProtocolGlobal(); ++ } else { ++ disablePcaSyncProtocolGlobal(); ++ } ++ } ++ ++ @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_block_entity") ++ private static void cancelSyncBlockEntityHandler(ServerPlayer player, EmptyPayload payload) { ++ if (!net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ return; ++ } ++ PcaSyncProtocol.clearPlayerWatchBlock(player); ++ } ++ ++ @ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_entity") ++ private static void cancelSyncEntityHandler(ServerPlayer player, EmptyPayload payload) { ++ if (!net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ return; ++ } ++ PcaSyncProtocol.clearPlayerWatchEntity(player); ++ } ++ ++ @ProtocolHandler.PayloadReceiver(payload = SyncBlockEntityPayload.class, payloadId = "sync_block_entity") ++ private static void syncBlockEntityHandler(ServerPlayer player, SyncBlockEntityPayload payload) { ++ if (!net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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.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 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 (!net.edenor.foldenor.config.FoldenorConfig.pcaSyncProtocolEnabled) { ++ 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) { ++ if (FoldenorConfig.pcaSyncProtocolOption == FoldenorConfig.SyncOption.NOBODY) { ++ return; ++ } else if (FoldenorConfig.pcaSyncProtocolOption == FoldenorConfig.SyncOption.BOT) { ++ // if (!(entity instanceof ServerBot)) { // TODO ++ // return; ++ // } ++ } else if (FoldenorConfig.pcaSyncProtocolOption == FoldenorConfig.SyncOption.OPS) { ++ // !(entity instanceof ServerBot) && // TODO ++ if (server.getProfilePermissions(player.getGameProfile()) < 2) { ++ return; ++ } ++ } else if (FoldenorConfig.pcaSyncProtocolOption == FoldenorConfig.SyncOption.OPS_AND_SELF) { ++ // !(entity instanceof ServerBot) && // TODO ++ if (server.getProfilePermissions(player.getGameProfile()) < 2 && ++ entity != player) { ++ return; ++ } ++ } ++ } ++ 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 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(blockEntity.getLevel().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.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 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 SyncBlockEntityPayload(BlockPos pos) implements LeavesCustomPayload { ++ ++ public static final ResourceLocation SYNC_BLOCK_ENTITY = PcaSyncProtocol.id("sync_block_entity"); ++ ++ @New ++ 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 LeavesCustomPayload { ++ ++ public static final ResourceLocation SYNC_ENTITY = PcaSyncProtocol.id("sync_entity"); ++ ++ @New ++ 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; ++ } ++ } ++} diff --git a/patches/server/0042-Divine-Optimize-CraftServer.getWorld-UUID.patch b/patches/server/0042-Divine-Optimize-CraftServer.getWorld-UUID.patch new file mode 100644 index 0000000..c074286 --- /dev/null +++ b/patches/server/0042-Divine-Optimize-CraftServer.getWorld-UUID.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Mon, 12 Aug 2024 15:41:49 +0400 +Subject: [PATCH] Divine: Optimize CraftServer.getWorld(UUID) + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index d6374c858101b66de307f79555e23c06da284f82..b3c18f91171dc6df0b1294f5bd7ba63eb910128f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -44,6 +44,7 @@ import java.util.logging.Logger; + import java.util.stream.Collectors; + import javax.imageio.ImageIO; + // import jline.console.ConsoleReader; ++import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; + import net.minecraft.advancements.AdvancementHolder; + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.commands.Commands; +@@ -285,6 +286,7 @@ public final class CraftServer implements Server { + protected final DedicatedPlayerList playerList; + private final Map worlds = new LinkedHashMap(); + // private final Map, Registry> registries = new HashMap<>(); // Paper - replace with RegistryAccess ++ private final Map worldsByUUID = new Object2ObjectLinkedOpenHashMap<>(); // DivineMC - MultiPaper - optimize getWorld(UUID) + private YamlConfiguration configuration; + private YamlConfiguration commandsConfiguration; + private final Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); +@@ -1538,6 +1540,7 @@ public final class CraftServer implements Server { + this.getLogger().log(Level.SEVERE, null, ex); + } + ++ this.worldsByUUID.remove(world.getUID()); // DivineMC - MultiPaper - optimize getWorld(UUID) + this.worlds.remove(world.getName().toLowerCase(Locale.ROOT)); + this.console.removeLevel(handle); + return true; +@@ -1556,6 +1559,7 @@ public final class CraftServer implements Server { + + @Override + public World getWorld(UUID uid) { ++ if (true) return this.worldsByUUID.get(uid); // DivineMC - MultiPaper - optimize getWorld(UUID) + for (World world : this.worlds.values()) { + if (world.getUID().equals(uid)) { + return world; +@@ -1579,6 +1583,7 @@ public final class CraftServer implements Server { + System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world."); + return; + } ++ this.worldsByUUID.put(world.getUID(), world); // DivineMC - MultiPaper - optimize getWorld(UUID) + this.worlds.put(world.getName().toLowerCase(Locale.ROOT), world); + } + diff --git a/patches/server/0043-LevelBukkit-Show-which-zone-has-which-players-in-tps.patch b/patches/server/0043-LevelBukkit-Show-which-zone-has-which-players-in-tps.patch new file mode 100644 index 0000000..4ff9188 --- /dev/null +++ b/patches/server/0043-LevelBukkit-Show-which-zone-has-which-players-in-tps.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Mon, 12 Aug 2024 16:00:16 +0400 +Subject: [PATCH] LevelBukkit: Show which zone has which players in tps + + +diff --git a/src/main/java/io/papermc/paper/threadedregions/commands/CommandServerHealth.java b/src/main/java/io/papermc/paper/threadedregions/commands/CommandServerHealth.java +index 012d3a7da7fe483393a0888c823bd2e78f5c3908..6ce10734ba77fd27993fec5b390156b4c1014300 100644 +--- a/src/main/java/io/papermc/paper/threadedregions/commands/CommandServerHealth.java ++++ b/src/main/java/io/papermc/paper/threadedregions/commands/CommandServerHealth.java +@@ -16,6 +16,7 @@ import net.kyori.adventure.text.format.NamedTextColor; + import net.kyori.adventure.text.format.TextColor; + import net.kyori.adventure.text.format.TextDecoration; + import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.ServerPlayer; + import net.minecraft.world.level.ChunkPos; + import org.bukkit.Bukkit; + import org.bukkit.World; +@@ -24,6 +25,8 @@ import org.bukkit.command.CommandSender; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.entity.Entity; + import org.bukkit.entity.Player; ++import org.jetbrains.annotations.NotNull; ++ + import java.text.DecimalFormat; + import java.util.ArrayList; + import java.util.Arrays; +@@ -79,6 +82,34 @@ public final class CommandServerHealth extends Command { + .build(); + } + ++ private static Component formatRegionStatsAsRegion(final TickRegions.RegionStats stats, final boolean newline) { ++ List players = TickRegionScheduler.getCurrentRegionizedWorldData().getLocalPlayers(); ++ final TextComponent builder = getTextComponent(players); ++ return Component.text() ++ .append(Component.text("Chunks: ", PRIMARY)) ++ .append(Component.text(NO_DECIMAL_PLACES.get().format(stats.getChunkCount()), INFORMATION)) ++ .append(Component.text(" Players: ", PRIMARY).hoverEvent(HoverEvent.showText(builder))) ++ .append(Component.text(NO_DECIMAL_PLACES.get().format(stats.getPlayerCount()), INFORMATION)) ++ .append(Component.text(" Entities: ", PRIMARY)) ++ .append(Component.text(NO_DECIMAL_PLACES.get().format(stats.getEntityCount()) + (newline ? "\n" : ""), INFORMATION)) ++ .build(); ++ } ++ ++ private static @NotNull TextComponent getTextComponent(List players) { ++ @NotNull TextComponent builder = Component.text("Region Player List: "); ++ for (final ServerPlayer player : players) { ++ builder = builder.append(Component.text("\n")). ++ append(Component.text(player.gameProfile.getName())) ++ .append(Component.text(" | In ")) ++ .append(Component.text(player.getOnPos().getX())) ++ .append(Component.text(", ")) ++ .append(Component.text(player.getOnPos().getY())) ++ .append(Component.text(", ")) ++ .append(Component.text(player.getOnPos().getZ())); ++ } ++ return builder; ++ } ++ + private static boolean executeRegion(final CommandSender sender, final String commandLabel, final String[] args) { + final ThreadedRegionizer.ThreadedRegion region = + TickRegionScheduler.getCurrentRegion(); +@@ -120,7 +151,7 @@ public final class CommandServerHealth extends Command { + formatRegionInfo("1m: ", util1m, mspt1m, tps1m, true) + ) + .append( +- formatRegionStats(region.getData().getRegionStats(), false) ++ formatRegionStatsAsRegion(region.getData().getRegionStats(), false) + ) + + .build(); diff --git a/patches/server/0044-ShreddedPaper-Optimization-entity-activation-check-f.patch b/patches/server/0044-ShreddedPaper-Optimization-entity-activation-check-f.patch new file mode 100644 index 0000000..3a0b6fd --- /dev/null +++ b/patches/server/0044-ShreddedPaper-Optimization-entity-activation-check-f.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Mon, 12 Aug 2024 17:06:35 +0400 +Subject: [PATCH] ShreddedPaper: Optimization: + entity-activation-check-frequency + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index e3ef04b52ed1c0dfb9e40cc2daedc0659f761fb7..418a3e07512b81c1fb6b6f8f2b1478cd033f05ff 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -59,6 +59,8 @@ public class FoldenorConfig { + public static int linearFlushFrequency = 5; + public static EnumRegionFileExtension regionFormatType; + ++ public static int entityActivationCheckFrequency = 20; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -134,6 +136,7 @@ public class FoldenorConfig { + piglinSpawnChancePersentInPortal = getInt("optimizations.piglin-spawn-chance-persent-in-portal", 100, + "Reduces piglin spawn in portal, by reducing change to spawn"); + skipMapItemUpdatesIfNoBukkitRender = getBoolean("optimizations.skip_map_item_updates_if_no_bukkit_render", skipMapItemUpdatesIfNoBukkitRender); ++ entityActivationCheckFrequency = getInt("optimizations.entity-activation-check-frequency", 20); + } + + private static void readMiscSettings() { +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 69784dbf1fc50b27d15582ca058b7b0027fa49ae..f758fdf1d6e4d42f0fcdb11026988c1b9115f940 100644 +--- a/src/main/java/org/spigotmc/ActivationRange.java ++++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -1,5 +1,6 @@ + package org.spigotmc; + ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.core.BlockPos; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerChunkCache; +@@ -197,6 +198,8 @@ public class ActivationRange + + for ( Player player : world.getLocalPlayers() ) // Folia - region threading + { ++ if (FoldenorConfig.entityActivationCheckFrequency > 1 && (player.getId() + io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick()) % FoldenorConfig ++ .entityActivationCheckFrequency != 0) continue; // ShreddedPaper - Configurable entity activation check frequency + player.activatedTick = io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick(); // Folia - region threading + if ( world.spigotConfig.ignoreSpectatorActivation && player.isSpectator() ) + { +@@ -263,16 +266,16 @@ public class ActivationRange + */ + private static void activateEntity(Entity entity, AABB[] bbByType) // Folia - threaded regions + { +- if ( io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() > entity.activatedTick ) // Folia - threaded regions ++ if ( io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + Math.max(0, FoldenorConfig.entityActivationCheckFrequency) > entity.activatedTick ) // Folia - threaded regions // ShreddedPaper - Configurable entity activation check frequency + { + if ( entity.defaultActivationState ) + { +- entity.activatedTick = io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick(); // Folia - threaded regions ++ entity.activatedTick = io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + Math.max(0, FoldenorConfig.entityActivationCheckFrequency); // Folia - threaded regions // ShreddedPaper - Configurable entity activation check frequency + return; + } + if ( bbByType[entity.activationType.ordinal()].intersects( entity.getBoundingBox() ) ) // Folia - threaded regions + { +- entity.activatedTick = io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick(); // Folia - threaded regions ++ entity.activatedTick = io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + Math.max(0, FoldenorConfig.entityActivationCheckFrequency); // Folia - threaded regions // ShreddedPaper - Configurable entity activation check frequency + } + } + }