From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MartijnMuijsers Date: Thu, 13 Oct 2022 18:09:28 +0200 Subject: [PATCH] Suki Patches Original code by SuCraft, licensed under GPL-3.0. You can find the original code on https://github.com/SuCraft/Suki 0002-Suki-configuration-and-commands.patch 0012-Only-refresh-lootables-for-players.patch 0013-Optimize-harmless-explosions.patch 0016-Send-more-packets-immediately.patch 0017-Flush-after-more-packets.patch 0018-Do-not-relocate-corrupted-chunks.patch 0015-Multithreading environment variables.patch diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index 2cc44fbf8e5bd436b6d4e19f6c06b351e750cb31..991b06836df310913a14ce1c60979a2c2944b0e5 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -29,6 +29,7 @@ import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -242,7 +243,8 @@ public class TimingsExport extends Thread { pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Pufferfish - pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)) // Pufferfish + pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)), // Pufferfish + pair("suki", mapAsJSON(Bukkit.spigot().getSukiConfig(), null)) // Suki - Suki configuration )); new TimingsExport(listeners, parent, history).start(); diff --git a/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java b/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java index 7a4a7a654fe2516ed894a68f2657344df9d70f4c..0a501c3ba5382dae230122066c131de631edff0d 100644 --- a/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java +++ b/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java @@ -1,6 +1,6 @@ package io.papermc.paper.configuration; -abstract class ConfigurationPart { +public abstract class ConfigurationPart { // Suki - Suki configuration public static abstract class Post extends ConfigurationPart { diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java index c2dca89291361d60cbf160cab77749cb0130035a..aedd5aec308dd206ce149e2b303323e5a8adb88e 100644 --- a/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -5,7 +5,10 @@ import io.leangen.geantyref.TypeToken; import io.papermc.paper.configuration.constraint.Constraint; import io.papermc.paper.configuration.constraint.Constraints; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.MustBeInvokedByOverriders; import org.slf4j.Logger; @@ -17,6 +20,8 @@ import org.spongepowered.configurate.objectmapping.ObjectMapper; import org.spongepowered.configurate.serialize.SerializationException; import org.spongepowered.configurate.util.CheckedFunction; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; +import org.sucraft.suki.configuration.SukiConfigurations; +import org.sucraft.suki.configuration.SukiWorldConfiguration; import java.io.IOException; import java.lang.reflect.Type; @@ -88,7 +93,7 @@ public abstract class Configurations { }; } - static CheckedFunction reloader(Class type, T instance) { + public static CheckedFunction reloader(Class type, T instance) { // Suki - Suki configuration return node -> { ObjectMapper.Factory factory = (ObjectMapper.Factory) Objects.requireNonNull(node.options().serializers().get(type)); ObjectMapper.Mutable mutable = (ObjectMapper.Mutable) factory.get(type); @@ -148,7 +153,7 @@ public abstract class Configurations { final YamlConfigurationLoader loader = result.loader(); final ConfigurationNode node = loader.load(); if (result.isNewFile()) { // add version to new files - node.node(Configuration.VERSION_FIELD).raw(WorldConfiguration.CURRENT_VERSION); + node.node(Configuration.VERSION_FIELD).raw(getWorldConfigurationCurrentVersion()); // Suki - Suki configuration } this.applyWorldConfigTransformations(contextMap, node); final W instance = node.require(this.worldConfigClass); @@ -207,7 +212,7 @@ public abstract class Configurations { .build(); final ConfigurationNode worldNode = worldLoader.load(); if (newFile) { // set the version field if new file - worldNode.node(Configuration.VERSION_FIELD).set(WorldConfiguration.CURRENT_VERSION); + worldNode.node(Configuration.VERSION_FIELD).set(getWorldConfigurationCurrentVersion()); // Suki - Suki configuration } this.applyWorldConfigTransformations(contextMap, worldNode); this.applyDefaultsAwareWorldConfigTransformations(contextMap, worldNode, defaultsNode); @@ -308,4 +313,21 @@ public abstract class Configurations { return "ContextKey{" + this.name + "}"; } } + + // Suki start - Suki configuration + + @Deprecated + public YamlConfiguration createLegacyObject(final MinecraftServer server) { + YamlConfiguration global = YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.globalConfigFileName).toFile()); + ConfigurationSection worlds = global.createSection("__________WORLDS__________"); + worlds.set("__defaults__", YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.defaultWorldConfigFileName).toFile())); + for (ServerLevel level : server.getAllLevels()) { + worlds.set(level.getWorld().getName(), YamlConfiguration.loadConfiguration(getWorldConfigFile(level).toFile())); + } + return global; + } + + public abstract int getWorldConfigurationCurrentVersion(); + // Suki end - Suki configuration + } diff --git a/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java b/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java index a0aa1f1a7adf986d500a2135aa42e138aa3c4f08..bc3907beb2aa63259300e8960039bd59397d071e 100644 --- a/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java +++ b/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java @@ -5,6 +5,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.configurate.objectmapping.FieldDiscoverer; import org.spongepowered.configurate.serialize.SerializationException; import org.spongepowered.configurate.util.CheckedSupplier; +import org.sucraft.suki.configuration.SukiWorldConfiguration; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; @@ -17,7 +18,7 @@ import java.util.Map; import static io.leangen.geantyref.GenericTypeReflector.erase; -final class InnerClassFieldDiscoverer implements FieldDiscoverer> { +public final class InnerClassFieldDiscoverer implements FieldDiscoverer> { // Suki - Suki configuration private final Map, Object> instanceMap = new HashMap<>(); private final Map, Object> overrides; @@ -136,7 +137,19 @@ final class InnerClassFieldDiscoverer implements FieldDiscoverer globalConfig() { + // Suki start - Suki configuration + public static FieldDiscoverer sukiWorldConfig(Configurations.ContextMap contextMap) { + final Map, Object> overrides = Map.of( + SukiWorldConfiguration.class, new SukiWorldConfiguration( + contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get(), + contextMap.require(Configurations.WORLD_KEY) + ) + ); + return new InnerClassFieldDiscoverer(overrides); + } + // Suki end - Suki configuration + + public static FieldDiscoverer globalConfig() { // Suki - Suki configuration return new InnerClassFieldDiscoverer(Collections.emptyMap()); } } diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java index e731de3ac158c5a4cff236c6f5001674cd488a77..06a6cecfa01c676285ea894c3ed77d0e2d96ba91 100644 --- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java @@ -125,13 +125,13 @@ public class PaperConfigurations extends Configurations SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { + public static final Supplier SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { // Suki - Suki configuration @Override // override to ensure "verbose" is false public void init() { SpigotConfig.readConfig(SpigotWorldConfig.class, this); } }); - static final ContextKey> SPIGOT_WORLD_CONFIG_CONTEXT_KEY = new ContextKey<>(new TypeToken>() {}, "spigot world config"); + public static final ContextKey> SPIGOT_WORLD_CONFIG_CONTEXT_KEY = new ContextKey<>(new TypeToken>() {}, "spigot world config"); // Suki - Suki configuration public PaperConfigurations(final Path globalFolder) { @@ -293,7 +293,7 @@ public class PaperConfigurations extends Configurations 0) { + i = specificThreads; + } + // Suki end - multithreading environment variables ExecutorService executorService; if (i <= 0) { @@ -173,7 +183,7 @@ public class Util { } else { //executorService = new java.util.concurrent.ThreadPoolExecutor(i, i,0L, TimeUnit.MILLISECONDS, new java.util.concurrent.LinkedBlockingQueue(), target -> new net.minecraft.server.ServerWorkerThread(target, s, priorityModifier)); // JettPack // JettPack start - executorService = Integer.getInteger("Paper.WorkerThreadCount", i) <= 0 ? MoreExecutors.newDirectExecutorService() : new AbstractExecutorService(){ + executorService = new AbstractExecutorService(){ // Suki - multithreading environment variables private volatile boolean shutdown = false; @Override diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java index e1563410e0711de5cf9363978125e637929392da..948e988cdcf3edb0ceb0bd397d0579a1784d9ae1 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java @@ -355,7 +355,11 @@ public class Connection extends SimpleChannelInboundHandler> { packet instanceof net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket || packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket || packet instanceof net.minecraft.network.protocol.game.ClientboundClearTitlesPacket || - packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket; + // Suki start - send more packets immediately + packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; + // Suki end - send more packets immediately } // Paper end } @@ -406,7 +410,15 @@ public class Connection extends SimpleChannelInboundHandler> { private void sendPacket(Packet packet, @Nullable PacketSendListener callbacks, Boolean flushConditional) { this.packetWrites.getAndIncrement(); // must be befeore using canFlush boolean effectiveFlush = flushConditional == null ? this.canFlush : flushConditional.booleanValue(); - final boolean flush = effectiveFlush || packet instanceof net.minecraft.network.protocol.game.ClientboundKeepAlivePacket || packet instanceof ClientboundDisconnectPacket; // no delay for certain packets + // Suki start - flush after more packets + final boolean flush = effectiveFlush || + packet instanceof net.minecraft.network.protocol.game.ClientboundKeepAlivePacket || + packet instanceof ClientboundDisconnectPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; // no delay for certain packets + // Suki end - flush after more packets // Paper end - add flush parameter ConnectionProtocol enumprotocol = ConnectionProtocol.getProtocolForPacket(packet); ConnectionProtocol enumprotocol1 = this.getCurrentProtocol(); diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java index 07b0b0993c01763b39d1c9a387447ec76b5de190..72e700a775403eec2d1a32859279401351b836b1 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -55,8 +55,8 @@ import java.util.function.Supplier; public final class MCUtil { public static final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor( - 0, 2, 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), + Integer.getInteger("suki.threads.asyncexecutor", 4), Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, // JettPack // Suki - multithreading environment variables + new LinkedBlockingQueue<>(), new ThreadFactoryBuilder() .setNameFormat("Paper Async Task Handler Thread - %1$d") .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)) diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java index 5962f7a2b185d7d54a0f9e341a4fdf6e6f1c1ec5..8c80520396bc7e34bac25354c1fcdb38b71000c7 100644 --- a/src/main/java/net/minecraft/server/Main.java +++ b/src/main/java/net/minecraft/server/Main.java @@ -318,7 +318,7 @@ public class Main { // Paper start - fix and optimise world upgrading public static void convertWorldButItWorks(net.minecraft.resources.ResourceKey dimensionType, net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess worldSession, DataFixer dataFixer, Optional>> generatorKey, boolean removeCaches) { - int threads = Runtime.getRuntime().availableProcessors() * 3 / 8; + int threads = Integer.getInteger("suki.threads.upgradeworld", Integer.getInteger("suki.systemcpus.forupgradeworld", Runtime.getRuntime().availableProcessors()) * 3 / 8); // Suki - multithreading environment variables final ThreadedWorldUpgrader worldUpgrader = new ThreadedWorldUpgrader(dimensionType, worldSession.getLevelId(), worldSession.levelDirectory.path().toFile(), threads, dataFixer, generatorKey, removeCaches); worldUpgrader.convert(); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index a444c8c57a2ab16eba45b0379841a69ff60a5586..ede16b015b9dde07a7346e0bffde53082145798b 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -294,6 +294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { MinecraftServer.LOGGER.error("Uncaught exception in server thread", throwable); }); - if (Runtime.getRuntime().availableProcessors() > 4) { - thread.setPriority(8); + // Suki start - multithreading environment variables + if (Integer.getInteger("suki.mainthreadpriority", -1) != -1 || Runtime.getRuntime().availableProcessors() > 4) { + thread.setPriority(Integer.getInteger("suki.mainthreadpriority", 8)); + // Suki end - multithreading environment variables } S s0 = serverFactory.apply(thread); // CraftBukkit - decompile error @@ -400,6 +403,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public public static Services create(YggdrasilAuthenticationService authenticationService, File rootDirectory, File userCacheFile, joptsimple.OptionSet optionSet) throws Exception { // Paper @@ -30,7 +36,11 @@ public record Services(MinecraftSessionService sessionService, SignatureValidato final java.nio.file.Path legacyConfigPath = ((File) optionSet.valueOf("paper-settings")).toPath(); final java.nio.file.Path configDirPath = ((File) optionSet.valueOf("paper-settings-directory")).toPath(); io.papermc.paper.configuration.PaperConfigurations paperConfigurations = io.papermc.paper.configuration.PaperConfigurations.setup(legacyConfigPath, configDirPath, rootDirectory.toPath(), (File) optionSet.valueOf("spigot-settings")); - return new Services(minecraftSessionService, signatureValidator, gameProfileRepository, gameProfileCache, paperConfigurations); + // Suki start - Suki configuration + final java.nio.file.Path sukiLegacyConfigPath = legacyConfigPath.resolveSibling("suki.yml"); + org.sucraft.suki.configuration.SukiConfigurations sukiConfigurations = org.sucraft.suki.configuration.SukiConfigurations.setup(sukiLegacyConfigPath, configDirPath, rootDirectory.toPath(), (File) optionSet.valueOf("spigot-settings")); + return new Services(minecraftSessionService, signatureValidator, gameProfileRepository, gameProfileCache, paperConfigurations, sukiConfigurations); + // Suki end - Suki configuration // Paper end } } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 383045ae681d5366761c807a0032156203cdf9c9..3e7dd41555263e8361db08d1380c767fe0ba8886 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -213,6 +213,10 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc. paperConfigurations.initializeGlobalConfiguration(); paperConfigurations.initializeWorldDefaultsConfiguration(); + // Suki start - Suki configuration + sukiConfigurations.initializeGlobalConfiguration(); + sukiConfigurations.initializeWorldDefaultsConfiguration(); + // Suki end - Suki configuration // Paper start - moved up to right after PlayerList creation but before file load/save if (this.convertOldUsers()) { this.getProfileCache().save(false); // Paper diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java index 01ca7156d86243a80cd343a2a66be9ebedcc3b7c..852ec490e2a53803ca72a3ec0d994cbefb1f5fcd 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java @@ -97,7 +97,7 @@ public class DedicatedServerProperties extends Settings.MutableValue playerIdleTimeout; public final Settings.MutableValue whiteList; public final boolean enforceSecureProfile; - private final DedicatedServerProperties.WorldGenProperties worldGenProperties; + public final DedicatedServerProperties.WorldGenProperties worldGenProperties; // Suki - Suki configuration- include vanilla server.properties in timings @Nullable private WorldGenSettings worldGenSettings; diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 2a9219e0139670674319a7cf17ad664582b42715..ba95c8ba2fdcb73065c2fc219f94528239751f33 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -533,7 +533,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { // Holder holder = worlddimension.typeHolder(); // CraftBukkit - decompile error // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error - super(iworlddataserver, resourcekey, worlddimension.typeHolder(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig)), executor); // Paper - Async-Anti-Xray - Pass executor + super(iworlddataserver, resourcekey, worlddimension.typeHolder(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig)), spigotConfig -> minecraftserver.sukiConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig)), executor); // Paper - Async-Anti-Xray - Pass executor // Suki - Suki configuration this.pvpMode = minecraftserver.isPvpAllowed(); this.convertable = convertable_conversionsession; this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile()); diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java index c9def2202d7c2a523858ec124df2beaf994d9888..3a7bcee2e4b1c4ef2ba303b0200ae846357c5982 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -152,6 +152,7 @@ public class Explosion { int i; int j; + if (this.fire || this.blockInteraction != BlockInteraction.NONE) { // Suki - optimize explosions - don't run block processing if neither flag is set for (int k = 0; k < 16; ++k) { for (i = 0; i < 16; ++i) { for (j = 0; j < 16; ++j) { @@ -191,8 +192,8 @@ public class Explosion { if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && iblockdata.getBlock() == Blocks.MOVING_PISTON) { BlockEntity extension = this.level.getBlockEntity(blockposition); if (extension instanceof net.minecraft.world.level.block.piston.PistonMovingBlockEntity blockEntity && blockEntity.isSourcePiston()) { - net.minecraft.core.Direction direction = iblockdata.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); - set.add(blockposition.relative(direction.getOpposite())); + net.minecraft.core.Direction direction = iblockdata.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); + set.add(blockposition.relative(direction.getOpposite())); } } // Paper end @@ -206,6 +207,7 @@ public class Explosion { } } } + } // Suki - optimize explosions - don't run block processing if neither flag is set this.toBlow.addAll(set); float f2 = this.radius * 2.0F; @@ -296,7 +298,7 @@ public class Explosion { boolean flag1 = this.blockInteraction != Explosion.BlockInteraction.NONE; if (particles) { - if (this.radius >= 2.0F && flag1) { + if (this.radius >= 2.0F) { this.level.addParticle(ParticleTypes.EXPLOSION_EMITTER, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D); } else { this.level.addParticle(ParticleTypes.EXPLOSION, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 9f0126aedb94f74d01dc47112496160cc4e1f02c..dc3f2b26e19619adf80827d26b95209ded973c2a 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -172,7 +172,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return this.paperConfig; } // Paper end + // Suki start - Suki configuration + private final org.sucraft.suki.configuration.SukiWorldConfiguration sukiConfig; + public org.sucraft.suki.configuration.SukiWorldConfiguration sukiConfig() { + return this.sukiConfig; + } + + // Suki end - Suki configuration public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur @@ -327,10 +334,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @Override public final int getHeight() { return this.height; } // Pufferfish end - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.function.Function sukiWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor // Suki - Suki configuration this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur + this.sukiConfig = sukiWorldConfigCreator.apply(this.spigotConfig); // Suki - Suki configuration this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur this.generator = gen; this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java index 13e749a3c40f0b2cc002f13675a9a56eedbefdac..01ea0068e1c5a2bf0ad18bfa58bc98a7a2a98ef7 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java @@ -69,6 +69,15 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc } public void unpackLootTable(@Nullable Player player) { + // Suki start - only refresh lootables for players + if (this.level.getServer() != null) { + if (this.level.sukiConfig().lootables.onlyRefreshForPlayers) { + if (player == null) { + return; + } + } + } + // Suki end - only refresh lootables for players if (this.lootableData.shouldReplenish(player) && this.level.getServer() != null) { // Paper LootTable lootTable = this.level.getServer().getLootTables().get(this.lootTable); if (player instanceof ServerPlayer) { diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java index 34e351e04ac57e47e3cea671c61cc01d17983b77..f583ae81fb60027c47aaecdb6451462cd0d72936 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java @@ -30,6 +30,7 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.ShortTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ThreadedLevelLightEngine; @@ -157,7 +158,14 @@ public class ChunkSerializer { ChunkPos chunkcoordintpair1 = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos")); // Paper - diff on change, see ChunkSerializer#getChunkCoordinate if (!Objects.equals(chunkPos, chunkcoordintpair1)) { - ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", new Object[]{chunkPos, chunkPos, chunkcoordintpair1}); + // Suki start - do not relocate corrupted chunks + if (world.sukiConfig().relocateCorruptedChunks.enabled) { + ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", new Object[]{chunkPos, chunkPos, chunkcoordintpair1}); + } else { + ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; stopping the server. (Expected {}, got {})", new Object[]{chunkPos, chunkPos, chunkcoordintpair1}); + MinecraftServer.getServer().stopServer(); + } + // Suki end - do not relocate corrupted chunks } UpgradeData chunkconverter = nbt.contains("UpgradeData", 10) ? new UpgradeData(nbt.getCompound("UpgradeData"), world) : UpgradeData.EMPTY; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 4f029b96bf8deee597dbb56974e4519a1422f96d..9e47bbefdb373a75a9903b19c23e9191b9c84846 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -242,6 +242,7 @@ import org.jetbrains.annotations.NotNull; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; +import net.minecraft.Util; // KeYi import net.md_5.bungee.api.chat.BaseComponent; // Spigot @@ -1013,6 +1014,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur + this.console.sukiConfigurations.reloadConfigs(this.console); // Suki - Suki configuration for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) @@ -1295,9 +1297,11 @@ public final class CraftServer implements Server { worldKey = ResourceKey.create(net.minecraft.core.Registry.DIMENSION_REGISTRY, new net.minecraft.resources.ResourceLocation(creator.key().getNamespace().toLowerCase(java.util.Locale.ENGLISH), creator.key().getKey().toLowerCase(java.util.Locale.ENGLISH))); // Paper } - ServerLevel internal = (ServerLevel) new ServerLevel(this.console, console.executor, worldSession, worlddata, worldKey, worlddimension, this.getServer().progressListenerFactory.create(11), + // Suki start - multithreading environment variables + int levelExecutorThreads = Integer.getInteger("suki.threads.levelexecutor", -1); + ServerLevel internal = (ServerLevel) new ServerLevel(this.console, levelExecutorThreads > 0 ? Util.makeExecutor(name, -1, levelExecutorThreads) : console.executor, worldSession, worlddata, worldKey, worlddimension, this.getServer().progressListenerFactory.create(11), worlddata.worldGenSettings().isDebug(), j, creator.environment() == Environment.NORMAL ? list : ImmutableList.of(), true, creator.environment(), generator, biomeProvider); - + // Suki end - multithreading environment variables if (!(this.worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) { return null; } @@ -2840,6 +2844,13 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } + // Suki start - Suki configuration + @Override + public YamlConfiguration getSukiConfig() { + return CraftServer.this.console.sukiConfigurations.createLegacyObject(CraftServer.this.console); + } + // Suki end - Suki configuration + // Purpur start @Override public YamlConfiguration getPurpurConfig() { diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java index 8e06bc11fb28baee3407bbfe9d7b3689d6f85ff2..ce1190a6b0fb1e4f00e0d573edd42e2456c7e1e2 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java +++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java @@ -5,6 +5,7 @@ import org.bukkit.util.permissions.DefaultPermissions; public final class CraftDefaultPermissions { private static final String ROOT = "minecraft"; + private static final String SUKI_ROOT = "suki"; // Suki - Suki permissions private CraftDefaultPermissions() {} diff --git a/src/main/java/org/sucraft/suki/configuration/SukiConfigurations.java b/src/main/java/org/sucraft/suki/configuration/SukiConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..b64077fa2ff5c85b7c07868841a133f9e1b20c15 --- /dev/null +++ b/src/main/java/org/sucraft/suki/configuration/SukiConfigurations.java @@ -0,0 +1,296 @@ +// Suki - Suki configuration + +package org.sucraft.suki.configuration; + +import com.google.common.collect.Table; +import com.mojang.logging.LogUtils; +import io.leangen.geantyref.TypeToken; +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import io.papermc.paper.configuration.Configurations; +import io.papermc.paper.configuration.InnerClassFieldDiscoverer; +import io.papermc.paper.configuration.NestedSetting; +import io.papermc.paper.configuration.PaperConfigurations; +import io.papermc.paper.configuration.legacy.RequiresSpigotInitialization; +import io.papermc.paper.configuration.serializer.ComponentSerializer; +import io.papermc.paper.configuration.serializer.EnumValueSerializer; +import io.papermc.paper.configuration.serializer.FastutilMapSerializer; +import io.papermc.paper.configuration.serializer.PacketClassSerializer; +import io.papermc.paper.configuration.serializer.StringRepresentableSerializer; +import io.papermc.paper.configuration.serializer.TableSerializer; +import io.papermc.paper.configuration.serializer.collections.MapSerializer; +import io.papermc.paper.configuration.serializer.registry.RegistryHolderSerializer; +import io.papermc.paper.configuration.serializer.registry.RegistryValueSerializer; +import io.papermc.paper.configuration.transformation.Transformations; +import io.papermc.paper.configuration.type.BooleanOrDefault; +import io.papermc.paper.configuration.type.DoubleOrDefault; +import io.papermc.paper.configuration.type.Duration; +import io.papermc.paper.configuration.type.EngineMode; +import io.papermc.paper.configuration.type.IntOr; +import io.papermc.paper.configuration.type.fallback.FallbackValueSerializer; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2LongMap; +import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap; +import net.minecraft.core.Registry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import org.slf4j.Logger; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.ConfigurationOptions; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.objectmapping.ObjectMapper; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import static io.leangen.geantyref.GenericTypeReflector.erase; + +@SuppressWarnings("Convert2Diamond") +public class SukiConfigurations extends Configurations { + + private static final Logger LOGGER = LogUtils.getLogger(); + static final String GLOBAL_CONFIG_FILE_NAME = "suki-global.yml"; + static final String WORLD_DEFAULTS_CONFIG_FILE_NAME = "suki-world-defaults.yml"; + static final String WORLD_CONFIG_FILE_NAME = "suki-world.yml"; + public static final String CONFIG_DIR = "config"; + + private static final String GLOBAL_HEADER = String.format(""" + This is the global configuration file for Suki. + As you can see, there's a lot 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 Suki, + join us in our Discord for SuCraft, or check the GitHub Wiki pages. + + The world configuration options are inside + their respective world folder. The files are named %s + + Wiki: https://github.com/SuCraft/Suki/wiki + Discord: https://discord.com/invite/pbsPkpUjG4""", WORLD_CONFIG_FILE_NAME); + + private static final String WORLD_DEFAULTS_HEADER = """ + This is the world defaults configuration file for Suki. + As you can see, there's a lot 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 Suki, + join us in our Discord for SuCraft, or check the GitHub Wiki pages. + + Configuration options here apply to all worlds, unless you specify overrides inside + the world-specific config file inside each world folder. + + Wiki: https://github.com/SuCraft/Suki/wiki + Discord: https://discord.com/invite/pbsPkpUjG4"""; + + private static final Function WORLD_HEADER = map -> String.format(""" + This is a world configuration file for Suki. + This file may start empty but can be filled with settings to override ones in the %s/%s + + World: %s (%s)""", + CONFIG_DIR, + WORLD_DEFAULTS_CONFIG_FILE_NAME, + map.require(WORLD_NAME), + map.require(WORLD_KEY) + ); + + private static final String MOVED_NOTICE = """ + The global and world default configuration files have moved to %s + and the world-specific configuration file has been moved inside + the respective world folder. + + See https://github.com/SuCraft/Suki/wiki for more information. + """; + + public SukiConfigurations(final Path globalFolder) { + super(globalFolder, SukiGlobalConfiguration.class, SukiWorldConfiguration.class, GLOBAL_CONFIG_FILE_NAME, WORLD_DEFAULTS_CONFIG_FILE_NAME, WORLD_CONFIG_FILE_NAME); + } + + @Override + protected YamlConfigurationLoader.Builder createLoaderBuilder() { + return super.createLoaderBuilder() + .defaultOptions(SukiConfigurations::defaultOptions); + } + + private static ConfigurationOptions defaultOptions(ConfigurationOptions options) { + return options.serializers(builder -> builder + .register(MapSerializer.TYPE, new MapSerializer(false)) + .register(new EnumValueSerializer()) + .register(new ComponentSerializer()) + ); + } + + @Override + protected ObjectMapper.Factory.Builder createGlobalObjectMapperFactoryBuilder() { + return defaultGlobalFactoryBuilder(super.createGlobalObjectMapperFactoryBuilder()); + } + + private static ObjectMapper.Factory.Builder defaultGlobalFactoryBuilder(ObjectMapper.Factory.Builder builder) { + return builder.addDiscoverer(InnerClassFieldDiscoverer.globalConfig()); + } + + @Override + protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() { + return super.createGlobalLoaderBuilder() + .defaultOptions(SukiConfigurations::defaultGlobalOptions); + } + + private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) { + return options + .header(GLOBAL_HEADER) + .serializers(builder -> builder.register(new PacketClassSerializer())); + } + + @Override + public SukiGlobalConfiguration initializeGlobalConfiguration() throws ConfigurateException { + SukiGlobalConfiguration configuration = super.initializeGlobalConfiguration(); + SukiGlobalConfiguration.set(configuration); + return configuration; + } + + @Override + protected ContextMap.Builder createDefaultContextMap() { + return super.createDefaultContextMap() + .put(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY, PaperConfigurations.SPIGOT_WORLD_DEFAULTS); + } + + @Override + protected ObjectMapper.Factory.Builder createWorldObjectMapperFactoryBuilder(final ContextMap contextMap) { + return super.createWorldObjectMapperFactoryBuilder(contextMap) + .addNodeResolver(new RequiresSpigotInitialization.Factory(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get())) + .addNodeResolver(new NestedSetting.Factory()) + .addDiscoverer(InnerClassFieldDiscoverer.sukiWorldConfig(contextMap)); + } + + @Override + protected YamlConfigurationLoader.Builder createWorldConfigLoaderBuilder(final ContextMap contextMap) { + return super.createWorldConfigLoaderBuilder(contextMap) + .defaultOptions(options -> options + .header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap)) + .serializers(serializers -> serializers + .register(new TypeToken>() {}, new FastutilMapSerializer.SomethingToPrimitive>(Reference2IntOpenHashMap::new, Integer.TYPE)) + .register(new TypeToken>() {}, new FastutilMapSerializer.SomethingToPrimitive>(Reference2LongOpenHashMap::new, Long.TYPE)) + .register(new TypeToken>() {}, new TableSerializer()) + .register(new StringRepresentableSerializer()) + .register(IntOr.Default.SERIALIZER) + .register(DoubleOrDefault.SERIALIZER) + .register(BooleanOrDefault.SERIALIZER) + .register(Duration.SERIALIZER) + .register(EngineMode.SERIALIZER) + .register(FallbackValueSerializer.create(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get(), MinecraftServer::getServer)) + .register(new RegistryValueSerializer<>(new TypeToken>() {}, Registry.ENTITY_TYPE_REGISTRY, true)) + .register(new RegistryValueSerializer<>(Item.class, Registry.ITEM_REGISTRY, true)) + .register(new RegistryHolderSerializer<>(new TypeToken>() {}, Registry.CONFIGURED_FEATURE_REGISTRY, false)) + .register(new RegistryHolderSerializer<>(Item.class, Registry.ITEM_REGISTRY, true)) + ) + ); + } + + @Override + protected void applyWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode node) throws ConfigurateException { + final ConfigurationNode version = node.node(Configuration.VERSION_FIELD); + final String world = contextMap.require(WORLD_NAME); + if (version.virtual()) { + LOGGER.warn("The Suki world config file for " + world + " didn't have a version set, assuming latest"); + version.raw(SukiWorldConfiguration.CURRENT_VERSION); + } + if (SukiRemovedConfigurations.REMOVED_WORLD_PATHS.length > 0) { + ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + for (NodePath path : SukiRemovedConfigurations.REMOVED_WORLD_PATHS) { + builder.addAction(path, TransformAction.remove()); + } + builder.build().apply(node); + } + // ADD FUTURE TRANSFORMS HERE + } + + @Override + protected void applyGlobalConfigTransformations(ConfigurationNode node) throws ConfigurateException { + if (SukiRemovedConfigurations.REMOVED_GLOBAL_PATHS.length > 0) { + ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + for (NodePath path : SukiRemovedConfigurations.REMOVED_GLOBAL_PATHS) { + builder.addAction(path, TransformAction.remove()); + } + builder.build().apply(node); + } + // ADD FUTURE TRANSFORMS HERE + } + + private static final List DEFAULT_AWARE_TRANSFORMATIONS = Collections.emptyList(); + + @Override + protected void applyDefaultsAwareWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode worldNode, final ConfigurationNode defaultsNode) throws ConfigurateException { + final ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + // ADD FUTURE TRANSFORMS HERE (these transforms run after the defaults have been merged into the node) + DEFAULT_AWARE_TRANSFORMATIONS.forEach(transform -> transform.apply(builder, contextMap, defaultsNode)); + + ConfigurationTransformation transformation; + try { + transformation = builder.build(); // build throws IAE if no actions were provided (bad zml) + } catch (IllegalArgumentException ignored) { + return; + } + transformation.apply(worldNode); + } + + @Override + public SukiWorldConfiguration createWorldConfig(final ContextMap contextMap) { + final String levelName = contextMap.require(WORLD_NAME); + try { + return super.createWorldConfig(contextMap); + } catch (IOException exception) { + throw new RuntimeException("Could not create Suki world config for " + levelName, exception); + } + } + + @Override + protected boolean isConfigType(final Type type) { + return ConfigurationPart.class.isAssignableFrom(erase(type)); + } + + public void reloadConfigs(MinecraftServer server) { + try { + this.initializeGlobalConfiguration(reloader(this.globalConfigClass, SukiGlobalConfiguration.get())); + this.initializeWorldDefaultsConfiguration(); + for (ServerLevel level : server.getAllLevels()) { + this.createWorldConfig(PaperConfigurations.createWorldContextMap(level), reloader(this.worldConfigClass, level.sukiConfig())); + } + } catch (Exception ex) { + throw new RuntimeException("Could not reload Suki configuration files", ex); + } + } + + public static SukiConfigurations setup(final Path legacyConfig, final Path configDir, final Path worldFolder, final File spigotConfig) throws Exception { + final Path legacy = Files.isSymbolicLink(legacyConfig) ? Files.readSymbolicLink(legacyConfig) : legacyConfig; + final Path replacementFile = legacy.resolveSibling(legacyConfig.getFileName() + "-README.txt"); + if (Files.notExists(replacementFile)) { + Files.createFile(replacementFile); + Files.writeString(replacementFile, String.format(MOVED_NOTICE, configDir.toAbsolutePath())); + } + try { + PaperConfigurations.createDirectoriesSymlinkAware(configDir); + return new SukiConfigurations(configDir); + } catch (final IOException ex) { + throw new RuntimeException("Could not setup SukiConfigurations", ex); + } + } + + @Override + public int getWorldConfigurationCurrentVersion() { + return SukiWorldConfiguration.CURRENT_VERSION; + } + +} diff --git a/src/main/java/org/sucraft/suki/configuration/SukiGlobalConfiguration.java b/src/main/java/org/sucraft/suki/configuration/SukiGlobalConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..ba7c3818d17db725e6c39f4e0837b1fd1a3d0dfb --- /dev/null +++ b/src/main/java/org/sucraft/suki/configuration/SukiGlobalConfiguration.java @@ -0,0 +1,31 @@ +// Suki - Suki configuration + +package org.sucraft.suki.configuration; + +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) +public class SukiGlobalConfiguration extends ConfigurationPart { + static final int CURRENT_VERSION = 18; + private static SukiGlobalConfiguration instance; + public static SukiGlobalConfiguration get() { + return instance; + } + static void set(SukiGlobalConfiguration instance) { + SukiGlobalConfiguration.instance = instance; + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public GlobalDummyPart globalDummyPart; + + public class GlobalDummyPart extends ConfigurationPart { + + int globalDummyValue = 0; + + } + +} diff --git a/src/main/java/org/sucraft/suki/configuration/SukiRemovedConfigurations.java b/src/main/java/org/sucraft/suki/configuration/SukiRemovedConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..b4fd4190b899d4dea9420074d96e6431c2a3fafb --- /dev/null +++ b/src/main/java/org/sucraft/suki/configuration/SukiRemovedConfigurations.java @@ -0,0 +1,13 @@ +// Suki - Suki configuration + +package org.sucraft.suki.configuration; + +import org.spongepowered.configurate.NodePath; + +interface SukiRemovedConfigurations { + + NodePath[] REMOVED_WORLD_PATHS = {}; + + NodePath[] REMOVED_GLOBAL_PATHS = {}; + +} diff --git a/src/main/java/org/sucraft/suki/configuration/SukiWorldConfiguration.java b/src/main/java/org/sucraft/suki/configuration/SukiWorldConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..03d16da38f36b3c2e21c8c16758fe219c1d2f597 --- /dev/null +++ b/src/main/java/org/sucraft/suki/configuration/SukiWorldConfiguration.java @@ -0,0 +1,60 @@ +// Suki - Suki configuration + +package org.sucraft.suki.configuration; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import io.papermc.paper.configuration.PaperConfigurations; +import net.minecraft.resources.ResourceLocation; +import org.slf4j.Logger; +import org.spigotmc.SpigotWorldConfig; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) +public class SukiWorldConfiguration extends ConfigurationPart { + private static final Logger LOGGER = LogUtils.getLogger(); + public static final int CURRENT_VERSION = 19; + + private transient final SpigotWorldConfig spigotConfig; + private transient final ResourceLocation worldKey; + public SukiWorldConfiguration(SpigotWorldConfig spigotConfig, ResourceLocation worldKey) { + this.spigotConfig = spigotConfig; + this.worldKey = worldKey; + } + + public boolean isDefault() { + return this.worldKey.equals(PaperConfigurations.WORLD_DEFAULTS_KEY); + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public WorldDummyPart worldDummyPart; + + public class WorldDummyPart extends ConfigurationPart { + + int worldDummyValue = 0; + + } + // Suki start - only refresh lootables for players + + public Lootables lootables; + + public class Lootables extends ConfigurationPart { + public boolean onlyRefreshForPlayers = false; + } + + // Suki end - only refresh lootables for players + + // Suki start - do not relocate corrupted chunks + + public RelocateCorruptedChunks relocateCorruptedChunks; + + public class RelocateCorruptedChunks extends ConfigurationPart { + public boolean enabled = true; + } + + // Suki end - do not relocated corrupted chunks + +}