diff --git a/patches/server/0038-Add-Scissors-configuration-file-command.patch b/patches/server/0038-Add-Scissors-configuration-file-command.patch new file mode 100644 index 0000000..0ba75f3 --- /dev/null +++ b/patches/server/0038-Add-Scissors-configuration-file-command.patch @@ -0,0 +1,699 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Telesphoreo +Date: Wed, 15 Mar 2023 23:13:56 -0500 +Subject: [PATCH] Add Scissors configuration file & command + + +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +index 06bff37e4c1fddd3be6343049a66787c63fb420c..f6667c193e1c52dd954acb90d09748251976dd42 100644 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -241,7 +241,8 @@ public class TimingsExport extends Thread { + parent.put("config", createObject( + pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), + pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), +- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) ++ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), ++ pair("scissors", mapAsJSON(ScissorsConfig.config, null)) // Scissors + )); + + new TimingsExport(listeners, parent, history).start(); +diff --git a/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java b/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..797677d892d83cf54d9a60af1e277b67ed3d6e95 +--- /dev/null ++++ b/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java +@@ -0,0 +1,150 @@ ++package me.totalfreedom.scissors; ++ ++import com.google.common.base.Functions; ++import com.google.common.base.Joiner; ++import com.google.common.collect.ImmutableSet; ++import com.google.common.collect.Iterables; ++import com.google.common.collect.Lists; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Bukkit; ++import org.bukkit.ChatColor; ++import org.bukkit.Location; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandSender; ++ ++import java.io.File; ++import java.util.*; ++import java.util.stream.Collectors; ++ ++public class ScissorsCommand extends Command ++{ ++ ++ private static final String BASE_PERM = "bukkit.command.scissors."; ++ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("reload", "version").build(); ++ ++ public ScissorsCommand(String name) ++ { ++ super(name); ++ this.description = "Scissors related commands"; ++ this.usageMessage = "/scissors [" + Joiner.on(" | ").join(SUBCOMMANDS) + "]"; ++ this.setPermission("bukkit.command.scissors;" + Joiner.on(';').join(SUBCOMMANDS.stream().map(s -> BASE_PERM + s).collect(Collectors.toSet()))); ++ } ++ ++ private static boolean testPermission(CommandSender commandSender, String permission) ++ { ++ if (commandSender.hasPermission(BASE_PERM + permission) || commandSender.hasPermission("bukkit.command.scissors")) ++ return true; ++ commandSender.sendMessage(Bukkit.getPermissionMessage()); // Sorry, kashike ++ return false; ++ } ++ ++ // Code from Mojang - copyright them ++ public static List getListMatchingLast(CommandSender sender, String[] args, String... matches) ++ { ++ return getListMatchingLast(sender, args, Arrays.asList(matches)); ++ } ++ ++ public static boolean matches(String s, String s1) ++ { ++ return s1.regionMatches(true, 0, s, 0, s.length()); ++ } ++ ++ public static List getListMatchingLast(CommandSender sender, String[] strings, Collection collection) ++ { ++ String last = strings[strings.length - 1]; ++ ArrayList results = Lists.newArrayList(); ++ ++ if (!collection.isEmpty()) ++ { ++ Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator(); ++ ++ while (iterator.hasNext()) ++ { ++ String s1 = (String) iterator.next(); ++ ++ if (matches(last, s1) && (sender.hasPermission(BASE_PERM + s1) || sender.hasPermission("bukkit.command.scissors"))) ++ { ++ results.add(s1); ++ } ++ } ++ ++ if (results.isEmpty()) ++ { ++ iterator = collection.iterator(); ++ ++ while (iterator.hasNext()) ++ { ++ Object object = iterator.next(); ++ ++ if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) ++ { ++ results.add(String.valueOf(object)); ++ } ++ } ++ } ++ } ++ ++ return results; ++ } ++ ++ @Override ++ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException ++ { ++ if (args.length <= 1) ++ return getListMatchingLast(sender, args, SUBCOMMANDS); ++ ++ return Collections.emptyList(); ++ } ++ // end copy stuff ++ ++ @Override ++ public boolean execute(CommandSender sender, String commandLabel, String[] args) ++ { ++ if (!testPermission(sender)) return true; ++ ++ if (args.length == 0) ++ { ++ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); ++ return false; ++ } ++ if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) ++ { ++ if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true; ++ } ++ switch (args[0].toLowerCase(Locale.ENGLISH)) ++ { ++ case "reload": ++ doReload(sender); ++ break; ++ case "ver": ++ if (!testPermission(sender, "version")) ++ break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set) ++ case "version": ++ Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); ++ if (ver != null) ++ { ++ ver.execute(sender, commandLabel, new String[0]); ++ break; ++ } ++ // else - fall through to default ++ default: ++ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); ++ return false; ++ } ++ ++ return true; ++ } ++ ++ private void doReload(CommandSender sender) ++ { ++ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); ++ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); ++ ++ MinecraftServer console = MinecraftServer.getServer(); ++ ScissorsConfig.init((File) console.options.valueOf("scissors-settings")); ++ console.server.reloadCount++; ++ ++ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Scissors config reload complete."); ++ } ++} +diff --git a/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java b/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9f2a51fddb692f5152c32dcbe1f5a6ba150634e4 +--- /dev/null ++++ b/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java +@@ -0,0 +1,213 @@ ++package me.totalfreedom.scissors; ++ ++ ++import com.google.common.base.Throwables; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.dedicated.DedicatedServer; ++import org.bukkit.Bukkit; ++import org.bukkit.command.Command; ++import org.bukkit.configuration.InvalidConfigurationException; ++import org.bukkit.configuration.file.YamlConfiguration; ++ ++import java.io.File; ++import java.io.IOException; ++import java.lang.reflect.InvocationTargetException; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.logging.Level; ++import java.util.regex.Pattern; ++ ++// TODO - Migrate to new format ++public class ScissorsConfig ++{ ++ ++ private static final String HEADER = """ ++ This is the main configuration file for Scissors. ++ As you can see, there's tons to configure. Some options may impact gameplay, so use ++ with caution, and make sure you know what each option does before configuring. ++ ++ If you need help with the configuration or have any questions related to Scissors, ++ join us in our Discord. ++ ++ Discord: https://discord.com/invite/mtVQcHn58h ++ Website: https://scissors.gg/\s ++ Docs: https://scissors.gg/javadoc/1.19.4/\s ++ """; ++ private static final Pattern SPACE = Pattern.compile(" "); ++ private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]"); ++ /*========================================================================*/ ++ public static YamlConfiguration config; ++ static int version; ++ /*========================================================================*/ ++ static Map commands; ++ private static File CONFIG_FILE; ++ ++ public static void init(File configFile) ++ { ++ final File configFolder = (File) DedicatedServer.getServer().options.valueOf("scissors-settings" + "-directory"); ++ final Path configFolderPath = configFolder.toPath(); ++ final Path oldConfigFilePath = configFile.toPath(); ++ final Path newConfigFilePath = configFolderPath.resolve(configFile.toPath()); ++ ++ if (configFile.exists()) ++ { ++ try ++ { ++ Files.move(oldConfigFilePath, newConfigFilePath); ++ } ++ catch (IOException e) ++ { ++ throw new RuntimeException("Error migrating configuration file to new directory!", e); ++ } ++ } ++ ++ CONFIG_FILE = newConfigFilePath.toFile(); ++ config = new YamlConfiguration(); ++ try ++ { ++ config.load(CONFIG_FILE); ++ } ++ catch (IOException ex) ++ { ++ } ++ catch (InvalidConfigurationException ex) ++ { ++ Bukkit.getLogger().log(Level.SEVERE, "Could not load scissors.yml, please correct your syntax errors", ex); ++ throw Throwables.propagate(ex); ++ } ++ ++ commands = new HashMap<>(); ++ commands.put("scissors", new ScissorsCommand("scissors")); ++ ++ config.options().header(HEADER); ++ config.options().copyDefaults(true); ++ ++ version = getInt("config-version", 3); ++ set("config-version", 3); ++ readConfig(ScissorsConfig.class, null); ++ } ++ ++ protected static void logError(String s) ++ { ++ Bukkit.getLogger().severe(s); ++ } ++ ++ protected static void fatal(String s) ++ { ++ throw new RuntimeException("Fatal scissors.yml config error: " + s); ++ } ++ ++ public static void registerCommands() ++ { ++ for (Map.Entry entry : commands.entrySet()) ++ { ++ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Scissors", entry.getValue()); ++ } ++ } ++ ++ static void readConfig(Class clazz, Object instance) ++ { ++ for (Method method : clazz.getDeclaredMethods()) ++ { ++ if (Modifier.isPrivate(method.getModifiers())) ++ { ++ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) ++ { ++ try ++ { ++ method.setAccessible(true); ++ method.invoke(instance); ++ } ++ catch (InvocationTargetException ex) ++ { ++ throw Throwables.propagate(ex.getCause()); ++ } ++ catch (Exception ex) ++ { ++ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); ++ } ++ } ++ } ++ } ++ saveConfig(); ++ } ++ ++ static void saveConfig() ++ { ++ try ++ { ++ config.save(CONFIG_FILE); ++ } ++ catch (IOException ex) ++ { ++ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); ++ } ++ } ++ ++ public static boolean runCommandsInBooks = false; ++ ++ private static void runCommandsInBooks() ++ { ++ runCommandsInBooks = getBoolean("runCommandsInBooks", false); ++ } ++ ++ // people still may want them to bypass permissions for warps ++ public static boolean commandSignsBypassPermissions = false; ++ private static void commandSignsBypassPermissions() ++ { ++ commandSignsBypassPermissions = getBoolean("commandSignsBypassPermissions", false); ++ } ++ ++ public static boolean chatSignaturesEnabled = true; ++ private static void chatSignaturesEnabled() ++ { ++ chatSignaturesEnabled = getBoolean("chatSignaturesEnabled", true); ++ } ++ ++ private static void set(String path, Object val) ++ { ++ config.set(path, val); ++ } ++ ++ private static boolean getBoolean(String path, boolean def) ++ { ++ config.addDefault(path, def); ++ return config.getBoolean(path, config.getBoolean(path)); ++ } ++ ++ private static double getDouble(String path, double def) ++ { ++ config.addDefault(path, def); ++ return config.getDouble(path, config.getDouble(path)); ++ } ++ ++ private static float getFloat(String path, float def) ++ { ++ // TODO: Figure out why getFloat() always returns the default value. ++ return (float) getDouble(path, def); ++ } ++ ++ private static int getInt(String path, int def) ++ { ++ config.addDefault(path, def); ++ return config.getInt(path, config.getInt(path)); ++ } ++ ++ private static List getList(String path, T def) ++ { ++ config.addDefault(path, def); ++ return config.getList(path, config.getList(path)); ++ } ++ ++ private static String getString(String path, String def) ++ { ++ config.addDefault(path, def); ++ return config.getString(path, config.getString(path)); ++ } ++} ++ +diff --git a/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java b/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java +index 74cf1c043beef03cfd5adf481414a5ee78bef2a6..182f68359a7a8c0b331acd103ed3952ea3c33b93 100644 +--- a/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java ++++ b/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java +@@ -1,5 +1,6 @@ + package net.minecraft.network.chat; + ++import me.totalfreedom.scissors.ScissorsConfig; + import net.minecraft.server.level.ServerPlayer; + + public interface OutgoingChatMessage { +@@ -42,12 +43,16 @@ public interface OutgoingChatMessage { + } + public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) { + // Paper end ++ // Scissors start - Add configuration option to disable chat signatures + PlayerChatMessage playerChatMessage = this.message.filter(filterMaskEnabled); + playerChatMessage = unsigned != null ? playerChatMessage.withUnsignedContent(unsigned) : playerChatMessage; // Paper +- if (!playerChatMessage.isFullyFiltered()) { ++ if (!playerChatMessage.isFullyFiltered() && /* Scissors */ ScissorsConfig.chatSignaturesEnabled) { + sender.connection.sendPlayerChatMessage(playerChatMessage, params); ++ return; + } + ++ sender.connection.sendDisguisedChatMessage(playerChatMessage.decoratedContent(), params); ++ // Scissors end + } + } + } +diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java +index c6fa6bcd66d61359124a8426b919493c6ec43f06..0b44304fa7f6c2856f9dd18ffc1239058c1de0a8 100644 +--- a/src/main/java/net/minecraft/server/Main.java ++++ b/src/main/java/net/minecraft/server/Main.java +@@ -136,6 +136,7 @@ public class Main { + // Paper start - load config files for access below if needed + org.bukkit.configuration.file.YamlConfiguration bukkitConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("bukkit-settings")); + org.bukkit.configuration.file.YamlConfiguration spigotConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("spigot-settings")); ++ org.bukkit.configuration.file.YamlConfiguration scissorsConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("scissors-settings")); // Scissors - TODO Change this + // Paper end + + if (optionset.has("initSettings")) { // CraftBukkit +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index a7e133f3495e9132a5fdae2c24f225e7b026295a..16f0daad2456ce23f8d73389f62aac9f10235100 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -222,6 +222,16 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider + // Paper end + ++ // Scissors start ++ try { ++ me.totalfreedom.scissors.ScissorsConfig.init((java.io.File) options.valueOf("scissors-settings")); ++ } catch (Exception e) { ++ DedicatedServer.LOGGER.error("Unable to load server configuration", e); ++ return false; ++ } ++ me.totalfreedom.scissors.ScissorsConfig.registerCommands(); ++ // Scissors end ++ + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); + this.setMotd(dedicatedserverproperties.motd); +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index c15c5855dca11cd69f316c42783d32b800a93bd7..1ec1cc90c7e698036f6ae446f0dab97f03ebb064 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1,5 +1,6 @@ + package net.minecraft.server.network; + ++import me.totalfreedom.scissors.ScissorsConfig; + import me.totalfreedom.scissors.event.player.SpectatorTeleportEvent; + import com.google.common.collect.Lists; + import com.google.common.primitives.Floats; +@@ -2360,6 +2361,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + Optional optional = this.lastSeenMessages.applyUpdate(acknowledgment); + + if (optional.isEmpty()) { ++ // Scissors start - Add configuration option to disable chat signatures ++ if (!ScissorsConfig.chatSignaturesEnabled) ++ { ++ return optional; ++ } ++ // Scissors end + ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); + this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } +@@ -2592,11 +2599,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + @Override + public void handleChatAck(ServerboundChatAckPacket packet) { + LastSeenMessagesValidator lastseenmessagesvalidator = this.lastSeenMessages; +- +- synchronized (this.lastSeenMessages) { + if (!this.lastSeenMessages.applyOffset(packet.offset())) { +- ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); +- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes ++ synchronized (this.lastSeenMessages) { ++ // Scissors start - Add configuration option to disable chat signatures ++ if (!ScissorsConfig.chatSignaturesEnabled) ++ { ++ return; ++ } ++ // Scissors end ++ ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); ++ this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } + + } +diff --git a/src/main/java/net/minecraft/world/item/WrittenBookItem.java b/src/main/java/net/minecraft/world/item/WrittenBookItem.java +index 31911c09fe15753ae32fa39417bdc9e9de552a88..97ce45c6e44b1400b8d60b52397617038f8fe911 100644 +--- a/src/main/java/net/minecraft/world/item/WrittenBookItem.java ++++ b/src/main/java/net/minecraft/world/item/WrittenBookItem.java +@@ -2,6 +2,8 @@ package net.minecraft.world.item; + + import java.util.List; + import javax.annotation.Nullable; ++ ++import me.totalfreedom.scissors.ScissorsConfig; + import net.minecraft.ChatFormatting; + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.core.BlockPos; +@@ -9,8 +11,7 @@ import net.minecraft.nbt.CompoundTag; + import net.minecraft.nbt.ListTag; + import net.minecraft.nbt.StringTag; + import net.minecraft.nbt.Tag; +-import net.minecraft.network.chat.Component; +-import net.minecraft.network.chat.ComponentUtils; ++import net.minecraft.network.chat.*; + import net.minecraft.stats.Stats; + import net.minecraft.util.StringUtil; + import net.minecraft.world.InteractionHand; +@@ -111,8 +112,7 @@ public class WrittenBookItem extends Item { + + public static boolean resolveBookComponents(ItemStack book, @Nullable CommandSourceStack commandSource, @Nullable Player player) { + CompoundTag compoundTag = book.getTag(); +- if (io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.resolveSelectorsInBooks && compoundTag != null && !compoundTag.getBoolean("resolved")) { // Paper +- compoundTag.putBoolean("resolved", true); ++ if (compoundTag != null) { // Paper + if (!makeSureTagIsValid(compoundTag)) { + return false; + } else { +@@ -161,8 +161,42 @@ public class WrittenBookItem extends Item { + component2 = Component.literal(text); + } + +- return Component.Serializer.toJson(component2); ++ return Component.Serializer.toJson(!ScissorsConfig.runCommandsInBooks ? sanitize(component2, 0) : component2); // Scissors - Allow server owners to disable run command in books ++ } ++ ++ // Scissors start - Allow server owners to disable run command in books ++ public static Component sanitize(Component component, int depth) ++ { ++ if (depth > 128) ++ { ++ return Component.nullToEmpty("Sanitization function depth limit exceeded"); ++ } ++ ++ MutableComponent component2 = component.copy(); ++ ++ final Style style = component2.getStyle(); ++ final ClickEvent clickEvent = style.getClickEvent(); ++ ++ if (clickEvent != null && clickEvent.getAction().equals(ClickEvent.Action.RUN_COMMAND)) ++ { ++ final String clickEventValue = clickEvent.getValue(); ++ ++ component2 = component2.copy().setStyle(style ++ .withClickEvent(null) ++ .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.nullToEmpty("Would've " + (clickEventValue.startsWith("/") ? "ran" : "said") + ": " + clickEvent.getValue()))) ++ ); ++ } ++ ++ final List processedExtra = component2.getSiblings() ++ .stream() ++ .map(comp -> sanitize(comp, depth + 1)) ++ .toList(); ++ component2.getSiblings().clear(); ++ component2.getSiblings().addAll(processedExtra); ++ ++ return component2; + } ++ // Scissors end + + @Override + public boolean isFoil(ItemStack stack) { +diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java +index 4da4edae517a0efec6e03a719ec47b700509dab1..837b20be528bc33f62c0c675d90afebd0a781b6f 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -4,8 +4,11 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; + import java.util.UUID; + import java.util.function.Function; + import javax.annotation.Nullable; ++ ++import me.totalfreedom.scissors.ScissorsConfig; + import net.minecraft.commands.CommandSource; + import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.Commands; + import net.minecraft.core.BlockPos; + import net.minecraft.nbt.CompoundTag; + import net.minecraft.network.chat.ClickEvent; +@@ -15,6 +18,7 @@ import net.minecraft.network.chat.ComponentUtils; + import net.minecraft.network.chat.MutableComponent; + import net.minecraft.network.chat.Style; + import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; ++import net.minecraft.server.MinecraftServer; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.server.level.ServerPlayer; + import net.minecraft.util.FormattedCharSequence; +@@ -24,6 +28,8 @@ import net.minecraft.world.item.DyeColor; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.phys.Vec2; + import net.minecraft.world.phys.Vec3; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.entity.CraftPlayer; + + public class SignBlockEntity extends BlockEntity implements CommandSource { // CraftBukkit - implements + private static final boolean CONVERT_LEGACY_SIGNS = Boolean.getBoolean("convertLegacySigns"); // Paper +@@ -272,6 +278,21 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C + } + player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle()), event.getMessage()); + // Paper end ++ // Scissors start - Add optional permissions to command signs ++ final MinecraftServer vanillaServer = player.getServer(); ++ final CraftServer craftServer = vanillaServer.server; ++ final CraftPlayer craftPlayer = player.getBukkitEntity(); ++ final Commands commands = vanillaServer.getCommands(); ++ ++ if (ScissorsConfig.commandSignsBypassPermissions) ++ { ++ commands.performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle()), event.getMessage()); ++ } ++ else ++ { ++ craftServer.dispatchCommand(craftPlayer, command.substring(1)); ++ } ++ // Scissors end + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index ccc578bf2d7edfe866a5892978ce9e116787c4b4..4cb94d9e1f4ec56e01289b25c952c00df25f71a1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -955,6 +955,7 @@ public final class CraftServer implements Server { + } + + org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot ++ me.totalfreedom.scissors.ScissorsConfig.init(((File) console.options.valueOf("scissors-settings"))); // Scissors + this.console.paperConfigurations.reloadConfigs(this.console); + for (ServerLevel world : this.console.getAllLevels()) { + // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty +@@ -986,6 +987,7 @@ public final class CraftServer implements Server { + this.reloadData(); + org.spigotmc.SpigotConfig.registerCommands(); // Spigot + io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper ++ me.totalfreedom.scissors.ScissorsConfig.registerCommands(); // Scissors + this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); + this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); + +@@ -2744,6 +2746,12 @@ public final class CraftServer implements Server { + return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); + } + ++ @Override ++ public YamlConfiguration getScissorsConfig() ++ { ++ return me.totalfreedom.scissors.ScissorsConfig.config; ++ } ++ + @Override + public void restart() { + org.spigotmc.RestartCommand.restart(); +diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java +index cd87e192ff09e77538521bbde38416c52daa22b8..1bc7a99a457d0dd8befecfcd11da39a6b76291f1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/Main.java ++++ b/src/main/java/org/bukkit/craftbukkit/Main.java +@@ -180,9 +180,27 @@ public class Main { + .defaultsTo("Unknown Server") + .describedAs("Name"); + // Paper end ++ ++ // Scissors start ++ acceptsAll(asList("scissors-dir", "scissors-settings-directory"), "Directory for Scissors settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File(io.papermc.paper.configuration.PaperConfigurations.CONFIG_DIR)) ++ .describedAs("Config directory"); ++ // Scissors end ++ ++ // Scissors start ++ acceptsAll(asList("scissors", "scissors-settings"), "File for Scissors settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("scissors.yml")) ++ .describedAs("YAML file"); ++ // Scissors end + } + }; + ++ ++ + OptionSet options = null; + + // Paper start - preload logger classes to avoid plugins mixing versions