From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Allink 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 a2f71a6d1a9e98133dff6cd0f625da9435a8af14..f83b2c4298bd1a5f65487f64bd6a11fb190a622d 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -25,6 +25,7 @@ package co.aikar.timings; import com.google.common.collect.Sets; import io.papermc.paper.adventure.PaperAdventure; +import me.totalfreedom.scissors.ScissorsConfig; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; @@ -242,7 +243,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/PositionUtility.java b/src/main/java/me/totalfreedom/scissors/PositionUtility.java index c54535042a7ac12fe46b3b37db8d7b116e9469d5..62bb706e6a11749f588b3f0c3ffb1fbd70f15d64 100644 --- a/src/main/java/me/totalfreedom/scissors/PositionUtility.java +++ b/src/main/java/me/totalfreedom/scissors/PositionUtility.java @@ -8,7 +8,7 @@ import net.minecraft.world.phys.Vec3; public class PositionUtility { - public static Vec3 getValidVec3(double x, double y, double z, Entity entity) + /*public static Vec3 getValidVec3(double x, double y, double z, Entity entity) { final Level level = entity.level; @@ -37,7 +37,7 @@ public class PositionUtility { // If we throw some sort of exception due to the position being crazy, catch it return new Vec3(0, 0, 0); } - } + }*/ public static Vec3 getValidVec3FromBlockPos(BlockPos blockPos, Entity entity) { 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..ec93980fefcab57b7bac78d2fc4a217b376b7eca --- /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://javadoc.scissors.gg/1.20.1/\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/network/chat/SignedMessageChain.java b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java index c0a80824a0307ea673805015119cc834b268f0dc..cc67d0e351333731d953d5732215b19d1c36d4bc 100644 --- a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java +++ b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java @@ -4,6 +4,8 @@ import com.mojang.logging.LogUtils; import java.time.Instant; import java.util.UUID; import javax.annotation.Nullable; + +import me.totalfreedom.scissors.ScissorsConfig; import net.minecraft.util.SignatureUpdater; import net.minecraft.util.SignatureValidator; import net.minecraft.util.Signer; @@ -41,7 +43,8 @@ public class SignedMessageChain { if (!playerChatMessage.verify(signatureValidator)) { throw new SignedMessageChain.DecodeException(Component.translatable("multiplayer.disconnect.unsigned_chat"), true, org.bukkit.event.player.PlayerKickEvent.Cause.UNSIGNED_CHAT); // Paper - kick event causes } else { - if (playerChatMessage.hasExpiredServer(Instant.now())) { + // Scissors - Do not log this message if chat signatures are disabled + if (playerChatMessage.hasExpiredServer(Instant.now()) && ScissorsConfig.chatSignaturesEnabled) { LOGGER.warn("Received expired chat: '{}'. Is the client/server system time unsynchronized?", (Object)body.content()); } diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java index dae36c6452ccd57a436dd918547b64d59957ab0a..d450cf3cf8ab5cbb5d32a28ee2e4a6717941adef 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 9f422cbeaa52b3e6a0a27af4f8ad4ddb7808483f..bbfb536ddec7f44d2a75d35ab495be26104678a1 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 7e473ebbc8d89b01e1ee09047d43d075906d0d7c..0f7311755c5718a96d8a295a44fd85290d5d4425 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; @@ -2323,7 +2324,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic } private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) { - if (exception.shouldDisconnect()) { + if (exception.shouldDisconnect() && ScissorsConfig.chatSignaturesEnabled) { // Scissors - Do not kick when chat signatures are disabled this.disconnect(exception.getComponent(), exception.kickCause); // Paper - kick event causes } else { this.player.sendSystemMessage(exception.getComponent().copy().withStyle(ChatFormatting.RED)); @@ -2379,6 +2380,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 } @@ -2611,11 +2618,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 0521240dddde12d78cc05deda7fac11690f5d155..f19d82fd11633d9f91a70fb14cb5a1bcd91bce73 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 @@ -8,8 +8,11 @@ import java.util.Objects; import java.util.UUID; import java.util.function.UnaryOperator; 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.nbt.NbtOps; @@ -19,6 +22,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentUtils; 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.server.network.FilteredText; @@ -30,13 +34,14 @@ import net.minecraft.world.level.block.SignBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; import org.slf4j.Logger; import org.bukkit.block.sign.Side; import org.bukkit.craftbukkit.block.CraftBlock; -import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; // CraftBukkit end +import org.bukkit.craftbukkit.CraftServer; public class SignBlockEntity extends BlockEntity implements CommandSource { // CraftBukkit - implements @@ -286,6 +291,21 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C } player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), world, pos), event.getMessage()); // Paper end + // Scissors start - Add optional permissions to command signs + final MinecraftServer vanillaServer = player.getServer(); + final CraftServer craftServer = vanillaServer.server; + final CraftHumanEntity craftPlayer = player.getBukkitEntity(); + final Commands commands = vanillaServer.getCommands(); + + if (ScissorsConfig.commandSignsBypassPermissions) + { + commands.performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), world, pos), event.getMessage()); + } + else + { + craftServer.dispatchCommand(craftPlayer, command.substring(1)); + } + // Scissors end flag1 = true; } } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 454dcf7006cfbc86d19dde3fe3a6aa0591b2001d..5293040c7ec5824e40b0779ce507fc4994eb80b6 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -1052,6 +1052,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 @@ -1083,6 +1084,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"); @@ -2850,6 +2852,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 2a834708001ea940d8ab6f6559024df32b1e9520..70268a0bec5172bf723e3f9a8fe3208276f3bfa8 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