diff --git a/leaf-server/minecraft-patches/features/0278-Op-lock.patch b/leaf-server/minecraft-patches/features/0278-Op-lock.patch new file mode 100644 index 00000000..bcd47f94 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0278-Op-lock.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Tue, 22 Jul 2025 22:31:58 +0200 +Subject: [PATCH] Op lock + + +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index 570602e0fb00465eed1dcfb923775807c600a7bb..27763ca0ae77d271aa30a3feea76728cad5b4990 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -1213,6 +1213,9 @@ public abstract class PlayerList { + } + + public void op(GameProfile profile) { ++ if (org.dreeam.leaf.config.modules.misc.OpLock.preventOpChanges) { ++ return; ++ } + this.ops.add(new ServerOpListEntry(profile, this.server.getOperatorUserPermissionLevel(), this.ops.canBypassPlayerLimit(profile))); + ServerPlayer player = this.getPlayer(profile.getId()); + if (player != null) { +@@ -1221,6 +1224,9 @@ public abstract class PlayerList { + } + + public void deop(GameProfile profile) { ++ if (org.dreeam.leaf.config.modules.misc.OpLock.preventOpChanges) { ++ return; ++ } + this.ops.remove(profile); + ServerPlayer player = this.getPlayer(profile.getId()); + if (player != null) { diff --git a/leaf-server/paper-patches/features/0062-Op-lock.patch b/leaf-server/paper-patches/features/0062-Op-lock.patch new file mode 100644 index 00000000..8fe0bec7 --- /dev/null +++ b/leaf-server/paper-patches/features/0062-Op-lock.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Tue, 22 Jul 2025 22:33:07 +0200 +Subject: [PATCH] Op lock + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +index 3b516f57854f6482b3748d0c3a037fdeacfe78ab..b4e393da9d445aa7779868310cabfebff78f64dc 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +@@ -100,10 +100,14 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa + + @Override + public void setOp(boolean value) { ++ if (org.dreeam.leaf.config.modules.misc.OpLock.preventOpChanges) { ++ Bukkit.getLogger().severe("[Leaf] A plugin tried to change the player op! Dumping stacktrace now."); ++ Thread.dumpStack(); ++ return; ++ } + if (value == this.isOp()) { + return; + } +- + if (value) { + this.server.getHandle().op(this.profile); + } else { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +index b02826fc7390eb1fd7c2a1b597a5f1abf156b1c1..736563bd3a462911ec99a8158fc5bd3e4115aa62 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +@@ -306,6 +306,11 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { + + @Override + public void setOp(boolean value) { ++ if (org.dreeam.leaf.config.modules.misc.OpLock.preventOpChanges) { ++ org.bukkit.Bukkit.getLogger().severe("[Leaf] A plugin tried to change the player op! Dumping stacktrace now."); ++ Thread.dumpStack(); ++ return; ++ } + this.op = value; + this.perm.recalculatePermissions(); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 0f085618f6866bef1a526c6404f92e26f7bef0fd..f044acf8862642231369fc81af56d0b018be1d50 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -389,6 +389,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa + + @Override + public void setOp(boolean value) { ++ if (org.dreeam.leaf.config.modules.misc.OpLock.preventOpChanges) { ++ Bukkit.getLogger().severe("[Leaf] A plugin tried to change the player op! Dumping stacktrace now."); ++ Thread.dumpStack(); ++ return; ++ } + if (value == this.isOp()) return; + + if (value) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/OpLock.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/OpLock.java new file mode 100644 index 00000000..cb8c3e2a --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/OpLock.java @@ -0,0 +1,27 @@ +package org.dreeam.leaf.config.modules.misc; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OpLock extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.MISC.getBaseKeyName() + ".op-system-protection"; + } + + public static boolean preventOpChanges = false; + + @Override + public void onLoaded() { + config.addCommentRegionBased(getBasePath(), """ + When enabled, prevents plugins from programmatically changing player operator status. + This helps maintain server security by blocking unauthorized op modifications. + Server administrators can still manually manage ops through console/commands.""", + """ + 启用后,防止插件以编程方式更改玩家操作员状态。 + 这有助于通过阻止未经授权的op修改来维护服务器安全性。 + 服务器管理员仍可通过控制台/命令手动管理ops。"""); + + preventOpChanges = config.getBoolean(getBasePath() + ".prevent-op-changes", preventOpChanges); + } +}