From 13af97da181e6ea51a9ea886e175ee7ae41e6704 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Thu, 14 Nov 2024 11:48:49 +0000 Subject: [PATCH] Rewrite player fps settings --- .../0002-Client-Visibility-Settings-API.patch | 186 ++++ patches/api/0002-Player-GUI-API.patch | 149 --- ...patch => 0003-Merge-Cannon-Entities.patch} | 0 patches/api/0003-Visibility-API.patch | 230 ----- ...I.patch => 0004-isPushedByFluid-API.patch} | 2 +- ...ch => 0005-Falling-Block-Parity-API.patch} | 0 ...tch => 0006-Local-Value-Storage-API.patch} | 0 ...tch => 0007-Add-physics-version-API.patch} | 0 ...ch => 0008-Add-durable-material-API.patch} | 0 ...009-Add-redstone-implementation-API.patch} | 0 ...Add-consistent-explosion-radius-api.patch} | 2 +- ...atch => 0011-Add-redstone-cache-api.patch} | 2 +- ...0012-Entity-tracking-range-modifier.patch} | 6 +- .../0005-Client-Visibility-Settings.patch | 974 ++++++++++++++++++ .../0005-Visibility-API-and-Command.patch | 579 ----------- patches/server/0008-TPS-Graph-Command.patch | 20 +- .../server/0016-Merge-Cannon-Entities.patch | 12 +- ...0017-Replace-explosion-density-cache.patch | 6 +- .../server/0019-Specialised-Explosions.patch | 8 +- ...Reduce-entity-tracker-player-updates.patch | 8 +- ...56-Add-entity-travel-distance-limits.patch | 4 +- .../0071-Entity-tracking-range-modifier.patch | 12 +- 22 files changed, 1201 insertions(+), 999 deletions(-) create mode 100644 patches/api/0002-Client-Visibility-Settings-API.patch delete mode 100644 patches/api/0002-Player-GUI-API.patch rename patches/api/{0004-Merge-Cannon-Entities.patch => 0003-Merge-Cannon-Entities.patch} (100%) delete mode 100644 patches/api/0003-Visibility-API.patch rename patches/api/{0005-isPushedByFluid-API.patch => 0004-isPushedByFluid-API.patch} (91%) rename patches/api/{0006-Falling-Block-Parity-API.patch => 0005-Falling-Block-Parity-API.patch} (100%) rename patches/api/{0007-Local-Value-Storage-API.patch => 0006-Local-Value-Storage-API.patch} (100%) rename patches/api/{0008-Add-physics-version-API.patch => 0007-Add-physics-version-API.patch} (100%) rename patches/api/{0009-Add-durable-material-API.patch => 0008-Add-durable-material-API.patch} (100%) rename patches/api/{0010-Add-redstone-implementation-API.patch => 0009-Add-redstone-implementation-API.patch} (100%) rename patches/api/{0011-Add-consistent-explosion-radius-api.patch => 0010-Add-consistent-explosion-radius-api.patch} (90%) rename patches/api/{0012-Add-redstone-cache-api.patch => 0011-Add-redstone-cache-api.patch} (90%) rename patches/api/{0013-Entity-tracking-range-modifier.patch => 0012-Entity-tracking-range-modifier.patch} (76%) create mode 100644 patches/server/0005-Client-Visibility-Settings.patch delete mode 100644 patches/server/0005-Visibility-API-and-Command.patch diff --git a/patches/api/0002-Client-Visibility-Settings-API.patch b/patches/api/0002-Client-Visibility-Settings-API.patch new file mode 100644 index 0000000..0854075 --- /dev/null +++ b/patches/api/0002-Client-Visibility-Settings-API.patch @@ -0,0 +1,186 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Tue, 21 Sep 2021 23:55:45 +0100 +Subject: [PATCH] Client Visibility Settings API + + +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilitySettings.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilitySettings.java +new file mode 100644 +index 0000000000000000000000000000000000000000..50ba48ebb99fac7f42fa1db76808f84ed9e601c4 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilitySettings.java +@@ -0,0 +1,48 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public interface VisibilitySettings { ++ default boolean isEnabled(VisibilityType type) { ++ return this.get(type) == VisibilityState.ON; ++ } ++ ++ default boolean isDisabled(VisibilityType type) { ++ return this.get(type) == VisibilityState.OFF; ++ } ++ ++ default boolean isToggled(VisibilityType type) { ++ return !type.isDefault(this.get(type)); ++ } ++ ++ default VisibilityState toggle(VisibilityType type) { ++ VisibilityState state = this.get(type); ++ return this.set(type, toggleState(state)); ++ } ++ ++ default VisibilityState cycle(VisibilityType type) { ++ VisibilityState state = this.get(type); ++ return this.set(type, type.cycle(state)); ++ } ++ ++ default void toggleAll() { ++ VisibilityState state = this.currentState(); ++ VisibilityState newState = toggleState(state); ++ for (VisibilityType type : VisibilityTypes.types()) { ++ this.set(type, newState); ++ } ++ } ++ ++ VisibilityState get(VisibilityType type); ++ ++ VisibilityState set(VisibilityType type, VisibilityState state); ++ ++ VisibilityState currentState(); ++ ++ boolean playerModified(); ++ ++ static VisibilityState toggleState(VisibilityState state) { ++ return state != VisibilityState.OFF ? VisibilityState.OFF : VisibilityState.ON; ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityState.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityState.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d9cb2ea30f1cb1117437aa2c04a9471ece4be052 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityState.java +@@ -0,0 +1,5 @@ ++package me.samsuik.sakura.player.visibility; ++ ++public enum VisibilityState { ++ ON, MODIFIED, MINIMAL, OFF; ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityType.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6b6b482d1c34e2318e5ca3b7a68e628818f048f8 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityType.java +@@ -0,0 +1,35 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import com.google.common.collect.ImmutableList; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public record VisibilityType(String key, ImmutableList states) { ++ public VisibilityState getDefault() { ++ return this.states.getFirst(); ++ } ++ ++ public boolean isDefault(VisibilityState state) { ++ return state == this.getDefault(); ++ } ++ ++ public VisibilityState cycle(VisibilityState state) { ++ int index = this.states.indexOf(state); ++ int next = (index + 1) % this.states.size(); ++ return this.states.get(next); ++ } ++ ++ public static VisibilityType from(String key, boolean minimal) { ++ return new VisibilityType(key, states(minimal)); ++ } ++ ++ private static ImmutableList states(boolean minimal) { ++ ImmutableList.Builder listBuilder = ImmutableList.builder(); ++ listBuilder.add(VisibilityState.ON); ++ if (minimal) { ++ listBuilder.add(VisibilityState.MINIMAL); ++ } ++ listBuilder.add(VisibilityState.OFF); ++ return listBuilder.build(); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityTypes.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityTypes.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f323cfc2d7370ff8ea9020e79c5148a86e51e8c8 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityTypes.java +@@ -0,0 +1,31 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import com.google.common.collect.ImmutableList; ++import org.jspecify.annotations.NullMarked; ++ ++import java.util.ArrayList; ++import java.util.List; ++ ++@NullMarked ++public final class VisibilityTypes { ++ private static final List TYPES = new ArrayList<>(); ++ ++ public static final VisibilityType TNT = register(create("tnt", true)); ++ public static final VisibilityType SAND = register(create("sand", true)); ++ public static final VisibilityType EXPLOSIONS = register(create("explosions", true)); ++ public static final VisibilityType SPAWNERS = register(create("spawners", false)); ++ public static final VisibilityType PISTONS = register(create("pistons", false)); ++ ++ public static ImmutableList types() { ++ return ImmutableList.copyOf(TYPES); ++ } ++ ++ private static VisibilityType create(String key, boolean minimal) { ++ return VisibilityType.from(key, minimal); ++ } ++ ++ private static VisibilityType register(VisibilityType type) { ++ TYPES.add(type); ++ return type; ++ } ++} +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 95f0b3186e313c7fbd5c8531d52b82a69e525f94..0560784e8b71a927db6d8cdf9d36a23e3b56ef74 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -9,6 +9,8 @@ import java.util.Date; + import java.util.Map; + import java.util.UUID; + import java.util.concurrent.CompletableFuture; ++ ++import me.samsuik.sakura.player.visibility.VisibilitySettings; + import org.bukkit.BanEntry; + import org.bukkit.DyeColor; + import org.bukkit.Effect; +@@ -49,7 +51,6 @@ import org.bukkit.plugin.Plugin; + import org.bukkit.plugin.messaging.PluginMessageRecipient; + import org.bukkit.potion.PotionEffect; + import org.bukkit.potion.PotionEffectType; +-import org.bukkit.profile.PlayerProfile; + import org.bukkit.scoreboard.Scoreboard; + import org.jetbrains.annotations.ApiStatus; + import org.jetbrains.annotations.NotNull; +@@ -60,6 +61,15 @@ import org.jetbrains.annotations.Nullable; + */ + public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient { // Paper + ++ // Sakura start ++ /** ++ * Visibility API for FPS settings. ++ * ++ * @return visibility api ++ */ ++ VisibilitySettings getVisibility(); ++ // Sakura end ++ + // Paper start + @Override + default net.kyori.adventure.identity.@NotNull Identity identity() { diff --git a/patches/api/0002-Player-GUI-API.patch b/patches/api/0002-Player-GUI-API.patch deleted file mode 100644 index 203029a..0000000 --- a/patches/api/0002-Player-GUI-API.patch +++ /dev/null @@ -1,149 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Thu, 12 Jan 2023 22:59:28 +0000 -Subject: [PATCH] Player GUI API - - -diff --git a/src/main/java/me/samsuik/sakura/player/gui/ItemIcon.java b/src/main/java/me/samsuik/sakura/player/gui/ItemIcon.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cc0645aa27e6e96922f26242e71fef5d6c06fcfd ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/player/gui/ItemIcon.java -@@ -0,0 +1,9 @@ -+package me.samsuik.sakura.player.gui; -+ -+import org.bukkit.entity.Player; -+import org.bukkit.inventory.ItemStack; -+ -+import java.util.function.Consumer; -+import java.util.function.Function; -+ -+public record ItemIcon(Function itemUpdate, Consumer onClick, int slot) {} -diff --git a/src/main/java/me/samsuik/sakura/player/gui/PlayerGUI.java b/src/main/java/me/samsuik/sakura/player/gui/PlayerGUI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8eec1182c82ea9f1ca88998494280ad1d7b7d653 ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/player/gui/PlayerGUI.java -@@ -0,0 +1,122 @@ -+package me.samsuik.sakura.player.gui; -+ -+import org.bukkit.entity.Player; -+import org.bukkit.event.inventory.InventoryClickEvent; -+import org.bukkit.inventory.Inventory; -+import org.bukkit.inventory.InventoryHolder; -+import org.bukkit.inventory.ItemStack; -+import org.jetbrains.annotations.ApiStatus; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.ArrayList; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.function.BiFunction; -+import java.util.function.Consumer; -+ -+public abstract class PlayerGUI { -+ -+ private static final List REGISTERED_GUIS = new ArrayList<>(); -+ protected static final Consumer NOTHING = (p) -> {}; -+ -+ private final Map iconMap = new HashMap<>(); -+ private final Holder holder = new Holder(); -+ private final BiFunction base; -+ private boolean registered = false; -+ -+ public PlayerGUI(BiFunction base) { -+ this.base = base; -+ } -+ -+ protected void registerIcon(ItemIcon icon) { -+ iconMap.put(icon.slot(), icon); -+ } -+ -+ protected abstract void register(); -+ -+ protected void refresh(Player player) { -+ Inventory inventory = player.getOpenInventory().getTopInventory(); -+ -+ if (inventory.getHolder() instanceof Holder) { -+ iconMap.forEach((slot, icon) -> updateIcon(player, icon, inventory)); -+ } -+ } -+ -+ protected void refreshSlot(Player player, int slot) { -+ Inventory inventory = player.getOpenInventory().getTopInventory(); -+ -+ if (inventory.getHolder() instanceof Holder && iconMap.containsKey(slot)) { -+ updateIcon(player, iconMap.get(slot), inventory); -+ } -+ } -+ -+ public void unregister() { -+ REGISTERED_GUIS.remove(this); -+ registered = false; -+ } -+ -+ @ApiStatus.Internal -+ public static void onWindowClick(InventoryClickEvent event) { -+ for (PlayerGUI ui : REGISTERED_GUIS) { -+ Inventory inventory = event.getClickedInventory(); -+ -+ if (inventory != null && inventory.getHolder() == ui.holder) { -+ ui.handleSlotClick(event, inventory); -+ } -+ } -+ } -+ -+ private void handleSlotClick(InventoryClickEvent event, Inventory inventory) { -+ event.setCancelled(true); -+ -+ Player player = (Player) event.getWhoClicked(); -+ int slot = event.getSlot(); -+ ItemIcon icon = iconMap.get(slot); -+ -+ if (icon != null) { -+ icon.onClick() -+ .andThen(p -> updateIcon(player, icon, inventory)) -+ .accept(player); -+ } -+ } -+ -+ private void updateIcon(Player player, ItemIcon icon, Inventory inventory) { -+ ItemStack item = icon.itemUpdate().apply(player); -+ inventory.setItem(icon.slot(), item); -+ } -+ -+ public void showTo(Player player) { -+ player.openInventory(createInventory(player)); -+ } -+ -+ private Inventory createInventory(Player player) { -+ if (!registered) { -+ registerUI(); -+ } -+ -+ Inventory inventory = base.apply(player, holder); -+ -+ for (ItemIcon icon : iconMap.values()) { -+ updateIcon(player, icon, inventory); -+ } -+ -+ return inventory; -+ } -+ -+ private void registerUI() { -+ register(); -+ REGISTERED_GUIS.add(this); -+ registered = true; -+ } -+ -+ protected static class Holder implements InventoryHolder { -+ private Holder() {} -+ -+ @Override -+ public @NotNull Inventory getInventory() { -+ return null; -+ } -+ } -+ -+} diff --git a/patches/api/0004-Merge-Cannon-Entities.patch b/patches/api/0003-Merge-Cannon-Entities.patch similarity index 100% rename from patches/api/0004-Merge-Cannon-Entities.patch rename to patches/api/0003-Merge-Cannon-Entities.patch diff --git a/patches/api/0003-Visibility-API.patch b/patches/api/0003-Visibility-API.patch deleted file mode 100644 index 5b4b143..0000000 --- a/patches/api/0003-Visibility-API.patch +++ /dev/null @@ -1,230 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Tue, 21 Sep 2021 23:55:45 +0100 -Subject: [PATCH] Visibility API - - -diff --git a/src/main/java/me/samsuik/sakura/player/visibility/Visibility.java b/src/main/java/me/samsuik/sakura/player/visibility/Visibility.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3df11f07ce533b8b911ec423be850374fcc8a7c1 ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/player/visibility/Visibility.java -@@ -0,0 +1,198 @@ -+package me.samsuik.sakura.player.visibility; -+ -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextColor; -+import org.bukkit.Material; -+ -+import java.util.EnumSet; -+import java.util.Locale; -+ -+public class Visibility { -+ -+ private final EnumSet settings = EnumSet.noneOf(Setting.class); -+ -+ /** -+ * Checks if the provided setting has been modified. -+ * -+ * @param setting provided -+ * @return the setting has been modified -+ */ -+ public boolean isToggled(Setting setting) { -+ return settings.contains(setting); -+ } -+ -+ /** -+ * Checks if the provided setting is enabled. -+ * -+ * @param setting provided -+ * @return the setting is enabled -+ */ -+ public boolean isEnabled(Setting setting) { -+ return setting.defaultValue != isToggled(setting); -+ } -+ -+ /** -+ * Checks if the provided setting is disabled. -+ * -+ * @param setting provided -+ * @return the setting is disabled -+ */ -+ public boolean isDisabled(Setting setting) { -+ return !isEnabled(setting); -+ } -+ -+ /** -+ * Toggles the provided settings state. -+ * [ true -> false, false -> true ] -+ * NOTE: the default value can vary. -+ * -+ * @param setting provided -+ */ -+ public void toggle(Setting setting) { -+ if (!settings.remove(setting)) { -+ settings.add(setting); -+ } -+ } -+ -+ /** -+ * Toggles the provided settings state if it matches the modified state. -+ * -+ * @param setting provided -+ * @param modified fun -+ */ -+ public void toggle(Setting setting, boolean modified) { -+ if (isToggled(setting) == modified) { -+ toggle(setting); -+ } -+ } -+ -+ /** -+ * Sets the provided settings state. -+ * -+ * @param setting provided -+ * @param enable whether to enable this setting -+ */ -+ public void set(Setting setting, boolean enable) { -+ if (isEnabled(setting) && !enable || isDisabled(setting) && enable) { -+ toggle(setting); -+ } -+ } -+ -+ /** -+ * Toggles all settings based on the logic below. -+ * IF: settings are untouched or have been modified -+ * THEN: disable all settings -+ * IF: all settings are disabled -+ * THEN: enable all settings -+ */ -+ public void toggleAll() { -+ boolean toggled = true; // true == all toggled -+ -+ // If something is enabled set state to true -+ for (Setting setting : Setting.values()) { -+ toggled &= isToggled(setting); -+ } -+ -+ // apply the opposite of the state so that it toggles -+ for (Setting setting : Setting.values()) { -+ toggle(setting, toggled); -+ } -+ } -+ -+ /** -+ * Whether the settings have been modified. -+ * This is exposed to allow for quick return optimisations. -+ * -+ * @return settings have been modified -+ */ -+ public boolean isModified() { -+ return !settings.isEmpty(); -+ } -+ -+ /** -+ * Current visibility settings state. -+ * -+ * @return state -+ */ -+ public State getState() { -+ if (this.settings.isEmpty()) { -+ return State.ENABLED; -+ } else if (this.settings.size() != Setting.values().length) { -+ return State.MODIFIED; -+ } else { -+ return State.DISABLED; -+ } -+ } -+ -+ public enum Setting { -+ TNT_VISIBILITY("TNT Visibility", Material.TNT, NamedTextColor.RED, true), -+ SAND_VISIBILITY("Sand Visibility", Material.SAND, NamedTextColor.YELLOW, true), -+ MINIMAL("Minimal TNT/Sand", Material.NETHER_BRICK_SLAB, NamedTextColor.GOLD, false), -+ EXPLOSIONS("Explosion Particles", Material.COBWEB, NamedTextColor.WHITE, true), -+ ENCHANTMENT_GLINT("Enchantment Glint", Material.ENCHANTED_BOOK, NamedTextColor.DARK_PURPLE, true), -+ SPAWNERS("Spawner Visibility", Material.SPAWNER, NamedTextColor.DARK_GRAY, true), -+ REDSTONE("Redstone Animations", Material.REDSTONE, NamedTextColor.DARK_RED, true), -+ PISTONS("Piston Animations", Material.PISTON, NamedTextColor.GOLD, true); -+ -+ private final String friendlyName; -+ private final Material material; -+ private final TextColor colour; -+ private final boolean defaultValue; -+ -+ Setting(String friendlyName, Material material, TextColor color, boolean defaultValue) { -+ this.friendlyName = friendlyName; -+ this.material = material; -+ this.colour = color; -+ this.defaultValue = defaultValue; -+ } -+ -+ public boolean getDefault() { -+ return this.defaultValue; -+ } -+ -+ public String friendlyName() { -+ return this.friendlyName; -+ } -+ -+ public Material material() { -+ return this.material; -+ } -+ -+ public TextColor colour() { -+ return this.colour; -+ } -+ -+ public String basicName() { -+ return this.name().replace("_", "").toLowerCase(Locale.ROOT); -+ } -+ } -+ -+ public enum State { -+ ENABLED("Enabled", Material.GREEN_STAINED_GLASS_PANE, NamedTextColor.GREEN), -+ MODIFIED("Modified", Material.MAGENTA_STAINED_GLASS_PANE, NamedTextColor.LIGHT_PURPLE), -+ DISABLED("Disabled", Material.RED_STAINED_GLASS_PANE, NamedTextColor.RED); -+ -+ private final String title; -+ private final Material material; -+ private final TextColor colour; -+ -+ State(String name, Material material, TextColor colour) { -+ this.title = name; -+ this.material = material; -+ this.colour = colour; -+ } -+ -+ public String title() { -+ return this.title; -+ } -+ -+ public Material material() { -+ return material; -+ } -+ -+ public TextColor colour() { -+ return colour; -+ } -+ } -+ -+} -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 95f0b3186e313c7fbd5c8531d52b82a69e525f94..a1d6e58d466b94d65c830a7e50b9a5eae76138aa 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -60,6 +60,15 @@ import org.jetbrains.annotations.Nullable; - */ - public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient { // Paper - -+ // Sakura start -+ /** -+ * Visibility API for FPS settings. -+ * -+ * @return visibility api -+ */ -+ me.samsuik.sakura.player.visibility.Visibility getVisibility(); -+ // Sakura end -+ - // Paper start - @Override - default net.kyori.adventure.identity.@NotNull Identity identity() { diff --git a/patches/api/0005-isPushedByFluid-API.patch b/patches/api/0004-isPushedByFluid-API.patch similarity index 91% rename from patches/api/0005-isPushedByFluid-API.patch rename to patches/api/0004-isPushedByFluid-API.patch index 3e18824..60e5d0c 100644 --- a/patches/api/0005-isPushedByFluid-API.patch +++ b/patches/api/0004-isPushedByFluid-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] isPushedByFluid API diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index 1d0fd7ff8449f815a7d980af0b378181ea8bf8d8..e0ef6e5a4664b69a5797afeafac2c6436137cab3 100644 +index d0ae8a94db20281d3664d74718c65234eb2e5f83..cc4c1a60585f923dbe48c5432a425dec99ae22ff 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java @@ -111,6 +111,22 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent diff --git a/patches/api/0006-Falling-Block-Parity-API.patch b/patches/api/0005-Falling-Block-Parity-API.patch similarity index 100% rename from patches/api/0006-Falling-Block-Parity-API.patch rename to patches/api/0005-Falling-Block-Parity-API.patch diff --git a/patches/api/0007-Local-Value-Storage-API.patch b/patches/api/0006-Local-Value-Storage-API.patch similarity index 100% rename from patches/api/0007-Local-Value-Storage-API.patch rename to patches/api/0006-Local-Value-Storage-API.patch diff --git a/patches/api/0008-Add-physics-version-API.patch b/patches/api/0007-Add-physics-version-API.patch similarity index 100% rename from patches/api/0008-Add-physics-version-API.patch rename to patches/api/0007-Add-physics-version-API.patch diff --git a/patches/api/0009-Add-durable-material-API.patch b/patches/api/0008-Add-durable-material-API.patch similarity index 100% rename from patches/api/0009-Add-durable-material-API.patch rename to patches/api/0008-Add-durable-material-API.patch diff --git a/patches/api/0010-Add-redstone-implementation-API.patch b/patches/api/0009-Add-redstone-implementation-API.patch similarity index 100% rename from patches/api/0010-Add-redstone-implementation-API.patch rename to patches/api/0009-Add-redstone-implementation-API.patch diff --git a/patches/api/0011-Add-consistent-explosion-radius-api.patch b/patches/api/0010-Add-consistent-explosion-radius-api.patch similarity index 90% rename from patches/api/0011-Add-consistent-explosion-radius-api.patch rename to patches/api/0010-Add-consistent-explosion-radius-api.patch index 57716ff..f823928 100644 --- a/patches/api/0011-Add-consistent-explosion-radius-api.patch +++ b/patches/api/0010-Add-consistent-explosion-radius-api.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add consistent explosion radius api diff --git a/src/main/java/me/samsuik/sakura/local/LocalValueKey.java b/src/main/java/me/samsuik/sakura/local/LocalValueKey.java -index 098ce4d0356fb1dddbd4e34c02f77848da1fb546..34434bf1fa8018bd79a0af8ae0f8e7ca24da6ec6 100644 +index 346f6392c82ff27ec4439c645948337ce1832ba3..07807b9a3c7c1ccd2074333eda82fcdd850ce655 100644 --- a/src/main/java/me/samsuik/sakura/local/LocalValueKey.java +++ b/src/main/java/me/samsuik/sakura/local/LocalValueKey.java @@ -25,6 +25,10 @@ public record LocalValueKey(NamespacedKey key, Supplier defaultSupplier) { diff --git a/patches/api/0012-Add-redstone-cache-api.patch b/patches/api/0011-Add-redstone-cache-api.patch similarity index 90% rename from patches/api/0012-Add-redstone-cache-api.patch rename to patches/api/0011-Add-redstone-cache-api.patch index 5ffecb9..276b002 100644 --- a/patches/api/0012-Add-redstone-cache-api.patch +++ b/patches/api/0011-Add-redstone-cache-api.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add redstone cache api diff --git a/src/main/java/me/samsuik/sakura/local/LocalValueKey.java b/src/main/java/me/samsuik/sakura/local/LocalValueKey.java -index 34434bf1fa8018bd79a0af8ae0f8e7ca24da6ec6..780a02fb451d5b4b98f6895b488b90c75a6507a8 100644 +index 07807b9a3c7c1ccd2074333eda82fcdd850ce655..741e5466f00b34832ac69ff01503146278e952a9 100644 --- a/src/main/java/me/samsuik/sakura/local/LocalValueKey.java +++ b/src/main/java/me/samsuik/sakura/local/LocalValueKey.java @@ -29,6 +29,10 @@ public record LocalValueKey(NamespacedKey key, Supplier defaultSupplier) { diff --git a/patches/api/0013-Entity-tracking-range-modifier.patch b/patches/api/0012-Entity-tracking-range-modifier.patch similarity index 76% rename from patches/api/0013-Entity-tracking-range-modifier.patch rename to patches/api/0012-Entity-tracking-range-modifier.patch index 77b7560..b35a45c 100644 --- a/patches/api/0013-Entity-tracking-range-modifier.patch +++ b/patches/api/0012-Entity-tracking-range-modifier.patch @@ -5,11 +5,11 @@ Subject: [PATCH] Entity tracking range modifier diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index a1d6e58d466b94d65c830a7e50b9a5eae76138aa..a4a497d20b17fa89106681c00918b08ebca7e87f 100644 +index 0560784e8b71a927db6d8cdf9d36a23e3b56ef74..7cf2361157ac64c1bb959a0c911e27505acf25d0 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -69,6 +69,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - me.samsuik.sakura.player.visibility.Visibility getVisibility(); +@@ -70,6 +70,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + VisibilitySettings getVisibility(); // Sakura end + // Sakura start - entity tracking range modifier diff --git a/patches/server/0005-Client-Visibility-Settings.patch b/patches/server/0005-Client-Visibility-Settings.patch new file mode 100644 index 0000000..1ca1a25 --- /dev/null +++ b/patches/server/0005-Client-Visibility-Settings.patch @@ -0,0 +1,974 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Tue, 21 Sep 2021 23:54:25 +0100 +Subject: [PATCH] Client Visibility Settings + + +diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java +index 3e85c19db0655034c203eaab8d2e6b5504da5da8..cbb2e57f9ab3b48a6e5f792711c4c6fd2d34d445 100644 +--- a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java ++++ b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java +@@ -1,6 +1,9 @@ + package me.samsuik.sakura.command; + + import me.samsuik.sakura.command.subcommands.ConfigCommand; ++import me.samsuik.sakura.command.subcommands.FPSCommand; ++import me.samsuik.sakura.command.subcommands.VisualCommand; ++import me.samsuik.sakura.player.visibility.VisibilityTypes; + import net.minecraft.server.MinecraftServer; + import org.bukkit.command.Command; + +@@ -12,6 +15,9 @@ public final class SakuraCommands { + static { + COMMANDS.put("sakura", new SakuraCommand("sakura")); + COMMANDS.put("config", new ConfigCommand("config")); ++ COMMANDS.put("fps", new FPSCommand("fps")); ++ COMMANDS.put("tntvisibility", new VisualCommand(VisibilityTypes.TNT, "tnttoggle")); ++ COMMANDS.put("sandvisibility", new VisualCommand(VisibilityTypes.SAND, "sandtoggle")); + } + + public static void registerCommands(MinecraftServer server) { +diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a85f84ebfef4ed3f3dc2a337a057d541f64c9d0e +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java +@@ -0,0 +1,24 @@ ++package me.samsuik.sakura.command.subcommands; ++ ++import me.samsuik.sakura.command.BaseSubCommand; ++import me.samsuik.sakura.player.visibility.VisibilityGui; ++import org.bukkit.command.CommandSender; ++import org.bukkit.entity.Player; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public final class FPSCommand extends BaseSubCommand { ++ private final VisibilityGui visibilityGui = new VisibilityGui(); ++ ++ public FPSCommand(String name) { ++ super(name); ++ } ++ ++ @Override ++ public void execute(CommandSender sender, String[] args) { ++ if (sender instanceof Player player) { ++ this.visibilityGui.showTo(player); ++ } ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cd9ebbb7bbb90c87faf75fcc4b487e93573f1387 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java +@@ -0,0 +1,41 @@ ++package me.samsuik.sakura.command.subcommands; ++ ++import me.samsuik.sakura.command.BaseSubCommand; ++import me.samsuik.sakura.configuration.GlobalConfiguration; ++import me.samsuik.sakura.player.visibility.VisibilitySettings; ++import me.samsuik.sakura.player.visibility.VisibilityState; ++import me.samsuik.sakura.player.visibility.VisibilityType; ++import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; ++import org.bukkit.command.CommandSender; ++import org.bukkit.entity.Player; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import java.util.Arrays; ++ ++@DefaultQualifier(NonNull.class) ++public final class VisualCommand extends BaseSubCommand { ++ private final VisibilityType type; ++ ++ public VisualCommand(VisibilityType type, String... aliases) { ++ super(type.key() + "visibility"); ++ this.setAliases(Arrays.asList(aliases)); ++ this.type = type; ++ } ++ ++ @Override ++ public void execute(CommandSender sender, String[] args) { ++ if (!(sender instanceof Player player)) { ++ return; ++ } ++ ++ VisibilitySettings settings = player.getVisibility(); ++ VisibilityState state = settings.toggle(type); ++ ++ String stateName = (state == VisibilityState.ON) ? "Enabled" : "Disabled"; ++ player.sendRichMessage(GlobalConfiguration.get().messages.fpsSettingChange, ++ Placeholder.unparsed("name", this.type.key()), ++ Placeholder.unparsed("state", stateName) ++ ); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java b/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java +index 1b49023726fc80231ba952c555923228130a2bdb..d1cfd66e8eea7a181d23d931af2402ce88ff1641 100644 +--- a/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java ++++ b/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java +@@ -9,6 +9,8 @@ import org.slf4j.Logger; + import org.spongepowered.configurate.objectmapping.meta.Comment; + import org.spongepowered.configurate.objectmapping.meta.Setting; + ++import java.util.Locale; ++ + @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic", "RedundantSuppression"}) + public final class GlobalConfiguration extends ConfigurationPart { + +@@ -35,7 +37,7 @@ public final class GlobalConfiguration extends ConfigurationPart { + + public Fps fps; + public class Fps extends ConfigurationPart { +- public Material material = Material.PINK_STAINED_GLASS_PANE; ++ public String guiColour = "pink"; + } + + public Players players; +diff --git a/src/main/java/me/samsuik/sakura/player/gui/FeatureGui.java b/src/main/java/me/samsuik/sakura/player/gui/FeatureGui.java +new file mode 100644 +index 0000000000000000000000000000000000000000..49f53c01d61cbe551aa68a91f53782e8f01d9c67 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/FeatureGui.java +@@ -0,0 +1,44 @@ ++package me.samsuik.sakura.player.gui; ++ ++import me.samsuik.sakura.player.gui.components.GuiComponent; ++import net.kyori.adventure.text.Component; ++import org.bukkit.entity.Player; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.inventory.Inventory; ++import org.jetbrains.annotations.ApiStatus; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public abstract class FeatureGui { ++ private final int size; ++ private final Component title; ++ ++ public FeatureGui(int size, Component title) { ++ this.size = size; ++ this.title = title; ++ } ++ ++ protected abstract void fillInventory(Inventory inventory); ++ ++ protected abstract void afterFill(Player player, FeatureGuiInventory inventory); ++ ++ public final void showTo(Player bukkitPlayer) { ++ FeatureGuiInventory featureInventory = new FeatureGuiInventory(this, this.size, this.title); ++ this.fillInventory(featureInventory.getInventory()); ++ this.afterFill(bukkitPlayer, featureInventory); ++ bukkitPlayer.openInventory(featureInventory.getInventory()); ++ } ++ ++ @ApiStatus.Internal ++ public static void clickEvent(InventoryClickEvent event) { ++ Inventory clicked = event.getClickedInventory(); ++ if (clicked != null && clicked.getHolder(false) instanceof FeatureGuiInventory featureInventory) { ++ event.setCancelled(true); ++ for (GuiComponent component : featureInventory.getComponents().reversed()) { ++ if (component.interaction(event, featureInventory)) { ++ break; ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/FeatureGuiInventory.java b/src/main/java/me/samsuik/sakura/player/gui/FeatureGuiInventory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6c86501b8a392ed04c46646bfac0cad9b2ca33bc +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/FeatureGuiInventory.java +@@ -0,0 +1,90 @@ ++package me.samsuik.sakura.player.gui; ++ ++import com.google.common.base.Preconditions; ++import com.google.common.collect.HashMultimap; ++import com.google.common.collect.ImmutableList; ++import com.google.common.collect.Multimap; ++import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; ++import it.unimi.dsi.fastutil.objects.Object2ObjectMap; ++import me.samsuik.sakura.player.gui.components.GuiComponent; ++import net.kyori.adventure.text.Component; ++import org.bukkit.Bukkit; ++import org.bukkit.NamespacedKey; ++import org.bukkit.inventory.Inventory; ++import org.bukkit.inventory.InventoryHolder; ++import org.jspecify.annotations.NullMarked; ++ ++import java.util.Collection; ++import java.util.Optional; ++ ++@NullMarked ++public final class FeatureGuiInventory implements InventoryHolder { ++ private final Inventory inventory; ++ private final FeatureGui gui; ++ private final Multimap componentsUnderKey = HashMultimap.create(); ++ private final Object2ObjectMap componentKeys = new Object2ObjectLinkedOpenHashMap<>(); ++ ++ public FeatureGuiInventory(FeatureGui gui, int size, Component component) { ++ this.inventory = Bukkit.createInventory(this, size, component); ++ this.gui = gui; ++ } ++ ++ @Override ++ public Inventory getInventory() { ++ return this.inventory; ++ } ++ ++ public FeatureGui getGui() { ++ return this.gui; ++ } ++ ++ public ImmutableList getComponents() { ++ return ImmutableList.copyOf(this.componentKeys.keySet()); ++ } ++ ++ public ImmutableList findComponents(NamespacedKey key) { ++ return ImmutableList.copyOf(this.componentsUnderKey.get(key)); ++ } ++ ++ public Optional findFirst(NamespacedKey key) { ++ Collection components = this.componentsUnderKey.get(key); ++ return components.stream().findFirst(); ++ } ++ ++ public void removeComponents(NamespacedKey key) { ++ Collection removed = this.componentsUnderKey.removeAll(key); ++ for (GuiComponent component : removed) { ++ this.componentKeys.remove(component); ++ } ++ } ++ ++ public void addComponent(GuiComponent component, NamespacedKey key) { ++ Preconditions.checkArgument(!this.componentKeys.containsKey(component), "component has already been added"); ++ this.componentKeys.put(component, key); ++ this.componentsUnderKey.put(key, component); ++ this.inventoryUpdate(component); ++ } ++ ++ public void removeComponent(GuiComponent component) { ++ NamespacedKey key = this.componentKeys.remove(component); ++ this.componentsUnderKey.remove(key, component); ++ } ++ ++ public void replaceComponent(GuiComponent component, GuiComponent replacement) { ++ NamespacedKey key = this.componentKeys.remove(component); ++ Preconditions.checkNotNull(key, "component does not exist"); ++ this.componentKeys.put(replacement, key); ++ this.componentsUnderKey.remove(key, component); ++ this.componentsUnderKey.put(key, replacement); ++ this.inventoryUpdate(replacement); ++ } ++ ++ public void removeAllComponents() { ++ this.componentKeys.clear(); ++ this.componentsUnderKey.clear(); ++ } ++ ++ private void inventoryUpdate(GuiComponent component) { ++ component.creation(this.inventory); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/ItemStackUtil.java b/src/main/java/me/samsuik/sakura/player/gui/ItemStackUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..eddf7632f194142c48f4012734128a10e422eea6 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/ItemStackUtil.java +@@ -0,0 +1,19 @@ ++package me.samsuik.sakura.player.gui; ++ ++import net.kyori.adventure.text.Component; ++import org.bukkit.Material; ++import org.bukkit.inventory.ItemStack; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public class ItemStackUtil { ++ public static ItemStack itemWithBlankName(Material material) { ++ return itemWithName(material, Component.empty()); ++ } ++ ++ public static ItemStack itemWithName(Material material, Component component) { ++ ItemStack item = new ItemStack(material); ++ item.editMeta(m -> m.itemName(component)); ++ return item; ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/components/GuiClickEvent.java b/src/main/java/me/samsuik/sakura/player/gui/components/GuiClickEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..39065d5003bfa60e7453079eb3202bea1b605d4a +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/components/GuiClickEvent.java +@@ -0,0 +1,10 @@ ++package me.samsuik.sakura.player.gui.components; ++ ++import me.samsuik.sakura.player.gui.FeatureGuiInventory; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public interface GuiClickEvent { ++ void doSomething(InventoryClickEvent event, FeatureGuiInventory inventory); ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/components/GuiComponent.java b/src/main/java/me/samsuik/sakura/player/gui/components/GuiComponent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5a2ff0ca3462254bc4e01192b48c321c6f34e909 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/components/GuiComponent.java +@@ -0,0 +1,13 @@ ++package me.samsuik.sakura.player.gui.components; ++ ++import me.samsuik.sakura.player.gui.FeatureGuiInventory; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.inventory.Inventory; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public interface GuiComponent { ++ boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory); ++ ++ void creation(Inventory inventory); ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/components/ItemButton.java b/src/main/java/me/samsuik/sakura/player/gui/components/ItemButton.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6d49d3390b2e01fcffd638abdde569d1788f3256 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/components/ItemButton.java +@@ -0,0 +1,34 @@ ++package me.samsuik.sakura.player.gui.components; ++ ++import me.samsuik.sakura.player.gui.FeatureGuiInventory; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.inventory.Inventory; ++import org.bukkit.inventory.ItemStack; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public final class ItemButton implements GuiComponent { ++ private final ItemStack bukkitItem; ++ private final int slot; ++ private final GuiClickEvent whenClicked; ++ ++ public ItemButton(ItemStack bukkitItem, int slot, GuiClickEvent whenClicked) { ++ this.bukkitItem = bukkitItem; ++ this.slot = slot; ++ this.whenClicked = whenClicked; ++ } ++ ++ @Override ++ public boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory) { ++ if (event.getSlot() == this.slot) { ++ this.whenClicked.doSomething(event, featureInventory); ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public void creation(Inventory inventory) { ++ inventory.setItem(this.slot, this.bukkitItem); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/gui/components/ItemSwitch.java b/src/main/java/me/samsuik/sakura/player/gui/components/ItemSwitch.java +new file mode 100644 +index 0000000000000000000000000000000000000000..243f1cbabf7054d7d5092f21e923e4dea5085123 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/gui/components/ItemSwitch.java +@@ -0,0 +1,44 @@ ++package me.samsuik.sakura.player.gui.components; ++ ++import com.google.common.base.Preconditions; ++import me.samsuik.sakura.player.gui.FeatureGuiInventory; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.inventory.Inventory; ++import org.bukkit.inventory.ItemStack; ++import org.jspecify.annotations.NullMarked; ++ ++import java.util.Collections; ++import java.util.List; ++ ++@NullMarked ++public final class ItemSwitch implements GuiComponent { ++ private final List items; ++ private final int slot; ++ private final int selected; ++ private final GuiClickEvent whenClicked; ++ ++ public ItemSwitch(List items, int slot, int selected, GuiClickEvent whenClicked) { ++ Preconditions.checkArgument(!items.isEmpty()); ++ this.items = Collections.unmodifiableList(items); ++ this.slot = slot; ++ this.selected = selected; ++ this.whenClicked = whenClicked; ++ } ++ ++ @Override ++ public boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory) { ++ if (this.slot == event.getSlot()) { ++ int next = (this.selected + 1) % this.items.size(); ++ ItemSwitch itemSwitch = new ItemSwitch(this.items, this.slot, next, this.whenClicked); ++ featureInventory.replaceComponent(this, itemSwitch); ++ this.whenClicked.doSomething(event, featureInventory); ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public void creation(Inventory inventory) { ++ inventory.setItem(this.slot, this.items.get(this.selected)); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/PlayerVisibilitySettings.java b/src/main/java/me/samsuik/sakura/player/visibility/PlayerVisibilitySettings.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bf765e17032d8d1d203e07350dcfed91791bc9bb +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/PlayerVisibilitySettings.java +@@ -0,0 +1,66 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; ++import net.minecraft.nbt.CompoundTag; ++import org.jetbrains.annotations.NotNull; ++import org.jspecify.annotations.NonNull; ++ ++public final class PlayerVisibilitySettings implements VisibilitySettings { ++ private static final String SETTINGS_COMPOUND_TAG = "clientVisibilitySettings"; ++ private final Reference2ObjectMap visibilityStates = new Reference2ObjectOpenHashMap<>(); ++ ++ @Override ++ public @NonNull VisibilityState get(@NonNull VisibilityType type) { ++ VisibilityState state = this.visibilityStates.get(type); ++ return state != null ? state : type.getDefault(); ++ } ++ ++ @Override ++ public @NotNull VisibilityState set(@NonNull VisibilityType type, @NonNull VisibilityState state) { ++ if (type.isDefault(state)) { ++ this.visibilityStates.remove(type); ++ } else { ++ this.visibilityStates.put(type, state); ++ } ++ return state; ++ } ++ ++ @Override ++ public @NonNull VisibilityState currentState() { ++ int modifiedCount = this.visibilityStates.size(); ++ if (modifiedCount == 0) { ++ return VisibilityState.ON; ++ } else if (modifiedCount != VisibilityTypes.types().size()) { ++ return VisibilityState.MODIFIED; ++ } else { ++ return VisibilityState.OFF; ++ } ++ } ++ ++ @Override ++ public boolean playerModified() { ++ return !this.visibilityStates.isEmpty(); ++ } ++ ++ public void loadData(@NonNull CompoundTag tag) { ++ if (!tag.contains(SETTINGS_COMPOUND_TAG, CompoundTag.TAG_COMPOUND)) { ++ return; ++ } ++ ++ CompoundTag settingsTag = tag.getCompound(SETTINGS_COMPOUND_TAG); ++ for (VisibilityType type : VisibilityTypes.types()) { ++ if (settingsTag.contains(type.key(), CompoundTag.TAG_STRING)) { ++ VisibilityState state = VisibilityState.valueOf(settingsTag.getString(type.key())); ++ this.visibilityStates.put(type, state); ++ break; ++ } ++ } ++ } ++ ++ public void saveData(@NonNull CompoundTag tag) { ++ CompoundTag settingsTag = new CompoundTag(); ++ this.visibilityStates.forEach((t, s) -> settingsTag.putString(t.key(), s.name())); ++ tag.put(SETTINGS_COMPOUND_TAG, settingsTag); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGui.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGui.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3777b8077794c2e780aa31dc86cded539a375750 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGui.java +@@ -0,0 +1,106 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; ++import me.samsuik.sakura.configuration.GlobalConfiguration; ++import me.samsuik.sakura.player.gui.FeatureGui; ++import me.samsuik.sakura.player.gui.FeatureGuiInventory; ++import me.samsuik.sakura.player.gui.components.ItemButton; ++import me.samsuik.sakura.player.gui.components.ItemSwitch; ++import net.kyori.adventure.text.Component; ++import org.bukkit.Material; ++import org.bukkit.NamespacedKey; ++import org.bukkit.entity.Player; ++import org.bukkit.inventory.Inventory; ++import org.jspecify.annotations.NullMarked; ++ ++import java.util.Locale; ++import java.util.Objects; ++ ++import static me.samsuik.sakura.player.gui.ItemStackUtil.itemWithBlankName; ++ ++@NullMarked ++public final class VisibilityGui extends FeatureGui { ++ private static final NamespacedKey TOGGLE_BUTTON_KEY = new NamespacedKey("sakura", "toggle_button"); ++ private static final NamespacedKey MENU_ITEMS_KEY = new NamespacedKey("sakura", "menu_items"); ++ private static final Material FALLBACK_MATERIAL = Material.PINK_STAINED_GLASS_PANE; ++ ++ public VisibilityGui() { ++ super(45, Component.text("FPS Settings")); ++ } ++ ++ @Override ++ protected void fillInventory(Inventory inventory) { ++ for (int slot = 0; slot < inventory.getSize(); ++slot) { ++ // x, y from top left of the inventory ++ int x = slot % 9; ++ int y = slot / 9; ++ // from center ++ int rx = x - 4; ++ int ry = y - 2; ++ double d = Math.sqrt(rx * rx + ry * ry); ++ if (d <= 3.25) { ++ inventory.setItem(slot, itemWithBlankName(getColouredGlassPane())); ++ } else if (x % 8 == 0) { ++ inventory.setItem(slot, itemWithBlankName(Material.BLACK_STAINED_GLASS_PANE)); ++ } else { ++ inventory.setItem(slot, itemWithBlankName(Material.WHITE_STAINED_GLASS_PANE)); ++ } ++ } ++ } ++ ++ @Override ++ protected void afterFill(Player player, FeatureGuiInventory inventory) { ++ VisibilitySettings settings = player.getVisibility(); ++ IntArrayFIFOQueue slots = this.availableSlots(); ++ this.updateToggleButton(settings, player, inventory); ++ for (VisibilityType type : VisibilityTypes.types()) { ++ VisibilityState state = settings.get(type); ++ int index = type.states().indexOf(state); ++ int slot = slots.dequeueInt(); ++ ++ ItemSwitch itemSwitch = new ItemSwitch( ++ VisibilityGuiItems.GUI_ITEMS.get(type), ++ slot, index, ++ (e, inv) -> { ++ settings.cycle(type); ++ this.updateToggleButton(settings, player, inv); ++ } ++ ); ++ ++ inventory.addComponent(itemSwitch, MENU_ITEMS_KEY); ++ } ++ } ++ ++ private void updateToggleButton(VisibilitySettings settings, Player player, FeatureGuiInventory inventory) { ++ inventory.removeComponents(TOGGLE_BUTTON_KEY); ++ VisibilityState settingsState = settings.currentState(); ++ ItemButton button = new ItemButton( ++ VisibilityGuiItems.TOGGLE_BUTTON_ITEMS.get(settingsState), ++ (2 * 9) + 8, ++ (e, inv) -> { ++ settings.toggleAll(); ++ inventory.removeAllComponents(); ++ this.afterFill(player, inv); ++ } ++ ); ++ inventory.addComponent(button, TOGGLE_BUTTON_KEY); ++ } ++ ++ private IntArrayFIFOQueue availableSlots() { ++ IntArrayFIFOQueue slots = new IntArrayFIFOQueue(); ++ for (int row = 1; row < 4; ++row) { ++ for (int column = 3; column < 6; ++column) { ++ if ((column + row) % 2 == 0) { ++ slots.enqueue((row * 9) + column); ++ } ++ } ++ } ++ return slots; ++ } ++ ++ private static Material getColouredGlassPane() { ++ String upperCase = GlobalConfiguration.get().fps.guiColour.toUpperCase(Locale.ENGLISH); ++ Material foundMaterial = Material.matchMaterial(upperCase + "_STAINED_GLASS_PANE"); ++ return Objects.requireNonNullElse(foundMaterial, FALLBACK_MATERIAL); ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGuiItems.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGuiItems.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ca26410b394602f3aa0e50efc8109ad9c6ec0611 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGuiItems.java +@@ -0,0 +1,55 @@ ++package me.samsuik.sakura.player.visibility; ++ ++import com.google.common.collect.ImmutableList; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; ++import me.samsuik.sakura.player.gui.ItemStackUtil; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import org.apache.commons.lang.StringUtils; ++import org.bukkit.Material; ++import org.bukkit.inventory.ItemStack; ++ ++import java.util.Locale; ++ ++public final class VisibilityGuiItems { ++ static final Reference2ObjectMap> GUI_ITEMS = new Reference2ObjectOpenHashMap<>(); ++ static final Reference2ObjectMap TOGGLE_BUTTON_ITEMS = new Reference2ObjectOpenHashMap<>(); ++ ++ static { ++ Reference2ObjectMap items = new Reference2ObjectOpenHashMap<>(); ++ ++ items.put(VisibilityTypes.TNT, ItemStackUtil.itemWithName(Material.TNT, Component.text("Tnt", NamedTextColor.RED))); ++ items.put(VisibilityTypes.SAND, ItemStackUtil.itemWithName(Material.SAND, Component.text("Sand", NamedTextColor.YELLOW))); ++ items.put(VisibilityTypes.EXPLOSIONS, ItemStackUtil.itemWithName(Material.COBWEB, Component.text("Explosions", NamedTextColor.WHITE))); ++ items.put(VisibilityTypes.SPAWNERS, ItemStackUtil.itemWithName(Material.SPAWNER, Component.text("Spawners", NamedTextColor.DARK_GRAY))); ++ items.put(VisibilityTypes.PISTONS, ItemStackUtil.itemWithName(Material.PISTON, Component.text("Pistons", NamedTextColor.GOLD))); ++ ++ for (VisibilityType type : VisibilityTypes.types()) { ++ ItemStack item = items.get(type); ++ ++ ImmutableList stateItems = type.states().stream() ++ .map(s -> createItemForState(item, s)) ++ .collect(ImmutableList.toImmutableList()); ++ ++ GUI_ITEMS.put(type, stateItems); ++ } ++ ++ TOGGLE_BUTTON_ITEMS.put(VisibilityState.ON, ItemStackUtil.itemWithName(Material.GREEN_STAINED_GLASS_PANE, Component.text("Enabled", NamedTextColor.GREEN))); ++ TOGGLE_BUTTON_ITEMS.put(VisibilityState.MODIFIED, ItemStackUtil.itemWithName(Material.MAGENTA_STAINED_GLASS_PANE, Component.text("Modified", NamedTextColor.LIGHT_PURPLE))); ++ TOGGLE_BUTTON_ITEMS.put(VisibilityState.OFF, ItemStackUtil.itemWithName(Material.RED_STAINED_GLASS_PANE, Component.text("Disabled", NamedTextColor.RED))); ++ } ++ ++ private static String lowercaseThenCapitalise(String name) { ++ String lowercaseName = name.toLowerCase(Locale.ENGLISH); ++ return StringUtils.capitalize(lowercaseName); ++ } ++ ++ private static ItemStack createItemForState(ItemStack in, VisibilityState state) { ++ String capitalisedName = lowercaseThenCapitalise(state.name()); ++ Component component = Component.text(" | " + capitalisedName, NamedTextColor.GRAY); ++ ItemStack itemCopy = in.clone(); ++ itemCopy.editMeta(m -> m.itemName(m.itemName().append(component))); ++ return itemCopy; ++ } ++} +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +index 1a37654aff9a9c86c9f7af10a1cf721371f0c5ec..82644b34a77dc5e5af38260b7b07b3ec9aceae33 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +@@ -19,7 +19,7 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet public + + public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section) { + this.sectionPos = sectionPos; +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 281a821c6a53013f8fe798781755ab7be2a359bd..9f34a0c6f4f8157d77ed3cb36a546ec8c1b9b1bd 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1887,6 +1887,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { + super(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); +@@ -222,6 +223,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.poiManager = new PoiManager(new RegionStorageInfo(session.getLevelId(), world.dimension(), "poi"), path.resolve("poi"), dataFixer, dsync, iregistrycustom, world.getServer(), world); + this.setServerViewDistance(viewDistance); + this.worldGenContext = new WorldGenContext(world, chunkGenerator, structureTemplateManager, this.lightEngine, null, this::setChunkUnsaved); // Paper - rewrite chunk system ++ // Sakura start - client visibility settings; minimal tnt/sand ++ this.minimalEntities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); ++ this.minimalEntities.defaultReturnValue(Integer.MIN_VALUE); ++ // Sakura end - client visibility settings; minimal tnt/sand + } + + private void setChunkUnsaved(ChunkPos pos) { +@@ -979,6 +984,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + tracker.serverEntity.sendChanges(); + } + } ++ ++ this.minimalEntities.clear(); // Sakura - client visibility settings; minimal tnt/sand + } + // Paper end - optimise entity tracker + +@@ -1216,6 +1223,32 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + return !this.seenBy.isEmpty(); + } + // Paper end - optimise entity tracker ++ // Sakura start - client visibility settings; entity visibility ++ private boolean checkEntityVisibility(final ServerPlayer player) { ++ final Entity entity = this.entity; ++ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = player.visibilitySettings; ++ if (!settings.playerModified() || !(entity.isPrimedTNT || entity.isFallingBlock)) { ++ return true; ++ } ++ final me.samsuik.sakura.player.visibility.VisibilityType type; ++ if (entity.isPrimedTNT) { ++ type = me.samsuik.sakura.player.visibility.VisibilityTypes.TNT; ++ } else { ++ type = me.samsuik.sakura.player.visibility.VisibilityTypes.SAND; ++ } ++ final me.samsuik.sakura.player.visibility.VisibilityState state = settings.get(type); ++ if (state == me.samsuik.sakura.player.visibility.VisibilityState.MINIMAL) { ++ final long key = entity.blockPosition().asLong() ^ entity.getType().hashCode(); ++ final long visibleEntity = ChunkMap.this.minimalEntities.get(key); ++ if (visibleEntity != Integer.MIN_VALUE) { ++ return entity.getId() == visibleEntity; ++ } else { ++ ChunkMap.this.minimalEntities.put(key, entity.getId()); ++ } ++ } ++ return state != me.samsuik.sakura.player.visibility.VisibilityState.OFF; ++ } ++ // Sakura end - client visibility settings; entity visibility + + public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit +@@ -1293,6 +1326,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); + // Paper end - Configurable entity tracking range by Y ++ flag = flag && this.checkEntityVisibility(player); // Sakura start - client visibility settings; entity visibility + + // CraftBukkit start - respect vanish API + if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 32742470eea2ec86c0fff02aa157bfcc89530ad3..8042d55095ea16cd370ead0f5de2fa2f65510b6c 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -587,6 +587,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.lagCompensationTick = (System.nanoTime() - net.minecraft.server.MinecraftServer.SERVER_INIT) / (java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(50L)); + } + // Paper end - lag compensation ++ // Sakura start - client visibility settings ++ public final LongSet explosionPositions = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(); ++ ++ public final boolean checkExplosionVisibility(final Vec3 position, final ServerPlayer player) { ++ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = player.visibilitySettings; ++ if (settings.isDisabled(me.samsuik.sakura.player.visibility.VisibilityTypes.EXPLOSIONS)) { ++ return false; ++ } else if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.EXPLOSIONS)) { ++ final BlockPos blockPosition = BlockPos.containing(position); ++ final long encodedPosition = blockPosition.asLong(); ++ return this.explosionPositions.add(encodedPosition); ++ } ++ return true; ++ } ++ // Sakura end - client visibility settings + + // Add env and gen to constructor, IWorldDataServer -> WorldDataServer + public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { +@@ -1898,7 +1913,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + if (entityplayer.distanceToSqr(vec3d) < 4096.0D) { + Optional optional = Optional.ofNullable((Vec3) serverexplosion.getHitPlayers().get(entityplayer)); + +- entityplayer.connection.send(new ClientboundExplodePacket(vec3d, optional, particleparam2, holder)); ++ // Sakura start - client visibility settings; let players toggle explosion particles ++ ParticleOptions particle = particleparam2; ++ Vec3 position = vec3d; ++ // In 1.22 and later this should be replaced with sending the motion through a PlayerPositionPacket. ++ // The problem here is SetEntityMotion is capped to 3.9 b/pt and the only other alternate mean was ++ // implemented in 1.21.3. I believe it's best to just wait on this issue and deal with this hack. ++ if (this.checkExplosionVisibility(vec3d, entityplayer)) { ++ position = new Vec3(0.0, -1024.0, 0.0); ++ particle = net.minecraft.core.particles.ParticleTypes.SMOKE; ++ } ++ entityplayer.connection.send(new ClientboundExplodePacket(position, optional, particle, holder)); ++ // Sakura end - client visibility settings; let players toggle explosion particles + } + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index cffbd3300967e5d80b5973b35a76235bb2aa1b73..55423353ee674d97fbe6a2d659ce8fb5a466e378 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -359,6 +359,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple + return this.viewDistanceHolder; + } + // Paper end - rewrite chunk system ++ public final me.samsuik.sakura.player.visibility.PlayerVisibilitySettings visibilitySettings = new me.samsuik.sakura.player.visibility.PlayerVisibilitySettings(); // Sakura - client visibility settings + + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) { + super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); +diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index b0bc66dc7248aae691dcab68b925b52a1695e63f..2f9413b9442cc7f95e1974a772812fe397d4c4bd 100644 +--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -101,6 +101,22 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + protected final org.bukkit.craftbukkit.CraftServer cserver; + public boolean processedDisconnect; + ++ // Sakura start - client visibility settings ++ private @Nullable Packet recreatePacket(final Packet packet) { ++ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = this.player.visibilitySettings; ++ if (packet instanceof net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket bedPacket) { ++ if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.SPAWNERS) && bedPacket.getType() == net.minecraft.world.level.block.entity.BlockEntityType.MOB_SPAWNER) { ++ return null; ++ } ++ } else if (packet instanceof net.minecraft.network.protocol.game.ClientboundBlockEventPacket bePacket) { ++ if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.PISTONS) && bePacket.getBlock() instanceof net.minecraft.world.level.block.piston.PistonBaseBlock) { ++ return null; ++ } ++ } ++ return packet; ++ } ++ // Sakura end - client visibility settings ++ + public CraftPlayer getCraftPlayer() { + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + // CraftBukkit end +@@ -309,6 +325,12 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + ClientboundSetDefaultSpawnPositionPacket packet6 = (ClientboundSetDefaultSpawnPositionPacket) packet; + this.player.compassTarget = CraftLocation.toBukkit(packet6.pos, this.getCraftPlayer().getWorld()); + } ++ // Sakura start - client visibility settings ++ if (this.player.visibilitySettings.playerModified()) { ++ packet = this.recreatePacket(packet); ++ if (packet == null) return; ++ } ++ // Sakura end - client visibility settings + // CraftBukkit end + if (packet.isTerminal()) { + this.close(); +@@ -322,8 +344,11 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + CrashReport crashreport = CrashReport.forThrowable(throwable, "Sending packet"); + CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Packet being sent"); + ++ // Sakura start - this has to be effectively final as we're modifying the packet above ++ var packetFinal = packet; + crashreportsystemdetails.setDetail("Packet class", () -> { +- return packet.getClass().getCanonicalName(); ++ return packetFinal.getClass().getCanonicalName(); ++ // Sakura end + }); + throw new ReportedException(crashreport); + } +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index b5d5dbc50a7b8c40739a15f164ffd08fdc534f9c..5cfcc7a1e9461fd7dfd6d7100bdc978945c369ad 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -3247,6 +3247,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + event.setCancelled(cancelled); + AbstractContainerMenu oldContainer = this.player.containerMenu; // SPIGOT-1224 ++ me.samsuik.sakura.player.gui.FeatureGui.clickEvent(event); // Sakura - client visibility settings + this.cserver.getPluginManager().callEvent(event); + if (this.player.containerMenu != oldContainer) { + return; +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index a0876d3f88620bb24ef69101fc67b0dcd5dca0d2..35da89072cea86c1b1bc54784908bfb7bd449f4a 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -611,6 +611,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + // Paper end - optimise entity tracker ++ // Sakura start - visibility api and command ++ public boolean isPrimedTNT; ++ public boolean isFallingBlock; ++ // Sakura end - visibility api and command + + public Entity(EntityType type, Level world) { + this.id = Entity.ENTITY_COUNTER.incrementAndGet(); +diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +index 06d9a519e64d4b8b8764b3ad7691ad93b5cee065..4a50ef9b25b9162815b879c60c294ed4be2edd5c 100644 +--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +@@ -78,6 +78,7 @@ public class FallingBlockEntity extends Entity { + this.blockState = Blocks.SAND.defaultBlockState(); + this.dropItem = true; + this.fallDamageMax = 40; ++ this.isFallingBlock = true; // Sakura + } + + public FallingBlockEntity(Level world, double x, double y, double z, BlockState block) { +diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +index de87483600e55d88176fe25db621bbd3e464729f..5bd189fad703ac99d57258fa448dbcc103077297 100644 +--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java ++++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +@@ -63,6 +63,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { + super(type, world); + this.explosionPower = 4.0F; + this.blocksBuilding = true; ++ this.isPrimedTNT = true; // Sakura + } + + public PrimedTnt(Level world, double x, double y, double z, @Nullable LivingEntity igniter) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 2c7ec674f55b3178b9dcba7f2bc1ff5efccb50ea..503d7ffab7d7a792e3db740dcba2221f7dbd62eb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -2404,6 +2404,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + handle.keepLevel = data.getBoolean("keepLevel"); + } + } ++ ++ // Sakura start - client visibility settings; load from nbt ++ if (nbttagcompound.contains("sakura", 10)) { ++ CompoundTag sakuraTag = nbttagcompound.getCompound("sakura"); ++ this.getHandle().visibilitySettings.loadData(sakuraTag); ++ } ++ // Sakura end - client visibility settings; load from nbt + } + + public void setExtraData(CompoundTag nbttagcompound) { +@@ -2433,6 +2440,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + paper.putLong("LastLogin", handle.loginTime); + paper.putLong("LastSeen", System.currentTimeMillis()); + // Paper end ++ // Sakura start - client visibility settings; save to nbt ++ CompoundTag sakuraTag = nbttagcompound.getCompound("sakura"); ++ this.getHandle().visibilitySettings.saveData(sakuraTag); ++ nbttagcompound.put("sakura", sakuraTag); ++ // Sakura end - client visibility settings; save to nbt + } + + @Override +@@ -3107,6 +3119,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + return this.getHandle().allowsListing(); + } + ++ // Sakura start - client visibility settings; api ++ @Override ++ public final me.samsuik.sakura.player.visibility.VisibilitySettings getVisibility() { ++ return this.getHandle().visibilitySettings; ++ } ++ // Sakura end - client visibility settings; api ++ + // Paper start + @Override + public net.kyori.adventure.text.Component displayName() { diff --git a/patches/server/0005-Visibility-API-and-Command.patch b/patches/server/0005-Visibility-API-and-Command.patch deleted file mode 100644 index 7fe9600..0000000 --- a/patches/server/0005-Visibility-API-and-Command.patch +++ /dev/null @@ -1,579 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik -Date: Tue, 21 Sep 2021 23:54:25 +0100 -Subject: [PATCH] Visibility API and Command - - -diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java -index 3e85c19db0655034c203eaab8d2e6b5504da5da8..9b550fad76a45fb6ea8d07f75c2ff901d56ae514 100644 ---- a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java -+++ b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java -@@ -1,6 +1,9 @@ - package me.samsuik.sakura.command; - - import me.samsuik.sakura.command.subcommands.ConfigCommand; -+import me.samsuik.sakura.command.subcommands.FPSCommand; -+import me.samsuik.sakura.command.subcommands.VisualCommand; -+import me.samsuik.sakura.player.visibility.Visibility; - import net.minecraft.server.MinecraftServer; - import org.bukkit.command.Command; - -@@ -12,6 +15,10 @@ public final class SakuraCommands { - static { - COMMANDS.put("sakura", new SakuraCommand("sakura")); - COMMANDS.put("config", new ConfigCommand("config")); -+ COMMANDS.put("fps", new FPSCommand("fps")); -+ COMMANDS.put("tntvisibility", new VisualCommand(Visibility.Setting.TNT_VISIBILITY, "tnttoggle")); -+ COMMANDS.put("sandvisibility", new VisualCommand(Visibility.Setting.SAND_VISIBILITY, "sandtoggle")); -+ COMMANDS.put("minimal", new VisualCommand(Visibility.Setting.MINIMAL, "minimaltnt", "tntlag")); - } - - public static void registerCommands(MinecraftServer server) { -diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..65f7f7dee78e352bdc88ff42714d10585aa25e21 ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/command/subcommands/FPSCommand.java -@@ -0,0 +1,24 @@ -+package me.samsuik.sakura.command.subcommands; -+ -+import me.samsuik.sakura.command.BaseSubCommand; -+import me.samsuik.sakura.player.visibility.VisibilityGUI; -+import org.bukkit.command.CommandSender; -+import org.bukkit.entity.Player; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.framework.qual.DefaultQualifier; -+ -+@DefaultQualifier(NonNull.class) -+public final class FPSCommand extends BaseSubCommand { -+ private final VisibilityGUI gui = new VisibilityGUI(); -+ -+ public FPSCommand(String name) { -+ super(name); -+ } -+ -+ @Override -+ public void execute(CommandSender sender, String[] args) { -+ if (sender instanceof Player player) { -+ this.gui.showTo(player); -+ } -+ } -+} -diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2931294d1063eb78b43f637269b27c257726b0ff ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/command/subcommands/VisualCommand.java -@@ -0,0 +1,43 @@ -+package me.samsuik.sakura.command.subcommands; -+ -+import me.samsuik.sakura.command.BaseSubCommand; -+import me.samsuik.sakura.configuration.GlobalConfiguration; -+import me.samsuik.sakura.player.visibility.Visibility; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import org.bukkit.command.CommandSender; -+import org.bukkit.entity.Player; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.framework.qual.DefaultQualifier; -+ -+import java.util.Arrays; -+ -+@DefaultQualifier(NonNull.class) -+public final class VisualCommand extends BaseSubCommand { -+ private final Visibility.Setting type; -+ -+ public VisualCommand(Visibility.Setting type, String... aliases) { -+ super(type.basicName()); -+ this.setAliases(Arrays.asList(aliases)); -+ this.type = type; -+ } -+ -+ @Override -+ public void execute(CommandSender sender, String[] args) { -+ if (!(sender instanceof Player player)) { -+ return; -+ } -+ -+ Visibility visibility = player.getVisibility(); -+ -+ // Toggle clicked setting visibility -+ visibility.toggle(this.type); -+ -+ // Send message to player -+ String state = visibility.isEnabled(this.type) ? "Enabled" : "Disabled"; -+ player.sendRichMessage(GlobalConfiguration.get().messages.fpsSettingChange, -+ Placeholder.unparsed("name", this.type.friendlyName()), -+ Placeholder.unparsed("state", state) -+ ); -+ } -+} -diff --git a/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGUI.java b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGUI.java -new file mode 100644 -index 0000000000000000000000000000000000000000..049befac3219413be44c0a56f7b930a2eb55cee9 ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/player/visibility/VisibilityGUI.java -@@ -0,0 +1,123 @@ -+package me.samsuik.sakura.player.visibility; -+ -+import me.samsuik.sakura.configuration.GlobalConfiguration; -+import me.samsuik.sakura.player.gui.ItemIcon; -+import me.samsuik.sakura.player.gui.PlayerGUI; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextDecoration; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import org.bukkit.Bukkit; -+import org.bukkit.Material; -+import org.bukkit.entity.Player; -+import org.bukkit.inventory.Inventory; -+import org.bukkit.inventory.ItemStack; -+import org.bukkit.inventory.meta.ItemMeta; -+ -+import java.util.function.BiFunction; -+ -+public final class VisibilityGUI extends PlayerGUI { -+ -+ private static final BiFunction CREATE_INVENTORY = (player, holder) -> { -+ Inventory inventory = Bukkit.createInventory(holder, 45, Component.text("FPS GUI")); -+ -+ for (int i = 0; i < inventory.getSize(); ++i) { -+ int column = i % 9; -+ int row = (i + 1) / 9; -+ -+ Material background = column > 0 && column < 8 ? (row > 0 && row < 4 || column > 1 && column < 7) -+ ? GlobalConfiguration.get().fps.material -+ : Material.WHITE_STAINED_GLASS_PANE -+ : Material.BLACK_STAINED_GLASS_PANE; -+ -+ ItemStack itemstack = new ItemStack(background); -+ ItemMeta meta = itemstack.getItemMeta(); -+ meta.displayName(Component.space()); -+ itemstack.setItemMeta(meta); -+ -+ inventory.setItem(i, itemstack); -+ } -+ -+ return inventory; -+ }; -+ -+ public VisibilityGUI() { -+ super(CREATE_INVENTORY); -+ } -+ -+ @Override -+ protected void register() { -+ registerFPSIcon(Visibility.Setting.TNT_VISIBILITY, 12); -+ registerFPSIcon(Visibility.Setting.SAND_VISIBILITY, 14); -+ registerFPSIcon(Visibility.Setting.MINIMAL, 13); -+ registerFPSIcon(Visibility.Setting.SPAWNERS, 20); -+ // ...22 -+ registerFPSIcon(Visibility.Setting.EXPLOSIONS, 24); -+ registerFPSIcon(Visibility.Setting.PISTONS, 30); -+ registerFPSIcon(Visibility.Setting.REDSTONE, 31); -+ registerFPSIcon(Visibility.Setting.ENCHANTMENT_GLINT, 32); -+ -+ registerIcon(new ItemIcon(player -> { -+ Visibility.State state = player.getVisibility().getState(); -+ ItemStack itemstack = new ItemStack(state.material()); -+ -+ itemstack.editMeta(meta -> { -+ Component title = Component.text("Toggle all", state.colour()); -+ -+ // italic is default -+ title = title.decoration(TextDecoration.ITALIC, false); -+ -+ meta.displayName(title.append(Component.space()) -+ .append(Component.text(state.title(), NamedTextColor.GRAY))); -+ }); -+ -+ return itemstack; -+ }, player -> { -+ player.getVisibility().toggleAll(); -+ // refresh icons after toggling -+ this.refresh(player); -+ }, 26)); -+ } -+ -+ private void registerFPSIcon(Visibility.Setting setting, int slot) { -+ registerIcon(new ItemIcon(player -> { -+ Visibility visibility = player.getVisibility(); -+ ItemStack itemstack = new ItemStack(setting.material()); -+ -+ itemstack.editMeta(meta -> { -+ // Get the current state as a string -+ String state = visibility.isEnabled(setting) ? "Enabled" : "Disabled"; -+ -+ // Friendly name as a component -+ Component title = Component.text(setting.friendlyName(), setting.colour()); -+ -+ // Display names are italic by default -+ title = title.decoration(TextDecoration.ITALIC, false); -+ -+ // Set the display name -+ meta.displayName(title.append(Component.space()) -+ .append(Component.text(state, NamedTextColor.GRAY))); -+ }); -+ -+ return itemstack; -+ }, player -> { -+ Visibility visibility = player.getVisibility(); -+ -+ // Toggle clicked setting visibility -+ visibility.toggle(setting); -+ -+ // Get the current state as a string -+ String state = visibility.isEnabled(setting) ? "Enabled" : "Disabled"; -+ -+ // Send message to player -+ player.sendRichMessage(GlobalConfiguration.get().messages.fpsSettingChange, -+ Placeholder.unparsed("name", setting.friendlyName()), -+ Placeholder.unparsed("state", state) -+ ); -+ -+ // Update toggle all icon -+ this.refreshSlot(player, 26); -+ }, slot)); -+ } -+ -+} -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -index 1a37654aff9a9c86c9f7af10a1cf721371f0c5ec..82644b34a77dc5e5af38260b7b07b3ec9aceae33 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -@@ -19,7 +19,7 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet public - - public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section) { - this.sectionPos = sectionPos; -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 5b3a886c624b36557cbfaccdc3fb05a46a4ba36a..4d3f28d86f6ef1c34f883f1f551201ac7541fae0 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -179,6 +179,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.handleLegacyStructureIndex(pos); - } - // Paper end - rewrite chunk system -+ private final it.unimi.dsi.fastutil.longs.Long2IntMap minimalEntities; // Sakura - visibility api; minimal tnt/sand - - public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { - super(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); -@@ -222,6 +223,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.poiManager = new PoiManager(new RegionStorageInfo(session.getLevelId(), world.dimension(), "poi"), path.resolve("poi"), dataFixer, dsync, iregistrycustom, world.getServer(), world); - this.setServerViewDistance(viewDistance); - this.worldGenContext = new WorldGenContext(world, chunkGenerator, structureTemplateManager, this.lightEngine, null, this::setChunkUnsaved); // Paper - rewrite chunk system -+ // Sakura start - visibility api; minimal tnt/sand -+ this.minimalEntities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); -+ this.minimalEntities.defaultReturnValue(Integer.MIN_VALUE); -+ // Sakura end - visibility api; minimal tnt/sand - } - - private void setChunkUnsaved(ChunkPos pos) { -@@ -979,6 +984,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - tracker.serverEntity.sendChanges(); - } - } -+ -+ this.minimalEntities.clear(); // Sakura - visibility api and command - } - // Paper end - optimise entity tracker - -@@ -1216,6 +1223,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return !this.seenBy.isEmpty(); - } - // Paper end - optimise entity tracker -+ // Sakura start - visibility api and command -+ private boolean checkEntityVisibility(final ServerPlayer player) { -+ final Entity entity = this.entity; -+ final me.samsuik.sakura.player.visibility.Visibility visibility = player.visibility; -+ if (!visibility.isModified() || !(entity.isPrimedTNT || entity.isFallingBlock)) { -+ return true; -+ } -+ if (entity.isPrimedTNT && visibility.isToggled(me.samsuik.sakura.player.visibility.Visibility.Setting.TNT_VISIBILITY)) { -+ return false; -+ } -+ if (entity.isFallingBlock && visibility.isToggled(me.samsuik.sakura.player.visibility.Visibility.Setting.SAND_VISIBILITY)) { -+ return false; -+ } -+ if (visibility.isToggled(me.samsuik.sakura.player.visibility.Visibility.Setting.MINIMAL)) { -+ final long key = entity.blockPosition().asLong() ^ entity.getType().hashCode(); -+ final long visibleEntity = ChunkMap.this.minimalEntities.get(key); -+ if (visibleEntity != Integer.MIN_VALUE) { -+ return entity.getId() == visibleEntity; -+ } else { -+ ChunkMap.this.minimalEntities.put(key, entity.getId()); -+ } -+ } -+ return true; -+ } -+ // Sakura end - visibility api and command - - public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) { - this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit -@@ -1293,6 +1325,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); - // Paper end - Configurable entity tracking range by Y -+ flag = flag && this.checkEntityVisibility(player); // Sakura start - visibility api and command - - // CraftBukkit start - respect vanish API - if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 32742470eea2ec86c0fff02aa157bfcc89530ad3..ec76caea3ae07efc6f5debcd14b24dfed50aa04b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1898,7 +1898,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - if (entityplayer.distanceToSqr(vec3d) < 4096.0D) { - Optional optional = Optional.ofNullable((Vec3) serverexplosion.getHitPlayers().get(entityplayer)); - -- entityplayer.connection.send(new ClientboundExplodePacket(vec3d, optional, particleparam2, holder)); -+ // Sakura start - visibility api; let players toggle explosion particles -+ ParticleOptions particle = particleparam2; -+ Vec3 position = vec3d; -+ // In 1.22 and later this should be replaced with sending the motion through a PlayerPositionPacket. -+ // The problem here is SetEntityMotion is capped to 3.9 b/pt and the only other alternate mean was -+ // implemented in 1.21.3. I believe it's best to just wait on this issue and deal with this hack. -+ if (entityplayer.visibility.isToggled(me.samsuik.sakura.player.visibility.Visibility.Setting.EXPLOSIONS)) { -+ position = new Vec3(0.0, -1024.0, 0.0); -+ particle = net.minecraft.core.particles.ParticleTypes.SMOKE; -+ } -+ entityplayer.connection.send(new ClientboundExplodePacket(position, optional, particle, holder)); -+ // Sakura end - visibility api; let players toggle explosion particles - } - } - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index cffbd3300967e5d80b5973b35a76235bb2aa1b73..ad85e5ebe00918a7fea4071351741566e4883dd0 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -308,6 +308,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - // Paper end - Optional per player mob spawns - public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff -+ public final me.samsuik.sakura.player.visibility.Visibility visibility = new me.samsuik.sakura.player.visibility.Visibility(); // Sakura - visiblity api - - // CraftBukkit start - public CraftPlayer.TransferCookieConnection transferCookieConnection; -@@ -678,6 +679,15 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - this.respawnDimension = (ResourceKey) dataresult1.resultOrPartial(logger1::error).orElse(Level.OVERWORLD); - } - } -+ // Sakura start - visibility api -+ CompoundTag tag = nbt.getCompound("Sakura.Visuals"); -+ -+ for (me.samsuik.sakura.player.visibility.Visibility.Setting setting : me.samsuik.sakura.player.visibility.Visibility.Setting.values()) { -+ if (tag.getBoolean(setting.name())) { -+ visibility.toggle(setting); -+ } -+ } -+ // Sakura end - visibility api - - this.spawnExtraParticlesOnFall = nbt.getBoolean("spawn_extra_particles_on_fall"); - Tag nbtbase = nbt.get("raid_omen_position"); -@@ -731,6 +741,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - }); - } - this.getBukkitEntity().setExtraData(nbt); // CraftBukkit -+ // Sakura start - visibility api -+ CompoundTag tag = new CompoundTag(); -+ for (me.samsuik.sakura.player.visibility.Visibility.Setting setting : me.samsuik.sakura.player.visibility.Visibility.Setting.values()) { -+ tag.putBoolean(setting.name(), visibility.isToggled(setting)); -+ } -+ nbt.put("Sakura.Visuals", tag); -+ // Sakura end - visibility api - - nbt.putBoolean("spawn_extra_particles_on_fall", this.spawnExtraParticlesOnFall); - if (this.raidOmenPosition != null) { -diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index b0bc66dc7248aae691dcab68b925b52a1695e63f..b1a04865b0efee94227f0868910fc00712bcd6fa 100644 ---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -@@ -44,6 +44,23 @@ import org.bukkit.craftbukkit.util.CraftLocation; - import org.bukkit.craftbukkit.util.Waitable; - import org.bukkit.event.player.PlayerKickEvent; - import org.bukkit.event.player.PlayerResourcePackStatusEvent; -+// Sakura start -+import com.mojang.datafixers.util.Pair; -+import me.samsuik.sakura.player.visibility.Visibility.Setting; -+import net.minecraft.core.component.DataComponents; -+import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -+import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; -+import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; -+import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; -+import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; -+import net.minecraft.world.entity.EquipmentSlot; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.level.block.Blocks; -+import net.minecraft.world.level.block.DiodeBlock; -+import net.minecraft.world.level.block.RedStoneWireBlock; -+import net.minecraft.world.level.block.piston.PistonBaseBlock; -+import net.minecraft.world.level.block.piston.PistonHeadBlock; -+// Sakura end - - public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener, CraftPlayer.TransferCookieConnection { - -@@ -101,6 +118,65 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - protected final org.bukkit.craftbukkit.CraftServer cserver; - public boolean processedDisconnect; - -+ // Sakura start - visibility api -+ private @Nullable Packet recreatePacket(Packet packet) { -+ me.samsuik.sakura.player.visibility.Visibility visibility = this.player.visibility; -+ if (packet instanceof ClientboundSetEquipmentPacket equipment && visibility.isToggled(Setting.ENCHANTMENT_GLINT)) { -+ java.util.List> slots = new java.util.ArrayList<>(); -+ -+ for (int i = 0; i < equipment.getSlots().size(); i++) { -+ Pair pair = equipment.getSlots().get(i); -+ ItemStack itemstack = pair.getSecond(); -+ -+ if (itemstack.isEnchanted()) { -+ ItemStack copy = itemstack.copy(); -+ copy.remove(DataComponents.ENCHANTMENTS); -+ itemstack = copy; -+ } -+ -+ slots.add(new Pair<>(pair.getFirst(), itemstack)); -+ } -+ -+ packet = new ClientboundSetEquipmentPacket(equipment.getEntity(), slots); -+ } else if (packet instanceof ClientboundBlockEntityDataPacket blockdata -+ && visibility.isToggled(Setting.SPAWNERS) -+ && this.player.level().getBlockIfLoaded(blockdata.getPos()) == Blocks.SPAWNER) { -+ packet = new ClientboundBlockUpdatePacket(blockdata.getPos(), Blocks.BLACK_STAINED_GLASS.defaultBlockState()); -+ } else if (packet instanceof ClientboundBlockUpdatePacket updatePacket) { -+ if (visibility.isToggled(Setting.SPAWNERS) && updatePacket.blockState.getBlock() == Blocks.SPAWNER) { -+ packet = new ClientboundBlockUpdatePacket(updatePacket.getPos(), Blocks.BLACK_STAINED_GLASS.defaultBlockState()); -+ } else if (visibility.isToggled(Setting.REDSTONE) -+ && (updatePacket.blockState.getBlock() instanceof DiodeBlock -+ || updatePacket.blockState.getBlock() instanceof RedStoneWireBlock)) { -+ return null; -+ } else if (visibility.isToggled(Setting.PISTONS) -+ && (updatePacket.blockState.getBlock() instanceof PistonBaseBlock -+ || updatePacket.blockState.getBlock() instanceof PistonHeadBlock)) { -+ return null; -+ } -+ } else if (packet instanceof ClientboundSectionBlocksUpdatePacket sectionPacket) { -+ for (net.minecraft.world.level.block.state.BlockState state : sectionPacket.states) { -+ if (visibility.isToggled(Setting.REDSTONE) -+ && (state.getBlock() instanceof DiodeBlock -+ || state.getBlock() instanceof RedStoneWireBlock)) { -+ return null; -+ } else if (visibility.isToggled(Setting.PISTONS) -+ && (state.getBlock() instanceof PistonBaseBlock -+ || state.getBlock() instanceof PistonHeadBlock)) { -+ return null; -+ } -+ } -+ } else if (packet instanceof ClientboundBlockEventPacket blockevent -+ && visibility.isToggled(Setting.PISTONS) -+ && (blockevent.getBlock() instanceof PistonBaseBlock -+ || blockevent.getBlock() instanceof PistonHeadBlock)) { -+ return null; -+ } -+ -+ return packet; -+ } -+ // Sakura end - visibility api -+ - public CraftPlayer getCraftPlayer() { - return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); - // CraftBukkit end -@@ -309,6 +385,12 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - ClientboundSetDefaultSpawnPositionPacket packet6 = (ClientboundSetDefaultSpawnPositionPacket) packet; - this.player.compassTarget = CraftLocation.toBukkit(packet6.pos, this.getCraftPlayer().getWorld()); - } -+ // Sakura start - visibility api -+ if (this.player.visibility.isModified()) { -+ packet = this.recreatePacket(packet); -+ if (packet == null) return; -+ } -+ // Sakura end - visibility api - // CraftBukkit end - if (packet.isTerminal()) { - this.close(); -@@ -322,8 +404,11 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - CrashReport crashreport = CrashReport.forThrowable(throwable, "Sending packet"); - CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Packet being sent"); - -+ // Sakura start - this has to be effectively final as we're modifying the packet above -+ var packetFinal = packet; - crashreportsystemdetails.setDetail("Packet class", () -> { -- return packet.getClass().getCanonicalName(); -+ return packetFinal.getClass().getCanonicalName(); -+ // Sakura end - }); - throw new ReportedException(crashreport); - } -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index b5d5dbc50a7b8c40739a15f164ffd08fdc534f9c..5af07a78bf29c52f4138d551b5fc6d7ce41bb8ca 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -3247,6 +3247,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - - event.setCancelled(cancelled); - AbstractContainerMenu oldContainer = this.player.containerMenu; // SPIGOT-1224 -+ me.samsuik.sakura.player.gui.PlayerGUI.onWindowClick(event); // Sakura - visibility gui - this.cserver.getPluginManager().callEvent(event); - if (this.player.containerMenu != oldContainer) { - return; -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index a0876d3f88620bb24ef69101fc67b0dcd5dca0d2..35da89072cea86c1b1bc54784908bfb7bd449f4a 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -611,6 +611,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - } - // Paper end - optimise entity tracker -+ // Sakura start - visibility api and command -+ public boolean isPrimedTNT; -+ public boolean isFallingBlock; -+ // Sakura end - visibility api and command - - public Entity(EntityType type, Level world) { - this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 06d9a519e64d4b8b8764b3ad7691ad93b5cee065..4a50ef9b25b9162815b879c60c294ed4be2edd5c 100644 ---- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -78,6 +78,7 @@ public class FallingBlockEntity extends Entity { - this.blockState = Blocks.SAND.defaultBlockState(); - this.dropItem = true; - this.fallDamageMax = 40; -+ this.isFallingBlock = true; // Sakura - } - - public FallingBlockEntity(Level world, double x, double y, double z, BlockState block) { -diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index de87483600e55d88176fe25db621bbd3e464729f..5bd189fad703ac99d57258fa448dbcc103077297 100644 ---- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -63,6 +63,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - super(type, world); - this.explosionPower = 4.0F; - this.blocksBuilding = true; -+ this.isPrimedTNT = true; // Sakura - } - - public PrimedTnt(Level world, double x, double y, double z, @Nullable LivingEntity igniter) { -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 2c7ec674f55b3178b9dcba7f2bc1ff5efccb50ea..7976eb7496d57d5d1fe29c902e167d79a86bac32 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -552,6 +552,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - this.getHandle().displayName = name == null ? this.getName() : name; - } - -+ // Sakura start - visiblity api -+ @Override -+ public me.samsuik.sakura.player.visibility.Visibility getVisibility() { -+ return getHandle().visibility; -+ } -+ // Sakura end -+ - // Paper start - @Override - public void playerListName(net.kyori.adventure.text.Component name) { diff --git a/patches/server/0008-TPS-Graph-Command.patch b/patches/server/0008-TPS-Graph-Command.patch index dac04aa..bd7bee7 100644 --- a/patches/server/0008-TPS-Graph-Command.patch +++ b/patches/server/0008-TPS-Graph-Command.patch @@ -5,21 +5,21 @@ Subject: [PATCH] TPS Graph Command diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java -index 9b550fad76a45fb6ea8d07f75c2ff901d56ae514..14abbe35a401d4d21a13adcbf6afd836a76cb2bd 100644 +index cbb2e57f9ab3b48a6e5f792711c4c6fd2d34d445..8e93fc5d7e1bc200f79b0e54edb62dc4d0bf5e74 100644 --- a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java +++ b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java -@@ -3,6 +3,7 @@ package me.samsuik.sakura.command; - import me.samsuik.sakura.command.subcommands.ConfigCommand; +@@ -4,6 +4,7 @@ import me.samsuik.sakura.command.subcommands.ConfigCommand; import me.samsuik.sakura.command.subcommands.FPSCommand; import me.samsuik.sakura.command.subcommands.VisualCommand; + import me.samsuik.sakura.player.visibility.VisibilityTypes; +import me.samsuik.sakura.command.subcommands.TPSCommand; - import me.samsuik.sakura.player.visibility.Visibility; import net.minecraft.server.MinecraftServer; import org.bukkit.command.Command; -@@ -19,6 +20,7 @@ public final class SakuraCommands { - COMMANDS.put("tntvisibility", new VisualCommand(Visibility.Setting.TNT_VISIBILITY, "tnttoggle")); - COMMANDS.put("sandvisibility", new VisualCommand(Visibility.Setting.SAND_VISIBILITY, "sandtoggle")); - COMMANDS.put("minimal", new VisualCommand(Visibility.Setting.MINIMAL, "minimaltnt", "tntlag")); + +@@ -18,6 +19,7 @@ public final class SakuraCommands { + COMMANDS.put("fps", new FPSCommand("fps")); + COMMANDS.put("tntvisibility", new VisualCommand(VisibilityTypes.TNT, "tnttoggle")); + COMMANDS.put("sandvisibility", new VisualCommand(VisibilityTypes.SAND, "sandtoggle")); + COMMANDS.put("tps", new TPSCommand("tps")); } @@ -414,7 +414,7 @@ index 0000000000000000000000000000000000000000..6903863ad293a335a8ed1aeaa06fccb4 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 24aa17ae5f15f330b128a752dee4f79fa619b75d..06c09e3e4233a7a9ce3fac3c432a6bf645db9cd4 100644 +index 9f34a0c6f4f8157d77ed3cb36a546ec8c1b9b1bd..a4a692c46b7c223c7ade46252e5f01c0b72eee7e 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1229,6 +1229,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { if (!entity.isRemoved()) { if (!tickratemanager.isEntityFrozen(entity)) { -@@ -801,6 +802,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -816,6 +817,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe entity.stopRiding(); } diff --git a/patches/server/0017-Replace-explosion-density-cache.patch b/patches/server/0017-Replace-explosion-density-cache.patch index 0ec30cb..b40cb1c 100644 --- a/patches/server/0017-Replace-explosion-density-cache.patch +++ b/patches/server/0017-Replace-explosion-density-cache.patch @@ -126,12 +126,12 @@ index 0000000000000000000000000000000000000000..d7e24638f07f243502004970ab4ce646 + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 61b24eac8f17d5a433817ae2ebb3baf2599af210..3f3a7035a07efce5b8438abf989312b6df137ec8 100644 +index efc1a8bb3d91369fc47b7553a144cd25528ac9bf..8bfa214dccc6e721ba68a442a2175c3f6c15bc95 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1891,6 +1891,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop