From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Fri, 29 Oct 2021 16:52:57 +0800 Subject: [PATCH] Leaves Server Config And Command diff --git a/build.gradle.kts b/build.gradle.kts index 16b742b5131376ceca04570dd4c088caff62e677..fe1f6f1a97ab35c44f596596d16765b8b81753a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -212,6 +212,14 @@ tasks.registerRunTask("runDevServer") { jvmArgs("-DPaper.pushPaperAssetsRoot=true") } +// Leaves start - create config file +tasks.registerRunTask("createLeavesConfig") { + description = "Create a new leaves.yml" + mainClass = "org.leavesmc.leaves.config.GlobalConfigCreator" + classpath(sourceSets.main.map { it.runtimeClasspath }) +} +// Leaves end - create config file + tasks.registerRunTask("runBundler") { description = "Spin up a test server from the Mojang mapped bundler jar" classpath(rootProject.tasks.named("createMojmapBundlerJar").flatMap { it.outputZip }) diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 026dc226ace6b067df8987f10039d4c037c47124..421b67ab81330975f6404578ffcc57feb0c9c0be 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1147,6 +1147,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + snowballAndEggCanKnockback = config.getBoolean("settings.snowball-and-egg-can-knockback-player", snowballAndEggCanKnockback); + fakeplayerSupport = config.getBoolean("settings.fakeplayer.enable", fakeplayerSupport); + unableFakeplayerNames = (List) config.getList("settings.fakeplayer.unable-fakeplayer-names", unableFakeplayerNames); + shearsInDispenserCanZeroAmount = config.getBoolean("settings.shears-in-dispenser-can-zero-amount", shearsInDispenserCanZeroAmount); + redstoneShearsWrench = config.getBoolean("settings.redstone-shears-wrench", redstoneShearsWrench); + buddingAmethystCanPushByPiston = config.getBoolean("settings.budding-amethyst-can-push-by-piston", buddingAmethystCanPushByPiston); + spectatorDontGetAdvancement = config.getBoolean("settings.spectator-dont-get-advancement", spectatorDontGetAdvancement); + stickChangeArmorStandArmStatus = config.getBoolean("settings.stick-change-armorstand-arm-status", stickChangeArmorStandArmStatus); + noChatSign = config.getBoolean("settings.no-chat-sign", noChatSign); + + config.set("settings.snowball-and-egg-can-knockback-player", null); + config.set("settings.player-can-edit-sign", null); + config.set("settings.fakeplayer", null); + config.set("settings.shears-in-dispenser-can-zero-amount", null); + config.set("settings.redstone-shears-wrench", null); + config.set("settings.budding-amethyst-can-push-by-piston", null); + config.set("settings.spectator-dont-get-advancement", null); + config.set("settings.stick-change-armorstand-arm-status", null); + config.set("settings.no-chat-sign", null); + } + + case 2 -> { + config.set("settings.modify.player-can-edit-sign", null); + config.set("settings.performance.skip-clone-loot-parameters", null); + } + + case 3 -> { + boolean carpetAlternative = config.getBoolean("settings.protocol.carpet-alternative-block-placement", false); + alternativeBlockPlacement = carpetAlternative ? AlternativePlaceType.CARPET : AlternativePlaceType.NONE; + config.set("settings.protocol.carpet-alternative-block-placement", null); + } + + case 4 -> { + shearsInDispenserCanZeroAmount = config.getBoolean("settings.modify.shears-in-dispenser-can-zero-amount", shearsInDispenserCanZeroAmount); + instantBlockUpdaterReintroduced = config.getBoolean("settings.modify.instant-block-updater-reintroduced", instantBlockUpdaterReintroduced); + redstoneDontCantOnTrapDoor = config.getBoolean("settings.modify.redstone-wire-dont-connect-if-on-trapdoor", redstoneDontCantOnTrapDoor); + mendingCompatibilityInfinity = config.getBoolean("settings.modify.mending-compatibility-infinity", mendingCompatibilityInfinity); + zeroTickPlants = config.getBoolean("settings.modify.zero-tick-plants", zeroTickPlants); + + config.set("settings.modify.shears-in-dispenser-can-zero-amount", null); + config.set("settings.modify.instant-block-updater-reintroduced", null); + config.set("settings.modify.redstone-wire-dont-connect-if-on-trapdoor", null); + config.set("settings.modify.mending-compatibility-infinity", null); + config.set("settings.modify.zero-tick-plants", null); + } + } + } + } + + public static void registerCommand(String name, Command command) { + MinecraftServer.getServer().server.getCommandMap().register(name, "leaves", command); + MinecraftServer.getServer().server.syncCommands(); + } + + public static void unregisterCommand(String name) { + name = name.toLowerCase(java.util.Locale.ENGLISH).trim(); + MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove(name); + MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove("leaves:" + name); + MinecraftServer.getServer().server.syncCommands(); + } + + // Leaves start - modify + + // Leaves start - modify - fakeplayer + + @GlobalConfig(name = "enable", category = {"modify", "fakeplayer"}, verify = FakeplayerVerify.class) + public static boolean fakeplayerSupport = true; + + private static class FakeplayerVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + if (value) { + registerCommand("bot", new org.leavesmc.leaves.bot.BotCommand("bot")); + org.leavesmc.leaves.bot.agent.Actions.registerAll(); + } else { + unregisterCommand("bot"); + } + return null; + } + } + + @GlobalConfig(name = "unable-fakeplayer-names", category = {"modify", "fakeplayer"}, verify = ConfigVerify.ListConfigVerify.class) + public static List unableFakeplayerNames = List.of("player-name"); + + @GlobalConfig(name = "limit", category = {"modify", "fakeplayer"}, verify = ConfigVerify.IntConfigVerify.class) + public static int fakeplayerLimit = 10; + + @GlobalConfig(name = "prefix", category = {"modify", "fakeplayer"}, verify = ConfigVerify.StringConfigVerify.class) + public static String fakeplayerPrefix = ""; + + @GlobalConfig(name = "suffix", category = {"modify", "fakeplayer"}, verify = ConfigVerify.StringConfigVerify.class) + public static String fakeplayerSuffix = ""; + + @GlobalConfig(name = "always-send-data", category = {"modify", "fakeplayer"}) + public static boolean alwaysSendFakeplayerData = true; + + @GlobalConfig(name = "resident-fakeplayer", category = {"modify", "fakeplayer"}) + public static boolean fakeplayerResident = false; + + @GlobalConfig(name = "open-fakeplayer-inventory", category = {"modify", "fakeplayer"}) + public static boolean openFakeplayerInventory = false; + + @GlobalConfig(name = "skip-sleep-check", category = {"modify", "fakeplayer"}) + public static boolean fakeplayerSkipSleep = false; + + @GlobalConfig(name = "spawn-phantom", category = {"modify", "fakeplayer"}) + public static boolean fakeplayerSpawnPhantom = false; + + @GlobalConfig(name = "regen-amount", category = {"modify", "fakeplayer"}, verify = RegenAmountVerify.class) + public static double fakeplayerRegenAmount = 0.010; + + private static class RegenAmountVerify extends ConfigVerify.DoubleConfigVerify { + @Override + public String check(Double old, Double value) { + return value > 0.0 ? null : "regen-amount need > 0.0f"; + } + } + + // Leaves end - modify - fakeplayer + + // Leaves start - modify - minecraft-old + + @GlobalConfig(name = "shears-in-dispenser-can-zero-amount", category = {"modify", "minecraft-old"}) + public static boolean shearsInDispenserCanZeroAmount = false; + + @GlobalConfig(name = "instant-block-updater-reintroduced", category = {"modify", "minecraft-old"}, lock = true) + public static boolean instantBlockUpdaterReintroduced = false; + + @GlobalConfig(name = "armor-stand-cant-kill-by-mob-projectile", category = {"modify", "minecraft-old"}) + public static boolean armorStandCantKillByMobProjectile = false; + + @GlobalConfig(name = "cce-update-suppression", category = {"modify", "minecraft-old"}) + public static boolean cceUpdateSuppression = false; + + @GlobalConfig(name = "villager-infinite-discounts", category = {"modify", "minecraft-old"}, verify = VillagerInfiniteDiscountsVerify.class) + public static boolean villagerInfiniteDiscounts = false; + + private static class VillagerInfiniteDiscountsVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + org.leavesmc.leaves.util.VillagerInfiniteDiscountHelper.doVillagerInfiniteDiscount(value); + return null; + } + } + + @GlobalConfig(name = "copper-bulb-1gt-delay", category = {"modify", "minecraft-old"}) + public static boolean copperBulb1gt = false; + + @GlobalConfig(name = "crafter-1gt-delay", category = {"modify", "minecraft-old"}) + public static boolean crafter1gt = false; + + @GlobalConfig(name = "redstone-wire-dont-connect-if-on-trapdoor", category = {"modify", "minecraft-old"}) + public static boolean redstoneDontCantOnTrapDoor = false; + + @GlobalConfig(name = "mending-compatibility-infinity", category = {"modify", "minecraft-old"}) + public static boolean mendingCompatibilityInfinity = false; + + @GlobalConfig(name = "zero-tick-plants", category = {"modify", "minecraft-old"}) + public static boolean zeroTickPlants = false; + + @RemovedConfig(name = "loot-world-random", category = {"modify", "minecraft-old"}, transform = true) + @GlobalConfig(name = "rng-fishing", category = {"modify", "minecraft-old"}, lock = true, verify = RNGFishingVerify.class) + public static boolean rngFishing = false; + + private static class RNGFishingVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { +// LeavesFeatureSet.register(LeavesFeature.of("rng_fishing", value)); // Leaves - remove protocol temporarily + return null; + } + } + + @GlobalConfig(name = "protection-stacking", category = {"modify", "minecraft-old"}) + public static boolean protectionStacking = false; + + // Leaves end - modify - minecraft-old + + // Leaves start - modify - elytra-aeronautics + + @GlobalConfig(name = "no-chunk-load", category = {"modify", "elytra-aeronautics"}) + public static boolean elytraAeronauticsNoChunk = false; + + @GlobalConfig(name = "no-chunk-height", category = {"modify", "elytra-aeronautics"}, verify = ConfigVerify.DoubleConfigVerify.class) + public static double elytraAeronauticsNoChunkHeight = 500.0D; + + @GlobalConfig(name = "no-chunk-speed", category = {"modify", "elytra-aeronautics"}, verify = ConfigVerify.DoubleConfigVerify.class) + public static double elytraAeronauticsNoChunkSpeed = -1.0D; + + @GlobalConfig(name = "message", category = {"modify", "elytra-aeronautics"}) + public static boolean elytraAeronauticsNoChunkMes = true; + + @GlobalConfig(name = "message-start", category = {"modify", "elytra-aeronautics"}, verify = ConfigVerify.StringConfigVerify.class) + public static String elytraAeronauticsNoChunkStartMes = "Flight enter cruise mode"; + + @GlobalConfig(name = "message-end", category = {"modify", "elytra-aeronautics"}, verify = ConfigVerify.StringConfigVerify.class) + public static String elytraAeronauticsNoChunkEndMes = "Flight exit cruise mode"; + + // Leaves end - modify - elytra-aeronautics + + @GlobalConfig(name = "redstone-shears-wrench", category = "modify") + public static boolean redstoneShearsWrench = true; + + @GlobalConfig(name = "budding-amethyst-can-push-by-piston", category = "modify") + public static boolean buddingAmethystCanPushByPiston = false; + + @GlobalConfig(name = "spectator-dont-get-advancement", category = "modify") + public static boolean spectatorDontGetAdvancement = false; + + @GlobalConfig(name = "stick-change-armorstand-arm-status", category = "modify") + public static boolean stickChangeArmorStandArmStatus = true; + + @GlobalConfig(name = "snowball-and-egg-can-knockback-player", category = "modify") + public static boolean snowballAndEggCanKnockback = true; + + @GlobalConfig(name = "flatten-triangular-distribution", category = "modify") + public static boolean flattenTriangularDistribution = false; + + @GlobalConfig(name = "player-operation-limiter", category = "modify") + public static boolean playerOperationLimiter = false; + + @GlobalConfig(name = "renewable-elytra", category = "modify", verify = RenewableElytraVerify.class) + public static double renewableElytra = -1.0F; + + private static class RenewableElytraVerify extends ConfigVerify.DoubleConfigVerify { + @Override + public String check(Double old, Double value) { + return value <= 1.0 ? null : "renewable-elytra need <= 1.0f"; + } + } + + public static int shulkerBoxStackSize = 1; + @GlobalConfig(name = "stackable-shulker-boxes", category = "modify", verify = StackableShulkerVerify.class) + private static String stackableShulkerBoxes = "false"; + + private static class StackableShulkerVerify extends ConfigVerify.StringConfigVerify { + @Override + public String check(String old, String value) { + String realValue = MathUtils.isNumeric(value) ? value : value.equals("true") ? "2" : "1"; + shulkerBoxStackSize = Integer.parseInt(realValue); + return null; + } + } + + @GlobalConfig(name = "force-void-trade", category = "modify") + public static boolean forceVoidTrade = false; + + @GlobalConfig(name = "disable-moved-wrongly-threshold", category = "modify") + public static boolean disableMovedWronglyThreshold = false; + + @GlobalConfig(name = "mc-technical-survival-mode", category = "modify", verify = McTechnicalModeVerify.class, lock = true) + public static boolean mcTechnicalMode = true; + + private static class McTechnicalModeVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + if (value) { + org.leavesmc.leaves.util.McTechnicalModeHelper.doMcTechnicalMode(); + } + return null; + } + } + + @GlobalConfig(name = "return-nether-portal-fix", category = "modify") + public static boolean netherPortalFix = false; + + @GlobalConfig(name = "use-vanilla-random", category = "modify", lock = true, verify = UseVanillaRandomVerify.class) + public static boolean useVanillaRandom = false; + + private static class UseVanillaRandomVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { +// LeavesFeatureSet.register(LeavesFeature.of("use_vanilla_random", value)); // Leaves - remove protocol temporarily + return null; + } + } + + @GlobalConfig(name = "fix-update-suppression-crash", category = "modify") + public static boolean updateSuppressionCrashFix = true; + + @GlobalConfig(name = "bedrock-break-list", category = "modify", lock = true) + public static boolean bedrockBreakList = false; + + @GlobalConfig(name = "disable-distance-check-for-use-item", category = "modify") + public static boolean disableDistanceCheckForUseItem = false; + + @GlobalConfig(name = "no-feather-falling-trample", category = "modify") + public static boolean noFeatherFallingTrample = false; + + @GlobalConfig(name = "shared-villager-discounts", category = "modify") + public static boolean sharedVillagerDiscounts = false; + + @GlobalConfig(name = "disable-check-out-of-order-command", category = "modify") + public static boolean disableCheckOutOfOrderCommand = false; + + @GlobalConfig(name = "despawn-enderman-with-block", category = "modify") + public static boolean despawnEndermanWithBlock = false; + + @GlobalConfig(name = "creative-no-clip", category = "modify", verify = CreativeNoClipVerify.class) + public static boolean creativeNoClip = false; + + private static class CreativeNoClipVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + CarpetRules.register(CarpetRule.of("carpet", "creativeNoClip", value)); + return null; + } + } + + @GlobalConfig(name = "shave-snow-layers", category = "modify") + public static boolean shaveSnowLayers = true; + + @GlobalConfig(name = "ignore-lc", category = "modify") + public static boolean ignoreLC = false; + + @GlobalConfig(name = "disable-packet-limit", category = "modify") + public static boolean disablePacketLimit = false; + + @GlobalConfig(name = "lava-riptide", category = "modify", verify = LavaRiptideVerify.class) + public static boolean lavaRiptide = false; + + private static class LavaRiptideVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { +// LeavesFeatureSet.register(LeavesFeature.of("lava_riptide", value)); // Leaves - remove protocol temporarily + return null; + } + } + + @GlobalConfig(name = "no-block-update-command", category = "modify", verify = NoBlockUpdateVerify.class) + public static boolean noBlockUpdateCommand = false; + + @GlobalConfig(name = "no-tnt-place-update", category = "modify") + public static boolean noTNTPlaceUpdate = false; + + private static class NoBlockUpdateVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + if (value) { + registerCommand("blockupdate", new org.leavesmc.leaves.command.NoBlockUpdateCommand("blockupdate")); + } else { + unregisterCommand("blockupdate"); + } + return null; + } + } + + @GlobalConfig(name = "raider-die-skip-self-raid-check", category = "modify") + public static boolean skipSelfRaidCheck = false; + + @GlobalConfig(name = "container-passthrough", category = "modify") + public static boolean containerPassthrough = false; + + @GlobalConfig(name = "avoid-anvil-too-expensive", category = "modify", verify = AnvilNotExpensiveVerify.class) + public static boolean avoidAnvilTooExpensive = false; + + private static class AnvilNotExpensiveVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + CarpetRules.register(CarpetRule.of("pca", "avoidAnvilTooExpensive", value)); + return null; + } + } + + @GlobalConfig(name = "bow-infinity-fix", category = "modify") + public static boolean bowInfinityFix = false; + + @GlobalConfig(name = "hopper-counter", category = "modify") + public static boolean hopperCounter = false; + + @GlobalConfig(name = "spider-jockeys-drop-gapples", category = "modify", verify = JockeysDropGAppleVerify.class) + public static double spiderJockeysDropGapples = -1.0; + + private static class JockeysDropGAppleVerify extends ConfigVerify.DoubleConfigVerify { + @Override + public String check(Double old, Double value) { + return value <= 1.0 ? null : "spider-jockeys-drop-gapples need <= 1.0f"; + } + } + + @GlobalConfig(name = "renewable-deepslate", category = "modify") + public static boolean renewableDeepslate = false; + + @GlobalConfig(name = "renewable-sponges", category = "modify") + public static boolean renewableSponges = false; + + @GlobalConfig(name = "renewable-coral", category = "modify", verify = RenewableCoralVerify.class) + public static RenewableCoralType renewableCoral = RenewableCoralType.FALSE; + + public enum RenewableCoralType { + FALSE, TRUE, EXPANDED + } + + private static class RenewableCoralVerify extends ConfigVerify.EnumConfigVerify { + @Override + public String check(Enum old, Enum value) { + CarpetRules.register(CarpetRule.of("carpet", "renewableCoral", value)); + return null; + } + } + + @GlobalConfig(name = "fast-resume", category = "modify") + public static boolean fastResume = false; + + @GlobalConfig(name = "force-peaceful-mode", category = "modify", verify = ForcePeacefulModeVerify.class) + public static int forcePeacefulMode = -1; + + private static class ForcePeacefulModeVerify extends ConfigVerify.IntConfigVerify { + @Override + public String check(Integer old, Integer value) { + for (ServerLevel level : MinecraftServer.getServer().getAllLevels()) { + level.chunkSource.peacefulModeSwitchTick = value; + } + return null; + } + } + + // Leaves start - modify - removed + + @RemovedConfig(name = "tick-command", category = "modify") + public static boolean tickCommand = false; + + // Leaves end - modify - removed + + // Leaves end - modify + + // Leaves start - performance + + // Leaves start - performance - remove + + @GlobalConfig(name = "tick-guard-lambda", category = {"performance", "remove"}) + public static boolean removeTickGuardLambda = true; + + @GlobalConfig(name = "inventory-contains-iterators", category = {"performance", "remove"}) + public static boolean removeInventoryContainsIterators = true; + + @RemovedConfig(name = "get-nearby-players-streams", category = {"performance", "remove"}) + public static boolean removeGetNearPlayerStreams = true; + + @GlobalConfig(name = "range-check-streams-and-iterators", category = {"performance", "remove"}) + public static boolean removeRangeCheckStreams = true; + + @GlobalConfig(name = "damage-lambda", category = {"performance", "remove"}) + public static boolean removeDamageLambda = true; + + // Leaves end - performance - remove + + @GlobalConfig(name = "optimized-dragon-respawn", category = "performance") + public static boolean optimizedDragonRespawn = false; + + @GlobalConfig(name = "dont-send-useless-entity-packets", category = "performance") + public static boolean dontSendUselessEntityPackets = true; + + @GlobalConfig(name = "optimize-entity-coordinate-key", category = "performance") + public static boolean optimizeEntityCoordinateKey = true; + + @GlobalConfig(name = "enable-suffocation-optimization", category = "performance") + public static boolean enableSuffocationOptimization = true; + + @RemovedConfig(name = "strip-raytracing-for-entity", category = "performance") + public static boolean entityStripRaytracing = true; + + @GlobalConfig(name = "check-spooky-season-once-an-hour", category = "performance") + public static boolean checkSpookySeasonOnceAnHour = true; + + @GlobalConfig(name = "optimize-chunk-ticking", category = "performance", lock = true) + public static boolean optimizeChunkTicking = true; + + @RemovedConfig(name = "skip-poi-find-in-vehicle", category = "performance") + public static boolean skipPOIFindingInVehicle = true; + + @GlobalConfig(name = "entity-target-find-optimization", category = "performance") + public static boolean entityTargetFindingOptimization = true; + + @GlobalConfig(name = "use-more-thread-unsafe-random", category = "performance") + public static boolean useMoreThreadUnsafeRandom = true; + + @GlobalConfig(name = "inactive-goal-selector-disable", category = "performance") + public static boolean throttleInactiveGoalSelectorTick = false; + + @GlobalConfig(name = "reduce-entity-allocations", category = "performance") + public static boolean reduceEntityAllocations = true; + + @GlobalConfig(name = "cache-climb-check", category = "performance") + public static boolean cacheClimbCheck = true; + + @GlobalConfig(name = "biome-temperatures-use-aging-cache", category = "performance", lock = true) + public static boolean biomeTemperaturesUseAgingCache = true; + + @GlobalConfig(name = "reduce-entity-fluid-lookup", category = "performance") + public static boolean reduceEntityFluidLookup = true; + + @GlobalConfig(name = "reduce-chuck-load-and-lookup", category = "performance") + public static boolean reduceChuckLoadAndLookup = true; + + @GlobalConfig(name = "improve-fluid-direction-caching", category = "performance", lock = true) + public static boolean improveFluidDirectionCaching = true; + + @GlobalConfig(name = "cache-ignite-odds", category = "performance") + public static boolean cacheIgniteOdds = true; + + @GlobalConfig(name = "faster-chunk-serialization", category = "performance") + public static boolean fasterChunkSerialization = true; + + @GlobalConfig(name = "optimize-world-generation-and-block-access", category = "performance") + public static boolean optimizeWorldGenerationAccess = true; + + @GlobalConfig(name = "cache-world-generator-sea-level", category = "performance") + public static boolean cacheWorldGeneratorSeaLevel = true; + + @GlobalConfig(name = "skip-secondary-POI-sensor-if-absent", category = "performance") + public static boolean skipSecondaryPOISensorIfAbsent = true; + + @GlobalConfig(name = "cache-CubeVoxelShape-shape-array", category = "performance") + public static boolean cacheCubeVoxelShapeShapeArray = true; + + @GlobalConfig(name = "store-mob-counts-in-array", category = "performance") + public static boolean storeMobCountsInArray = true; + + @GlobalConfig(name = "cache-BlockStatePairKey-hash", category = "performance") + public static boolean cacheBlockStatePairKeyHash = true; + + @GlobalConfig(name = "optimize-noise-generation", category = "performance") + public static boolean optimizeNoiseGeneration = false; + + @GlobalConfig(name = "optimize-sun-burn-tick", category = "performance") + public static boolean optimizeSunBurnTick = true; + + @GlobalConfig(name = "optimized-CubePointRange", category = "performance") + public static boolean optimizedCubePointRange = true; + + @GlobalConfig(name = "check-frozen-ticks-before-landing-block", category = "performance") + public static boolean checkFrozenTicksBeforeLandingBlock = true; + + @GlobalConfig(name = "skip-entity-move-if-movement-is-zero", category = "performance") + public static boolean skipEntityMoveIfMovementIsZero = true; + + @GlobalConfig(name = "skip-cloning-advancement-criteria", category = "performance") + public static boolean skipCloningAdvancementCriteria = false; + + @GlobalConfig(name = "skip-negligible-planar-movement-multiplication", category = "performance") + public static boolean skipNegligiblePlanarMovementMultiplication = true; + + @GlobalConfig(name = "fix-villagers-dont-release-memory", category = "performance") + public static boolean villagersDontReleaseMemoryFix = false; + + // Leaves start - performance - removed + + @RemovedConfig(name = "cache-ominous-banner-item", category = "performance") + public static boolean cacheOminousBannerItem = true; + + @RemovedConfig(name = "use-optimized-collection", category = "performance") + public static boolean useOptimizedCollection = true; + + @RemovedConfig(name = "async-pathfinding", category = "performance") + public static boolean asyncPathfinding = false; + + @RemovedConfig(name = "async-mob-spawning", category = "performance") + public static boolean asyncMobSpawning = false; + + @RemovedConfig(name = "async-entity-tracker", category = "performance") + public static boolean asyncEntityTracker = false; + + @RemovedConfig(name = "fix-paper-6045", category = {"performance", "fix"}) + public static boolean fixPaper6045 = true; + + @RemovedConfig(name = "fix-paper-9372", category = {"performance", "fix"}) + public static boolean fixPaper9372 = true; + + // Leaves end - performance - removed + + // Leaves end - performance + + // Leaves start - protocol + + // Leaves start - protocol - bladeren + + @GlobalConfig(name = "protocol", category = {"protocol", "bladeren"}) + public static boolean bladerenLeavesProtocol = true; + + @GlobalConfig(name = "mspt-sync-protocol", category = {"protocol", "bladeren"}, verify = MSPTSyncVerify.class) + public static boolean msptSyncProtocol = false; + + private static class MSPTSyncVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { +// LeavesFeatureSet.register(LeavesFeature.of("mspt_sync", value)); // Leaves - remove protocol temporarily + return null; + } + } + + @GlobalConfig(name = "mspt-sync-tick-interval", category = {"protocol", "bladeren"}, verify = MSPTSyncIntervalVerify.class) + public static int msptSyncTickInterval = 20; + + private static class MSPTSyncIntervalVerify extends ConfigVerify.IntConfigVerify { + @Override + public String check(Integer old, Integer value) { + return value > 0 ? null : "mspt-sync-tick-interval need > 0"; + } + } + + // Leaves end - protocol - bladeren + + // Leaves start - protocol - syncmatica + + @GlobalConfig(name = "enable", category = {"protocol", "syncmatica"}, verify = SyncmaticaVerify.class) + public static boolean syncmaticaProtocol = false; + + @GlobalConfig(name = "quota", category = {"protocol", "syncmatica"}) + public static boolean syncmaticaQuota = false; + + @GlobalConfig(name = "quota-limit", category = {"protocol", "syncmatica"}, verify = ConfigVerify.IntConfigVerify.class) + public static int syncmaticaQuotaLimit = 40000000; + + public static class SyncmaticaVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + if (value) { +// org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol.init(); // Leaves - remove protocol temporarily + } + return null; + } + } + + // Leaves end - protocol - syncmatica + + @GlobalConfig(name = "pca-sync-protocol", category = "protocol") + public static boolean pcaSyncProtocol = false; + + @GlobalConfig(name = "pca-sync-player-entity", category = "protocol", verify = PcaPlayerEntityVerify.class) + public static String pcaSyncPlayerEntity = "OPS"; + + private static class PcaPlayerEntityVerify extends ConfigVerify.StringConfigVerify { + private static final List pcaSyncPlayerEntityList = List.of("NOBODY", "BOT", "OPS", "OPS_AND_SELF", "EVERYONE"); + + @Override + public String check(String old, String value) { + if (!pcaSyncPlayerEntityList.contains(value)) { + return "pca-sync-player-entity value error"; + } + return null; + } + } + + @GlobalConfig(name = "bbor-protocol", category = "protocol") + public static boolean bborProtocol = false; + + @GlobalConfig(name = "jade-protocol", category = "protocol") + public static boolean jadeProtocol = false; + + @GlobalConfig(name = "alternative-block-placement", category = "protocol", verify = AlternativePlaceVerify.class) + public static AlternativePlaceType alternativeBlockPlacement = AlternativePlaceType.NONE; + + public enum AlternativePlaceType { + NONE, CARPET, CARPET_FIX, LITEMATICA + } + + private static class AlternativePlaceVerify extends ConfigVerify.EnumConfigVerify { + } + + @GlobalConfig(name = "appleskin-protocol", category = "protocol") + public static boolean appleskinProtocol = false; + + @GlobalConfig(name = "xaero-map-protocol", category = "protocol") + public static boolean xaeroMapProtocol = false; + + @GlobalConfig(name = "xaero-map-server-id", category = "protocol", verify = ConfigVerify.IntConfigVerify.class) + public static int xaeroMapServerID = new Random().nextInt(); + + @GlobalConfig(name = "servux-protocol", category = "protocol") + public static boolean servuxProtocol = false; + + @GlobalConfig(name = "leaves-carpet-support", category = "protocol") + public static boolean leavesCarpetSupport = false; + + // Leaves end - protocol + + // Leaves start - misc + + // Leaves start - misc - auto-update + + @GlobalConfig(name = "enable", category = {"misc", "auto-update"}, lock = true, verify = AutoUpdateVerify.class) + public static boolean autoUpdate = false; + + private static class AutoUpdateVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public void runAfterLoader(Boolean value) { + org.leavesmc.leaves.util.LeavesUpdateHelper.init(); + if (value) { + LeavesLogger.LOGGER.warning("Auto-Update is not completely safe. Enabling it may cause data security problems!"); + } + } + } + + @GlobalConfig(name = "time", category = {"misc", "auto-update"}, lock = true, verify = ConfigVerify.ListConfigVerify.class) + public static List autoUpdateTime = List.of("14:00", "2:00"); + + // Leaves end - misc - auto-update + + // Leaves start - misc - extra-yggdrasil-service + + @GlobalConfig(name = "enable", category = {"misc", "extra-yggdrasil-service"}, verify = ExtraYggdrasilVerify.class) + public static boolean extraYggdrasilService = false; + + public static class ExtraYggdrasilVerify extends ConfigVerify.BooleanConfigVerify { + @Override + public String check(Boolean old, Boolean value) { + if (value) { + LeavesLogger.LOGGER.warning("extra-yggdrasil-service is an unofficial support. Enabling it may cause data security problems!"); + GlobalConfiguration.get().unsupportedSettings.performUsernameValidation = true; // always check username + } + return null; + } + } + + @GlobalConfig(name = "login-protect", category = {"misc", "extra-yggdrasil-service"}) + public static boolean extraYggdrasilLoginProtect = false; + + @GlobalConfig(name = "urls", category = {"misc", "extra-yggdrasil-service"}, verify = ExtraYggdrasilUrlsVerify.class) + public static List extraYggdrasilServiceList = List.of("https://url.with.authlib-injector-yggdrasil"); + + public static class ExtraYggdrasilUrlsVerify extends ConfigVerify.ListConfigVerify { + @Override + public String check(List old, List value) { + org.leavesmc.leaves.profile.LeavesMinecraftSessionService.initExtraYggdrasilList(); + return null; + } + } + + // Leaves end - misc - extra-yggdrasil-service + + @GlobalConfig(name = "disable-method-profiler", category = "misc") + public static boolean disableMethodProfiler = true; + + @GlobalConfig(name = "no-chat-sign", category = "misc") + public static boolean noChatSign = true; + + @GlobalConfig(name = "dont-respond-ping-before-start-fully", category = "misc") + public static boolean dontRespondPingBeforeStart = true; + + @GlobalConfig(name = "server-lang", category = "misc", lock = true, verify = ServerLangVerify.class) + public static String serverLang = "en_us"; + + private static class ServerLangVerify extends ConfigVerify.StringConfigVerify { + private static final List supportLang = List.of("en_us", "zh_cn"); + + @Override + public String check(String old, String value) { + if (!supportLang.contains(value)) { + return "server-lang value error"; + } + return null; + } + } + + @GlobalConfig(name = "server-mod-name", category = "misc", verify = ConfigVerify.StringConfigVerify.class) + public static String serverModName = "Leaves"; + + @GlobalConfig(name = "bstats-privacy-mode", category = "misc") + public static boolean bstatsPrivacyMode = false; + + @GlobalConfig(name = "force-minecraft-command", category = "misc") + public static boolean forceMinecraftCommand = true; + + @GlobalConfig(name = "leaves-packet-event", category = "misc") + public static boolean leavesPacketEvent = true; + + // Leaves end - misc + + // Leaves start - region + + public static org.leavesmc.leaves.region.RegionFileFormat regionFormatName = org.leavesmc.leaves.region.RegionFileFormat.ANVIL; + @GlobalConfig(name = "format", category = "region", lock = true, verify = RegionFormatVerify.class) + public static String regionFormat = "ANVIL"; + + private static class RegionFormatVerify extends ConfigVerify.StringConfigVerify { + @Override + public String check(String old, String value) { + org.leavesmc.leaves.region.RegionFileFormat format = org.leavesmc.leaves.region.RegionFileFormat.fromString(value); + if (format == org.leavesmc.leaves.region.RegionFileFormat.INVALID) { + return "Unknown region format " + value; + } + regionFormatName = format; + return null; + } + } + + @GlobalConfig(name = "flush-frequency", category = {"region", "linear"}, lock = true, verify = ConfigVerify.IntConfigVerify.class) + public static int linearFlushFrequency = 10; + + @GlobalConfig(name = "auto-convert-anvil-to-linear", category = {"region", "linear"}, lock = true) + public static boolean autoConvertAnvilToLinear = false; + + @GlobalConfig(name = "flush-max-threads", category = {"region", "linear"}, lock = true, verify = ConfigVerify.IntConfigVerify.class) + public static int linearFlushThreads = 1; + + public static int getLinearFlushThreads() { + if (linearFlushThreads < 0) { + return Math.max(Runtime.getRuntime().availableProcessors() + linearFlushThreads, 1); + } else { + return Math.max(linearFlushThreads, 1); + } + } + + @GlobalConfig(name = "compression-level", category = {"region", "linear"}, lock = true, verify = LinearCompressVerify.class) + public static int linearCompressionLevel = 1; + + private static class LinearCompressVerify extends ConfigVerify.IntConfigVerify { + @Override + public String check(Integer old, Integer value) { + if (value < 1 || value > 22) { + return "linear.compression-level need between 1 and 22"; + } + return null; + } + } + + @RemovedConfig(name = "crash-on-broken-symlink", category = {"region", "linear"}) + public static boolean linearCrashOnBrokenSymlink = true; + + // Leaves end - region + + // Leaves start - fix + + @GlobalConfig(name = "vanilla-hopper", category = "fix") + public static boolean vanillaHopper = false; + + // Leaves end - region +} diff --git a/src/main/java/org/leavesmc/leaves/command/CommandArgument.java b/src/main/java/org/leavesmc/leaves/command/CommandArgument.java new file mode 100644 index 0000000000000000000000000000000000000000..381cd8b33137a5b7dc688306b56805f35c57012a --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/CommandArgument.java @@ -0,0 +1,43 @@ +package org.leavesmc.leaves.command; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CommandArgument { + + private final List> argumentTypes; + private final List> tabComplete; + + public CommandArgument(CommandArgumentType... argumentTypes) { + this.argumentTypes = List.of(argumentTypes); + this.tabComplete = new ArrayList<>(); + for (int i = 0; i < argumentTypes.length; i++) { + tabComplete.add(new ArrayList<>()); + } + } + + public List tabComplete(int n) { + if (tabComplete.size() > n) { + return tabComplete.get(n); + } else { + return List.of(); + } + } + + public CommandArgument setTabComplete(int index, List list) { + tabComplete.set(index, list); + return this; + } + + public CommandArgumentResult parse(int index, String @NotNull [] args) { + Object[] result = new Object[argumentTypes.size()]; + Arrays.fill(result, null); + for (int i = index, j = 0; i < args.length && j < result.length; i++, j++) { + result[j] = argumentTypes.get(j).pasre(args[i]); + } + return new CommandArgumentResult(new ArrayList<>(Arrays.asList(result))); + } +} diff --git a/src/main/java/org/leavesmc/leaves/command/CommandArgumentResult.java b/src/main/java/org/leavesmc/leaves/command/CommandArgumentResult.java new file mode 100644 index 0000000000000000000000000000000000000000..e50ca0473ab4d40e2623ab15f8566276cc14f4e7 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/CommandArgumentResult.java @@ -0,0 +1,62 @@ +package org.leavesmc.leaves.command; + +import net.minecraft.core.BlockPos; +import org.bukkit.util.Vector; + +import java.util.List; +import java.util.Objects; + +public class CommandArgumentResult { + + private final List result; + + public CommandArgumentResult(List result) { + this.result = result; + } + + public Integer readInt(int def) { + return Objects.requireNonNullElse(read(Integer.class), def); + } + + public Double readDouble(double def) { + return Objects.requireNonNullElse(read(Double.class), def); + } + + public String readString(String def) { + return Objects.requireNonNullElse(read(String.class), def); + } + + public BlockPos readPos() { + Integer[] pos = {read(Integer.class), read(Integer.class), read(Integer.class)}; + for (Integer po : pos) { + if (po == null) { + return null; + } + } + return new BlockPos(pos[0], pos[1], pos[2]); + } + + public Vector readVector() { + Double[] pos = {read(Double.class), read(Double.class), read(Double.class)}; + for (Double po : pos) { + if (po == null) { + return null; + } + } + return new Vector(pos[0], pos[1], pos[2]); + } + + public T read(Class tClass) { + if (result.isEmpty()) { + return null; + } + + Object obj = result.remove(0); + if (tClass.isInstance(obj)) { + return tClass.cast(obj); + } else { + return null; + } + } + +} diff --git a/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java b/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java new file mode 100644 index 0000000000000000000000000000000000000000..dccd32714b86d1fa3e0a9b10a23f765fc54a2c66 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java @@ -0,0 +1,37 @@ +package org.leavesmc.leaves.command; + +import org.jetbrains.annotations.NotNull; + +public abstract class CommandArgumentType { + + public static final CommandArgumentType INTEGER = new CommandArgumentType<>() { + @Override + public Integer pasre(@NotNull String arg) { + try { + return Integer.parseInt(arg); + } catch (NumberFormatException e) { + return null; + } + } + }; + + public static final CommandArgumentType DOUBLE = new CommandArgumentType<>() { + @Override + public Double pasre(@NotNull String arg) { + try { + return Double.parseDouble(arg); + } catch (NumberFormatException e) { + return null; + } + } + }; + + public static final CommandArgumentType STRING = new CommandArgumentType<>() { + @Override + public String pasre(@NotNull String arg) { + return arg; + } + }; + + public abstract E pasre(@NotNull String arg); +} diff --git a/src/main/java/org/leavesmc/leaves/command/LeavesCommand.java b/src/main/java/org/leavesmc/leaves/command/LeavesCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..b9e2bdb034dcd5119680ddea44a1eda11a0189b2 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/LeavesCommand.java @@ -0,0 +1,118 @@ +package org.leavesmc.leaves.command; + +import io.papermc.paper.command.CommandUtil; +import it.unimi.dsi.fastutil.Pair; +import net.minecraft.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; +import org.leavesmc.leaves.command.subcommands.ConfigCommand; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public final class LeavesCommand extends Command { + static final String BASE_PERM = "bukkit.command.leaves."; + // subcommand label -> subcommand + private static final Map SUBCOMMANDS = Util.make(() -> { + final Map, LeavesSubcommand> commands = new HashMap<>(); + commands.put(Set.of("config"), new ConfigCommand()); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + private static final Set COMPLETABLE_SUBCOMMANDS = SUBCOMMANDS.entrySet().stream().filter(entry -> entry.getValue().tabCompletes()).map(Map.Entry::getKey).collect(Collectors.toSet()); + + public LeavesCommand(final String name) { + super(name); + this.description = "Leaves related commands"; + this.usageMessage = "/leaves [" + String.join(" | ", SUBCOMMANDS.keySet()) + "]"; + final List permissions = new ArrayList<>(); + permissions.add("bukkit.command.leaves"); + permissions.addAll(SUBCOMMANDS.keySet().stream().map(s -> BASE_PERM + s).toList()); + this.setPermission(String.join(";", permissions)); + final PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + for (final String perm : permissions) { + if (pluginManager.getPermission(perm) == null) { + pluginManager.addPermission(new Permission(perm, PermissionDefault.OP)); + } + } + } + + private static boolean testPermission(final CommandSender sender, final String permission) { + if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.leaves")) { + return true; + } + sender.sendMessage(Bukkit.permissionMessage()); + return false; + } + + @NotNull + + @Override + public List tabComplete(final @NotNull CommandSender sender, final @NotNull String alias, final String[] args, final @Nullable Location location) throws IllegalArgumentException { + if (args.length <= 1) { + return CommandUtil.getListMatchingLast(sender, args, COMPLETABLE_SUBCOMMANDS); + } + + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand != null) { + return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length)); + } + + return Collections.emptyList(); + } + + @Override + public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final String[] args) { + if (!testPermission(sender)) { + return true; + } + + if (args.length == 0) { + sender.sendMessage(text("Usage: " + this.usageMessage, RED)); + return false; + } + final Pair subCommand = resolveCommand(args[0]); + + if (subCommand == null) { + sender.sendMessage(text("Usage: " + this.usageMessage, RED)); + return false; + } + + if (!testPermission(sender, subCommand.first())) { + return true; + } + final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length); + return subCommand.second().execute(sender, subCommand.first(), choppedArgs); + } + + @Nullable + private static Pair resolveCommand(String label) { + label = label.toLowerCase(Locale.ENGLISH); + LeavesSubcommand subCommand = SUBCOMMANDS.get(label); + + if (subCommand != null) { + return Pair.of(label, subCommand); + } + + return null; + } +} diff --git a/src/main/java/org/leavesmc/leaves/command/LeavesSubcommand.java b/src/main/java/org/leavesmc/leaves/command/LeavesSubcommand.java new file mode 100644 index 0000000000000000000000000000000000000000..4b61fccc71d98a7b69bb7f88fb88ea0a55ca1ed2 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/LeavesSubcommand.java @@ -0,0 +1,18 @@ +package org.leavesmc.leaves.command; + +import org.bukkit.command.CommandSender; + +import java.util.Collections; +import java.util.List; + +public interface LeavesSubcommand { + boolean execute(CommandSender sender, String subCommand, String[] args); + + default List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return Collections.emptyList(); + } + + default boolean tabCompletes() { + return true; + } +} diff --git a/src/main/java/org/leavesmc/leaves/command/subcommands/ConfigCommand.java b/src/main/java/org/leavesmc/leaves/command/subcommands/ConfigCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..4b937f9626e89f2de1ae990151ca241573380e46 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/command/subcommands/ConfigCommand.java @@ -0,0 +1,86 @@ +package org.leavesmc.leaves.command.subcommands; + +import io.papermc.paper.command.CommandUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; +import org.leavesmc.leaves.command.LeavesSubcommand; +import org.leavesmc.leaves.config.GlobalConfigManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ConfigCommand implements LeavesSubcommand { + + @Override + public boolean execute(CommandSender sender, String subCommand, String[] args) { + if (args.length < 1) { + sender.sendMessage(Component.text("Leaves Config", NamedTextColor.GRAY)); + return true; + } + + GlobalConfigManager.VerifiedConfig verifiedConfig = GlobalConfigManager.getVerifiedConfig(args[0]); + if (verifiedConfig == null) { + sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), + Component.text("Config ", NamedTextColor.GRAY), + Component.text(args[0], NamedTextColor.RED), + Component.text(" is Not Found.", NamedTextColor.GRAY) + )); + return true; + } + + if (args.length > 1) { + try { + verifiedConfig.set(args[1]); + sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), + Component.text("Config ", NamedTextColor.GRAY), + Component.text(args[0], NamedTextColor.AQUA), + Component.text(" changed to ", NamedTextColor.GRAY), + Component.text(verifiedConfig.getString(), NamedTextColor.AQUA) + )); + } catch (IllegalArgumentException exception) { + sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), + Component.text("Config ", NamedTextColor.GRAY), + Component.text(args[0], NamedTextColor.RED), + Component.text(" modify error by ", NamedTextColor.GRAY), + Component.text(exception.getMessage(), NamedTextColor.RED) + )); + } + } else { + sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), + Component.text("Config ", NamedTextColor.GRAY), + Component.text(args[0], NamedTextColor.AQUA), + Component.text(" value is ", NamedTextColor.GRAY), + Component.text(verifiedConfig.getString(), NamedTextColor.AQUA) + )); + } + + return true; + } + + @Override + public List tabComplete(CommandSender sender, String subCommand, String[] args) { + switch (args.length) { + case 1 -> { + List list = new ArrayList<>(GlobalConfigManager.getVerifiedConfigPaths()); + return CommandUtil.getListMatchingLast(sender, args, list); + } + + case 2 -> { + GlobalConfigManager.VerifiedConfig verifiedConfig = GlobalConfigManager.getVerifiedConfig(args[0]); + if (verifiedConfig != null) { + if (verifiedConfig.config().lock()) { + return Collections.singletonList(""); + } + return CommandUtil.getListMatchingLast(sender, args, verifiedConfig.verify().valueSuggest()); + } else { + return Collections.singletonList(""); + } + } + } + + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/leavesmc/leaves/config/ConfigConvert.java b/src/main/java/org/leavesmc/leaves/config/ConfigConvert.java new file mode 100644 index 0000000000000000000000000000000000000000..3ddeff82e31178c9c1bb1737c9a28a81e70f71e5 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/ConfigConvert.java @@ -0,0 +1,7 @@ +package org.leavesmc.leaves.config; + +public interface ConfigConvert { + + E convert(String value); + +} diff --git a/src/main/java/org/leavesmc/leaves/config/ConfigVerify.java b/src/main/java/org/leavesmc/leaves/config/ConfigVerify.java new file mode 100644 index 0000000000000000000000000000000000000000..08ffa951d8fd835f075c45021f56cec19f03e7fc --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/ConfigVerify.java @@ -0,0 +1,90 @@ +package org.leavesmc.leaves.config; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public interface ConfigVerify extends ConfigConvert { + + default String check(E old, E value) { + return null; + } + + default List valueSuggest() { + return List.of(""); + } + + default void runAfterLoader(E value) { + } + + class BooleanConfigVerify implements ConfigVerify { + + @Override + public Boolean convert(String value) { + return Boolean.parseBoolean(value); + } + + @Override + public List valueSuggest() { + return List.of("false", "true"); + } + } + + class IntConfigVerify implements ConfigVerify { + @Override + public Integer convert(String value) { + return Integer.parseInt(value); + } + } + + class StringConfigVerify implements ConfigVerify { + @Override + public String convert(String value) { + return value; + } + } + + class DoubleConfigVerify implements ConfigVerify { + @Override + public Double convert(String value) { + return Double.parseDouble(value); + } + } + + class ListConfigVerify implements ConfigVerify> { + @Override + public List convert(String value) { + throw new IllegalArgumentException("not support"); // TODO + } + } + + class EnumConfigVerify> implements ConfigVerify> { + + private final Class enumClass; + private final List enumValues; + + @SuppressWarnings({"unchecked", "unused"}) + public EnumConfigVerify() { + Type genericSuperclass = getClass().getGenericSuperclass(); + Type[] typeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments(); + this.enumClass = (Class) typeArguments[0]; + this.enumValues = new ArrayList<>(){{ + for (E e : enumClass.getEnumConstants()) { + add(e.name().toLowerCase(Locale.ROOT)); + } + }}; + } + + @Override + public Enum convert(String value) { + return Enum.valueOf(enumClass, value.toUpperCase(Locale.ROOT)); + } + + @Override + public List valueSuggest() { + return enumValues; + } + } +} diff --git a/src/main/java/org/leavesmc/leaves/config/GlobalConfig.java b/src/main/java/org/leavesmc/leaves/config/GlobalConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1f18e2a32b937c71ddf68546c10a298b9baf8e1a --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/GlobalConfig.java @@ -0,0 +1,19 @@ +package org.leavesmc.leaves.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GlobalConfig { + + String name(); + + String[] category(); + + boolean lock() default false; + + Class> verify() default ConfigVerify.BooleanConfigVerify.class; +} diff --git a/src/main/java/org/leavesmc/leaves/config/GlobalConfigCreator.java b/src/main/java/org/leavesmc/leaves/config/GlobalConfigCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..67d4365d25714c3732274e5ab21981a31e0299a6 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/GlobalConfigCreator.java @@ -0,0 +1,56 @@ +package org.leavesmc.leaves.config; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.leavesmc.leaves.LeavesConfig; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collections; + +public class GlobalConfigCreator { + + public static void main(String[] args) { + YamlConfiguration config = new YamlConfiguration(); + config.options().setHeader(Collections.singletonList(LeavesConfig.CONFIG_HEADER)); + + config.set("config-version", LeavesConfig.CURRENT_CONFIG_VERSION); + + Class clazz = LeavesConfig.class; + + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + field.setAccessible(true); + + GlobalConfig globalConfig = field.getAnnotation(GlobalConfig.class); + if (globalConfig != null) { + try { + GlobalConfigManager.VerifiedConfig verifiedConfig = GlobalConfigManager.VerifiedConfig.build(globalConfig, field); + + ConfigVerify verify = verifiedConfig.verify(); + boolean isEnumConfig = verify instanceof ConfigVerify.EnumConfigVerify; + + Object defValue = isEnumConfig ? field.get(null).toString() : field.get(null); + config.set(verifiedConfig.path(), defValue); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + config.set("settings.protocol.xaero-map-server-id", 0); + + try { + File file = new File("leaves.yml"); + if (file.exists()) { + file.delete(); + } + file.createNewFile(); + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/leavesmc/leaves/config/GlobalConfigManager.java b/src/main/java/org/leavesmc/leaves/config/GlobalConfigManager.java new file mode 100644 index 0000000000000000000000000000000000000000..828bbd8b048df8e48ef007d2c1eefe1458b4b2c3 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/GlobalConfigManager.java @@ -0,0 +1,188 @@ +package org.leavesmc.leaves.config; + +import org.bukkit.Bukkit; +import org.leavesmc.leaves.LeavesConfig; +import org.leavesmc.leaves.LeavesLogger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; + +public class GlobalConfigManager { + + private static boolean firstLoad = true; + private static final Map verifiedConfigs = new HashMap<>(); + + public static void init() { + verifiedConfigs.clear(); + + Class clazz = LeavesConfig.class; + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + field.setAccessible(true); + + RemovedConfig removedConfig = field.getAnnotation(RemovedConfig.class); + if (removedConfig != null) { + RemovedVerifiedConfig verifiedConfig = RemovedVerifiedConfig.build(removedConfig, field); + verifiedConfig.run(); + } + + GlobalConfig globalConfig = field.getAnnotation(GlobalConfig.class); + if (globalConfig != null) { + try { + VerifiedConfig verifiedConfig = VerifiedConfig.build(globalConfig, field); + + if (globalConfig.lock() && !firstLoad) { + verifiedConfigs.put(verifiedConfig.path.substring("settings.".length()), verifiedConfig); + continue; + } + + ConfigVerify verify = verifiedConfig.verify; + boolean isEnumConfig = verify instanceof ConfigVerify.EnumConfigVerify; + + Object defValue = isEnumConfig ? field.get(null).toString() : field.get(null); + LeavesConfig.config.addDefault(verifiedConfig.path, defValue); + + try { + Object savedValue = LeavesConfig.config.get(verifiedConfig.path); + if (isEnumConfig) { + savedValue = verify.convert(savedValue.toString()); + } + String checkInfo = verify.check(null, savedValue); + + if (checkInfo == null) { + field.set(null, savedValue); + } else { + throw new IllegalArgumentException(checkInfo); + } + } catch (IllegalArgumentException | ClassCastException e) { + LeavesConfig.config.set(verifiedConfig.path, defValue); + LeavesLogger.LOGGER.warning(e.getMessage() + ", reset to " + defValue); + } + + verifiedConfigs.put(verifiedConfig.path.substring("settings.".length()), verifiedConfig); + } catch (Exception e) { + Bukkit.getLogger().log(Level.SEVERE, "Failure to load leaves config", e); + } + } + } + } + + verifiedConfigs.forEach((path, config) -> config.verify.runAfterLoader(config.get())); + + firstLoad = false; + LeavesConfig.save(); + } + + public static VerifiedConfig getVerifiedConfig(String path) { + return verifiedConfigs.get(path); + } + + public static Set getVerifiedConfigPaths() { + return verifiedConfigs.keySet(); + } + + public record RemovedVerifiedConfig(RemovedConfig config, ConfigConvert convert, Field field, String path) { + + public void run() { + if (config.transform()) { + if (LeavesConfig.config.contains(path)) { + String string = LeavesConfig.config.get(path).toString(); + try { + Object object = convert.convert(string); + field.set(null, object); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + LeavesConfig.config.set(path, null); + } + + public static RemovedVerifiedConfig build(RemovedConfig config, Field field) { + StringBuilder path = new StringBuilder("settings."); + for (int i = 0; i < config.category().length; i++) { + path.append(config.category()[i]).append("."); + } + path.append(config.name()); + + ConfigConvert configConvert = null; + try { + Constructor> constructor = config.convert().getDeclaredConstructor(); + constructor.setAccessible(true); + configConvert = constructor.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + + return new RemovedVerifiedConfig(config, configConvert, field, path.toString()); + } + } + + public record VerifiedConfig(GlobalConfig config, ConfigVerify verify, Field field, String path) { + + public void set(String realValue) throws IllegalArgumentException { + if (config.lock()) { + throw new IllegalArgumentException("locked"); + } + + Object value; + try { + value = verify.convert(realValue); + } catch (Exception e) { + throw new IllegalArgumentException("value parse error: " + e.getMessage()); + } + + String checkInfo = verify.check(this.get(), value); + if (checkInfo != null) { + throw new IllegalArgumentException(checkInfo); + } + + try { + field.set(null, value); + LeavesConfig.config.set(path, verify instanceof ConfigVerify.EnumConfigVerify ? realValue.toUpperCase(Locale.ROOT) : value); + LeavesConfig.save(); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("?"); + } + } + + public String getString() { + return this.get().toString(); + } + + public Object get() { + try { + return field.get(null); + } catch (IllegalAccessException e) { + LeavesLogger.LOGGER.log(Level.SEVERE, "Failure to get " + path + " value", e); + return ""; + } + } + + public static VerifiedConfig build(GlobalConfig config, Field field) { + StringBuilder path = new StringBuilder("settings."); + for (int i = 0; i < config.category().length; i++) { + path.append(config.category()[i]).append("."); + } + path.append(config.name()); + + ConfigVerify configVerify = null; + try { + Constructor> constructor = config.verify().getDeclaredConstructor(); + constructor.setAccessible(true); + configVerify = constructor.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + + + return new VerifiedConfig(config, configVerify, field, path.toString()); + } + } +} diff --git a/src/main/java/org/leavesmc/leaves/config/RemovedConfig.java b/src/main/java/org/leavesmc/leaves/config/RemovedConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..90137a284acaf9036334ee2fe6ab34025acb4f4d --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/config/RemovedConfig.java @@ -0,0 +1,19 @@ +package org.leavesmc.leaves.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RemovedConfig { + + String name(); + + String[] category(); + + boolean transform() default false; + + Class> convert() default ConfigVerify.BooleanConfigVerify.class; +}