diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index e7f2701e07..898097da24 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -131,7 +131,18 @@ thread.setDaemon(true); thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); thread.start(); -@@ -132,7 +199,7 @@ +@@ -126,13 +193,18 @@ + this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections); + this.setLocalIp(dedicatedserverproperties.serverIp); + } ++ // Spigot start ++ this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); ++ org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings")); ++ org.spigotmc.SpigotConfig.registerCommands(); ++ // Spigot end + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); this.setMotd(dedicatedserverproperties.motd); super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get()); this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist); @@ -140,12 +151,12 @@ DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); InetAddress inetaddress = null; -@@ -156,6 +223,12 @@ +@@ -156,6 +228,12 @@ return false; } + // CraftBukkit start -+ this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); ++ // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up + this.server.loadPlugins(); + this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP); + // CraftBukkit end @@ -153,7 +164,7 @@ if (!this.usesAuthentication()) { DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); -@@ -170,7 +243,7 @@ +@@ -170,7 +248,7 @@ if (!OldUsersConverter.serverReadyAfterUserconversion(this)) { return false; } else { @@ -162,7 +173,7 @@ this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList()); this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME); long i = Util.getNanos(); -@@ -178,13 +251,13 @@ +@@ -178,13 +256,13 @@ SkullBlockEntity.setup(this.services, this); GameProfileCache.setUsesAuthentication(this.usesAuthentication()); DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); @@ -178,7 +189,7 @@ } if (dedicatedserverproperties.enableQuery) { -@@ -293,6 +366,7 @@ +@@ -293,6 +371,7 @@ this.queryThreadGs4.stop(); } @@ -186,7 +197,7 @@ } @Override -@@ -302,8 +376,8 @@ +@@ -302,8 +381,8 @@ } @Override @@ -197,7 +208,7 @@ } public void handleConsoleInput(String command, CommandSourceStack commandSource) { -@@ -314,7 +388,15 @@ +@@ -314,7 +393,15 @@ while (!this.consoleInput.isEmpty()) { ConsoleInput servercommand = (ConsoleInput) this.consoleInput.remove(0); @@ -214,7 +225,7 @@ } } -@@ -383,7 +465,7 @@ +@@ -383,7 +470,7 @@ @Override public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) { @@ -223,7 +234,7 @@ return false; } else if (this.getPlayerList().getOps().isEmpty()) { return false; -@@ -541,16 +623,52 @@ +@@ -541,16 +628,52 @@ @Override public String getPluginNames() { @@ -280,7 +291,7 @@ } public void storeUsingWhiteList(boolean useWhitelist) { -@@ -660,4 +778,15 @@ +@@ -660,4 +783,15 @@ } } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch index 3e9884077b..986068b80f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch @@ -56,7 +56,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION); -@@ -121,23 +143,58 @@ +@@ -121,23 +143,60 @@ private final DamageSources damageSources; private long subTickCount; @@ -79,6 +79,7 @@ + public List captureDrops; + public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>(); + public boolean populating; ++ public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot + + public CraftWorld getWorld() { + return this.world; @@ -91,6 +92,7 @@ + public abstract ResourceKey getTypeKey(); + + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) { ++ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot + this.generator = gen; + this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); + @@ -124,7 +126,7 @@ } }; } else { -@@ -145,11 +202,47 @@ +@@ -145,11 +204,47 @@ } this.thread = Thread.currentThread(); @@ -177,7 +179,7 @@ } @Override -@@ -207,6 +300,18 @@ +@@ -207,6 +302,18 @@ @Override public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) { @@ -196,7 +198,7 @@ if (this.isOutsideBuildHeight(pos)) { return false; } else if (!this.isClientSide && this.isDebug()) { -@@ -214,44 +319,117 @@ +@@ -214,45 +321,118 @@ } else { LevelChunk chunk = this.getChunkAt(pos); Block block = state.getBlock(); @@ -274,10 +276,10 @@ + // CraftBukkit end + return true; -+ } -+ } -+ } -+ + } + } + } + + // CraftBukkit start - Split off from above in order to directly send client and physic updates + public void notifyAndUpdatePhysics(BlockPos blockposition, LevelChunk chunk, BlockState oldBlock, BlockState newBlock, BlockState actualBlock, int i, int j) { + BlockState iblockdata = newBlock; @@ -297,7 +299,7 @@ + if (!this.isClientSide && iblockdata.hasAnalogOutputSignal()) { + this.updateNeighbourForOutputSignal(blockposition, newBlock.getBlock()); + } - } ++ } + + if ((i & 16) == 0 && j > 0) { + int k = i & -34; @@ -323,13 +325,14 @@ + this.onBlockStateChange(blockposition, iblockdata1, iblockdata2); + } + // CraftBukkit end - } - } ++ } ++ } + // CraftBukkit end - ++ public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {} -@@ -340,6 +518,14 @@ + @Override +@@ -340,6 +520,14 @@ @Override public BlockState getBlockState(BlockPos pos) { @@ -344,7 +347,7 @@ if (this.isOutsideBuildHeight(pos)) { return Blocks.VOID_AIR.defaultBlockState(); } else { -@@ -510,13 +696,29 @@ +@@ -510,13 +698,29 @@ @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { @@ -375,7 +378,7 @@ this.getChunkAt(blockposition).addAndRegisterBlockEntity(blockEntity); } } -@@ -643,7 +845,7 @@ +@@ -643,7 +847,7 @@ for (int k = 0; k < j; ++k) { EnderDragonPart entitycomplexpart = aentitycomplexpart[k]; @@ -384,7 +387,7 @@ if (t0 != null && predicate.test(t0)) { result.add(t0); -@@ -912,7 +1114,7 @@ +@@ -912,7 +1116,7 @@ public static enum ExplosionInteraction implements StringRepresentable { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 238658122a..41590d8d87 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -945,6 +945,7 @@ public final class CraftServer implements Server { this.logger.log(Level.WARNING, "Failed to load banned-players.json, " + ex.getMessage()); } + org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot for (ServerLevel world : this.console.getAllLevels()) { world.serverLevelData.setDifficulty(config.difficulty); world.setSpawnSettings(config.spawnMonsters); @@ -959,11 +960,13 @@ public final class CraftServer implements Server { } } } + world.spigotConfig.init(); // Spigot } this.pluginManager.clearPlugins(); this.commandMap.clearCommands(); this.reloadData(); + org.spigotmc.SpigotConfig.registerCommands(); // Spigot this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/Main.java b/paper-server/src/main/java/org/bukkit/craftbukkit/Main.java index efc2595982..37b916b97d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/Main.java @@ -134,6 +134,14 @@ public class Main { this.acceptsAll(Main.asList("demo"), "Demo mode"); this.acceptsAll(Main.asList("initSettings"), "Only create configuration files and then exit"); // SPIGOT-5761: Add initSettings option + + // Spigot Start + this.acceptsAll(Main.asList("S", "spigot-settings"), "File for spigot settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("spigot.yml")) + .describedAs("Yml file"); + // Spigot End } }; diff --git a/paper-server/src/main/java/org/spigotmc/SpigotCommand.java b/paper-server/src/main/java/org/spigotmc/SpigotCommand.java new file mode 100644 index 0000000000..ac0fd418fc --- /dev/null +++ b/paper-server/src/main/java/org/spigotmc/SpigotCommand.java @@ -0,0 +1,44 @@ +package org.spigotmc; + +import java.io.File; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class SpigotCommand extends Command { + + public SpigotCommand(String name) { + super(name); + this.description = "Spigot related commands"; + this.usageMessage = "/spigot reload"; + this.setPermission("bukkit.command.spigot"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!this.testPermission(sender)) return true; + + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Usage: " + this.usageMessage); + return false; + } + + if (args[0].equals("reload")) { + 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(); + org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); + for (ServerLevel world : console.getAllLevels()) { + world.spigotConfig.init(); + } + console.server.reloadCount++; + + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); + } + + return true; + } +} diff --git a/paper-server/src/main/java/org/spigotmc/SpigotConfig.java b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java new file mode 100644 index 0000000000..df05c3d052 --- /dev/null +++ b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java @@ -0,0 +1,140 @@ +package org.spigotmc; + +import com.google.common.base.Throwables; +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.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotConfig +{ + + private static File CONFIG_FILE; + private static final String HEADER = "This is the main configuration file for Spigot.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "For a reference for any variable inside this file, check out the Spigot wiki at\n" + + "http://www.spigotmc.org/wiki/spigot-configuration/\n" + + "\n" + + "If you need help with the configuration or have any questions related to Spigot,\n" + + "join us at the Discord or drop by our forums and leave a post.\n" + + "\n" + + "Discord: https://www.spigotmc.org/go/discord\n" + + "Forums: http://www.spigotmc.org/\n"; + /*========================================================================*/ + public static YamlConfiguration config; + static int version; + static Map commands; + /*========================================================================*/ + + public static void init(File configFile) + { + SpigotConfig.CONFIG_FILE = configFile; + SpigotConfig.config = new YamlConfiguration(); + try + { + SpigotConfig.config.load( SpigotConfig.CONFIG_FILE ); + } catch ( IOException ex ) + { + } catch ( InvalidConfigurationException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex ); + throw Throwables.propagate( ex ); + } + + SpigotConfig.config.options().header( SpigotConfig.HEADER ); + SpigotConfig.config.options().copyDefaults( true ); + + SpigotConfig.commands = new HashMap(); + SpigotConfig.commands.put( "spigot", new SpigotCommand( "spigot" ) ); + + SpigotConfig.version = SpigotConfig.getInt( "config-version", 12 ); + SpigotConfig.set( "config-version", 12 ); + SpigotConfig.readConfig( SpigotConfig.class, null ); + } + + public static void registerCommands() + { + for ( Map.Entry entry : SpigotConfig.commands.entrySet() ) + { + MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", 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 ); + } + } + } + } + + try + { + SpigotConfig.config.save( SpigotConfig.CONFIG_FILE ); + } catch ( IOException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not save " + SpigotConfig.CONFIG_FILE, ex ); + } + } + + private static void set(String path, Object val) + { + SpigotConfig.config.set( path, val ); + } + + private static boolean getBoolean(String path, boolean def) + { + SpigotConfig.config.addDefault( path, def ); + return SpigotConfig.config.getBoolean( path, SpigotConfig.config.getBoolean( path ) ); + } + + private static int getInt(String path, int def) + { + SpigotConfig.config.addDefault( path, def ); + return SpigotConfig.config.getInt( path, SpigotConfig.config.getInt( path ) ); + } + + private static List getList(String path, T def) + { + SpigotConfig.config.addDefault( path, def ); + return (List) SpigotConfig.config.getList( path, SpigotConfig.config.getList( path ) ); + } + + private static String getString(String path, String def) + { + SpigotConfig.config.addDefault( path, def ); + return SpigotConfig.config.getString( path, SpigotConfig.config.getString( path ) ); + } + + private static double getDouble(String path, double def) + { + SpigotConfig.config.addDefault( path, def ); + return SpigotConfig.config.getDouble( path, SpigotConfig.config.getDouble( path ) ); + } +} diff --git a/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java b/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java new file mode 100644 index 0000000000..3ca59d6d70 --- /dev/null +++ b/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -0,0 +1,82 @@ +package org.spigotmc; + +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotWorldConfig +{ + + private final String worldName; + private final YamlConfiguration config; + private boolean verbose; + + public SpigotWorldConfig(String worldName) + { + this.worldName = worldName; + this.config = SpigotConfig.config; + this.init(); + } + + public void init() + { + this.verbose = this.getBoolean( "verbose", true ); + + this.log( "-------- World Settings For [" + this.worldName + "] --------" ); + SpigotConfig.readConfig( SpigotWorldConfig.class, this ); + } + + private void log(String s) + { + if ( this.verbose ) + { + Bukkit.getLogger().info( s ); + } + } + + private void set(String path, Object val) + { + this.config.set( "world-settings.default." + path, val ); + } + + public boolean getBoolean(String path, boolean def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return this.config.getBoolean( "world-settings." + this.worldName + "." + path, this.config.getBoolean( "world-settings.default." + path ) ); + } + + public double getDouble(String path, double def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return this.config.getDouble( "world-settings." + this.worldName + "." + path, this.config.getDouble( "world-settings.default." + path ) ); + } + + public int getInt(String path) + { + return this.config.getInt( "world-settings." + this.worldName + "." + path ); + } + + public int getInt(String path, int def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return this.config.getInt( "world-settings." + this.worldName + "." + path, this.config.getInt( "world-settings.default." + path ) ); + } + + public List getList(String path, T def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return (List) this.config.getList( "world-settings." + this.worldName + "." + path, this.config.getList( "world-settings.default." + path ) ); + } + + public String getString(String path, String def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return this.config.getString( "world-settings." + this.worldName + "." + path, this.config.getString( "world-settings.default." + path ) ); + } + + private Object get(String path, Object def) + { + this.config.addDefault( "world-settings.default." + path, def ); + return this.config.get( "world-settings." + this.worldName + "." + path, this.config.get( "world-settings.default." + path ) ); + } +}