From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "kfian294ma4@gmail.com" Date: Sun, 5 Sep 2021 18:01:34 +0100 Subject: [PATCH] Sakura Configuration Files diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java index 8cf720f08514e8e4f62f4ad196f1277bd761c6b2..3e4a639f8ac00fee9b334b88da036c329a1a61d7 100644 --- a/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -94,7 +94,7 @@ public abstract class Configurations { }; } - static CheckedFunction reloader(Class type, T instance) { + public static CheckedFunction reloader(Class type, T instance) { // Sakura - package-protected -> public return node -> { ObjectMapper.Factory factory = (ObjectMapper.Factory) Objects.requireNonNull(node.options().serializers().get(type)); ObjectMapper.Mutable mutable = (ObjectMapper.Mutable) factory.get(type); @@ -229,7 +229,7 @@ public abstract class Configurations { .path(worldConfigFile) .build(); final ConfigurationNode worldNode = worldLoader.load(); - if (newFile) { // set the version field if new file + if (newFile && this instanceof PaperConfigurations) { // Sakura - hack this into working // set the version field if new file worldNode.node(Configuration.VERSION_FIELD).set(this.worldConfigVersion()); } else { this.verifyWorldConfigVersion(contextMap, worldNode); diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java index c5644d8d64f12073e39bc6ed79c8714f4560ff89..9dd822d20f8f3cfe65162a62e1407626831beabf 100644 --- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java @@ -167,7 +167,7 @@ public class PaperConfigurations extends Configurations builder .register(MapSerializer.TYPE, new MapSerializer(false)) .register(new EnumValueSerializer()) @@ -462,7 +462,7 @@ public class PaperConfigurations extends Configurations public if (!Files.isDirectory(path)) { Files.createDirectories(path); } diff --git a/src/main/java/me/samsuik/sakura/command/BaseSubCommand.java b/src/main/java/me/samsuik/sakura/command/BaseSubCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..07e80076a30ea26810b8adc014b5e1e921ecb93c --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/BaseSubCommand.java @@ -0,0 +1,72 @@ +package me.samsuik.sakura.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +@DefaultQualifier(NonNull.class) +public abstract class BaseSubCommand extends Command { + public BaseSubCommand(String name) { + super(name); + this.description = "Sakura Command " + name; + this.setPermission("bukkit.command." + name); + } + + public abstract void execute(CommandSender sender, String[] args); + + public void tabComplete(List list, String[] args) throws IllegalArgumentException {} + + @Override + @Deprecated + public final boolean execute(CommandSender sender, String label, String[] args) { + if (this.testPermission(sender)) { + this.execute(sender, args); + } + + return true; + } + + @Override + @NotNull + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + List completions = new ArrayList<>(0); + + if (this.testPermissionSilent(sender)) { + this.tabComplete(completions, args); + } + + return completions; + } + + protected final Optional parseInt(String[] args, int index) { + return this.parse(args, index, Integer::parseInt); + } + + protected final Optional parseLong(String[] args, int index) { + return this.parse(args, index, Long::parseLong); + } + + protected final Optional parseFloat(String[] args, int index) { + return this.parse(args, index, Float::parseFloat); + } + + protected final Optional parseDouble(String[] args, int index) { + return this.parse(args, index, Double::parseDouble); + } + + protected final Optional parse(String[] args, int index, Function func) { + try { + String arg = args[index]; + return Optional.of(func.apply(arg)); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) { + return Optional.empty(); + } + } +} diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommand.java b/src/main/java/me/samsuik/sakura/command/SakuraCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..f46883c6206b56af1b9f125b9b361e7efc666b49 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/SakuraCommand.java @@ -0,0 +1,86 @@ +package me.samsuik.sakura.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +@DefaultQualifier(NonNull.class) +public final class SakuraCommand extends Command { + private static final Component HEADER_MESSAGE = MiniMessage.miniMessage().deserialize(""" + . + | This is the main command for Sakura. + | All exclusive commands are listed below.""" + ); + + private static final String COMMAND_MSG = "| * /"; + + public SakuraCommand(String name) { + super(name); + this.description = ""; + this.usageMessage = "/sakura"; + this.setPermission("bukkit.command.sakura"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (args.length > 0) { + List commands = new ArrayList<>(SakuraCommands.COMMANDS.values()); + + // This part is copied from the VersionCommand SubCommand in paper + Command internalVersion = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (internalVersion != null) { + commands.add(internalVersion); + } + + for (Command base : commands) { + if (base.getName().equalsIgnoreCase(args[0])) { + return base.execute(sender, commandLabel, Arrays.copyOfRange(args, 1, args.length)); + } + } + } + + this.sendHelpMessage(sender); + return false; + } + + private void sendHelpMessage(CommandSender sender) { + sender.sendMessage(HEADER_MESSAGE); + + Stream uniqueCommands = SakuraCommands.COMMANDS.values() + .stream() + .filter(command -> command != this); + + uniqueCommands.forEach((command) -> { + sender.sendRichMessage(COMMAND_MSG, Placeholder.unparsed("command", command.getName())); + }); + + sender.sendMessage(Component.text("'", NamedTextColor.DARK_PURPLE)); + } + + @NotNull + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + if (!this.testPermissionSilent(sender)) { + return Collections.emptyList(); + } + + return SakuraCommands.COMMANDS.values().stream() + .filter(command -> command != this) + .map(Command::getName) + .filter(name -> args.length <= 1 || name.startsWith(args[args.length - 1])) + .toList(); + } +} diff --git a/src/main/java/me/samsuik/sakura/command/SakuraCommands.java b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java new file mode 100644 index 0000000000000000000000000000000000000000..3e85c19db0655034c203eaab8d2e6b5504da5da8 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/SakuraCommands.java @@ -0,0 +1,22 @@ +package me.samsuik.sakura.command; + +import me.samsuik.sakura.command.subcommands.ConfigCommand; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; + +import java.util.HashMap; +import java.util.Map; + +public final class SakuraCommands { + static final Map COMMANDS = new HashMap<>(); + static { + COMMANDS.put("sakura", new SakuraCommand("sakura")); + COMMANDS.put("config", new ConfigCommand("config")); + } + + public static void registerCommands(MinecraftServer server) { + COMMANDS.forEach((s, command) -> { + server.server.getCommandMap().register(s, "sakura", command); + }); + } +} diff --git a/src/main/java/me/samsuik/sakura/command/subcommands/ConfigCommand.java b/src/main/java/me/samsuik/sakura/command/subcommands/ConfigCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..c41f188fdaf510a127771b1782e957058b8cc355 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/subcommands/ConfigCommand.java @@ -0,0 +1,33 @@ +package me.samsuik.sakura.command.subcommands; + +import me.samsuik.sakura.command.BaseSubCommand; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GREEN; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +@DefaultQualifier(NonNull.class) +public final class ConfigCommand extends BaseSubCommand { + public ConfigCommand(String name) { + super(name); + this.description = "Command for reloading the sakura configuration file"; + } + + @Override + public void execute(CommandSender sender, String[] args) { + Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED)); + Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED)); + + MinecraftServer server = ((CraftServer) sender.getServer()).getServer(); + server.sakuraConfigurations.reloadConfigs(server); + server.server.reloadCount++; + + Command.broadcastCommandMessage(sender, text("Sakura config reload complete.", GREEN)); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java b/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..4f7208b6a1e2ac0e812019d4b42bde7fa5909b28 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/GlobalConfiguration.java @@ -0,0 +1,63 @@ +package me.samsuik.sakura.configuration; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import io.papermc.paper.configuration.type.number.IntOr; +import org.bukkit.Material; +import org.slf4j.Logger; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic", "RedundantSuppression"}) +public final class GlobalConfiguration extends ConfigurationPart { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + static final int CURRENT_VERSION = 3;// (when you change the version, change the comment, so it conflicts on rebases): rename filter bad nbt from spawn eggs + + private static GlobalConfiguration instance; + public static GlobalConfiguration get() { + return instance; + } + + static void set(GlobalConfiguration instance) { + GlobalConfiguration.instance = instance; + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public Messages messages; + public class Messages extends ConfigurationPart { + public String durableBlockInteraction = "(S) This block has of "; + public String fpsSettingChange = "(S) "; + public boolean tpsShowEntityAndChunkCount = true; + } + + public Fps fps; + public class Fps extends ConfigurationPart { + public Material material = Material.PINK_STAINED_GLASS_PANE; + } + + public Players players; + public class Players extends ConfigurationPart { + public IntOr.Default bucketStackSize = IntOr.Default.USE_DEFAULT; + } + + public Environment environment; + public class Environment extends ConfigurationPart { + @Comment("This is only intended for plot worlds. Will affect chunk generation on servers.") + public boolean calculateBiomeNoiseOncePerChunkSection = false; + + public MobSpawnerDefaults mobSpawnerDefaults = new MobSpawnerDefaults(); + public class MobSpawnerDefaults extends ConfigurationPart { + public int minSpawnDelay = 200; + public int maxSpawnDelay = 800; + public int spawnCount = 4; + public int maxNearbyEntities = 6; + public int requiredPlayerRange = 16; + public int spawnRange = 4; + } + } + +} diff --git a/src/main/java/me/samsuik/sakura/configuration/SakuraConfigurations.java b/src/main/java/me/samsuik/sakura/configuration/SakuraConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..9460afc669594ddb53b097ae105abb29da3a7e10 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/SakuraConfigurations.java @@ -0,0 +1,241 @@ +package me.samsuik.sakura.configuration; + +import com.google.common.collect.Table; +import com.mojang.logging.LogUtils; +import io.leangen.geantyref.TypeToken; +import io.papermc.paper.configuration.*; +import io.papermc.paper.configuration.mapping.InnerClassFieldDiscoverer; +import io.papermc.paper.configuration.serializer.*; +import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer; +import io.papermc.paper.configuration.serializer.collections.MapSerializer; +import io.papermc.paper.configuration.serializer.collections.TableSerializer; +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.Duration; +import io.papermc.paper.configuration.type.DurationOrDisabled; +import io.papermc.paper.configuration.type.number.DoubleOr; +import io.papermc.paper.configuration.type.number.IntOr; +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 me.samsuik.sakura.configuration.transformation.ConfigurationTransformations; +import me.samsuik.sakura.configuration.transformation.global.V1_RelocateMessages; +import me.samsuik.sakura.configuration.transformation.global.V2_ConvertIconToMaterial; +import me.samsuik.sakura.configuration.transformation.world.*; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +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.block.Block; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NullMarked; +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.FieldDiscoverer; +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.IOException; +import java.lang.reflect.Type; +import java.nio.file.Path; +import java.util.Map; +import java.util.function.Function; + +import static io.leangen.geantyref.GenericTypeReflector.erase; + +@NullMarked +@SuppressWarnings("Convert2Diamond") +public final class SakuraConfigurations extends Configurations { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + static final String GLOBAL_CONFIG_FILE_NAME = "sakura-global.yml"; + static final String WORLD_DEFAULTS_CONFIG_FILE_NAME = "sakura-world-defaults.yml"; + static final String WORLD_CONFIG_FILE_NAME = "sakura-world.yml"; + public static final String CONFIG_DIR = "config"; + + private static final String GLOBAL_HEADER = String.format(""" + This is the global configuration file for Sakura. + 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. + + The world configuration options have been moved inside + their respective world folder. The files are named %s""", WORLD_CONFIG_FILE_NAME); + + private static final String WORLD_DEFAULTS_HEADER = """ + This is the world defaults configuration file for Sakura. + 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. + + Configuration options here apply to all worlds, unless you specify overrides inside + the world-specific config file inside each world folder."""; + + private static final Function WORLD_HEADER = map -> String.format(""" + This is a world configuration file for Sakura. + This file may start empty but can be filled with settings to override ones in the %s/%s + + World: %s (%s)""", + SakuraConfigurations.CONFIG_DIR, + SakuraConfigurations.WORLD_DEFAULTS_CONFIG_FILE_NAME, + map.require(WORLD_NAME), + map.require(WORLD_KEY) + ); + + public SakuraConfigurations(final Path globalFolder) { + super(globalFolder, GlobalConfiguration.class, WorldConfiguration.class, GLOBAL_CONFIG_FILE_NAME, WORLD_DEFAULTS_CONFIG_FILE_NAME, WORLD_CONFIG_FILE_NAME); + } + + @Override + protected YamlConfigurationLoader.Builder createLoaderBuilder() { + return super.createLoaderBuilder() + .defaultOptions(PaperConfigurations::defaultOptions); + } + + @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(SakuraConfigurations::defaultGlobalOptions); + } + + private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) { + return options + .header(GLOBAL_HEADER) + .serializers(builder -> builder + .register(new PacketClassSerializer()) + ); + } + + @Override + public GlobalConfiguration initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException { + GlobalConfiguration configuration = super.initializeGlobalConfiguration(registryAccess); + GlobalConfiguration.set(configuration); + return configuration; + } + + @Override + protected ObjectMapper.Factory.Builder createWorldObjectMapperFactoryBuilder(final ContextMap contextMap) { + return super.createWorldObjectMapperFactoryBuilder(contextMap) + .addNodeResolver(new NestedSetting.Factory()) + .addDiscoverer(createWorldConfigFieldDiscoverer(contextMap)); + } + + private static FieldDiscoverer createWorldConfigFieldDiscoverer(final ContextMap contextMap) { + final Map, Object> overrides = Map.of( + WorldConfiguration.class, createWorldConfigInstance(contextMap) + ); + return new InnerClassFieldDiscoverer(overrides); + } + + private static WorldConfiguration createWorldConfigInstance(ContextMap contextMap) { + return new WorldConfiguration(contextMap.require(Configurations.WORLD_KEY)); + } + + @Override + protected YamlConfigurationLoader.Builder createWorldConfigLoaderBuilder(final ContextMap contextMap) { + final RegistryAccess access = contextMap.require(REGISTRY_ACCESS); + 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(StringRepresentableSerializer::isValidFor, new StringRepresentableSerializer()) + .register(new RegistryValueSerializer<>(new TypeToken>() {}, access, Registries.ENTITY_TYPE, true)) + .register(new RegistryValueSerializer<>(Item.class, access, Registries.ITEM, true)) + .register(new RegistryValueSerializer<>(Block.class, access, Registries.BLOCK, true)) + .register(new RegistryHolderSerializer<>(new TypeToken>() {}, access, Registries.CONFIGURED_FEATURE, false)) + ) + ); + } + + @Override + protected void applyWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode node, final @Nullable ConfigurationNode defaultsNode) throws ConfigurateException { + ConfigurationTransformations.worldTransformations(node); + } + + @Override + protected void applyGlobalConfigTransformations(final ConfigurationNode node) throws ConfigurateException { + ConfigurationTransformations.globalTransformations(node); + } + + @Override + public WorldConfiguration 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 world config for " + levelName, exception); + } + } + + @Override + protected boolean isConfigType(final Type type) { + return ConfigurationPart.class.isAssignableFrom(erase(type)); + } + + @Override + protected int globalConfigVersion() { + return GlobalConfiguration.CURRENT_VERSION; + } + + @Override + protected int worldConfigVersion() { + return WorldConfiguration.CURRENT_VERSION; + } + + public void reloadConfigs(MinecraftServer server) { + try { + this.initializeGlobalConfiguration(reloader(this.globalConfigClass, GlobalConfiguration.get())); + this.initializeWorldDefaultsConfiguration(server.registryAccess()); + for (ServerLevel level : server.getAllLevels()) { + this.createWorldConfig(createWorldContextMap(level), reloader(this.worldConfigClass, level.sakuraConfig())); + } + } catch (Exception ex) { + throw new RuntimeException("Could not reload sakura configuration files", ex); + } + } + + private static ContextMap createWorldContextMap(ServerLevel level) { + return createWorldContextMap(level.convertable.levelDirectory.path(), level.serverLevelData.getLevelName(), level.dimension().location(), level.registryAccess()); + } + + public static ContextMap createWorldContextMap(Path dir, String levelName, ResourceLocation worldKey, RegistryAccess registryAccess) { + return ContextMap.builder() + .put(WORLD_DIRECTORY, dir) + .put(WORLD_NAME, levelName) + .put(WORLD_KEY, worldKey) + .put(REGISTRY_ACCESS, registryAccess) + .build(); + } + + public static SakuraConfigurations setup(final Path configDir) { + try { + PaperConfigurations.createDirectoriesSymlinkAware(configDir); + return new SakuraConfigurations(configDir); + } catch (final IOException ex) { + throw new RuntimeException("Could not setup PaperConfigurations", ex); + } + } + +} diff --git a/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java b/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..7631050f589151cc61ef144e43e9abfb45c26bfd --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/WorldConfiguration.java @@ -0,0 +1,242 @@ +package me.samsuik.sakura.configuration; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import io.papermc.paper.configuration.NestedSetting; +import io.papermc.paper.configuration.PaperConfigurations; +import io.papermc.paper.configuration.type.number.DoubleOr; +import io.papermc.paper.configuration.type.number.IntOr; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import me.samsuik.sakura.entity.merge.MergeLevel; +import me.samsuik.sakura.explosion.durable.DurableMaterial; +import me.samsuik.sakura.physics.PhysicsVersion; +import net.minecraft.Util; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import org.slf4j.Logger; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +import java.util.List; +import java.util.Map; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic", "RedundantSuppression"}) +public final class WorldConfiguration extends ConfigurationPart { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + static final int CURRENT_VERSION = 6; // (when you change the version, change the comment, so it conflicts on rebases): rename filter bad nbt from spawn eggs + + private transient final ResourceLocation worldKey; + WorldConfiguration(ResourceLocation worldKey) { + this.worldKey = worldKey; + } + + public boolean isDefault() { + return this.worldKey.equals(PaperConfigurations.WORLD_DEFAULTS_KEY); + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public Cannons cannons; + public class Cannons extends ConfigurationPart { + public MergeLevel mergeLevel = MergeLevel.STRICT; + public boolean tntAndSandAffectedByBubbleColumns = true; + + @NestedSetting({"treat-collidable-blocks-as-full", "while-moving"}) + public boolean treatAllBlocksAsFullWhenMoving = false; + @NestedSetting({"treat-collidable-blocks-as-full", "moving-faster-than"}) + public double treatAllBlocksAsFullWhenMovingFasterThan = 64.0; + public boolean loadChunks = false; + + public Restrictions restrictions = new Restrictions(); + public class Restrictions extends ConfigurationPart { + @Comment("The amount of blocks that can be travelled before changing direction is restricted") + public IntOr.Disabled leftShootingThreshold = IntOr.Disabled.DISABLED; + @Comment( + "Maximum amount of blocks that a cannon can adjust\n" + + "It is recommended that this value kept sane and is more than 64 blocks" + ) + public IntOr.Disabled maxAdjustDistance = IntOr.Disabled.DISABLED; + } + + public Tnt tnt = new Tnt(); + public class Tnt extends ConfigurationPart { + public boolean forcePositionUpdates; + } + + public Sand sand = new Sand(); + public class Sand extends ConfigurationPart { + public boolean despawnInsideMovingPistons = true; + public boolean concreteSolidifyInWater = true; + + @NestedSetting({"prevent-stacking", "against-border"}) + public boolean preventAgainstBorder = false; + @NestedSetting({"prevent-stacking", "world-height"}) + public boolean preventAtWorldHeight = false; + + public boolean isFallingBlockInBounds(FallingBlockEntity entity) { + return (!this.preventAgainstBorder || !ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(entity.level().getWorldBorder(), entity.getBoundingBox().inflate(0.01))) + && (!this.preventAtWorldHeight || entity.blockPosition().getY() < entity.level().getMaxY() - 1); + } + } + + public Explosion explosion = new Explosion(); + public class Explosion extends ConfigurationPart { + public boolean optimiseProtectedRegions = true; + public boolean avoidRedundantBlockSearches = false; + public Map durableMaterials = Util.make(new Reference2ObjectOpenHashMap<>(), map -> { + map.put(Blocks.OBSIDIAN, new DurableMaterial(4, Blocks.COBBLESTONE.getExplosionResistance())); + map.put(Blocks.ANVIL, new DurableMaterial(3, Blocks.END_STONE.getExplosionResistance())); + map.put(Blocks.CHIPPED_ANVIL, new DurableMaterial(3, Blocks.END_STONE.getExplosionResistance())); + map.put(Blocks.DAMAGED_ANVIL, new DurableMaterial(3, Blocks.END_STONE.getExplosionResistance())); + }); + public boolean protectScaffoldingFromCreepers = false; + public boolean allowNonTntBreakingDurableBlocks = false; + public boolean destroyWaterloggedBlocks = false; + public boolean explodeLava = false; + public boolean consistentRadius = false; + public boolean explosionsHurtPlayers = true; + public boolean explosionsDropItems = true; + public boolean useBlockCacheAcrossExplosions = false; + } + + public Mechanics mechanics = new Mechanics(); + public class Mechanics extends ConfigurationPart { + public TNTSpread tntSpread = TNTSpread.ALL; + public boolean tntFlowsInWater = true; + public boolean fallingBlockParity = false; + public PhysicsVersion physicsVersion = PhysicsVersion.LATEST; + + public enum TNTSpread { + ALL, Y, NONE; + } + } + } + + public Technical technical; + public class Technical extends ConfigurationPart { + public boolean dispenserRandomItemSelection = true; + @Comment( + "Only tick hoppers when items are able to be moved\n" + + "This can cause issues with redstone contraptions that rely on DUD's to detect when hoppers fail to move items." + ) + public boolean optimiseIdleHopperTicking = true; + + public Redstone redstone = new Redstone(); + public class Redstone extends ConfigurationPart { + public boolean redstoneCache = false; + public boolean fluidsBreakRedstone = true; + } + + @Comment( + "Allow TNT duplication while `allow-piston-duplication` is disabled.\n" + + "This exists so servers can enable TNT duplication without reintroducing the other forms of piston duplication." + ) + public boolean allowTNTDuplication = false; + } + + public Players players; + public class Players extends ConfigurationPart { + public Combat combat = new Combat(); + public class Combat extends ConfigurationPart { + public boolean legacyCombatMechanics = false; + public boolean allowSweepAttacks = true; + public boolean shieldDamageReduction = false; + public boolean oldEnchantedGoldenApple = false; + public boolean oldSoundsAndParticleEffects = false; + public boolean fastHealthRegen = true; + public IntOr.Default maxArmourDamage = IntOr.Default.USE_DEFAULT; + } + + public Knockback knockback = new Knockback(); + public class Knockback extends ConfigurationPart { + public DoubleOr.Default knockbackVertical = DoubleOr.Default.USE_DEFAULT; + public double knockbackVerticalLimit = 0.4; + public boolean verticalKnockbackRequireGround = true; + public double baseKnockback = 0.4; + @Comment("Knockback caused by sweeping edge") + public double sweepingEdgeKnockback = 0.4; + + public Sprinting sprinting = new Sprinting(); + public class Sprinting extends ConfigurationPart { + public boolean requireFullAttack = true; + public double extraKnockback = 0.5; + @Comment("Delay between extra knockback hits in milliseconds") + public IntOr.Default knockbackDelay = IntOr.Default.USE_DEFAULT; + } + + @NestedSetting({"projectiles", "fishing-hooks-apply-knockback"}) + public boolean fishingHooksApplyKnockback; + + @Comment("Knockback resistance attribute modifier") + public double knockbackResistanceModifier = 1.0; + @Comment("Received by attacking a shielded enemy") + public double shieldHitKnockback = 0.5; + } + + @Comment("Prevents players swimming using elytra or riptide to enter holes") + public boolean posesShrinkCollisionBox = true; + public boolean fishingHooksPullEntities = true; + } + + public Entity entity; + public class Entity extends ConfigurationPart { + @Comment("Only modify if you know what you're doing") + public boolean disableMobAi = false; + public boolean waterSensitivity = true; + public boolean instantDeathAnimation = false; + public boolean ironGolemsTakeFalldamage = false; + + public Items items = new Items(); + public class Items extends ConfigurationPart { + public List explosionResistantItems = List.of(); + } + + @Comment("Entity travel distance limits") + public Map, Integer> chunkTravelLimit = Util.make(new Reference2ObjectOpenHashMap<>(), map -> { + map.put(EntityType.ENDER_PEARL, 8); + }); + + public ThrownPotion thrownPotion = new ThrownPotion(); + public class ThrownPotion extends ConfigurationPart { + public double horizontalSpeed = 1.0; + public double verticalSpeed = 1.0; + public boolean allowBreakingInsideEntities = false; + } + + public EnderPearl enderPearl = new EnderPearl(); + public class EnderPearl extends ConfigurationPart { + public boolean useOutlineForCollision = false; + } + } + + public Environment environment; + public class Environment extends ConfigurationPart { + public boolean allowWaterInTheNether = false; + public boolean disableFastNetherLava = false; + + public BlockGeneration blockGeneration = new BlockGeneration(); + public class BlockGeneration extends ConfigurationPart { + public boolean legacyBlockFormation = false; + } + + public Crops crops = new Crops(); + public class Crops extends ConfigurationPart { + public boolean useRandomChanceToGrow = false; + } + + public MobSpawner mobSpawner = new MobSpawner(); + public class MobSpawner extends ConfigurationPart { + public boolean checkSpawnConditions = true; + public boolean requireNearbyPlayer = true; + public boolean ignoreEntityLimit = false; + } + } + +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/ConfigurationTransformations.java b/src/main/java/me/samsuik/sakura/configuration/transformation/ConfigurationTransformations.java new file mode 100644 index 0000000000000000000000000000000000000000..154ab96b537efc3505cd85935150ddbe7ca1126f --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/ConfigurationTransformations.java @@ -0,0 +1,46 @@ +package me.samsuik.sakura.configuration.transformation; + +import io.papermc.paper.configuration.transformation.Transformations; +import me.samsuik.sakura.configuration.transformation.global.V1_RelocateMessages; +import me.samsuik.sakura.configuration.transformation.global.V2_ConvertIconToMaterial; +import me.samsuik.sakura.configuration.transformation.world.*; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import java.util.List; + +public final class ConfigurationTransformations { + private static final List REMOVED_GLOBAL_PATHS = List.of( + NodePath.path("cannons") + ); + + public static void worldTransformations(final ConfigurationNode node) throws ConfigurateException { + final ConfigurationTransformation.VersionedBuilder versionedBuilder = Transformations.versionedBuilder(); + V2_VerticalKnockbackUseDefault.apply(versionedBuilder); + V3_RenameKnockback.apply(versionedBuilder); + V4_RenameNonStrictMergeLevel.apply(versionedBuilder); + V5_CombineLoadChunksOptions.apply(versionedBuilder); + V6_FixIncorrectExtraKnockback.apply(versionedBuilder); + // ADD FUTURE VERSIONED TRANSFORMS TO versionedBuilder HERE + versionedBuilder.build().apply(node); + } + + public static void globalTransformations(final ConfigurationNode node) throws ConfigurateException { + final ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + for (final NodePath path : REMOVED_GLOBAL_PATHS) { + builder.addAction(path, TransformAction.remove()); + } + builder.build().apply(node); + + final ConfigurationTransformation.VersionedBuilder versionedBuilder = Transformations.versionedBuilder(); + V1_RelocateMessages.apply(versionedBuilder); + V2_ConvertIconToMaterial.apply(versionedBuilder); + // ADD FUTURE VERSIONED TRANSFORMS TO versionedBuilder HERE + versionedBuilder.build().apply(node); + } + + private ConfigurationTransformations() {} +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/global/V1_RelocateMessages.java b/src/main/java/me/samsuik/sakura/configuration/transformation/global/V1_RelocateMessages.java new file mode 100644 index 0000000000000000000000000000000000000000..6c72577e3fac9324ebe786e8d23969c3082ed618 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/global/V1_RelocateMessages.java @@ -0,0 +1,31 @@ +package me.samsuik.sakura.configuration.transformation.global; + +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import java.util.Map; + +import static org.spongepowered.configurate.NodePath.path; + +public final class V1_RelocateMessages { + private static final int VERSION = 2; // targeted version is always ahead by one + private static final Map RELOCATION = Map.of( + path("fps", "message"), path("messages", "fps-setting-change"), + path("players", "potato-message"), path("messages", "durable-block-interaction") + ); + + private V1_RelocateMessages() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + ConfigurationTransformation.Builder transformationBuilder = ConfigurationTransformation.builder(); + for (Map.Entry entry : RELOCATION.entrySet()) { + transformationBuilder.addAction(entry.getKey(), relocate(entry.getValue())); + } + builder.addVersion(VERSION, transformationBuilder.build()); + } + + private static TransformAction relocate(NodePath path) { + return (node, object) -> path.array(); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/global/V2_ConvertIconToMaterial.java b/src/main/java/me/samsuik/sakura/configuration/transformation/global/V2_ConvertIconToMaterial.java new file mode 100644 index 0000000000000000000000000000000000000000..c6f8bfeb83a7d108cd32caa0c88629aa49de81ff --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/global/V2_ConvertIconToMaterial.java @@ -0,0 +1,28 @@ +package me.samsuik.sakura.configuration.transformation.global; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +public final class V2_ConvertIconToMaterial implements TransformAction { + private static final int VERSION = 3; // targeted version is always ahead by one + private static final NodePath PATH = NodePath.path("fps", "material"); + private static final V2_ConvertIconToMaterial INSTANCE = new V2_ConvertIconToMaterial(); + + private V2_ConvertIconToMaterial() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + builder.addVersion(VERSION, ConfigurationTransformation.builder().addAction(PATH, INSTANCE).build()); + } + + @Override + public Object @Nullable [] visitPath(NodePath path, ConfigurationNode value) throws ConfigurateException { + if (value.raw() instanceof String stringValue) { + value.raw(stringValue.toUpperCase()); + } + return null; + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/world/V2_VerticalKnockbackUseDefault.java b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V2_VerticalKnockbackUseDefault.java new file mode 100644 index 0000000000000000000000000000000000000000..97d5224ef110cb2fa4f44a90a54a1611dc0182d9 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V2_VerticalKnockbackUseDefault.java @@ -0,0 +1,31 @@ +package me.samsuik.sakura.configuration.transformation.world; + +import io.papermc.paper.configuration.type.number.DoubleOr; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import static org.spongepowered.configurate.NodePath.path; + +public final class V2_VerticalKnockbackUseDefault implements TransformAction { + private static final int VERSION = 2; + private static final NodePath PATH = path("players", "knockback", "knockback-vertical"); + private static final V2_VerticalKnockbackUseDefault INSTANCE = new V2_VerticalKnockbackUseDefault(); + + private V2_VerticalKnockbackUseDefault() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + builder.addVersion(VERSION, ConfigurationTransformation.builder().addAction(PATH, INSTANCE).build()); + } + + @Override + public Object @Nullable [] visitPath(NodePath path, ConfigurationNode value) throws ConfigurateException { + if (value.getDouble() == 0.4) { + value.set(DoubleOr.Default.USE_DEFAULT); + } + return null; + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/world/V3_RenameKnockback.java b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V3_RenameKnockback.java new file mode 100644 index 0000000000000000000000000000000000000000..efba1f83cb079d0e75904adc439b715d17793979 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V3_RenameKnockback.java @@ -0,0 +1,27 @@ +package me.samsuik.sakura.configuration.transformation.world; + +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; + +import java.util.Map; + +import static org.spongepowered.configurate.NodePath.path; +import static org.spongepowered.configurate.transformation.TransformAction.*; + +public final class V3_RenameKnockback { + private static final int VERSION = 3; + private static final Map RENAME = Map.of( + path("players", "knockback", "vertical-limit-require-ground"), "vertical-knockback-require-ground", + path("players", "knockback", "knockback-horizontal"), "base-knockback" + ); + + private V3_RenameKnockback() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + ConfigurationTransformation.Builder transformationBuilder = ConfigurationTransformation.builder(); + for (Map.Entry entry : RENAME.entrySet()) { + transformationBuilder.addAction(entry.getKey(), rename(entry.getValue())); + } + builder.addVersion(VERSION, transformationBuilder.build()); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/world/V4_RenameNonStrictMergeLevel.java b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V4_RenameNonStrictMergeLevel.java new file mode 100644 index 0000000000000000000000000000000000000000..198eb5f030ec5def4d93dec51dde352261654bb5 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V4_RenameNonStrictMergeLevel.java @@ -0,0 +1,35 @@ +package me.samsuik.sakura.configuration.transformation.world; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import java.util.Locale; + +import static org.spongepowered.configurate.NodePath.path; + +public final class V4_RenameNonStrictMergeLevel implements TransformAction { + private static final int VERSION = 4; + private static final String OLD_LEVEL_NAME = "NON_STRICT"; + private static final String NEW_LEVEL_NAME = "LENIENT"; + private static final NodePath PATH = path("cannons", "merge-level"); + private static final V4_RenameNonStrictMergeLevel INSTANCE = new V4_RenameNonStrictMergeLevel(); + + private V4_RenameNonStrictMergeLevel() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + builder.addVersion(VERSION, ConfigurationTransformation.builder().addAction(PATH, INSTANCE).build()); + } + + @Override + public Object @Nullable [] visitPath(NodePath path, ConfigurationNode value) throws ConfigurateException { + String level = value.getString(); + if (level != null && OLD_LEVEL_NAME.equals(level.toUpperCase(Locale.ENGLISH))) { + value.set(NEW_LEVEL_NAME); + } + return null; + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/world/V5_CombineLoadChunksOptions.java b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V5_CombineLoadChunksOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..3d647a3db0862232f158d823da9a797d4e0d5608 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V5_CombineLoadChunksOptions.java @@ -0,0 +1,44 @@ +package me.samsuik.sakura.configuration.transformation.world; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import java.util.List; + +import static org.spongepowered.configurate.NodePath.path; + +public final class V5_CombineLoadChunksOptions implements TransformAction { + private static final int VERSION = 5; + private static final List ENTITY_PATHS = List.of("tnt", "sand"); + private static final String OLD_NAME = "loads-chunks"; + private static final String NAME = "load-chunks"; + private static final NodePath PATH = path("cannons"); + private static final V5_CombineLoadChunksOptions INSTANCE = new V5_CombineLoadChunksOptions(); + + private V5_CombineLoadChunksOptions() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + builder.addVersion(VERSION, ConfigurationTransformation.builder().addAction(PATH, INSTANCE).build()); + } + + @Override + public Object @Nullable [] visitPath(NodePath path, ConfigurationNode value) throws ConfigurateException { + boolean shouldLoadChunks = false; + + for (String entity : ENTITY_PATHS) { + NodePath entityPath = NodePath.path(entity, OLD_NAME); + if (value.hasChild(entityPath)) { + ConfigurationNode node = value.node(entityPath); + shouldLoadChunks |= node.getBoolean(); + node.raw(null); + } + } + + value.node(NAME).set(shouldLoadChunks); + return null; + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/transformation/world/V6_FixIncorrectExtraKnockback.java b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V6_FixIncorrectExtraKnockback.java new file mode 100644 index 0000000000000000000000000000000000000000..f9fdf1a87420c78136deccf8822f5cf177b41aed --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/transformation/world/V6_FixIncorrectExtraKnockback.java @@ -0,0 +1,30 @@ +package me.samsuik.sakura.configuration.transformation.world; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import static org.spongepowered.configurate.NodePath.path; + +public final class V6_FixIncorrectExtraKnockback implements TransformAction { + private static final int VERSION = 6; + private static final NodePath PATH = path("players", "knockback", "sprinting", "extra-knockback"); + private static final V6_FixIncorrectExtraKnockback INSTANCE = new V6_FixIncorrectExtraKnockback(); + + private V6_FixIncorrectExtraKnockback() {} + + public static void apply(ConfigurationTransformation.VersionedBuilder builder) { + builder.addVersion(VERSION, ConfigurationTransformation.builder().addAction(PATH, INSTANCE).build()); + } + + @Override + public Object @Nullable [] visitPath(NodePath path, ConfigurationNode value) throws ConfigurateException { + if (value.getDouble() == 1.0) { + value.set(0.5); + } + return null; + } +} diff --git a/src/main/java/me/samsuik/sakura/explosion/durable/DurableMaterial.java b/src/main/java/me/samsuik/sakura/explosion/durable/DurableMaterial.java new file mode 100644 index 0000000000000000000000000000000000000000..4024f9738e039ffffd560a07a2210f758879d3c0 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/explosion/durable/DurableMaterial.java @@ -0,0 +1,7 @@ +package me.samsuik.sakura.explosion.durable; + +import org.spongepowered.configurate.objectmapping.ConfigSerializable; + +@ConfigSerializable +public record DurableMaterial(int durability, float resistance) { +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index ae4ebf509837e8d44255781c61d02873f8b74be8..a39b00ebb37a0bf041bc7b9861fb7f9ebd962c40 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -327,6 +327,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping @@ -512,6 +513,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop WorldDataServer 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, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { - super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), 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, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor + super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), 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, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), () -> minecraftserver.sakuraConfigurations.createWorldConfig(me.samsuik.sakura.configuration.SakuraConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), minecraftserver.registryAccess())), executor); // Sakura - sakura configuration files // Paper - create paper world configs; Async-Anti-Xray: Pass executor 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/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 27f9d167b5ae9ce5117798ea44324107df59425f..34e7ae8b36e375280f9515c1580c55c0832fc194 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -173,6 +173,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl return this.paperConfig; } // Paper end - add paper world config + // Sakura start - sakura configuration files + private final me.samsuik.sakura.configuration.WorldConfiguration sakuraConfig; + public final me.samsuik.sakura.configuration.WorldConfiguration sakuraConfig() { + return this.sakuraConfig; + } + // Sakura end - sakura configuration files public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public static BlockPos lastPhysicsProblem; // Spigot @@ -831,7 +837,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } // Paper end - optimise random ticking - 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, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config & Anti-Xray + 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, java.util.function.Function paperWorldConfigCreator, java.util.function.Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura - sakura configuration files // Paper - create paper world config & Anti-Xray // Paper start - getblock optimisations - cache world height/sections final DimensionType dimType = holder.value(); this.minY = dimType.minY(); @@ -843,6 +849,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl // Paper end - getblock optimisations - cache world height/sections this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config + this.sakuraConfig = sakuraWorldConfigCreator.get(); // Sakura - sakura configuration files this.generator = gen; this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 97b5d6ba2b19a7c730730c74175a29157aed1840..6009e6dc0b687b942dde60d32fc5a7a19a67ef9d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -1086,6 +1086,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); + this.console.sakuraConfigurations.reloadConfigs(this.console); // Sakura - sakura configuration files; missing comment above :< for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) @@ -1117,6 +1118,7 @@ public final class CraftServer implements Server { this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper + me.samsuik.sakura.command.SakuraCommands.registerCommands(this.console); // Sakura - sakura configuration files this.spark.registerCommandBeforePlugins(this); // Paper - spark this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java index 1c2439ffc1e407ff69286817d22f127470ce07ba..58cf9672ed1826846db7a47b264c387de51020a5 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -176,6 +176,14 @@ public class Main { .describedAs("Jar file"); // Paper end + // Sakura start - sakura configuration files + acceptsAll(asList("sakura-dir", "sakura-settings-directory"), "Directory for Sakura settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File(me.samsuik.sakura.configuration.SakuraConfigurations.CONFIG_DIR)) + .describedAs("Config directory"); + // Sakura end - sakura configuration files + // Paper start acceptsAll(asList("server-name"), "Name of the server") .withRequiredArg()