diff --git a/patches/api/0010-Add-PlayerList-API.patch b/patches/api/0010-Add-PlayerList-API.patch new file mode 100644 index 000000000..7f850b633 --- /dev/null +++ b/patches/api/0010-Add-PlayerList-API.patch @@ -0,0 +1,446 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Wed, 25 Dec 2024 23:33:48 +0900 +Subject: [PATCH] Add PlayerList API + + +diff --git a/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java +index 901efb61fdc02b3228cc25649926d691c4617512..53680f712abf2d52cbf35f85cd7bb855860a98d6 100644 +--- a/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java ++++ b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java +@@ -40,7 +40,7 @@ import org.jspecify.annotations.Nullable; + * and dynamically change the kick message. + */ + @NullMarked +-public class ProfileWhitelistVerifyEvent extends Event { ++public class ProfileWhitelistVerifyEvent extends org.plazmamc.plazma.event.players.ProfileLoginVerifyEvent { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + +@@ -87,6 +87,7 @@ public class ProfileWhitelistVerifyEvent extends Event { + * @return the currently planned message to send to the user if they are not whitelisted + */ + @Contract(pure = true) ++ @Override // Plazma - Configurable persistent player list provider + public @Nullable Component kickMessage() { + return this.kickMessage; + } +@@ -94,6 +95,7 @@ public class ProfileWhitelistVerifyEvent extends Event { + /** + * @param kickMessage The message to send to the player on kick if not whitelisted. May set to {@code null} to use the server configured default + */ ++ @Override // Plazma - Configurable persistent player list provider + public void kickMessage(final @Nullable Component kickMessage) { + this.kickMessage = kickMessage; + } +@@ -124,6 +126,7 @@ public class ProfileWhitelistVerifyEvent extends Event { + /** + * @return if the player obtained whitelist status by having op + */ ++ @Override // Plazma - Configurable persistent player list provider + public boolean isOp() { + return this.isOp; + } +@@ -143,4 +146,11 @@ public class ProfileWhitelistVerifyEvent extends Event { + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } ++ ++ // Plazma start - Configurable persistent player list provider ++ @Override ++ public PlayerProfile getTarget() { ++ return this.getPlayerProfile(); ++ } ++ // Plazma end - Configurable persistent player list provider + } +diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java +index c5ae50d099989c96ed8d2c2d5dea4017da7c3079..7a8e247554c60b46b8a60f107bf574c730f4b568 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2727,4 +2727,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + */ + void clearBlockHighlights(); + // Purpur end ++ ++ // Plazma start - Configurable persistent player list provider ++ org.plazmamc.plazma.players.PersistentPlayerListProvider getPersistentPlayerListProvider(); ++ ++ void setPersistentPlayerListProvider(final @NotNull org.plazmamc.plazma.players.PersistentPlayerListProvider provider); ++ // Plazma end - Configurable persistent player list provider + } +diff --git a/src/main/java/org/plazmamc/plazma/event/players/BanVerifyEvent.java b/src/main/java/org/plazmamc/plazma/event/players/BanVerifyEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b236b876f51ad32ff64b111d7ecffc5372cbb66c +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/event/players/BanVerifyEvent.java +@@ -0,0 +1,29 @@ ++package org.plazmamc.plazma.event.players; ++ ++import net.kyori.adventure.text.Component; ++import org.jspecify.annotations.Nullable; ++import org.plazmamc.plazma.players.BanInfo; ++import java.util.Date; ++ ++public interface BanVerifyEvent { ++ ++ boolean isBanned(); ++ ++ void setBanned(boolean banned); ++ ++ @Nullable ++ BanInfo getBanInfo(); ++ ++ @Nullable ++ Date getCreated(); ++ ++ @Nullable ++ Date getExpires(); ++ ++ @Nullable ++ String getSource(); ++ ++ @Nullable ++ Component getReason(); ++ ++} +diff --git a/src/main/java/org/plazmamc/plazma/event/players/IpBanVerifyEvent.java b/src/main/java/org/plazmamc/plazma/event/players/IpBanVerifyEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ce1d5ff284b37a35a300bffbed70406c98a1bdea +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/event/players/IpBanVerifyEvent.java +@@ -0,0 +1,95 @@ ++package org.plazmamc.plazma.event.players; ++ ++import net.kyori.adventure.text.Component; ++import org.bukkit.event.HandlerList; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.NotNull; ++import org.jspecify.annotations.NonNull; ++import org.jspecify.annotations.Nullable; ++import org.plazmamc.plazma.players.BanInfo; ++import java.net.SocketAddress; ++import java.util.Date; ++ ++public class IpBanVerifyEvent extends LoginVerifyEvent implements BanVerifyEvent { ++ ++ private static final HandlerList HANDLERS = new HandlerList(); ++ ++ private final @Nullable BanInfo banInfo; ++ private final @NonNull SocketAddress target; ++ private boolean banned; ++ private @Nullable Component kickMessage; ++ ++ public IpBanVerifyEvent(final @NonNull SocketAddress target) { ++ this.banInfo = null; ++ this.target = target; ++ this.banned = false; ++ this.kickMessage = null; ++ } ++ ++ public IpBanVerifyEvent(final @NonNull SocketAddress target, final @NonNull BanInfo banInfo, final @Nullable Component kickMessage) { ++ this.banInfo = banInfo; ++ this.target = target; ++ this.banned = true; ++ this.kickMessage = kickMessage; ++ } ++ ++ @Override ++ public Component kickMessage() { ++ return this.kickMessage; ++ } ++ ++ @Override ++ public void kickMessage(final @NonNull Component kickMessage) { ++ this.kickMessage = kickMessage; ++ } ++ ++ @Override ++ public @NotNull SocketAddress getTarget() { ++ return this.target; ++ } ++ ++ @Override ++ public @NotNull HandlerList getHandlers() { ++ return HANDLERS; ++ } ++ ++ @Contract(pure = true) ++ public static HandlerList getHandlerList() { ++ return HANDLERS; ++ } ++ ++ @Override ++ public boolean isBanned() { ++ return this.banned; ++ } ++ ++ @Override ++ public void setBanned(final boolean banned) { ++ this.banned = banned; ++ } ++ ++ @Override ++ public @Nullable BanInfo getBanInfo() { ++ return this.banInfo; ++ } ++ ++ @Override ++ public @Nullable Date getCreated() { ++ return this.banInfo == null ? null : this.banInfo.getCreated(); ++ } ++ ++ @Override ++ public @Nullable Date getExpires() { ++ return this.banInfo == null ? null : this.banInfo.getExpires(); ++ } ++ ++ @Override ++ public @Nullable String getSource() { ++ return this.banInfo == null ? null : this.banInfo.getSource(); ++ } ++ ++ @Override ++ public Component getReason() { ++ return this.kickMessage; ++ } ++} +diff --git a/src/main/java/org/plazmamc/plazma/event/players/LoginVerifyEvent.java b/src/main/java/org/plazmamc/plazma/event/players/LoginVerifyEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ab74b6d2da4d64378afc75cbdb5b5c50f468a6b9 +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/event/players/LoginVerifyEvent.java +@@ -0,0 +1,15 @@ ++package org.plazmamc.plazma.event.players; ++ ++import net.kyori.adventure.text.Component; ++import org.bukkit.event.Event; ++import org.jspecify.annotations.NonNull; ++ ++public abstract class LoginVerifyEvent extends Event { ++ ++ public abstract Component kickMessage(); ++ ++ public abstract void kickMessage(final @NonNull Component kickMessage); ++ ++ public abstract T getTarget(); ++ ++} +diff --git a/src/main/java/org/plazmamc/plazma/event/players/ProfileBanVerifyEvent.java b/src/main/java/org/plazmamc/plazma/event/players/ProfileBanVerifyEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0e7d7718300e1422b2c0409655d2425114f98a21 +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/event/players/ProfileBanVerifyEvent.java +@@ -0,0 +1,108 @@ ++package org.plazmamc.plazma.event.players; ++ ++import com.destroystokyo.paper.profile.PlayerProfile; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.minimessage.MiniMessage; ++import org.bukkit.event.HandlerList; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.NotNull; ++import org.jspecify.annotations.NonNull; ++import org.jspecify.annotations.Nullable; ++import org.plazmamc.plazma.players.BanInfo; ++import java.util.Date; ++ ++public class ProfileBanVerifyEvent extends ProfileLoginVerifyEvent implements BanVerifyEvent { ++ ++ private static final HandlerList HANDLERS = new HandlerList(); ++ ++ private final @Nullable BanInfo banInfo; ++ private final @NonNull PlayerProfile target; ++ private final boolean isOp; ++ private boolean banned; ++ private @Nullable Component kickMessage; ++ ++ @ApiStatus.Internal ++ public ProfileBanVerifyEvent(final @NonNull PlayerProfile profile, final boolean isOp) { ++ this.banInfo = null; ++ this.target = profile; ++ this.isOp = isOp; ++ this.banned = false; ++ this.kickMessage = null; ++ } ++ ++ @ApiStatus.Internal ++ public ProfileBanVerifyEvent(final @NonNull BanInfo banInfo, final boolean isOp, final @Nullable Component kickMessage) { ++ this.banInfo = banInfo; ++ this.target = this.banInfo.getTarget(); ++ this.isOp = isOp; ++ this.banned = true; ++ this.kickMessage = kickMessage; ++ } ++ ++ @Override ++ public @Nullable BanInfo getBanInfo() { ++ return this.banInfo; ++ } ++ ++ @Override ++ public boolean isBanned() { ++ return this.banned; ++ } ++ ++ @Override ++ public void setBanned(final boolean banned) { ++ this.banned = banned; ++ } ++ ++ @Override ++ public Date getCreated() { ++ return this.banInfo == null ? null : this.banInfo.getCreated(); ++ } ++ ++ @Override ++ public Date getExpires() { ++ return this.banInfo == null ? null : this.banInfo.getExpires(); ++ } ++ ++ @Override ++ public String getSource() { ++ return this.banInfo == null ? null : this.banInfo.getSource(); ++ } ++ ++ @Override ++ public Component getReason() { ++ return this.banInfo == null ? null : MiniMessage.miniMessage().deserialize(this.banInfo.getReason()); ++ } ++ ++ @Override ++ public boolean isOp() { ++ return this.isOp; ++ } ++ ++ @Override ++ public Component kickMessage() { ++ return this.kickMessage; ++ } ++ ++ @Override ++ public void kickMessage(final @NonNull Component kickMessage) { ++ this.kickMessage = kickMessage; ++ } ++ ++ @Override ++ public @NotNull PlayerProfile getTarget() { ++ return this.target; ++ } ++ ++ @Override ++ public @NotNull HandlerList getHandlers() { ++ return HANDLERS; ++ } ++ ++ @Contract(pure = true) ++ public static HandlerList getHandlerList() { ++ return HANDLERS; ++ } ++ ++} +diff --git a/src/main/java/org/plazmamc/plazma/event/players/ProfileLoginVerifyEvent.java b/src/main/java/org/plazmamc/plazma/event/players/ProfileLoginVerifyEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..073da484fc4b792ed14b3880c5d705220fee6491 +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/event/players/ProfileLoginVerifyEvent.java +@@ -0,0 +1,9 @@ ++package org.plazmamc.plazma.event.players; ++ ++import com.destroystokyo.paper.profile.PlayerProfile; ++ ++public abstract class ProfileLoginVerifyEvent extends LoginVerifyEvent { ++ ++ public abstract boolean isOp(); ++ ++} +diff --git a/src/main/java/org/plazmamc/plazma/players/BanInfo.java b/src/main/java/org/plazmamc/plazma/players/BanInfo.java +new file mode 100644 +index 0000000000000000000000000000000000000000..62c16a30c3a0989af955646f8e3c24a709be8e10 +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/players/BanInfo.java +@@ -0,0 +1,24 @@ ++package org.plazmamc.plazma.players; ++ ++import org.jspecify.annotations.NonNull; ++import org.jspecify.annotations.Nullable; ++import java.util.Date; ++ ++public interface BanInfo { ++ ++ @NonNull ++ T getTarget(); ++ ++ @NonNull ++ Date getCreated(); ++ ++ @Nullable ++ Date getExpires(); ++ ++ @NonNull ++ String getSource(); ++ ++ @NonNull ++ String getReason(); ++ ++} +diff --git a/src/main/java/org/plazmamc/plazma/players/PersistentPlayerListProvider.java b/src/main/java/org/plazmamc/plazma/players/PersistentPlayerListProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c217754fc4350d14d52ceb1d9efb20427d8e185a +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/players/PersistentPlayerListProvider.java +@@ -0,0 +1,54 @@ ++package org.plazmamc.plazma.players; ++ ++import com.destroystokyo.paper.profile.PlayerProfile; ++import org.jspecify.annotations.NonNull; ++import java.net.SocketAddress; ++import java.util.Collection; ++ ++public interface PersistentPlayerListProvider { ++ ++ void load(); ++ ++ void save(); ++ ++ void reloadWhitelist(); ++ ++ void addOp(final @NonNull PlayerProfile profile, final int permissionsLevel, final boolean bypassPlayerLimit); ++ ++ void removeOp(final @NonNull PlayerProfile profile); ++ ++ void addWhitelist(final @NonNull PlayerProfile profile); ++ ++ void removeWhitelist(final @NonNull PlayerProfile profile); ++ ++ int getPermissionLevel(final @NonNull PlayerProfile profile); ++ ++ boolean isOp(final @NonNull PlayerProfile profile); ++ ++ boolean isBanned(final @NonNull PlayerProfile profile); ++ ++ boolean isBanned(final @NonNull SocketAddress address); ++ ++ boolean isWhitelisted(final @NonNull PlayerProfile profile); ++ ++ boolean canBypassPlayerLimit(final @NonNull PlayerProfile profile); ++ ++ @NonNull ++ BanInfo getBanInfo(final @NonNull PlayerProfile profile); ++ ++ @NonNull ++ BanInfo getBanInfo(final @NonNull SocketAddress address); ++ ++ @NonNull ++ Collection getOps(); ++ ++ @NonNull ++ Collection getWhitelists(); ++ ++ @NonNull ++ Collection getBannedPlayers(); ++ ++ @NonNull ++ Collection getBannedIps(); ++ ++} diff --git a/patches/server/0029-Save-Json-list-asynchronously.patch b/patches/server/0029-Save-Json-list-asynchronously.patch deleted file mode 100644 index 320733f4c..000000000 --- a/patches/server/0029-Save-Json-list-asynchronously.patch +++ /dev/null @@ -1,152 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AlphaKR93 -Date: Thu, 11 Jan 2024 13:40:41 +0900 -Subject: [PATCH] Save Json list asynchronously - - -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -index 22c4f8dea99f92a1eb3da2baf0a15bf9d2ca0462..20c531f11b310dab0a867e589c769393ed835df5 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -@@ -58,21 +58,23 @@ public class DedicatedPlayerList extends PlayerList { - this.loadWhiteList(); - } - -+ // Plazma start - Save JSON list asynchronously - private void saveIpBanList() { -- try { -- this.getIpBans().save(); -- } catch (IOException var2) { -- LOGGER.warn("Failed to save ip banlist: ", (Throwable)var2); -- } -+ this.getIpBans().save(); - } - - private void saveUserBanList() { -- try { -- this.getBans().save(); -- } catch (IOException var2) { -- LOGGER.warn("Failed to save user banlist: ", (Throwable)var2); -- } -+ this.getBans().save(); -+ } -+ -+ private void saveOps() { -+ this.getOps().save(); -+ } -+ -+ private void saveWhiteList() { -+ this.getWhiteList().save(); - } -+ // Plazma end - Save JSON list asynchronously - - private void loadIpBanList() { - try { -@@ -98,14 +100,6 @@ public class DedicatedPlayerList extends PlayerList { - } - } - -- private void saveOps() { -- try { -- this.getOps().save(); -- } catch (Exception var2) { -- LOGGER.warn("Failed to save operators list: ", (Throwable)var2); -- } -- } -- - private void loadWhiteList() { - try { - this.getWhiteList().load(); -@@ -114,14 +108,6 @@ public class DedicatedPlayerList extends PlayerList { - } - } - -- private void saveWhiteList() { -- try { -- this.getWhiteList().save(); -- } catch (Exception var2) { -- LOGGER.warn("Failed to save white-list: ", (Throwable)var2); -- } -- } -- - @Override - public boolean isWhiteListed(GameProfile profile) { - return !this.isUsingWhitelist() || this.isOp(profile) || this.getWhiteList().isWhiteListed(profile); -diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java -index c038da20b76c0b7b1c18471b20be01e849d29f3a..0735a0bd182635e1969d19336b46bc72b14e555f 100644 ---- a/src/main/java/net/minecraft/server/players/StoredUserList.java -+++ b/src/main/java/net/minecraft/server/players/StoredUserList.java -@@ -42,13 +42,7 @@ public abstract class StoredUserList> { - - public void add(V entry) { - this.map.put(this.getKeyForUser(entry.getUser()), entry); -- -- try { -- this.save(); -- } catch (IOException ioexception) { -- StoredUserList.LOGGER.warn("Could not save the list after adding a user.", ioexception); -- } -- -+ this.save(); // Plazma - Save Json list asynchronously - } - - @Nullable -@@ -62,13 +56,7 @@ public abstract class StoredUserList> { - - public void remove(K key) { - this.map.remove(this.getKeyForUser(key)); -- -- try { -- this.save(); -- } catch (IOException ioexception) { -- StoredUserList.LOGGER.warn("Could not save the list after removing a user.", ioexception); -- } -- -+ this.save(); // Plazma - Save Json list asynchronously - } - - public void remove(StoredUserEntry entry) { -@@ -102,7 +90,9 @@ public abstract class StoredUserList> { - return this.map.values(); - } - -- public void save() throws IOException { -+ // Plazma start - Save Json list asynchronously -+ public void save()/* throws IOException*/ { -+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> { - this.removeExpired(); // Paper - remove expired values before saving - JsonArray jsonarray = new JsonArray(); - Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error -@@ -114,27 +104,16 @@ public abstract class StoredUserList> { - - Objects.requireNonNull(jsonarray); - stream.forEach(jsonarray::add); -- BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); - -- try { -+ try (BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8)) { - StoredUserList.GSON.toJson(jsonarray, StoredUserList.GSON.newJsonWriter(bufferedwriter)); -- } catch (Throwable throwable) { -- if (bufferedwriter != null) { -- try { -- bufferedwriter.close(); -- } catch (Throwable throwable1) { -- throwable.addSuppressed(throwable1); -- } -- } -- -- throw throwable; -- } -- -- if (bufferedwriter != null) { -- bufferedwriter.close(); -+ } catch (IOException e) { -+ StoredUserList.LOGGER.warn("Failed to asynchronously save file " + this.file, e); - } - -+ }); - } -+ // Plazma end - Save Json list asynchronously - - public void load() throws IOException { - if (this.file.exists()) { diff --git a/patches/server/0030-Use-Akair-s-flag-when-running-the-test-server-with-g.patch b/patches/server/0029-Use-Akair-s-flag-when-running-the-test-server-with-g.patch similarity index 100% rename from patches/server/0030-Use-Akair-s-flag-when-running-the-test-server-with-g.patch rename to patches/server/0029-Use-Akair-s-flag-when-running-the-test-server-with-g.patch diff --git a/patches/server/0031-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch b/patches/server/0030-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch similarity index 100% rename from patches/server/0031-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch rename to patches/server/0030-Use-Plazma-logo-instead-if-server-favicon-doesn-t-ex.patch diff --git a/patches/server/0032-Implement-FreedomChat.patch b/patches/server/0031-Implement-FreedomChat.patch similarity index 100% rename from patches/server/0032-Implement-FreedomChat.patch rename to patches/server/0031-Implement-FreedomChat.patch diff --git a/patches/server/0033-Reset-dirty-flag-when-loading-maps-from-the-disk.patch b/patches/server/0032-Reset-dirty-flag-when-loading-maps-from-the-disk.patch similarity index 100% rename from patches/server/0033-Reset-dirty-flag-when-loading-maps-from-the-disk.patch rename to patches/server/0032-Reset-dirty-flag-when-loading-maps-from-the-disk.patch diff --git a/patches/server/0034-Allow-throttling-hopper-checks-if-the-target-contain.patch b/patches/server/0033-Allow-throttling-hopper-checks-if-the-target-contain.patch similarity index 100% rename from patches/server/0034-Allow-throttling-hopper-checks-if-the-target-contain.patch rename to patches/server/0033-Allow-throttling-hopper-checks-if-the-target-contain.patch diff --git a/patches/server/0035-Suppress-errors-from-dirty-attributes.patch b/patches/server/0034-Suppress-errors-from-dirty-attributes.patch similarity index 100% rename from patches/server/0035-Suppress-errors-from-dirty-attributes.patch rename to patches/server/0034-Suppress-errors-from-dirty-attributes.patch diff --git a/patches/server/0036-Implement-Rail-Optimazition.patch b/patches/server/0035-Implement-Rail-Optimazition.patch similarity index 100% rename from patches/server/0036-Implement-Rail-Optimazition.patch rename to patches/server/0035-Implement-Rail-Optimazition.patch diff --git a/patches/server/0037-Load-player-data-asynchronously.patch b/patches/server/0036-Load-player-data-asynchronously.patch similarity index 100% rename from patches/server/0037-Load-player-data-asynchronously.patch rename to patches/server/0036-Load-player-data-asynchronously.patch diff --git a/patches/server/0038-Configurable-RandomSource-factory-provider.patch b/patches/server/0037-Configurable-RandomSource-factory-provider.patch similarity index 100% rename from patches/server/0038-Configurable-RandomSource-factory-provider.patch rename to patches/server/0037-Configurable-RandomSource-factory-provider.patch diff --git a/patches/server/0039-Optimize-advancement-criteria-triggering.patch b/patches/server/0038-Optimize-advancement-criteria-triggering.patch similarity index 100% rename from patches/server/0039-Optimize-advancement-criteria-triggering.patch rename to patches/server/0038-Optimize-advancement-criteria-triggering.patch diff --git a/patches/server/0040-Configurable-water-flowing-speed.patch b/patches/server/0039-Configurable-water-flowing-speed.patch similarity index 100% rename from patches/server/0040-Configurable-water-flowing-speed.patch rename to patches/server/0039-Configurable-water-flowing-speed.patch diff --git a/patches/server/0041-Cleanup-logs.patch b/patches/server/0040-Cleanup-logs.patch similarity index 100% rename from patches/server/0041-Cleanup-logs.patch rename to patches/server/0040-Cleanup-logs.patch diff --git a/patches/server/0042-Completely-remove-timings-implementation.patch b/patches/server/0041-Completely-remove-timings-implementation.patch similarity index 100% rename from patches/server/0042-Completely-remove-timings-implementation.patch rename to patches/server/0041-Completely-remove-timings-implementation.patch diff --git a/patches/server/0043-Remove-persist-isClientSide-flag.patch b/patches/server/0042-Remove-persist-isClientSide-flag.patch similarity index 100% rename from patches/server/0043-Remove-persist-isClientSide-flag.patch rename to patches/server/0042-Remove-persist-isClientSide-flag.patch diff --git a/patches/server/0044-Process-pathfinding-asynchronously.patch b/patches/server/0043-Process-pathfinding-asynchronously.patch similarity index 100% rename from patches/server/0044-Process-pathfinding-asynchronously.patch rename to patches/server/0043-Process-pathfinding-asynchronously.patch diff --git a/patches/server/0045-Implement-alternative-noise-chunk-generator.patch b/patches/server/0044-Implement-alternative-noise-chunk-generator.patch similarity index 100% rename from patches/server/0045-Implement-alternative-noise-chunk-generator.patch rename to patches/server/0044-Implement-alternative-noise-chunk-generator.patch diff --git a/patches/server/0046-Reduce-allocations.patch b/patches/server/0045-Reduce-allocations.patch similarity index 99% rename from patches/server/0046-Reduce-allocations.patch rename to patches/server/0045-Reduce-allocations.patch index 0f0dfdee8..350429ef0 100644 --- a/patches/server/0046-Reduce-allocations.patch +++ b/patches/server/0045-Reduce-allocations.patch @@ -659,10 +659,10 @@ index ab6a7d20f5473d8bffa7f8d136c1d55f17bbfaff..dce1796f0a7dbc69596b0348f42fe8d5 CommonListenerCookie commonlistenercookie = CommonListenerCookie.createInitial((GameProfile) Objects.requireNonNull(this.authenticatedProfile), this.transferred); ServerConfigurationPacketListenerImpl serverconfigurationpacketlistenerimpl = new ServerConfigurationPacketListenerImpl(this.server, this.connection, commonlistenercookie, this.player); // CraftBukkit diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java -index 0735a0bd182635e1969d19336b46bc72b14e555f..efa47456c3be9a168457bfb19878a93ab26172f3 100644 +index c038da20b76c0b7b1c18471b20be01e849d29f3a..14e20543d097efb999c4ef3eb8fff641616622cf 100644 --- a/src/main/java/net/minecraft/server/players/StoredUserList.java +++ b/src/main/java/net/minecraft/server/players/StoredUserList.java -@@ -64,7 +64,7 @@ public abstract class StoredUserList> { +@@ -76,7 +76,7 @@ public abstract class StoredUserList> { } public String[] getUserList() { diff --git a/patches/server/0047-Hashed-rcon-password.patch b/patches/server/0046-Hashed-rcon-password.patch similarity index 100% rename from patches/server/0047-Hashed-rcon-password.patch rename to patches/server/0046-Hashed-rcon-password.patch diff --git a/patches/server/0048-Add-option-to-allow-shoot-fireball.patch b/patches/server/0047-Add-option-to-allow-shoot-fireball.patch similarity index 100% rename from patches/server/0048-Add-option-to-allow-shoot-fireball.patch rename to patches/server/0047-Add-option-to-allow-shoot-fireball.patch diff --git a/patches/server/0049-Remove-Mojang-Profiler-codes.patch b/patches/server/0048-Remove-Mojang-Profiler-codes.patch similarity index 100% rename from patches/server/0049-Remove-Mojang-Profiler-codes.patch rename to patches/server/0048-Remove-Mojang-Profiler-codes.patch diff --git a/patches/server/0050-Completely-remove-Mojang-profiler.patch b/patches/server/0049-Completely-remove-Mojang-profiler.patch similarity index 100% rename from patches/server/0050-Completely-remove-Mojang-profiler.patch rename to patches/server/0049-Completely-remove-Mojang-profiler.patch diff --git a/patches/server/0051-Port-minor-SparklyPaper-patches.patch b/patches/server/0050-Port-minor-SparklyPaper-patches.patch similarity index 100% rename from patches/server/0051-Port-minor-SparklyPaper-patches.patch rename to patches/server/0050-Port-minor-SparklyPaper-patches.patch diff --git a/patches/server/0052-SparklyPaper-Optimize-framed-map-tracker-ticking.patch b/patches/server/0051-SparklyPaper-Optimize-framed-map-tracker-ticking.patch similarity index 100% rename from patches/server/0052-SparklyPaper-Optimize-framed-map-tracker-ticking.patch rename to patches/server/0051-SparklyPaper-Optimize-framed-map-tracker-ticking.patch diff --git a/patches/server/0053-SparklyPaper-Skip-executeTick-check-if-there-s-no-ta.patch b/patches/server/0052-SparklyPaper-Skip-executeTick-check-if-there-s-no-ta.patch similarity index 100% rename from patches/server/0053-SparklyPaper-Skip-executeTick-check-if-there-s-no-ta.patch rename to patches/server/0052-SparklyPaper-Skip-executeTick-check-if-there-s-no-ta.patch diff --git a/patches/server/0054-SparklyPaper-MSPT-by-World.patch b/patches/server/0053-SparklyPaper-MSPT-by-World.patch similarity index 100% rename from patches/server/0054-SparklyPaper-MSPT-by-World.patch rename to patches/server/0053-SparklyPaper-MSPT-by-World.patch diff --git a/patches/server/0055-SparklyPaper-Optimize-farm-check.patch b/patches/server/0054-SparklyPaper-Optimize-farm-check.patch similarity index 100% rename from patches/server/0055-SparklyPaper-Optimize-farm-check.patch rename to patches/server/0054-SparklyPaper-Optimize-farm-check.patch diff --git a/patches/server/0056-SparklyPaper-Optimize-season-check.patch b/patches/server/0055-SparklyPaper-Optimize-season-check.patch similarity index 100% rename from patches/server/0056-SparklyPaper-Optimize-season-check.patch rename to patches/server/0055-SparklyPaper-Optimize-season-check.patch diff --git a/patches/server/0057-SparklyPaper-Optimize-tickingBlockEntity.patch b/patches/server/0056-SparklyPaper-Optimize-tickingBlockEntity.patch similarity index 100% rename from patches/server/0057-SparklyPaper-Optimize-tickingBlockEntity.patch rename to patches/server/0056-SparklyPaper-Optimize-tickingBlockEntity.patch diff --git a/patches/server/0058-Ticking-Controller.patch b/patches/server/0057-Ticking-Controller.patch similarity index 100% rename from patches/server/0058-Ticking-Controller.patch rename to patches/server/0057-Ticking-Controller.patch diff --git a/patches/server/0059-Add-option-to-disable-beacon-effect-ambient.patch b/patches/server/0058-Add-option-to-disable-beacon-effect-ambient.patch similarity index 100% rename from patches/server/0059-Add-option-to-disable-beacon-effect-ambient.patch rename to patches/server/0058-Add-option-to-disable-beacon-effect-ambient.patch diff --git a/patches/server/0060-Tick-toggle-subcommand.patch b/patches/server/0059-Tick-toggle-subcommand.patch similarity index 100% rename from patches/server/0060-Tick-toggle-subcommand.patch rename to patches/server/0059-Tick-toggle-subcommand.patch diff --git a/patches/server/0063-Add-heal-command.patch b/patches/server/0060-Add-heal-command.patch similarity index 100% rename from patches/server/0063-Add-heal-command.patch rename to patches/server/0060-Add-heal-command.patch diff --git a/patches/server/0064-Add-missing-argument-place-for-compass-command.patch b/patches/server/0061-Add-missing-argument-place-for-compass-command.patch similarity index 100% rename from patches/server/0064-Add-missing-argument-place-for-compass-command.patch rename to patches/server/0061-Add-missing-argument-place-for-compass-command.patch diff --git a/patches/server/0061-Add-options-to-modify-configurations-path.patch b/patches/server/0061-Add-options-to-modify-configurations-path.patch deleted file mode 100644 index 924abaec5..000000000 --- a/patches/server/0061-Add-options-to-modify-configurations-path.patch +++ /dev/null @@ -1,236 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AlphaKR93 -Date: Wed, 25 Dec 2024 13:24:51 +0900 -Subject: [PATCH] Add options to modify configurations path - - -diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -index 21a3761f075ace896c981936b2810fccb0b5d610..d99adf4a0b430b8a1ae41d3f4f04ad0c7a6e7eaa 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -@@ -135,7 +135,7 @@ public class PaperVersionFetcher implements VersionFetcher { - } - - private @Nullable Component getHistory() { -- final VersionHistoryManager.@Nullable VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); -+ final VersionHistoryManager.@Nullable VersionData data = VersionHistoryManager.getInstance().getVersionData(); // Plazma - Add options to modify the configuration files - if (data == null) { - return null; - } -diff --git a/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java b/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java -index 660b2ec6b63a4ceffee44ab11f54dfa7c0d0996f..aa936d21bd458deef5672ddd782897c41daa765e 100644 ---- a/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java -+++ b/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java -@@ -19,8 +19,24 @@ import org.bukkit.Bukkit; - import javax.annotation.Nonnull; - import javax.annotation.Nullable; - --public enum VersionHistoryManager { -- INSTANCE; -+// Plazma start - Add options to modify the configuration files -+public final class VersionHistoryManager { -+ private static VersionHistoryManager INSTANCE; -+ -+ public static VersionHistoryManager getInstance() { -+ if (INSTANCE == null) { -+ throw new IllegalStateException("VersionHistoryManager has not been initialized yet"); -+ } -+ return INSTANCE; -+ } -+ -+ public static void initialize(final joptsimple.OptionSet options) { -+ if (INSTANCE != null) { -+ throw new IllegalStateException("VersionHistoryManager has already been initialized"); -+ } -+ INSTANCE = new VersionHistoryManager((java.io.File) options.valueOf("version-history")); -+ } -+ // Plazma end - Add options to modify the configuration files - - private final Gson gson = new Gson(); - -@@ -28,8 +44,10 @@ public enum VersionHistoryManager { - - private VersionData currentData = null; - -- VersionHistoryManager() { -- final Path path = Paths.get("version_history.json"); -+ // Plazma start - Add options to modify the configuration files -+ private VersionHistoryManager(final @Nonnull java.io.File file) { -+ final Path path = file.toPath(); -+ // Plazma end - Add options to modify the configuration files - - if (Files.exists(path)) { - // Basic file santiy checks -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -index 06323dcc745aed16123980fc559d7b65c42f1e1c..ee37957264b7830c29a72f76ef4d7729bc66040c 100644 ---- a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -@@ -117,7 +117,7 @@ public class PufferfishVersionFetcher implements VersionFetcher { - } - - private @Nullable Component getHistory() { -- final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); -+ final VersionHistoryManager.VersionData data = VersionHistoryManager.getInstance().getVersionData(); // Plazma - Add options to modify the configuration files - if (data == null) { - return null; - } -@@ -129,4 +129,4 @@ public class PufferfishVersionFetcher implements VersionFetcher { - - return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); - } --} -\ No newline at end of file -+} -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 3e211e6ea16c8110e662d6201e8325ecd3d6a93b..011cdc8dbef3e823bdccf1a1c7cf945cf0cf7005 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -256,7 +256,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - } - org.purpurmc.purpur.PurpurConfig.registerCommands(); - // Purpur end - Purpur config files -- com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now -+ com.destroystokyo.paper.VersionHistoryManager.initialize(this.options); // Paper - load version history now // Plazma - Add options to modify the configuration files - gg.pufferfish.pufferfish.PufferfishConfig.pufferfishFile = (java.io.File) options.valueOf("pufferfish-settings"); // Purpur - Fix pufferfish issues - gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish - gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish -diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java -index 1f2958d21c279ecb377b7c90ba643ea83e217eca..6a411f609c48b28115b947494062f9f7bf5b2d93 100644 ---- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java -+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java -@@ -82,7 +82,7 @@ public class OldUsersConverter { - } - - public static boolean convertUserBanlist(final MinecraftServer server) { -- final UserBanList gameprofilebanlist = new UserBanList(PlayerList.USERBANLIST_FILE); -+ final UserBanList gameprofilebanlist = new UserBanList((File) server.options.valueOf("banned-players")); // Plazma - Configurable player list file path - - if (OldUsersConverter.OLD_USERBANLIST.exists() && OldUsersConverter.OLD_USERBANLIST.isFile()) { - if (gameprofilebanlist.getFile().exists()) { -@@ -140,7 +140,7 @@ public class OldUsersConverter { - } - - public static boolean convertIpBanlist(MinecraftServer server) { -- IpBanList ipbanlist = new IpBanList(PlayerList.IPBANLIST_FILE); -+ IpBanList ipbanlist = new IpBanList((File) server.options.valueOf("banned-ips")); // Plazma - Configurable player list file path - - if (OldUsersConverter.OLD_IPBANLIST.exists() && OldUsersConverter.OLD_IPBANLIST.isFile()) { - if (ipbanlist.getFile().exists()) { -@@ -181,7 +181,7 @@ public class OldUsersConverter { - } - - public static boolean convertOpsList(final MinecraftServer server) { -- final ServerOpList oplist = new ServerOpList(PlayerList.OPLIST_FILE); -+ final ServerOpList oplist = new ServerOpList((File) server.options.valueOf("ops")); // Plazma - Configurable player list file path - - if (OldUsersConverter.OLD_OPLIST.exists() && OldUsersConverter.OLD_OPLIST.isFile()) { - if (oplist.getFile().exists()) { -@@ -225,7 +225,7 @@ public class OldUsersConverter { - } - - public static boolean convertWhiteList(final MinecraftServer server) { -- final UserWhiteList whitelist = new UserWhiteList(PlayerList.WHITELIST_FILE); -+ final UserWhiteList whitelist = new UserWhiteList((File) server.options.valueOf("whitelist")); // Plazma - Configurable player list file path - - if (OldUsersConverter.OLD_WHITELIST.exists() && OldUsersConverter.OLD_WHITELIST.isFile()) { - if (whitelist.getFile().exists()) { -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index d5d09bf63f4b7fba73188f75d5fe9599b8da2844..8ab6df63d9a22ac76b2ba14bc8fef318e65484c8 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -126,10 +126,12 @@ import org.bukkit.event.player.PlayerSpawnChangeEvent; - - public abstract class PlayerList { - -+ /* // Plazma - Configurable player list file path - public static final File USERBANLIST_FILE = new File("banned-players.json"); - public static final File IPBANLIST_FILE = new File("banned-ips.json"); - public static final File OPLIST_FILE = new File("ops.json"); - public static final File WHITELIST_FILE = new File("whitelist.json"); -+ */ // Plazma - Configurable player list file path - public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full"); - public static final Component DUPLICATE_LOGIN_DISCONNECT_MESSAGE = Component.translatable("multiplayer.disconnect.duplicate_login"); - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -167,14 +169,12 @@ public abstract class PlayerList { - server.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper - // CraftBukkit end - -- this.bans = new UserBanList(PlayerList.USERBANLIST_FILE); -- this.ipBans = new IpBanList(PlayerList.IPBANLIST_FILE); -- this.ops = new ServerOpList(PlayerList.OPLIST_FILE); -- this.whitelist = new UserWhiteList(PlayerList.WHITELIST_FILE); -- // CraftBukkit start -- // this.stats = Maps.newHashMap(); -- // this.advancements = Maps.newHashMap(); -- // CraftBukkit end -+ // Plazma start - Configurable player list file path -+ this.bans = new UserBanList((File) server.options.valueOf("banned-players")); -+ this.ipBans = new IpBanList((File) server.options.valueOf("banned-ips")); -+ this.ops = new ServerOpList((File) server.options.valueOf("ops")); -+ this.whitelist = new UserWhiteList((File) server.options.valueOf("whitelist")); -+ // Plazma end - Configurable player list file path - this.server = server; - this.registries = registryManager; - this.maxPlayers = maxPlayers; -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index a75f3328ba32466b6ceeddb0069c856524f19c0a..913213c77fa2cf8038768a34b38bb59d698e714b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -205,6 +205,44 @@ public class Main { - .defaultsTo(new File(org.plazmamc.plazma.configurations.PlazmaConfigurations.CONFIG_DIR)) - .describedAs("Configuration Directory"); - // Plazma end - Configurable Plazma -+ -+ // Plazma start - Configurable player data storage -+ acceptsAll(asList("banned-ips"), "File for banned IPs") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("banned-ips.json")) -+ .describedAs("JSON file"); -+ -+ acceptsAll(asList("banned-players"), "File for banned players") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("banned-players.json")) -+ .describedAs("JSON file"); -+ -+ acceptsAll(asList("ops"), "File for ops") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("ops.json")) -+ .describedAs("JSON file"); -+ -+ acceptsAll(asList("whitelist"), "File for whitelist") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("whitelist.json")) -+ .describedAs("JSON file"); -+ -+ acceptsAll(asList("version-history"), "File for version history") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("version_history.json")) -+ .describedAs("JSON file"); -+ -+ acceptsAll(asList("help"), "File for help command") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("help.yml")) -+ .describedAs("Yaml file"); -+ // Plazma end - Configurable player data storage - } - }; - -diff --git a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java -index 5923d3c17756c489fcb392044c0679fe52e2d58f..a433d691831c620112a1c824f8f26cb50cfa8dbd 100644 ---- a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java -+++ b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java -@@ -25,7 +25,7 @@ public class HelpYamlReader { - public HelpYamlReader(Server server) { - this.server = server; - -- File helpYamlFile = new File("help.yml"); -+ File helpYamlFile = (File) ((org.bukkit.craftbukkit.CraftServer) server).getHandle().getServer().options.valueOf("help"); // Plazma - Add options to modify the configuration files - YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("configurations/help.yml"), Charsets.UTF_8)); - - try { diff --git a/patches/server/0062-Add-PlayerList-API.patch b/patches/server/0062-Add-PlayerList-API.patch new file mode 100644 index 000000000..2f17ff255 --- /dev/null +++ b/patches/server/0062-Add-PlayerList-API.patch @@ -0,0 +1,1347 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Wed, 25 Dec 2024 23:54:53 +0900 +Subject: [PATCH] Add PlayerList API + + +diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +index 21a3761f075ace896c981936b2810fccb0b5d610..d99adf4a0b430b8a1ae41d3f4f04ad0c7a6e7eaa 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java ++++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +@@ -135,7 +135,7 @@ public class PaperVersionFetcher implements VersionFetcher { + } + + private @Nullable Component getHistory() { +- final VersionHistoryManager.@Nullable VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); ++ final VersionHistoryManager.@Nullable VersionData data = VersionHistoryManager.getInstance().getVersionData(); // Plazma - Add options to modify the configuration files + if (data == null) { + return null; + } +diff --git a/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java b/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java +index 660b2ec6b63a4ceffee44ab11f54dfa7c0d0996f..aa936d21bd458deef5672ddd782897c41daa765e 100644 +--- a/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java ++++ b/src/main/java/com/destroystokyo/paper/VersionHistoryManager.java +@@ -19,8 +19,24 @@ import org.bukkit.Bukkit; + import javax.annotation.Nonnull; + import javax.annotation.Nullable; + +-public enum VersionHistoryManager { +- INSTANCE; ++// Plazma start - Add options to modify the configuration files ++public final class VersionHistoryManager { ++ private static VersionHistoryManager INSTANCE; ++ ++ public static VersionHistoryManager getInstance() { ++ if (INSTANCE == null) { ++ throw new IllegalStateException("VersionHistoryManager has not been initialized yet"); ++ } ++ return INSTANCE; ++ } ++ ++ public static void initialize(final joptsimple.OptionSet options) { ++ if (INSTANCE != null) { ++ throw new IllegalStateException("VersionHistoryManager has already been initialized"); ++ } ++ INSTANCE = new VersionHistoryManager((java.io.File) options.valueOf("version-history")); ++ } ++ // Plazma end - Add options to modify the configuration files + + private final Gson gson = new Gson(); + +@@ -28,8 +44,10 @@ public enum VersionHistoryManager { + + private VersionData currentData = null; + +- VersionHistoryManager() { +- final Path path = Paths.get("version_history.json"); ++ // Plazma start - Add options to modify the configuration files ++ private VersionHistoryManager(final @Nonnull java.io.File file) { ++ final Path path = file.toPath(); ++ // Plazma end - Add options to modify the configuration files + + if (Files.exists(path)) { + // Basic file santiy checks +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index bd8048bfe2a93c3a4b4bb84e7d992a8bb0c65778..1565d5a4142acc36bb45e08932e72840309913e1 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2591,22 +2591,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop list = Lists.newArrayList(playerlist.getPlayers()); +- Iterator iterator = list.iterator(); +- +- while (iterator.hasNext()) { +- ServerPlayer entityplayer = (ServerPlayer) iterator.next(); +- +- if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) +- entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause +- } +- } +- +- } ++ // Plazma start - Configurable persistent player list provider ++ if (!this.enforceWhitelist || !((DedicatedServer) getServer()).getProperties().whiteList.get()) return; ++ // PLAZMA - REMOVE THIS COMMENT ++ for (final ServerPlayer player : source.getServer().getPlayerList().getPlayers()) { ++ if (this.getPlayerList().isWhiteListed(player.getGameProfile())) continue; ++ player.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); ++ } // diff on changes ++ // Plazma end - Configurable persistent player list provider + } + + public PackRepository getPackRepository() { +@@ -2688,8 +2680,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop source.hasPermission(3)) + .then( + Commands.argument("targets", GameProfileArgument.gameProfile()) +- .suggests((context, builder) -> SharedSuggestionProvider.suggest(context.getSource().getServer().getPlayerList().getOpNames(), builder)) ++ .suggests((context, builder) -> SharedSuggestionProvider.suggest(context.getSource().getServer().getPlayerList().getOpsList().stream().map(GameProfile::getName), builder)) + .executes(context -> deopPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"))) + ) + ); +diff --git a/src/main/java/net/minecraft/server/commands/OpCommand.java b/src/main/java/net/minecraft/server/commands/OpCommand.java +index e7b444a10b244828827b3c66c53465206ea8e0ec..af455e7f1c22fda87dbc5b487b757d046dcc2d49 100644 +--- a/src/main/java/net/minecraft/server/commands/OpCommand.java ++++ b/src/main/java/net/minecraft/server/commands/OpCommand.java +@@ -34,20 +34,41 @@ public class OpCommand { + } + ) + .executes(context -> opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"))) ++ // Plazma start - Add an option to set player can bypass limit ++ .then( ++ Commands.argument("permissionLevel", com.mojang.brigadier.arguments.IntegerArgumentType.integer(0, 4)) ++ .executes(context -> ++ opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"), com.mojang.brigadier.arguments.IntegerArgumentType.getInteger(context, "permissionLevel"), false) ++ ) ++ .then( ++ Commands.argument("bypassPlayerLimit", com.mojang.brigadier.arguments.BoolArgumentType.bool()) ++ .executes(context -> ++ opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"), com.mojang.brigadier.arguments.IntegerArgumentType.getInteger(context, "permissionLevel"), com.mojang.brigadier.arguments.BoolArgumentType.getBool(context, "bypassPlayerLimit")) ++ ) ++ ) ++ ) ++ // Plazma end - Add an option to set player can bypass limit + ) + ); + } +- + private static int opPlayers(CommandSourceStack source, Collection targets) throws CommandSyntaxException { ++ // Plazma start - Add an option to set player can bypass limit ++ return opPlayers(source, targets, source.getServer().getOperatorUserPermissionLevel(), false); ++ } ++ ++ private static int opPlayers(final CommandSourceStack source, final Collection targets, final int permissionLevel, final boolean bypassPlayerLimit) throws CommandSyntaxException { ++ // Plazma end - Add an option to set player can bypass limit + PlayerList playerList = source.getServer().getPlayerList(); + int i = 0; + + for (GameProfile gameProfile : targets) { +- if (!playerList.isOp(gameProfile)) { +- playerList.op(gameProfile); +- i++; +- source.sendSuccess(() -> Component.translatable("commands.op.success", gameProfile.getName()), true); // Paper - fixes MC-253721 +- } ++ // Plazma start - Add an option to set player can bypass limit ++ if (playerList.isOp(gameProfile) && playerList.canBypassPlayerLimit(gameProfile) == bypassPlayerLimit && playerList.getPermissionLevel(gameProfile) == permissionLevel) continue; ++ ++ playerList.op(gameProfile, permissionLevel, bypassPlayerLimit); ++ source.sendSuccess(() -> Component.translatable("commands.op.success", gameProfile.getName()), true); // Paper - fixes MC-253721 ++ i++; ++ // Plazma end - Add an option to set player can bypass limit + } + + if (i == 0) { +diff --git a/src/main/java/net/minecraft/server/commands/WhitelistCommand.java b/src/main/java/net/minecraft/server/commands/WhitelistCommand.java +index bd14c15d21b77c0c86e2f9e439ab58906c44c919..a69c122ddb230faa3005b741a88677f4ed1cbbe2 100644 +--- a/src/main/java/net/minecraft/server/commands/WhitelistCommand.java ++++ b/src/main/java/net/minecraft/server/commands/WhitelistCommand.java +@@ -45,7 +45,7 @@ public class WhitelistCommand { + return SharedSuggestionProvider.suggest( + playerList.getPlayers() + .stream() +- .filter(player -> !playerList.getWhiteList().isWhiteListed(player.getGameProfile())) ++ .filter(player -> !playerList.isWhiteListed(player.getGameProfile())) + .map(player -> player.getGameProfile().getName()), + builder + ); +@@ -60,7 +60,7 @@ public class WhitelistCommand { + Commands.argument("targets", GameProfileArgument.gameProfile()) + .suggests( + (context, builder) -> SharedSuggestionProvider.suggest( +- context.getSource().getServer().getPlayerList().getWhiteListNames(), builder ++ context.getSource().getServer().getPlayerList().getWhitelists().stream().map(GameProfile::getName), builder + ) + ) + .executes(context -> removePlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"))) +@@ -78,13 +78,11 @@ public class WhitelistCommand { + } + + private static int addPlayers(CommandSourceStack source, Collection targets) throws CommandSyntaxException { +- UserWhiteList userWhiteList = source.getServer().getPlayerList().getWhiteList(); + int i = 0; + + for (GameProfile gameProfile : targets) { +- if (!userWhiteList.isWhiteListed(gameProfile)) { +- UserWhiteListEntry userWhiteListEntry = new UserWhiteListEntry(gameProfile); +- userWhiteList.add(userWhiteListEntry); ++ if (!source.getServer().getPlayerList().isWhiteListed(gameProfile)) { ++ source.getServer().getPlayerList().whitelist(gameProfile); + source.sendSuccess(() -> Component.translatable("commands.whitelist.add.success", Component.literal(gameProfile.getName())), true); + i++; + } +@@ -98,13 +96,11 @@ public class WhitelistCommand { + } + + private static int removePlayers(CommandSourceStack source, Collection targets) throws CommandSyntaxException { +- UserWhiteList userWhiteList = source.getServer().getPlayerList().getWhiteList(); + int i = 0; + + for (GameProfile gameProfile : targets) { +- if (userWhiteList.isWhiteListed(gameProfile)) { +- UserWhiteListEntry userWhiteListEntry = new UserWhiteListEntry(gameProfile); +- userWhiteList.remove(userWhiteListEntry); ++ if (source.getServer().getPlayerList().isWhiteListed(gameProfile)) { ++ source.getServer().getPlayerList().unWhitelist(gameProfile); + source.sendSuccess(() -> Component.translatable("commands.whitelist.remove.success", Component.literal(gameProfile.getName())), true); + i++; + } +@@ -142,7 +138,7 @@ public class WhitelistCommand { + } + + private static int showList(CommandSourceStack source) { +- String[] strings = source.getServer().getPlayerList().getWhiteListNames(); ++ String[] strings = source.getServer().getPlayerList().getWhitelists().stream().map(GameProfile::getName).toArray(String[]::new); + if (strings.length == 0) { + source.sendSuccess(() -> Component.translatable("commands.whitelist.none"), false); + } else { +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +index 22c4f8dea99f92a1eb3da2baf0a15bf9d2ca0462..c6ff4cfb24f489e5fd1db373b6b4a48e73502ef3 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +@@ -10,29 +10,12 @@ import net.minecraft.world.level.storage.PlayerDataStorage; + import org.slf4j.Logger; + + public class DedicatedPlayerList extends PlayerList { +- private static final Logger LOGGER = LogUtils.getLogger(); +- + public DedicatedPlayerList(DedicatedServer server, LayeredRegistryAccess tracker, PlayerDataStorage saveHandler) { + super(server, tracker, saveHandler, server.getProperties().maxPlayers); + DedicatedServerProperties dedicatedServerProperties = server.getProperties(); + this.setViewDistance(dedicatedServerProperties.viewDistance); + this.setSimulationDistance(dedicatedServerProperties.simulationDistance); + super.setUsingWhiteList(dedicatedServerProperties.whiteList.get()); +- // Paper start - fix converting txt to json file; moved from constructor +- } +- @Override +- public void loadAndSaveFiles() { +- // Paper end - fix converting txt to json file +- this.loadUserBanList(); +- this.saveUserBanList(); +- this.loadIpBanList(); +- this.saveIpBanList(); +- this.loadOps(); +- this.loadWhiteList(); +- this.saveOps(); +- if (!this.getWhiteList().getFile().exists()) { +- this.saveWhiteList(); +- } + } + + @Override +@@ -41,99 +24,8 @@ public class DedicatedPlayerList extends PlayerList { + this.getServer().storeUsingWhiteList(whitelistEnabled); + } + +- @Override +- public void op(GameProfile profile) { +- super.op(profile); +- this.saveOps(); +- } +- +- @Override +- public void deop(GameProfile profile) { +- super.deop(profile); +- this.saveOps(); +- } +- +- @Override +- public void reloadWhiteList() { +- this.loadWhiteList(); +- } +- +- private void saveIpBanList() { +- try { +- this.getIpBans().save(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to save ip banlist: ", (Throwable)var2); +- } +- } +- +- private void saveUserBanList() { +- try { +- this.getBans().save(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to save user banlist: ", (Throwable)var2); +- } +- } +- +- private void loadIpBanList() { +- try { +- this.getIpBans().load(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to load ip banlist: ", (Throwable)var2); +- } +- } +- +- private void loadUserBanList() { +- try { +- this.getBans().load(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to load user banlist: ", (Throwable)var2); +- } +- } +- +- private void loadOps() { +- try { +- this.getOps().load(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to load operators list: ", (Throwable)var2); +- } +- } +- +- private void saveOps() { +- try { +- this.getOps().save(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to save operators list: ", (Throwable)var2); +- } +- } +- +- private void loadWhiteList() { +- try { +- this.getWhiteList().load(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to load white-list: ", (Throwable)var2); +- } +- } +- +- private void saveWhiteList() { +- try { +- this.getWhiteList().save(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to save white-list: ", (Throwable)var2); +- } +- } +- +- @Override +- public boolean isWhiteListed(GameProfile profile) { +- return !this.isUsingWhitelist() || this.isOp(profile) || this.getWhiteList().isWhiteListed(profile); +- } +- + @Override + public DedicatedServer getServer() { + return (DedicatedServer)super.getServer(); + } +- +- @Override +- public boolean canBypassPlayerLimit(GameProfile profile) { +- return this.getOps().canBypassPlayerLimit(profile); +- } + } +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 3e211e6ea16c8110e662d6201e8325ecd3d6a93b..92923400f325c95d17728e7cf2b19b68503c1f07 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -256,7 +256,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + } + org.purpurmc.purpur.PurpurConfig.registerCommands(); + // Purpur end - Purpur config files +- com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now ++ com.destroystokyo.paper.VersionHistoryManager.initialize(this.options); // Paper - load version history now // Plazma - Add options to modify the configuration files + gg.pufferfish.pufferfish.PufferfishConfig.pufferfishFile = (java.io.File) options.valueOf("pufferfish-settings"); // Purpur - Fix pufferfish issues + gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish + gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish +@@ -630,7 +630,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) { + if (world.dimension() != net.minecraft.world.level.Level.OVERWORLD) { + return false; +- } else if (this.getPlayerList().getOps().isEmpty()) { ++ } else if (this.getPlayerList().getOpsList().isEmpty()) { + return false; + } else if (this.getPlayerList().isOp(player.getGameProfile())) { + return false; +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 982b172b188fb890fa7719662e67a007ab172668..37f1ee26caf2a6b7580b5e7fa4054ff2d50a51e5 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1495,7 +1495,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + ServerLevel worldserver1 = this; + this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings()); + this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess())); +- this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); ++ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, null); + } + // CraftBukkit end + } +@@ -1539,7 +1539,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings()); + this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess())); +- this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); ++ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, null); + // CraftBukkit end + } + +diff --git a/src/main/java/net/minecraft/server/players/IpBanListEntry.java b/src/main/java/net/minecraft/server/players/IpBanListEntry.java +index 59d8641bed5556314f6feb62ad190f437469b3ce..cf7c0c995e50d6cc437b8dde6ae663a48e8ae71a 100644 +--- a/src/main/java/net/minecraft/server/players/IpBanListEntry.java ++++ b/src/main/java/net/minecraft/server/players/IpBanListEntry.java +@@ -5,7 +5,7 @@ import java.util.Date; + import javax.annotation.Nullable; + import net.minecraft.network.chat.Component; + +-public class IpBanListEntry extends BanListEntry { ++public class IpBanListEntry extends BanListEntry implements org.plazmamc.plazma.players.BanInfo { + public IpBanListEntry(String ip) { + this(ip, null, null, null, null); + } +@@ -34,4 +34,9 @@ public class IpBanListEntry extends BanListEntry { + super.serialize(json); + } + } ++ ++ @Override ++ public @org.jspecify.annotations.NonNull String getTarget() { ++ return this.getUser(); ++ } + } +diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java +index 1f2958d21c279ecb377b7c90ba643ea83e217eca..6a411f609c48b28115b947494062f9f7bf5b2d93 100644 +--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java ++++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java +@@ -82,7 +82,7 @@ public class OldUsersConverter { + } + + public static boolean convertUserBanlist(final MinecraftServer server) { +- final UserBanList gameprofilebanlist = new UserBanList(PlayerList.USERBANLIST_FILE); ++ final UserBanList gameprofilebanlist = new UserBanList((File) server.options.valueOf("banned-players")); // Plazma - Configurable player list file path + + if (OldUsersConverter.OLD_USERBANLIST.exists() && OldUsersConverter.OLD_USERBANLIST.isFile()) { + if (gameprofilebanlist.getFile().exists()) { +@@ -140,7 +140,7 @@ public class OldUsersConverter { + } + + public static boolean convertIpBanlist(MinecraftServer server) { +- IpBanList ipbanlist = new IpBanList(PlayerList.IPBANLIST_FILE); ++ IpBanList ipbanlist = new IpBanList((File) server.options.valueOf("banned-ips")); // Plazma - Configurable player list file path + + if (OldUsersConverter.OLD_IPBANLIST.exists() && OldUsersConverter.OLD_IPBANLIST.isFile()) { + if (ipbanlist.getFile().exists()) { +@@ -181,7 +181,7 @@ public class OldUsersConverter { + } + + public static boolean convertOpsList(final MinecraftServer server) { +- final ServerOpList oplist = new ServerOpList(PlayerList.OPLIST_FILE); ++ final ServerOpList oplist = new ServerOpList((File) server.options.valueOf("ops")); // Plazma - Configurable player list file path + + if (OldUsersConverter.OLD_OPLIST.exists() && OldUsersConverter.OLD_OPLIST.isFile()) { + if (oplist.getFile().exists()) { +@@ -225,7 +225,7 @@ public class OldUsersConverter { + } + + public static boolean convertWhiteList(final MinecraftServer server) { +- final UserWhiteList whitelist = new UserWhiteList(PlayerList.WHITELIST_FILE); ++ final UserWhiteList whitelist = new UserWhiteList((File) server.options.valueOf("whitelist")); // Plazma - Configurable player list file path + + if (OldUsersConverter.OLD_WHITELIST.exists() && OldUsersConverter.OLD_WHITELIST.isFile()) { + if (whitelist.getFile().exists()) { +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index d5d09bf63f4b7fba73188f75d5fe9599b8da2844..b5396dda015ffa3d11864494a18560c23bbb6945 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -111,7 +111,6 @@ import org.bukkit.Location; + import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.entity.CraftPlayer; +-import org.bukkit.craftbukkit.util.CraftChatMessage; + import org.bukkit.craftbukkit.util.CraftLocation; + import org.bukkit.entity.Player; + import org.bukkit.event.entity.EntityRemoveEvent; +@@ -119,46 +118,49 @@ import org.bukkit.event.player.PlayerChangedWorldEvent; + import org.bukkit.event.player.PlayerJoinEvent; + import org.bukkit.event.player.PlayerLoginEvent; + import org.bukkit.event.player.PlayerQuitEvent; +-import org.bukkit.event.player.PlayerRespawnEvent; + import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; +-import org.bukkit.event.player.PlayerSpawnChangeEvent; + // CraftBukkit end + + public abstract class PlayerList { + ++ /* // Plazma - Configurable persistent player list provider + public static final File USERBANLIST_FILE = new File("banned-players.json"); + public static final File IPBANLIST_FILE = new File("banned-ips.json"); + public static final File OPLIST_FILE = new File("ops.json"); + public static final File WHITELIST_FILE = new File("whitelist.json"); ++ */ // Plazma - Configurable persistent player list provider + public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full"); + public static final Component DUPLICATE_LOGIN_DISCONNECT_MESSAGE = Component.translatable("multiplayer.disconnect.duplicate_login"); + private static final Logger LOGGER = LogUtils.getLogger(); +- private static final int SEND_PLAYER_INFO_INTERVAL = 600; ++ // private static final int SEND_PLAYER_INFO_INTERVAL = 600; + private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); + private static final ru.bk.oharass.freedomchat.FreedomChat FREEDOM_HANDLER = new ru.bk.oharass.freedomchat.FreedomChat(); // Plazma - Implement FreedomChat + private final MinecraftServer server; +- public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety ++ public final List players = new java.util.concurrent.CopyOnWriteArrayList<>(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + private final Map playersByUUID = Maps.newHashMap(); ++ /* // Plazma - Configurable persistent player list provider + private final UserBanList bans; + private final IpBanList ipBans; + private final ServerOpList ops; + private final UserWhiteList whitelist; ++ */ // Plazma - Configurable persistent player list provider + // CraftBukkit start + // private final Map stats; + // private final Map advancements; + // CraftBukkit end ++ private final org.plazmamc.plazma.players.PersistentPlayerListProvider provider; // Plazma - Configurable persistent player list provider + public final PlayerDataStorage playerIo; + private boolean doWhiteList; + private final LayeredRegistryAccess registries; + public int maxPlayers; + private int viewDistance; + private int simulationDistance; +- private boolean allowCommandsForAllPlayers; +- private static final boolean ALLOW_LOGOUTIVATOR = false; ++ // private boolean allowCommandsForAllPlayers; ++ // private static final boolean ALLOW_LOGOUTIVATOR = false; + private int sendAllPlayerInfoIn; + + // CraftBukkit start +- private CraftServer cserver; ++ private final CraftServer cserver; + private final Map playersByName = new java.util.HashMap<>(); + public @Nullable String collideRuleTeamName; // Paper - Configurable player collision + +@@ -167,20 +169,25 @@ public abstract class PlayerList { + server.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper + // CraftBukkit end + ++ /* // Plazma start - Configurable player list file path + this.bans = new UserBanList(PlayerList.USERBANLIST_FILE); + this.ipBans = new IpBanList(PlayerList.IPBANLIST_FILE); + this.ops = new ServerOpList(PlayerList.OPLIST_FILE); + this.whitelist = new UserWhiteList(PlayerList.WHITELIST_FILE); +- // CraftBukkit start +- // this.stats = Maps.newHashMap(); +- // this.advancements = Maps.newHashMap(); +- // CraftBukkit end ++ */ // Plazma end - Configurable player list file path ++ this.provider = cserver.getPersistentPlayerListProvider(); // Plazma - Configurable persistent player list provider + this.server = server; + this.registries = registryManager; + this.maxPlayers = maxPlayers; + this.playerIo = saveHandler; + } +- abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor ++ ++ // Plazma start - Configurable persistent player list provider ++ public void loadAndSaveFiles() { ++ this.provider.load(); ++ this.provider.save(); ++ } ++ // Plazma end - Configurable persistent player list provider + + public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie clientData) { + // Plazma start - load player information asynchronously +@@ -478,7 +485,6 @@ public abstract class PlayerList { + } + + public void addWorldborderListener(ServerLevel world) { +- if (this.playerIo != null) return; // CraftBukkit + world.getWorldBorder().addListener(new BorderChangeListener() { + @Override + public void onBorderSizeSet(WorldBorder border, double size) { +@@ -514,36 +520,15 @@ public abstract class PlayerList { + } + + public Optional load(ServerPlayer player) { +- CompoundTag nbttagcompound = this.server.getWorldData().getLoadedPlayerTag(); +- Optional optional; +- +- if (/*this.server.isSingleplayerOwner(player.getGameProfile()) &&*/ nbttagcompound != null) { // Plazma - Remove persist 'isClientSide' flag +- optional = Optional.of(nbttagcompound); +- player.load(nbttagcompound); +- PlayerList.LOGGER.debug("loading single player"); +- } else { +- optional = this.playerIo.load(player); +- } +- +- return optional; ++ return this.playerIo.load(player); + } + + protected void save(ServerPlayer player) { + if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit + player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving ++ player.getStats().save(); // CraftBukkit ++ player.getAdvancements().save(); // CraftBukkit + this.playerIo.save(player); +- ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit +- +- if (serverstatisticmanager != null) { +- serverstatisticmanager.save(); +- } +- +- PlayerAdvancements advancementdataplayer = (PlayerAdvancements) player.getAdvancements(); // CraftBukkit +- +- if (advancementdataplayer != null) { +- advancementdataplayer.save(); +- } +- + } + + public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer) { // CraftBukkit - return string // Paper - return Component +@@ -662,8 +647,6 @@ public abstract class PlayerList { + + // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer + public ServerPlayer canPlayerLogin(ServerLoginPacketListenerImpl loginlistener, GameProfile gameprofile) { +- MutableComponent ichatmutablecomponent; +- + // Moved from processLogin + UUID uuid = gameprofile.getId(); + List list = Lists.newArrayList(); +@@ -695,33 +678,7 @@ public abstract class PlayerList { + Player player = entity.getBukkitEntity(); + PlayerLoginEvent event = new PlayerLoginEvent(player, loginlistener.connection.hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.channel.remoteAddress()).getAddress()); + +- // Paper start - Fix MC-158900 +- UserBanListEntry gameprofilebanentry; +- if (this.bans.isBanned(gameprofile) && (gameprofilebanentry = this.bans.get(gameprofile)) != null) { +- // Paper end - Fix MC-158900 +- +- ichatmutablecomponent = Component.translatable("multiplayer.disconnect.banned.reason", gameprofilebanentry.getReason()); +- if (gameprofilebanentry.getExpires() != null) { +- ichatmutablecomponent.append((Component) Component.translatable("multiplayer.disconnect.banned.expiration", PlayerList.BAN_DATE_FORMAT.format(gameprofilebanentry.getExpires()))); +- } +- +- // return chatmessage; +- event.disallow(PlayerLoginEvent.Result.KICK_BANNED, io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure +- } else if (!this.isWhiteListed(gameprofile, event)) { // Paper - ProfileWhitelistVerifyEvent +- //ichatmutablecomponent = Component.translatable("multiplayer.disconnect.not_whitelisted"); // Paper +- //event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.whitelistMessage)); // Spigot // Paper - Adventure - moved to isWhitelisted +- } else if (this.getIpBans().isBanned(socketaddress) && getIpBans().get(socketaddress) != null && !this.getIpBans().get(socketaddress).hasExpired()) { // Paper - fix NPE with temp ip bans +- IpBanListEntry ipbanentry = this.ipBans.get(socketaddress); +- +- ichatmutablecomponent = Component.translatable("multiplayer.disconnect.banned_ip.reason", ipbanentry.getReason()); +- if (ipbanentry.getExpires() != null) { +- ichatmutablecomponent.append((Component) Component.translatable("multiplayer.disconnect.banned_ip.expiration", PlayerList.BAN_DATE_FORMAT.format(ipbanentry.getExpires()))); +- } +- +- // return chatmessage; +- event.disallow(PlayerLoginEvent.Result.KICK_BANNED, io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure +- } else { +- // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile) ? IChatBaseComponent.translatable("multiplayer.disconnect.server_full") : null; ++ if (this.isWhiteListed(gameprofile, event) && !this.isBanned(gameprofile, event) && this.isBanned(socketaddress, event)) { // Plazma - Configurable persistent player list provider + if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameprofile))) { // Purpur + event.disallow(PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure + } +@@ -1077,16 +1034,27 @@ public abstract class PlayerList { + return astring; + } + ++ // Plazma start - Configurable persistent player list provider ++ @io.papermc.paper.annotation.DoNotUse ++ @Deprecated(forRemoval = true) + public UserBanList getBans() { +- return this.bans; ++ throw new UnsupportedOperationException(); + } + ++ @io.papermc.paper.annotation.DoNotUse ++ @Deprecated(forRemoval = true) + public IpBanList getIpBans() { +- return this.ipBans; ++ throw new UnsupportedOperationException(); + } + ++ // Plazma start - Add an option to set player can bypass limit + public void op(GameProfile profile) { +- this.ops.add(new ServerOpListEntry(profile, this.server.getOperatorUserPermissionLevel(), this.ops.canBypassPlayerLimit(profile))); ++ this.op(profile, this.server.getOperatorUserPermissionLevel(), false); ++ } ++ ++ public void op(final GameProfile profile, final int permissionLevel, final boolean bypassPlayerLimit) { ++ // Plazma end - Add an option to set player can bypass limit ++ this.provider.addOp(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile), permissionLevel, bypassPlayerLimit); + ServerPlayer entityplayer = this.getPlayer(profile.getId()); + + if (entityplayer != null) { +@@ -1096,7 +1064,7 @@ public abstract class PlayerList { + } + + public void deop(GameProfile profile) { +- this.ops.remove(profile); // CraftBukkit - decompile error ++ this.provider.removeOp(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); + ServerPlayer entityplayer = this.getPlayer(profile.getId()); + + if (entityplayer != null) { +@@ -1104,6 +1072,7 @@ public abstract class PlayerList { + } + + } ++ // Plazma end - Configurable persistent player list provider + + private void sendPlayerPermissionLevel(ServerPlayer player, int permissionLevel) { + // Paper start - Add sendOpLevel API +@@ -1153,13 +1122,12 @@ public abstract class PlayerList { + //Purpur end + } + +- public boolean isWhiteListed(GameProfile profile) { +- // Paper start - ProfileWhitelistVerifyEvent +- return this.isWhiteListed(profile, null); +- } + public boolean isWhiteListed(GameProfile gameprofile, @Nullable org.bukkit.event.player.PlayerLoginEvent loginEvent) { +- boolean isOp = this.ops.contains(gameprofile); +- boolean isWhitelisted = !this.doWhiteList || isOp || this.whitelist.contains(gameprofile); ++ // Plazma start - Configurable persistent player list provider ++ if (!this.doWhiteList) return true; ++ boolean isOp = this.isOp(gameprofile); ++ boolean isWhitelisted = this.isWhiteListed(gameprofile); ++ // Plazma end - Configurable persistent player list provider + final com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent event; + + final net.kyori.adventure.text.Component configuredMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.whitelistMessage); +@@ -1172,11 +1140,63 @@ public abstract class PlayerList { + return false; + } + return true; +- // Paper end - ProfileWhitelistVerifyEvent + } + ++ // Plazma start - Configurable persistent player list provider ++ public boolean isBanned(SocketAddress address, @Nullable org.bukkit.event.player.PlayerLoginEvent loginEvent) { ++ final boolean isBanned = this.provider.isBanned(address); ++ final org.plazmamc.plazma.event.players.IpBanVerifyEvent event; ++ ++ final org.plazmamc.plazma.players.BanInfo info = isBanned ? this.provider.getBanInfo(address) : null; ++ ++ if (isBanned) { ++ net.kyori.adventure.text.Component component = net.kyori.adventure.text.Component.translatable("multiplayer.disconnect.banned_ip.reason", info.getReason()); ++ if (info.getExpires() != null) { ++ component = component.append(net.kyori.adventure.text.Component.translatable("multiplayer.disconnect.banned_ip.expiration", PlayerList.BAN_DATE_FORMAT.format(info.getExpires()))); ++ } ++ ++ event = new org.plazmamc.plazma.event.players.IpBanVerifyEvent(address, info, component); ++ } else { ++ event = new org.plazmamc.plazma.event.players.IpBanVerifyEvent(address); ++ } ++ event.callEvent(); ++ ++ if (!event.isBanned()) return false; ++ ++ if (loginEvent != null) loginEvent.disallow(PlayerLoginEvent.Result.KICK_BANNED, event.kickMessage()); ++ return false; ++ } ++ ++ public boolean isBanned(GameProfile gameprofile, @Nullable org.bukkit.event.player.PlayerLoginEvent loginEvent) { ++ final com.destroystokyo.paper.profile.PlayerProfile bukkit = com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(gameprofile); ++ ++ final boolean isOp = this.isOp(gameprofile); ++ final boolean isBanned = this.provider.isBanned(bukkit); ++ final org.plazmamc.plazma.event.players.ProfileBanVerifyEvent event; ++ ++ final org.plazmamc.plazma.players.BanInfo info = isBanned ? this.provider.getBanInfo(bukkit) : null; ++ ++ if (isBanned) { ++ net.kyori.adventure.text.Component component = net.kyori.adventure.text.Component.translatable("multiplayer.disconnect.banned.reason", info.getReason()); ++ if (info.getExpires() != null) { ++ component = component.append(net.kyori.adventure.text.Component.translatable("multiplayer.disconnect.banned.expiration", PlayerList.BAN_DATE_FORMAT.format(info.getExpires()))); ++ } ++ ++ event = new org.plazmamc.plazma.event.players.ProfileBanVerifyEvent(info, isOp, component); ++ } else { ++ event = new org.plazmamc.plazma.event.players.ProfileBanVerifyEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(gameprofile), isOp); ++ } ++ event.callEvent(); ++ ++ if (!event.isBanned()) return false; ++ ++ if (loginEvent != null) loginEvent.disallow(PlayerLoginEvent.Result.KICK_BANNED, event.kickMessage()); ++ return false; ++ } ++ // Plazma end - Configurable persistent player list provider ++ + public boolean isOp(GameProfile profile) { +- return this.ops.contains(profile) || /*this.server.isSingleplayerOwner(profile) &&*/ this.server.getWorldData().isAllowCommands() || this.allowCommandsForAllPlayers; // Plazma - Remove persist 'isClientSide' flag ++ return this.provider.isOp(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); // Plazma - Remove persist 'isClientSide' flag // Plazma - Configurable persistent player list provider + } + + @Nullable +@@ -1228,23 +1248,31 @@ public abstract class PlayerList { + return null; }); // Paper - ensure main + } + +- public UserWhiteList getWhiteList() { +- return this.whitelist; ++ // Plazma start - Configurable persistent player list provider ++ public void whitelist(GameProfile profile) { ++ this.provider.addWhitelist(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); ++ } ++ ++ public void unWhitelist(GameProfile profile) { ++ this.provider.removeWhitelist(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); + } + +- public String[] getWhiteListNames() { +- return this.whitelist.getUserList(); ++ public boolean isWhiteListed(GameProfile profile) { ++ return this.provider.isWhitelisted(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); + } + +- public ServerOpList getOps() { +- return this.ops; ++ public Collection getOpsList() { ++ return this.provider.getOps().stream().map(it -> ((com.destroystokyo.paper.profile.CraftPlayerProfile) it).getGameProfile()).toList(); + } + +- public String[] getOpNames() { +- return this.ops.getUserList(); ++ public Collection getWhitelists() { ++ return this.provider.getWhitelists().stream().map(it -> ((com.destroystokyo.paper.profile.CraftPlayerProfile) it).getGameProfile()).toList(); + } + +- public void reloadWhiteList() {} ++ public final void reloadWhiteList() { ++ this.provider.reloadWhitelist(); ++ } ++ // Plazma end - Configurable persistent player list provider + + public void sendLevelInfo(ServerPlayer player, ServerLevel world) { + WorldBorder worldborder = player.level().getWorldBorder(); // CraftBukkit +@@ -1324,15 +1352,6 @@ public abstract class PlayerList { + return this.server; + } + +- @Nullable +- public CompoundTag getSingleplayerData() { +- return null; +- } +- +- public void setAllowCommandsForAllPlayers(boolean cheatsAllowed) { +- this.allowCommandsForAllPlayers = cheatsAllowed; +- } +- + public void removeAll() { + // Paper start - Extract method to allow for restarting flag + this.removeAll(false); +@@ -1526,9 +1545,15 @@ public abstract class PlayerList { + return (ServerPlayer) this.playersByUUID.get(uuid); + } + +- public boolean canBypassPlayerLimit(GameProfile profile) { +- return false; ++ // Plazma start - Configurable persistent player list provider ++ public final boolean canBypassPlayerLimit(GameProfile profile) { ++ return this.provider.canBypassPlayerLimit(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); ++ } ++ ++ public final int getPermissionLevel(GameProfile profile) { ++ return this.provider.getPermissionLevel(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile)); + } ++ // Plazma end - Configurable persistent player list provider + + public void reloadResources() { + // Paper start - API for updating recipes on clients +@@ -1577,7 +1602,4 @@ public abstract class PlayerList { + + } + +- public boolean isAllowCommandsForAllPlayers() { +- return this.allowCommandsForAllPlayers; +- } + } +diff --git a/src/main/java/net/minecraft/server/players/ServerOpList.java b/src/main/java/net/minecraft/server/players/ServerOpList.java +index a1c9686043b5a8c5cb1614b46e10484000c920ae..c2f597375003065434f1559959783a5b7de838f1 100644 +--- a/src/main/java/net/minecraft/server/players/ServerOpList.java ++++ b/src/main/java/net/minecraft/server/players/ServerOpList.java +@@ -20,6 +20,13 @@ public class ServerOpList extends StoredUserList + return this.getEntries().stream().map(StoredUserEntry::getUser).filter(Objects::nonNull).map(GameProfile::getName).toArray(String[]::new); + } + ++ // Plazma start - Add an option to set player can bypass limit ++ public int getLevel(GameProfile profile) { ++ ServerOpListEntry serverOpListEntry = this.get(profile); ++ return serverOpListEntry != null ? serverOpListEntry.getLevel() : 0; ++ } ++ // Plazma end - Add an option to set player can bypass limit ++ + public boolean canBypassPlayerLimit(GameProfile profile) { + ServerOpListEntry serverOpListEntry = this.get(profile); + return serverOpListEntry != null && serverOpListEntry.getBypassesPlayerLimit(); +diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java +index 14e20543d097efb999c4ef3eb8fff641616622cf..efa47456c3be9a168457bfb19878a93ab26172f3 100644 +--- a/src/main/java/net/minecraft/server/players/StoredUserList.java ++++ b/src/main/java/net/minecraft/server/players/StoredUserList.java +@@ -42,13 +42,7 @@ public abstract class StoredUserList> { + + public void add(V entry) { + this.map.put(this.getKeyForUser(entry.getUser()), entry); +- +- try { +- this.save(); +- } catch (IOException ioexception) { +- StoredUserList.LOGGER.warn("Could not save the list after adding a user.", ioexception); +- } +- ++ this.save(); // Plazma - Save Json list asynchronously + } + + @Nullable +@@ -62,13 +56,7 @@ public abstract class StoredUserList> { + + public void remove(K key) { + this.map.remove(this.getKeyForUser(key)); +- +- try { +- this.save(); +- } catch (IOException ioexception) { +- StoredUserList.LOGGER.warn("Could not save the list after removing a user.", ioexception); +- } +- ++ this.save(); // Plazma - Save Json list asynchronously + } + + public void remove(StoredUserEntry entry) { +@@ -102,7 +90,9 @@ public abstract class StoredUserList> { + return this.map.values(); + } + +- public void save() throws IOException { ++ // Plazma start - Save Json list asynchronously ++ public void save()/* throws IOException*/ { ++ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> { + this.removeExpired(); // Paper - remove expired values before saving + JsonArray jsonarray = new JsonArray(); + Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error +@@ -114,27 +104,16 @@ public abstract class StoredUserList> { + + Objects.requireNonNull(jsonarray); + stream.forEach(jsonarray::add); +- BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); + +- try { ++ try (BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8)) { + StoredUserList.GSON.toJson(jsonarray, StoredUserList.GSON.newJsonWriter(bufferedwriter)); +- } catch (Throwable throwable) { +- if (bufferedwriter != null) { +- try { +- bufferedwriter.close(); +- } catch (Throwable throwable1) { +- throwable.addSuppressed(throwable1); +- } +- } +- +- throw throwable; +- } +- +- if (bufferedwriter != null) { +- bufferedwriter.close(); ++ } catch (IOException e) { ++ StoredUserList.LOGGER.warn("Failed to asynchronously save file " + this.file, e); + } + ++ }); + } ++ // Plazma end - Save Json list asynchronously + + public void load() throws IOException { + if (this.file.exists()) { +diff --git a/src/main/java/net/minecraft/server/players/UserBanListEntry.java b/src/main/java/net/minecraft/server/players/UserBanListEntry.java +index d4418c140ecbb23b17c46067118ea55fcecb6228..2dd708f804f87312398faa182c1acb36c7280e1a 100644 +--- a/src/main/java/net/minecraft/server/players/UserBanListEntry.java ++++ b/src/main/java/net/minecraft/server/players/UserBanListEntry.java +@@ -8,7 +8,7 @@ import java.util.UUID; + import javax.annotation.Nullable; + import net.minecraft.network.chat.Component; + +-public class UserBanListEntry extends BanListEntry { ++public class UserBanListEntry extends BanListEntry implements org.plazmamc.plazma.players.BanInfo { // Plazma - Configurable persistent player list provider + + public UserBanListEntry(@Nullable GameProfile profile) { + this(profile, (Date) null, (String) null, (Date) null, (String) null); +@@ -65,4 +65,11 @@ public class UserBanListEntry extends BanListEntry { + } + // Spigot End + } ++ ++ // Plazma start - Configurable persistent player list provider ++ @Override ++ public com.destroystokyo.paper.profile.PlayerProfile getTarget() { ++ return com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(this.getUser()); ++ } ++ // Plazma end - Configurable persistent player list provider + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +index a138e1b6b66d99f2035de054137a607aa6b7f0b9..7af22e48609626afe8675c764b5b59d28b3f9f0e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +@@ -146,15 +146,15 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa + + @Override + public boolean isWhitelisted() { +- return this.server.getHandle().getWhiteList().isWhiteListed(this.profile); ++ return this.server.getHandle().isWhiteListed(this.profile); + } + + @Override + public void setWhitelisted(boolean value) { + if (value) { +- this.server.getHandle().getWhiteList().add(new UserWhiteListEntry(this.profile)); ++ this.server.getHandle().whitelist(this.profile); + } else { +- this.server.getHandle().getWhiteList().remove(this.profile); ++ this.server.getHandle().unWhitelist(this.profile); + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 15527e902484496a6804c879d1de589bed3f8713..8f36492a193b495eb1a433f98232354465c72e31 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -282,7 +282,8 @@ public final class CraftServer implements Server { + public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager; // Paper + private final StructureManager structureManager; + protected final DedicatedServer console; +- protected final DedicatedPlayerList playerList; ++ private final DedicatedPlayerList playerList; // Plazma - Configurable player list provider ++ private org.plazmamc.plazma.players.PersistentPlayerListProvider playerListProvider; // Plazma - Configurable player list provider + private final Map worlds = new LinkedHashMap(); + // private final Map, Registry> registries = new HashMap<>(); // Paper - replace with RegistryAccess + private YamlConfiguration configuration; +@@ -407,6 +408,7 @@ public final class CraftServer implements Server { + public CraftServer(DedicatedServer console, PlayerList playerList) { + this.console = console; + this.playerList = (DedicatedPlayerList) playerList; ++ this.playerListProvider = new org.plazmamc.plazma.players.JsonPlayerListProvider(console); + this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, new Function() { + @Override + public CraftPlayer apply(ServerPlayer player) { +@@ -2382,24 +2384,12 @@ public final class CraftServer implements Server { + + @Override + public Set getWhitelistedPlayers() { +- Set result = new LinkedHashSet(); +- +- for (UserWhiteListEntry entry : this.playerList.getWhiteList().getEntries()) { +- result.add(this.getOfflinePlayer(entry.getUser())); +- } +- +- return result; ++ return this.playerList.getWhitelists().stream().map(this::getOfflinePlayer).collect(Collectors.toSet()); + } + + @Override + public Set getOperators() { +- Set result = new HashSet(); +- +- for (ServerOpListEntry entry : this.playerList.getOps().getEntries()) { +- result.add(this.getOfflinePlayer(entry.getUser())); +- } +- +- return result; ++ return this.playerList.getOpsList().stream().map(this::getOfflinePlayer).collect(Collectors.toSet()); + } + + @Override +@@ -3386,4 +3376,16 @@ public final class CraftServer implements Server { + return getServer().lagging; + } + // Purpur end - Lagging threshold ++ ++ // Plazma start - Configurable persistent player list provider ++ @Override ++ public @Nonnull org.plazmamc.plazma.players.PersistentPlayerListProvider getPersistentPlayerListProvider() { ++ return this.playerListProvider; ++ } ++ ++ @Override ++ public void setPersistentPlayerListProvider(final @Nonnull org.plazmamc.plazma.players.PersistentPlayerListProvider provider) { ++ this.playerListProvider = provider; ++ } ++ // Plazma end - Configurable persistent player list provider + } +diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java +index a75f3328ba32466b6ceeddb0069c856524f19c0a..913213c77fa2cf8038768a34b38bb59d698e714b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/Main.java ++++ b/src/main/java/org/bukkit/craftbukkit/Main.java +@@ -205,6 +205,44 @@ public class Main { + .defaultsTo(new File(org.plazmamc.plazma.configurations.PlazmaConfigurations.CONFIG_DIR)) + .describedAs("Configuration Directory"); + // Plazma end - Configurable Plazma ++ ++ // Plazma start - Configurable player data storage ++ acceptsAll(asList("banned-ips"), "File for banned IPs") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("banned-ips.json")) ++ .describedAs("JSON file"); ++ ++ acceptsAll(asList("banned-players"), "File for banned players") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("banned-players.json")) ++ .describedAs("JSON file"); ++ ++ acceptsAll(asList("ops"), "File for ops") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("ops.json")) ++ .describedAs("JSON file"); ++ ++ acceptsAll(asList("whitelist"), "File for whitelist") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("whitelist.json")) ++ .describedAs("JSON file"); ++ ++ acceptsAll(asList("version-history"), "File for version history") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("version_history.json")) ++ .describedAs("JSON file"); ++ ++ acceptsAll(asList("help"), "File for help command") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("help.yml")) ++ .describedAs("Yaml file"); ++ // Plazma end - Configurable player data storage + } + }; + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 20ffe2ac1a7ad4549f46855abdfa0579bcaf82ff..d2418f791b4c8946e4b92ea49ce1440c907fd7d7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1852,15 +1852,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public boolean isWhitelisted() { +- return this.server.getHandle().getWhiteList().isWhiteListed(this.getProfile()); ++ return this.server.getHandle().isWhiteListed(this.getProfile()); + } + + @Override + public void setWhitelisted(boolean value) { + if (value) { +- this.server.getHandle().getWhiteList().add(new UserWhiteListEntry(this.getProfile())); ++ this.server.getHandle().whitelist(this.getProfile()); + } else { +- this.server.getHandle().getWhiteList().remove(this.getProfile()); ++ this.server.getHandle().unWhitelist(this.getProfile()); + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index d52d41d8c56e017f95914da19b05c3d79f8f1640..61ed1fd04d57ff88086d5243eaa8275df46546c3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -263,7 +263,7 @@ public class CraftEventFactory { + + if (world.dimension() != Level.OVERWORLD) return true; + if (spawnSize <= 0) return true; +- if (((CraftServer) Bukkit.getServer()).getHandle().getOps().isEmpty()) return true; ++ if (((CraftServer) Bukkit.getServer()).getHandle().getOpsList().isEmpty()) return true; + if (player.isOp()) return true; + + BlockPos chunkcoordinates = world.getSharedSpawnPos(); +diff --git a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java +index 5923d3c17756c489fcb392044c0679fe52e2d58f..a433d691831c620112a1c824f8f26cb50cfa8dbd 100644 +--- a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java ++++ b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java +@@ -25,7 +25,7 @@ public class HelpYamlReader { + public HelpYamlReader(Server server) { + this.server = server; + +- File helpYamlFile = new File("help.yml"); ++ File helpYamlFile = (File) ((org.bukkit.craftbukkit.CraftServer) server).getHandle().getServer().options.valueOf("help"); // Plazma - Add options to modify the configuration files + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("configurations/help.yml"), Charsets.UTF_8)); + + try { +diff --git a/src/main/java/org/plazmamc/plazma/players/JsonPlayerListProvider.java b/src/main/java/org/plazmamc/plazma/players/JsonPlayerListProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3f8f95696a4ffabbdb9ccb427aa9c50b72a5b978 +--- /dev/null ++++ b/src/main/java/org/plazmamc/plazma/players/JsonPlayerListProvider.java +@@ -0,0 +1,148 @@ ++package org.plazmamc.plazma.players; ++ ++import com.destroystokyo.paper.profile.CraftPlayerProfile; ++import com.destroystokyo.paper.profile.PlayerProfile; ++import com.mojang.logging.LogUtils; ++import net.minecraft.server.dedicated.DedicatedServer; ++import net.minecraft.server.players.IpBanList; ++import net.minecraft.server.players.ServerOpList; ++import net.minecraft.server.players.ServerOpListEntry; ++import net.minecraft.server.players.UserBanList; ++import net.minecraft.server.players.UserWhiteList; ++import net.minecraft.server.players.UserWhiteListEntry; ++import org.jspecify.annotations.NonNull; ++import org.slf4j.Logger; ++import java.io.File; ++import java.net.SocketAddress; ++import java.util.Collection; ++import java.util.Objects; ++ ++public class JsonPlayerListProvider implements PersistentPlayerListProvider { ++ ++ private static final Logger LOGGER = LogUtils.getLogger(); ++ ++ private final UserBanList bans; ++ private final IpBanList ipBans; ++ private final ServerOpList ops; ++ private final UserWhiteList whitelist; ++ ++ public JsonPlayerListProvider(final @NonNull DedicatedServer server) { ++ this.bans = new UserBanList((File) server.options.valueOf("banned-players")); ++ this.ipBans = new IpBanList((File) server.options.valueOf("banned-ips")); ++ this.ops = new ServerOpList((File) server.options.valueOf("ops")); ++ this.whitelist = new UserWhiteList((File) server.options.valueOf("whitelist")); ++ } ++ ++ @Override ++ public void load() { ++ try { ++ this.bans.load(); ++ this.ipBans.load(); ++ this.ops.load(); ++ this.whitelist.load(); ++ } catch (Exception exception) { ++ LOGGER.warn("Failed to load player list", exception); ++ } ++ } ++ ++ @Override ++ public void save() { ++ this.bans.save(); ++ this.ipBans.save(); ++ this.ops.save(); ++ this.whitelist.save(); ++ } ++ ++ @Override ++ public void reloadWhitelist() { ++ try { ++ this.whitelist.load(); ++ } catch (Exception exception) { ++ LOGGER.warn("Failed to load player list", exception); ++ } ++ } ++ ++ @Override ++ public void addOp(final @NonNull PlayerProfile profile, final int permissionsLevel, final boolean bypassPlayerLimit) { ++ this.ops.add(new ServerOpListEntry(((CraftPlayerProfile) profile).getGameProfile(), permissionsLevel, bypassPlayerLimit)); ++ this.ops.save(); ++ } ++ ++ @Override ++ public void removeOp(final @NonNull PlayerProfile profile) { ++ this.ops.remove(((CraftPlayerProfile) profile).getGameProfile()); ++ this.ops.save(); ++ } ++ ++ @Override ++ public void addWhitelist(final @NonNull PlayerProfile profile) { ++ this.whitelist.add(new UserWhiteListEntry(((CraftPlayerProfile) profile).getGameProfile())); ++ this.whitelist.save(); ++ } ++ ++ @Override ++ public void removeWhitelist(final @NonNull PlayerProfile profile) { ++ this.whitelist.remove(((CraftPlayerProfile) profile).getGameProfile()); ++ this.whitelist.save(); ++ } ++ ++ @Override ++ public int getPermissionLevel(final @NonNull PlayerProfile profile) { ++ return this.ops.getLevel(((CraftPlayerProfile) profile).getGameProfile()); ++ } ++ ++ @Override ++ public boolean isOp(final @NonNull PlayerProfile profile) { ++ return this.ops.get(((CraftPlayerProfile) profile).getGameProfile()) != null; ++ } ++ ++ @Override ++ public boolean isBanned(final @NonNull PlayerProfile profile) { ++ return this.bans.isBanned(((CraftPlayerProfile) profile).getGameProfile()); ++ } ++ ++ @Override ++ public boolean isBanned(final @NonNull SocketAddress address) { ++ return this.ipBans.isBanned(address); ++ } ++ ++ @Override ++ public boolean isWhitelisted(final @NonNull PlayerProfile profile) { ++ return this.whitelist.isWhiteListed(((CraftPlayerProfile) profile).getGameProfile()); ++ } ++ ++ @Override ++ public boolean canBypassPlayerLimit(final @NonNull PlayerProfile profile) { ++ return this.ops.canBypassPlayerLimit(((CraftPlayerProfile) profile).getGameProfile()); ++ } ++ ++ @Override ++ public @NonNull BanInfo getBanInfo(final @NonNull PlayerProfile profile) { ++ return Objects.requireNonNull(this.bans.get(((CraftPlayerProfile) profile).getGameProfile())); ++ } ++ ++ @Override ++ public @NonNull BanInfo getBanInfo(final @NonNull SocketAddress address) { ++ return Objects.requireNonNull(this.ipBans.get(address)); ++ } ++ ++ @Override ++ public @NonNull Collection getOps() { ++ return this.ops.getEntries().stream().map(ServerOpListEntry::getUser).map(CraftPlayerProfile::asBukkitMirror).toList(); ++ } ++ ++ @Override ++ public @NonNull Collection getWhitelists() { ++ return this.whitelist.getEntries().stream().map(UserWhiteListEntry::getUser).map(CraftPlayerProfile::asBukkitMirror).toList(); ++ } ++ ++ @Override ++ public @NonNull Collection getBannedPlayers() { ++ return this.bans.getEntries().stream().map(BanInfo::getTarget).toList(); ++ } ++ ++ @Override ++ public @NonNull Collection getBannedIps() { ++ return this.ipBans.getEntries().stream().map(BanInfo::getTarget).toList(); ++ } ++} diff --git a/patches/server/0062-Add-option-to-set-player-can-bypass-limit.patch b/patches/server/0062-Add-option-to-set-player-can-bypass-limit.patch deleted file mode 100644 index a1274851a..000000000 --- a/patches/server/0062-Add-option-to-set-player-can-bypass-limit.patch +++ /dev/null @@ -1,125 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AlphaKR93 -Date: Wed, 25 Dec 2024 15:08:03 +0900 -Subject: [PATCH] Add option to set player can bypass limit - - -diff --git a/src/main/java/net/minecraft/server/commands/OpCommand.java b/src/main/java/net/minecraft/server/commands/OpCommand.java -index e7b444a10b244828827b3c66c53465206ea8e0ec..af455e7f1c22fda87dbc5b487b757d046dcc2d49 100644 ---- a/src/main/java/net/minecraft/server/commands/OpCommand.java -+++ b/src/main/java/net/minecraft/server/commands/OpCommand.java -@@ -34,20 +34,41 @@ public class OpCommand { - } - ) - .executes(context -> opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"))) -+ // Plazma start - Add an option to set player can bypass limit -+ .then( -+ Commands.argument("permissionLevel", com.mojang.brigadier.arguments.IntegerArgumentType.integer(0, 4)) -+ .executes(context -> -+ opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"), com.mojang.brigadier.arguments.IntegerArgumentType.getInteger(context, "permissionLevel"), false) -+ ) -+ .then( -+ Commands.argument("bypassPlayerLimit", com.mojang.brigadier.arguments.BoolArgumentType.bool()) -+ .executes(context -> -+ opPlayers(context.getSource(), GameProfileArgument.getGameProfiles(context, "targets"), com.mojang.brigadier.arguments.IntegerArgumentType.getInteger(context, "permissionLevel"), com.mojang.brigadier.arguments.BoolArgumentType.getBool(context, "bypassPlayerLimit")) -+ ) -+ ) -+ ) -+ // Plazma end - Add an option to set player can bypass limit - ) - ); - } -- - private static int opPlayers(CommandSourceStack source, Collection targets) throws CommandSyntaxException { -+ // Plazma start - Add an option to set player can bypass limit -+ return opPlayers(source, targets, source.getServer().getOperatorUserPermissionLevel(), false); -+ } -+ -+ private static int opPlayers(final CommandSourceStack source, final Collection targets, final int permissionLevel, final boolean bypassPlayerLimit) throws CommandSyntaxException { -+ // Plazma end - Add an option to set player can bypass limit - PlayerList playerList = source.getServer().getPlayerList(); - int i = 0; - - for (GameProfile gameProfile : targets) { -- if (!playerList.isOp(gameProfile)) { -- playerList.op(gameProfile); -- i++; -- source.sendSuccess(() -> Component.translatable("commands.op.success", gameProfile.getName()), true); // Paper - fixes MC-253721 -- } -+ // Plazma start - Add an option to set player can bypass limit -+ if (playerList.isOp(gameProfile) && playerList.canBypassPlayerLimit(gameProfile) == bypassPlayerLimit && playerList.getPermissionLevel(gameProfile) == permissionLevel) continue; -+ -+ playerList.op(gameProfile, permissionLevel, bypassPlayerLimit); -+ source.sendSuccess(() -> Component.translatable("commands.op.success", gameProfile.getName()), true); // Paper - fixes MC-253721 -+ i++; -+ // Plazma end - Add an option to set player can bypass limit - } - - if (i == 0) { -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -index 20c531f11b310dab0a867e589c769393ed835df5..7d9358f3e287d1f05d799b1c8f59509e6ede8581 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java -@@ -122,4 +122,11 @@ public class DedicatedPlayerList extends PlayerList { - public boolean canBypassPlayerLimit(GameProfile profile) { - return this.getOps().canBypassPlayerLimit(profile); - } -+ -+ // Plazma start - Add an option to set player can bypass limit -+ @Override -+ public int getPermissionLevel(GameProfile profile) { -+ return this.getOps().getLevel(profile); -+ } -+ // Plazma end - Add an option to set player can bypass limit - } -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 8ab6df63d9a22ac76b2ba14bc8fef318e65484c8..81bae0453e09454c43d547c48de2f201a5e41023 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1085,8 +1085,14 @@ public abstract class PlayerList { - return this.ipBans; - } - -+ // Plazma start - Add an option to set player can bypass limit - public void op(GameProfile profile) { -- this.ops.add(new ServerOpListEntry(profile, this.server.getOperatorUserPermissionLevel(), this.ops.canBypassPlayerLimit(profile))); -+ this.op(profile, this.server.getOperatorUserPermissionLevel(), this.ops.canBypassPlayerLimit(profile)); -+ } -+ -+ public void op(final GameProfile profile, final int permissionLevel, final boolean bypassPlayerLimit) { -+ // Plazma end - Add an option to set player can bypass limit -+ this.ops.add(new ServerOpListEntry(profile, permissionLevel, bypassPlayerLimit)); - ServerPlayer entityplayer = this.getPlayer(profile.getId()); - - if (entityplayer != null) { -@@ -1530,6 +1536,12 @@ public abstract class PlayerList { - return false; - } - -+ // Plazma start - Add an option to set player can bypass limit -+ public int getPermissionLevel(GameProfile profile) { -+ return 0; -+ } -+ // Plazma end - Add an option to set player can bypass limit -+ - public void reloadResources() { - // Paper start - API for updating recipes on clients - this.reloadAdvancementData(); -diff --git a/src/main/java/net/minecraft/server/players/ServerOpList.java b/src/main/java/net/minecraft/server/players/ServerOpList.java -index a1c9686043b5a8c5cb1614b46e10484000c920ae..c2f597375003065434f1559959783a5b7de838f1 100644 ---- a/src/main/java/net/minecraft/server/players/ServerOpList.java -+++ b/src/main/java/net/minecraft/server/players/ServerOpList.java -@@ -20,6 +20,13 @@ public class ServerOpList extends StoredUserList - return this.getEntries().stream().map(StoredUserEntry::getUser).filter(Objects::nonNull).map(GameProfile::getName).toArray(String[]::new); - } - -+ // Plazma start - Add an option to set player can bypass limit -+ public int getLevel(GameProfile profile) { -+ ServerOpListEntry serverOpListEntry = this.get(profile); -+ return serverOpListEntry != null ? serverOpListEntry.getLevel() : 0; -+ } -+ // Plazma end - Add an option to set player can bypass limit -+ - public boolean canBypassPlayerLimit(GameProfile profile) { - ServerOpListEntry serverOpListEntry = this.get(profile); - return serverOpListEntry != null && serverOpListEntry.getBypassesPlayerLimit();