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/me/samsuik/sakura/command/BaseSubCommand.java b/src/main/java/me/samsuik/sakura/command/BaseSubCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..9b5af05f7a4593eb44f36fff90d94e98d6999c7f --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/BaseSubCommand.java @@ -0,0 +1,47 @@ +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; + +@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 (testPermission(sender)) { + execute(sender, args); + } + + return true; + } + + @Override + @NotNull + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + var completions = new ArrayList(0); + + if (testPermissionSilent(sender)) { + tabComplete(completions, args); + } + + return completions; + } + +} 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..2b145614bf189ae56622016436bfefd63f5271eb --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/SakuraCommand.java @@ -0,0 +1,93 @@ +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.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +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 javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@DefaultQualifier(NonNull.class) +public final class SakuraCommand extends Command { + + private static final Component INFORMATION_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) { + var commands = new ArrayList<>(SakuraCommands.COMMANDS.values()); + + // This part is copied from the VersionCommand SubCommand in paper + @Nullable + var internalVersion = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (internalVersion != null) { + commands.add(internalVersion); + } + + for (var base : commands) { + if (base.getName().equalsIgnoreCase(args[0])) { + return base.execute(sender, commandLabel, Arrays.copyOfRange(args, 1, args.length)); + } + } + } + + sendHelpMessage(sender); + return false; + } + + private void sendHelpMessage(CommandSender sender) { + sender.sendMessage(INFORMATION_MESSAGE); + + var uniqueCommands = SakuraCommands.COMMANDS.values() + .stream() + .filter(command -> command != this); + + uniqueCommands.forEach((command) -> { + sender.sendMessage(MiniMessage.miniMessage().deserialize(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 (!testPermissionSilent(sender)) { + return Collections.emptyList(); + } + + return SakuraCommands.COMMANDS.values().stream() + .filter(command -> command != this) // ahem + .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..cd44b3400a1ab9544aa4a9e50b1054ea436a3643 --- /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(final 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..93c0f22cdf0c3b7ce4db55171ea397f5673231b1 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/command/subcommands/ConfigCommand.java @@ -0,0 +1,50 @@ +package me.samsuik.sakura.command.subcommands; + +import me.samsuik.sakura.command.BaseSubCommand; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +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 org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +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(); + me.samsuik.sakura.configuration.SakuraGlobalConfig.get().setup((File) server.options.valueOf("sakura-settings")); + for (ServerLevel world : server.getAllLevels()) { + world.sakuraConfig.init(); + } + server.server.reloadCount++; + + Command.broadcastCommandMessage(sender, text("Sakura config reload complete.", GREEN)); + } + + @NotNull + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/me/samsuik/sakura/configuration/SakuraGlobalConfig.java b/src/main/java/me/samsuik/sakura/configuration/SakuraGlobalConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..7000fa68886057fb742cf4f655a739b0ee3f62e1 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/SakuraGlobalConfig.java @@ -0,0 +1,92 @@ +package me.samsuik.sakura.configuration; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; + +public final class SakuraGlobalConfig { + private static final String HEADER = """ + This is the main 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. + + This configuration file is not compatible with Sakura for modern versions of minecraft. + """; + + private static final SakuraGlobalConfig INSTANCE = new SakuraGlobalConfig(); + + public static SakuraGlobalConfig get() { + return INSTANCE; + } + + private YamlConfiguration config; + private int version; + + public void setup(File configFile) { + this.config = YamlConfigHelper.loadConfig(configFile, HEADER, config -> { + this.version = this.getInt("config-version", 0); + this.set("config-version", 0); + }); + } + + public YamlConfiguration getYamlConfig() { + return this.config; + } + + private void set(String path, Object val) { + this.config.set(path, val); + } + + private boolean getBoolean(String path, boolean def) { + this.config.addDefault(path, def); + return this.config.getBoolean(path, this.config.getBoolean(path)); + } + + private int getInt(String path, int def) { + this.config.addDefault(path, def); + return this.config.getInt(path, this.config.getInt(path)); + } + + private String getString(String path, String def) { + this.config.addDefault(path, def); + return this.config.getString(path, this.config.getString(path)); + } + + public String fpsMessage = "(S) "; + public String fpsMaterial = "pink_stained_glass_pane"; + private void Fps() { + this.fpsMessage = this.getString("fps.messsage", this.fpsMessage); + this.fpsMaterial = this.getString("fps.material", this.fpsMaterial); + } + + public boolean reducedSearchRays; + private void Explosion() { + this.reducedSearchRays = this.getBoolean("cannons.explosion.reduced-search-rays", this.reducedSearchRays); + } + + public String potatoMessage = "(S) This block has of "; + private void Players() { + this.potatoMessage = this.getString("players.potato-message", this.potatoMessage); + } + + public boolean calculateBiomeNoiseOncePerChunkSection = false; + private void Environment() { + this.calculateBiomeNoiseOncePerChunkSection = this.getBoolean("environment.calculate-biome-noise-once-per-chunk-section", this.calculateBiomeNoiseOncePerChunkSection); + } + + public int minSpawnDelay = 200; + public int maxSpawnDelay = 800; + public int spawnCount = 4; + public int maxNearbyEntities = 6; + public int requiredPlayerRange = 16; + public int spawnRange = 4; + private void MobSpawnerDefaults() { + // as global and world configs are combined I moved the defaults into its own section + this.minSpawnDelay = this.getInt("environment.mob-spawner.defaults.min-spawn-delay", this.minSpawnDelay); + this.maxSpawnDelay = this.getInt("environment.mob-spawner.defaults.max-spawn-delay", this.maxSpawnDelay); + this.spawnCount = this.getInt("environment.mob-spawner.defaults.spawn-count", this.spawnCount); + this.maxNearbyEntities = this.getInt("environment.mob-spawner.defaults.max-nearby-entities", this.maxNearbyEntities); + this.requiredPlayerRange = this.getInt("environment.mob-spawner.defaults.required-player-range", this.requiredPlayerRange); + this.spawnRange = this.getInt("environment.mob-spawner.defaults.spawn-range", this.spawnRange); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/SakuraWorldConfig.java b/src/main/java/me/samsuik/sakura/configuration/SakuraWorldConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..34c4d0db007753f37cc4ca96c21175ce0f0d56e1 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/SakuraWorldConfig.java @@ -0,0 +1,325 @@ +package me.samsuik.sakura.configuration; + +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import me.samsuik.sakura.configuration.serializer.*; +import me.samsuik.sakura.entity.TntSpread; +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.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.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.*; + +public final class SakuraWorldConfig { + private final String worldName; + private YamlConfiguration config; + + public SakuraWorldConfig(String worldName) { + this.worldName = worldName; + this.init(); + } + + public void init() { + this.config = SakuraGlobalConfig.get().getYamlConfig(); // grab updated reference + } + + private String worldPath(String path) { + return "world-settings." + this.worldName + "." + path; + } + + private void set(String path, Object val) { + this.config.set(path, val); + if (this.config.get(this.worldPath(path)) != null) { + this.config.set(this.worldPath(path), val); + } + } + + private void remove(String path) { + this.config.addDefault(path, null); + this.set(path, null); + } + + private boolean getBoolean(String path, boolean def) { + this.config.addDefault(path, def); + return this.config.getBoolean(this.worldPath(path), this.config.getBoolean(path, def)); + } + + private double getDouble(String path, double def) { + this.config.addDefault(path, def); + return this.config.getDouble(this.worldPath(path), this.config.getDouble(path)); + } + + private int getInt(String path, int def) { + this.config.addDefault(path, def); + return this.config.getInt(this.worldPath(path), this.config.getInt(path, def)); + } + + private List getList(String path, List def) { + this.config.addDefault(path, def); + return (List) this.config.getList(this.worldPath(path), this.config.getList(path)); + } + + private String getString(String path, String def) { + this.config.addDefault(path, def); + return this.config.getString(this.worldPath(path), this.config.getString(path)); + } + + private > T getEnum(String path, T def, Class enumClass) { + String name = this.getString(path, def.name()); + return Enum.valueOf(enumClass, name); + } + + private OptionalDouble getDoubleWithContext(String path, String context) { + this.config.addDefault(path, context); + double present = this.config.getDouble(path, this.config.getDouble(this.worldPath(path), Double.MIN_VALUE)); + return present != Double.MIN_VALUE ? OptionalDouble.of(present) : OptionalDouble.empty(); + } + + private OptionalInt getIntWithContext(String path, String context) { + this.config.addDefault(path, context); + int present = this.config.getInt(path, this.config.getInt(this.worldPath(path), Integer.MIN_VALUE)); + return present != Integer.MIN_VALUE ? OptionalInt.of(present) : OptionalInt.empty(); + } + + private void loadSerializedMap(String path, Map def, MapSerializer serializer) { + this.config.addDefault(path, this.getSerializedMapDefaults(def, serializer)); + ConfigurationSection section = this.config.getConfigurationSection(path); + def.clear(); + for (String serializedKey : section.getKeys(false)) { + K key = serializer.deserializeKey(serializedKey); + V value = serializer.deserializeValue((C) this.config.get(serializedKey)); + def.put(key, value); + } + } + + private ConfigurationSection getSerializedMapDefaults(Map def, MapSerializer serializer) { + ConfigurationSection section = new MemoryConfiguration(); + def.forEach((k, v) -> { + String key = serializer.serializeKey(k); + Object value = serializer.serializeValue(v); + section.set(key, value); + }); + return section; + } + + private void loadSerializedList(String path, List def, ListSerializer serializer) { + List defaults = def.stream() + .map(serializer::serialize) + .toList(); + def.clear(); + for (C object : this.getList(path, defaults)) { + def.add(serializer.deserialize(object)); + } + } + + public MergeLevel mergeLevel = MergeLevel.STRICT; + public boolean tntAndSandAffectedByBubbleColumns = true; + public boolean loadChunks = false; + public boolean collideWhenMoving; + public double collideMovingFasterThan = 64.0; + private void Cannons() { + this.mergeLevel = this.getEnum("cannons.merge-level", this.mergeLevel, MergeLevel.class); + this.tntAndSandAffectedByBubbleColumns = this.getBoolean("cannons.tnt-and-sand-affected-by-bubble-columns", this.tntAndSandAffectedByBubbleColumns); + this.loadChunks = this.getBoolean("cannons.load-chunks", this.loadChunks); + + this.collideWhenMoving = this.getBoolean("cannons.treat-collidable-blocks-as-full.while-moving", this.collideWhenMoving); + this.collideMovingFasterThan = this.getDouble("cannons.treat-collidable-blocks-as-full.moving-faster-than", this.collideMovingFasterThan); + } + + public OptionalInt leftShootingThreshold = OptionalInt.empty(); + public OptionalInt maxAdjustDistance = OptionalInt.empty(); + private void Restrictions() { + this.leftShootingThreshold = this.getIntWithContext("cannons.restrictions.left-shooting-threshold", "disabled"); + this.maxAdjustDistance = this.getIntWithContext("cannons.restrictions.max-adjust-distance", "disabled"); + } + + public boolean forcePositionUpdates; + private void Tnt() { + this.forcePositionUpdates = this.getBoolean("cannons.tnt.force-position-updates", this.forcePositionUpdates); + } + + public boolean despawnInsideMovingPistons = true; + public boolean concreteSolidifyInWater = true; + public boolean preventStackingAgainstBorder = false; + public boolean preventStackingAtWorldHeight = false; + private void Sand() { + this.despawnInsideMovingPistons = this.getBoolean("cannons.sand.despawn-inside-moving-pistons", this.despawnInsideMovingPistons); + this.concreteSolidifyInWater = this.getBoolean("cannons.sand.concrete-solidify-in-water", this.concreteSolidifyInWater); + this.preventStackingAgainstBorder = this.getBoolean("cannons.sand.prevent-stacking.against-border", this.preventStackingAgainstBorder); + this.preventStackingAtWorldHeight = this.getBoolean("cannons.sand.prevent-stacking.world-height", this.preventStackingAtWorldHeight); + } + + public boolean isFallingBlockInBounds(FallingBlockEntity entity) { + if (this.preventStackingAtWorldHeight && entity.blockPosition().getY() >= entity.level.getMaxBuildHeight()) { + return false; + } + if (this.preventStackingAgainstBorder && io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(entity.level.getWorldBorder(), entity.getBoundingBox().inflate(0.01))) { + return false; + } + return true; + } + + 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; + private void Explosion() { + this.optimiseProtectedRegions = this.getBoolean("cannons.explosion.optimise-protected-regions", this.optimiseProtectedRegions); + this.avoidRedundantBlockSearches = this.getBoolean("cannons.explosion.avoid-redundant-block-searches", this.avoidRedundantBlockSearches); + this.loadSerializedMap("cannons.explosion.durable-materials", this.durableMaterials, DurableMaterialSerializer.get()); + this.protectScaffoldingFromCreepers = this.getBoolean("cannons.explosion.protect-scaffolding-from-creepers", this.protectScaffoldingFromCreepers); + this.allowNonTntBreakingDurableBlocks = this.getBoolean("cannons.explosion.allow-non-tnt-breaking-durable-blocks", this.allowNonTntBreakingDurableBlocks); + this.destroyWaterloggedBlocks = this.getBoolean("cannons.explosion.destroy-waterlogged-blocks", this.destroyWaterloggedBlocks); + this.explodeLava = this.getBoolean("cannons.explosion.explode-lava", this.explodeLava); + this.consistentRadius = this.getBoolean("cannons.explosion.consistent-radius", this.consistentRadius); + this.explosionsHurtPlayers = this.getBoolean("cannons.explosion.explosions-hurt-players", this.explosionsHurtPlayers); + this.explosionsDropItems = this.getBoolean("cannons.explosion.explosions-drop-items", this.explosionsDropItems); + } + + public TntSpread tntSpread = TntSpread.ALL; + public boolean tntFlowsInWater = true; + public boolean fallingBlockParity = false; + public PhysicsVersion physicsVersion = PhysicsVersion.LATEST; + private void Mechanics() { + this.tntSpread = this.getEnum("cannons.mechanics.tnt-spread", this.tntSpread, TntSpread.class); + this.tntFlowsInWater = this.getBoolean("cannons.mechanics.tnt-flows-in-water", this.tntFlowsInWater); + this.fallingBlockParity = this.getBoolean("cannons.mechanics.falling-block-parity", this.fallingBlockParity); + this.physicsVersion = this.getEnum("cannons.mechanics.physics-version", this.physicsVersion, PhysicsVersion.class); + } + + public boolean dispenserRandomItemSelection = true; + public boolean fluidsBreakRedstone = true; + private void Technical() { + this.dispenserRandomItemSelection = this.getBoolean("technical.dispenser-random-item-selection", this.dispenserRandomItemSelection); + this.fluidsBreakRedstone = this.getBoolean("technical.redstone.fluids-break-redstone", this.fluidsBreakRedstone); + } + + 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 OptionalInt maxArmourDamage = OptionalInt.empty(); + private void Combat() { + this.legacyCombatMechanics = this.getBoolean("players.combat.legacy-combat-mechanics", this.legacyCombatMechanics); + this.allowSweepAttacks = this.getBoolean("players.combat.allow-sweep-attacks", this.allowSweepAttacks); + this.shieldDamageReduction = this.getBoolean("players.combat.shield-damage-reduction", this.shieldDamageReduction); + this.oldEnchantedGoldenApple = this.getBoolean("players.combat.old-enchanted-golden-apple", this.oldEnchantedGoldenApple); + this.oldSoundsAndParticleEffects = this.getBoolean("players.combat.old-sounds-and-particle-effects", this.oldSoundsAndParticleEffects); + this.fastHealthRegen = this.getBoolean("players.combat.fast-health-regen", this.fastHealthRegen); + this.maxArmourDamage = this.getIntWithContext("players.combat.max-armour-damage", "default"); + } + + public OptionalDouble knockbackVertical = OptionalDouble.empty(); + public double knockbackVerticalLimit = 0.4; + public boolean verticalKnockbackRequireGround = true; + public double baseKnockback = 0.4; + public double sweepingEdgeKnockback = 0.4; + public boolean sprintingRequireFullAttack = true; + public double sprintingExtraKnockback = 0.5; + public OptionalInt sprintingKnockbackDelay = OptionalInt.empty(); + public boolean fishingHooksApplyKnockback; + public double knockbackResistanceModifier = 1.0; + public double shieldHitKnockback = 0.5; + private void Knockback() { + this.knockbackVertical = this.getDoubleWithContext("players.knockback.knockback-vertical", "default"); + this.knockbackVerticalLimit = this.getDouble("players.knockback.knockback-vertical-limit", this.knockbackVerticalLimit); + this.verticalKnockbackRequireGround = this.getBoolean("players.knockback.vertical-knockback-require-ground", this.verticalKnockbackRequireGround); + this.baseKnockback = this.getDouble("players.knockback.base-knockback", this.baseKnockback); + this.sweepingEdgeKnockback = this.getDouble("players.knockback.sweeping-edge-knockback", this.sweepingEdgeKnockback); + + this.sprintingRequireFullAttack = this.getBoolean("players.knockback.sprinting.require-full-attack", this.sprintingRequireFullAttack); + this.sprintingExtraKnockback = this.getDouble("players.knockback.sprinting.extra-knockback", this.sprintingExtraKnockback); + this.sprintingKnockbackDelay = this.getIntWithContext("players.knockback.sprinting.knockback-delay", "default"); + + this.fishingHooksApplyKnockback = this.getBoolean("players.knockback.projectiles.fishing-hooks-apply-knockback", this.fishingHooksApplyKnockback); + this.knockbackResistanceModifier = this.getDouble("players.knockback.knockback-resistance-modifier", this.knockbackResistanceModifier); + this.shieldHitKnockback = this.getDouble("players.knockback.shield-hit-knockback", this.shieldHitKnockback); + } + + public boolean posesShrinkCollisionBox = true; + public boolean fishingHooksPullEntities = true; + private void Players() { + this.posesShrinkCollisionBox = this.getBoolean("players.poses-shrink-collision-box", this.posesShrinkCollisionBox); + this.fishingHooksPullEntities = this.getBoolean("players.fishing-hooks-pull-entities", this.fishingHooksPullEntities); + } + + public boolean disableMobAi = false; + public boolean waterSensitivity = true; + public boolean instantDeathAnimation = false; + public boolean ironGolemsTakeFalldamage = false; + public Map, Integer> chunkTravelLimit = Util.make(new Reference2ObjectOpenHashMap<>(), map -> { + map.put(EntityType.ENDER_PEARL, 8); + }); + public boolean projectilesLoadChunksForCollisions = false; + public List explosionResistantItems = List.of(); + private void Entity() { + this.disableMobAi = this.getBoolean("entity.disable-mob-ai", this.disableMobAi); + this.waterSensitivity = this.getBoolean("entity.water-sensitivity", this.waterSensitivity); + this.instantDeathAnimation = this.getBoolean("entity.instant-death-animation", this.instantDeathAnimation); + this.ironGolemsTakeFalldamage = this.getBoolean("entity.iron-golems-take-falldamage", this.ironGolemsTakeFalldamage); + this.loadSerializedMap("entity.chunk-travel-limit", this.chunkTravelLimit, ChunkTravelLimitsSerializer.get()); + this.projectilesLoadChunksForCollisions = this.getBoolean("entity.projectiles-load-chunks-for-collisions", this.projectilesLoadChunksForCollisions); + this.loadSerializedList("entity.items.explosion-resistant-items", this.explosionResistantItems, ItemListSerializer.get()); + } + + public double horizontalSpeed = 1.0; + public double verticalSpeed = 1.0; + public boolean allowBreakingInsideEntities = false; + private void ThrownPotion() { + this.horizontalSpeed = this.getDouble("entity.thrown-potion.horizontal-speed", this.horizontalSpeed); + this.verticalSpeed = this.getDouble("entity.thrown-potion.vertical-speed", this.verticalSpeed); + this.allowBreakingInsideEntities = this.getBoolean("entity.thrown-potion.allow-breaking-inside-entities", this.allowBreakingInsideEntities); + } + + public boolean useOutlineForCollision = false; + private void EnderPearl() { + this.useOutlineForCollision = this.getBoolean("entity.ender-pearl.use-outline-for-collision", this.useOutlineForCollision); + } + + public boolean allowWaterInTheNether = false; + public boolean disableFastNetherLava = false; + private void Environment() { + this.allowWaterInTheNether = this.getBoolean("environment.allow-water-in-the-nether", this.allowWaterInTheNether); + this.disableFastNetherLava = this.getBoolean("environment.disable-fast-nether-lava", this.disableFastNetherLava); + } + + public boolean legacyBlockFormation = false; + private void BlockGeneration() { + this.legacyBlockFormation = this.getBoolean("environment.block-generation.legacy-block-formation", this.legacyBlockFormation); + } + + public boolean useRandomChanceToGrow = false; + private void Crops() { + this.useRandomChanceToGrow = this.getBoolean("environment.crops.use-random-chance-to-grow", this.useRandomChanceToGrow); + } + + public boolean checkSpawnConditions = true; + public boolean requireNearbyPlayer = true; + public boolean ignoreEntityLimit = false; + private void MobSpawner() { + this.checkSpawnConditions = this.getBoolean("environment.mob-spawner.check-spawn-conditions", this.checkSpawnConditions); + this.requireNearbyPlayer = this.getBoolean("environment.mob-spawner.require-nearby-player", this.requireNearbyPlayer); + this.ignoreEntityLimit = this.getBoolean("environment.mob-spawner.ignore-entity-limit", this.ignoreEntityLimit); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/YamlConfigHelper.java b/src/main/java/me/samsuik/sakura/configuration/YamlConfigHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..9340a0696d2a1bf4d91d67627df49191a40682b6 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/YamlConfigHelper.java @@ -0,0 +1,60 @@ +package me.samsuik.sakura.configuration; + +import com.google.common.base.Throwables; +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Consumer; +import java.util.logging.Level; + +public final class YamlConfigHelper { + public static YamlConfiguration loadConfig(File configFile, String header, Consumer afterLoad) { + YamlConfiguration config = new YamlConfiguration(); + try { + config.load(configFile); + } catch (IOException ignored) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, String.format("Could not load %s, please correct your syntax errors", configFile.getName()), ex); + throw Throwables.propagate(ex); + } + + config.options().header(header); + config.options().copyDefaults(true); + afterLoad.accept(config); + + readConfig(SakuraGlobalConfig.class, null); + saveConfig(config, configFile); + return config; + } + + public static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw Throwables.propagate(ex.getCause()); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); + } + } + } + } + } + + public static void saveConfig(YamlConfiguration config, File configFile) { + try { + config.save(configFile); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + configFile, ex); + } + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/serializer/ChunkTravelLimitsSerializer.java b/src/main/java/me/samsuik/sakura/configuration/serializer/ChunkTravelLimitsSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..37be21e53f33c1aa64e1919092daa4cb0b1515c4 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/serializer/ChunkTravelLimitsSerializer.java @@ -0,0 +1,33 @@ +package me.samsuik.sakura.configuration.serializer; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; + +public final class ChunkTravelLimitsSerializer implements MapSerializer, Integer, Integer> { + private static final ChunkTravelLimitsSerializer INSTANCE = new ChunkTravelLimitsSerializer(); + + public static ChunkTravelLimitsSerializer get() { + return INSTANCE; + } + + @Override + public String serializeKey(EntityType key) { + return Registry.ENTITY_TYPE.getKey(key).getPath(); + } + + @Override + public EntityType deserializeKey(String string) { + return Registry.ENTITY_TYPE.get(new ResourceLocation(string)); + } + + @Override + public Integer serializeValue(Integer val) { + return val; + } + + @Override + public Integer deserializeValue(Integer val) { + return val; + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/serializer/DurableMaterialSerializer.java b/src/main/java/me/samsuik/sakura/configuration/serializer/DurableMaterialSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..0b1670515231be1408780fa6d4cc2daa599b024c --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/serializer/DurableMaterialSerializer.java @@ -0,0 +1,41 @@ +package me.samsuik.sakura.configuration.serializer; + +import me.samsuik.sakura.explosion.durable.DurableMaterial; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; + +public final class DurableMaterialSerializer implements MapSerializer { + private static final DurableMaterialSerializer INSTANCE = new DurableMaterialSerializer(); + + public static DurableMaterialSerializer get() { + return INSTANCE; + } + + @Override + public String serializeKey(Block key) { + return Registry.BLOCK.getKey(key).getPath(); + } + + @Override + public Block deserializeKey(String blockName) { + return Registry.BLOCK.get(new ResourceLocation(blockName)); + } + + @Override + public ConfigurationSection serializeValue(DurableMaterial mat) { + MemoryConfiguration materialSection = new MemoryConfiguration(); + materialSection.set("durability", mat.durability()); + materialSection.set("resistance", mat.resistance()); + return materialSection; + } + + @Override + public DurableMaterial deserializeValue(ConfigurationSection materialSection) { + int durability = materialSection.getInt("durability", 0); + float resistance = (float) materialSection.getDouble("resistance", 0.0); + return new DurableMaterial(durability, resistance); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/serializer/ItemListSerializer.java b/src/main/java/me/samsuik/sakura/configuration/serializer/ItemListSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..1619656cd9d776ce6abb5de15364cc6cf1ba8f12 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/serializer/ItemListSerializer.java @@ -0,0 +1,23 @@ +package me.samsuik.sakura.configuration.serializer; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; + +public final class ItemListSerializer implements ListSerializer { + private static final ItemListSerializer INSTANCE = new ItemListSerializer(); + + public static ItemListSerializer get() { + return INSTANCE; + } + + @Override + public String serialize(Item val) { + return Registry.ITEM.getKey(val).getPath(); + } + + @Override + public Item deserialize(String any) { + return Registry.ITEM.get(new ResourceLocation(any)); + } +} diff --git a/src/main/java/me/samsuik/sakura/configuration/serializer/ListSerializer.java b/src/main/java/me/samsuik/sakura/configuration/serializer/ListSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..0937a8dd5b8dcb1c594c0afb82b5dfda3f6c8305 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/serializer/ListSerializer.java @@ -0,0 +1,7 @@ +package me.samsuik.sakura.configuration.serializer; + +public interface ListSerializer { + C serialize(V val); + + V deserialize(C any); +} diff --git a/src/main/java/me/samsuik/sakura/configuration/serializer/MapSerializer.java b/src/main/java/me/samsuik/sakura/configuration/serializer/MapSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..7eec08f2263ec1eef5571bcef33bb01771b36833 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/configuration/serializer/MapSerializer.java @@ -0,0 +1,13 @@ +package me.samsuik.sakura.configuration.serializer; + +public interface MapSerializer { + + + String serializeKey(K key); + + K deserializeKey(String string); + + C serializeValue(V val); + + V deserializeValue(C object); +} diff --git a/src/main/java/me/samsuik/sakura/entity/TntSpread.java b/src/main/java/me/samsuik/sakura/entity/TntSpread.java new file mode 100644 index 0000000000000000000000000000000000000000..83275db7686a656f8d932d03e092cc6ddaf31ebb --- /dev/null +++ b/src/main/java/me/samsuik/sakura/entity/TntSpread.java @@ -0,0 +1,5 @@ +package me.samsuik.sakura.entity; + +public enum TntSpread { + ALL, Y, NONE; +} 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/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 257c94f7c1cb00c9a91ab82e311dfd8eca29c538..edd21098ee4c2773acf61fd073d8c87fe2880201 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -235,6 +235,10 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc. io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider // Paper end + // Sakura start + me.samsuik.sakura.configuration.SakuraGlobalConfig.get().setup((java.io.File) this.options.valueOf("sakura-settings")); + me.samsuik.sakura.command.SakuraCommands.registerCommands(this); + // Sakura end this.setPvpAllowed(dedicatedserverproperties.pvp); this.setFlightAllowed(dedicatedserverproperties.allowFlight); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index fee8996f35b38fd79946cdfd677763e0201eb57d..db7335a079cf711a829b2e4fb7354717da2f8789 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -162,6 +162,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper + public final me.samsuik.sakura.configuration.SakuraWorldConfig sakuraConfig; // Sakura public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public final co.aikar.timings.WorldTimingsHandler timings; // Paper @@ -264,6 +265,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper + this.sakuraConfig = new me.samsuik.sakura.configuration.SakuraWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Sakura 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 3d07ef0cbd57d54d131cdc766dd55d210d67fb4c..62ee1aebdea1acb3c213d768f82f9131141343ad 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -956,6 +956,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper + me.samsuik.sakura.configuration.SakuraGlobalConfig.get().setup((File) console.options.valueOf("sakura-settings")); // Sakura 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)) @@ -972,6 +973,7 @@ public final class CraftServer implements Server { } world.spigotConfig.init(); // Spigot world.paperConfig.init(); // Paper + world.sakuraConfig.init(); // Sakura } Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper @@ -987,6 +989,7 @@ public final class CraftServer implements Server { this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper + me.samsuik.sakura.command.SakuraCommands.registerCommands(this.console); // Sakura 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 fbe68bd4739d9a0e7d9bc4c3d5ba8ecfd2d13954..c779e4a4df4c954167a7445f2ac29da232e04f20 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -153,6 +153,14 @@ public class Main { .describedAs("Jar file"); // Paper end + // Sakura start + acceptsAll(asList("sakura", "sakura-settings"), "File for sakura settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("sakura.yml")) + .describedAs("Yml file"); + // Sakura end + // Paper start acceptsAll(asList("server-name"), "Name of the server") .withRequiredArg()