Compare commits

..

6 Commits

Author SHA1 Message Date
MrHua269
0a2838607c Updated Upstream(Folia) 2025-06-07 07:30:45 +08:00
Helvetica Volubi
180b64aca2 feat: better config completer system 2025-06-07 01:13:03 +08:00
MrHua269
04233ea992 Updated Upstream(Folia) 2025-06-06 06:57:56 +08:00
MrHua269
d2fc625ce9 Fix off region thrown egg new entity creating
should set pos before so that we could correctly modify the entity's other attribute on-region without triggering the async catchers
2025-06-05 21:42:38 +08:00
Helvetica Volubi
70efb6748b fix: introduced two patches from leaves to fix some bugs
update [Fix Incorrect Collision Behavior for Block Shape] to [Configurable collision behavior]

fix chunk reload detector

20250604 23:22:57 UTC+8: use BLOCK_SHAPE_VANILLA as default instead of PAPER
2025-06-04 23:35:31 +08:00
Helvetica Volubi
4ea7b2833b feat: add config query, set and reset support 2025-06-03 23:42:29 +08:00
36 changed files with 272 additions and 147 deletions

View File

@@ -19,8 +19,8 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '22'
distribution: zulu
java-version: 22
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
@@ -28,17 +28,16 @@ jobs:
- name: Configure Git User Details
run: git config --global user.email "ci@luminolmc.com" && git config --global user.name "LuminolMC CI"
- name: Apply Patches
- name: Apply All Patches
run: ./gradlew applyAllPatches
- name: CreateJar
- name: CreateMojmapPaperclipJar
run: ./gradlew createMojmapPaperclipJar
- name: Publish to repo
if: github.event_name != 'pull_request'
continue-on-error: true
run: |
./gradlew generateDevelopmentBundle publish -PpublishDevBundle=true
run: ./gradlew generateDevelopmentBundle publish -PpublishDevBundle=true
env:
PRIVATE_MAVEN_REPO_PASSWORD: ${{ secrets.PRIVATE_MAVEN_REPO_PASSWORD }}
PRIVATE_MAVEN_REPO_USERNAME: ${{ secrets.PRIVATE_MAVEN_REPO_USERNAME }}
@@ -46,8 +45,8 @@ jobs:
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: "${{ env.project_id_b }} CI Artifacts"
path: "luminol-server/build/libs/*-paperclip-*-mojmap.jar"
name: ${{ env.project_id_b }} CI Artifacts
path: luminol-server/build/libs/*-paperclip-*-mojmap.jar
- name: SetENV
if: github.event_name != 'pull_request'
@@ -64,9 +63,8 @@ jobs:
This release is automatically compiled by GitHub Actions
### Commit Message
${{ env.commit_msg }}
artifacts: |
${{ env.jar_dir }}
artifacts: ${{ env.jar_dir }}
generateReleaseNotes: true
prerelease: false
prerelease: ${{ env.pre }}
makeLatest: ${{ env.make_latest }}
token: "${{ secrets.GITHUB_TOKEN }}"
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,7 +2,7 @@ group = me.earthme.luminol
version=1.21.5-R0.1-SNAPSHOT
mcVersion=1.21.5
foliaRef=da0d7cd1beb8eb7b84a5210dcefa6ffb4cce7415
foliaRef=3aba0068ded3f23bf8fa5cac6c70cb48f1d70478
org.gradle.configuration-cache=true
org.gradle.caching=true

View File

@@ -0,0 +1,56 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 4 Jun 2025 12:51:07 +0800
Subject: [PATCH] Leaves: Configurable collision behavior
Co-authored by: Fortern <blueten.ki@gmail.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/c5f18b7864206cea4411211b51787f10affbcb9c/leaves-server/minecraft-patches/features/0111-Configurable-collision-behavior.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
index e1bf7bc3cca63101b3c0550b5b4c0d28d0f3776e..d753d550b1df88ad9f2315ec3fc8f342be183c6d 100644
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
@@ -101,6 +101,14 @@ public final class CollisionUtil {
(box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON;
}
+ // Leaves start - Configurable collision behavior
+ public static boolean voxelShapeIntersectVanilla(final AABB box1, final AABB box2) {
+ return box1.minX < box2.maxX && box1.maxX > box2.minX &&
+ box1.minY < box2.maxY && box1.maxY > box2.minY &&
+ box1.minZ < box2.maxZ && box1.maxZ > box2.minZ;
+ }
+ // Leaves end - Configurable collision behavior
+
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
public static double collideX(final AABB target, final AABB source, final double source_move) {
if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON &&
@@ -2025,7 +2033,7 @@ public final class CollisionUtil {
AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
if (singleAABB != null) {
singleAABB = singleAABB.move((double)blockX, (double)blockY, (double)blockZ);
- if (!voxelShapeIntersect(aabb, singleAABB)) {
+ if (shouldSkip(aabb, blockCollision, singleAABB)) { // Leaves - Configurable collision behavior
continue;
}
@@ -2078,6 +2086,18 @@ public final class CollisionUtil {
return ret;
}
+ // Leaves start - Configurable collision behavior
+ private static boolean shouldSkip(AABB aabb, VoxelShape blockCollision, AABB singleAABB) {
+ boolean isBlockShape = blockCollision == Shapes.block();
+ return switch (me.earthme.luminol.config.modules.misc.CollisionBehaviorConfig.behaviorMode) {
+ case "VANILLA" -> !voxelShapeIntersectVanilla(aabb, singleAABB);
+ case "PAPER" -> !voxelShapeIntersect(aabb, singleAABB);
+ default -> isBlockShape && !voxelShapeIntersectVanilla(aabb, singleAABB) || !isBlockShape && !voxelShapeIntersect(aabb, singleAABB);
+ // All other value as BLOCK_SHAPE_VANILLA to process
+ };
+ }
+ // Leaves end - Configurable collision behavior
+
public static boolean getEntityHardCollisions(final Level world, final Entity entity, AABB aabb,
final List<AABB> into, final int collisionFlags, final Predicate<Entity> predicate) {
final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0;

View File

@@ -1,40 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <wangxyper@163.com>
Date: Sat, 8 Feb 2025 14:25:16 +0800
Subject: [PATCH] Leaves: Fix Incorrect Collision Behavior for Block Shape
Co-authored by: Fortern <blueten.ki@gmail.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/f553c53e4230aa032e54a69b6479f1959ed24a60/leaves-server/minecraft-patches/features/0110-Fix-Incorrect-Collision-Behavior-for-Block-Shape.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
index e1bf7bc3cca63101b3c0550b5b4c0d28d0f3776e..ea01087967dd9ff32b23661079e97ea468d31163 100644
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
@@ -101,6 +101,14 @@ public final class CollisionUtil {
(box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON;
}
+ // Leaves start
+ public static boolean voxelShapeIntersectVanilla(final net.minecraft.world.phys.AABB box1, final net.minecraft.world.phys.AABB box2) {
+ return box1.minX < box2.maxX && box1.maxX > box2.minX &&
+ box1.minY < box2.maxY && box1.maxY > box2.minY &&
+ box1.minZ < box2.maxZ && box1.maxZ > box2.minZ;
+ }
+ // Leaves end
+
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
public static double collideX(final AABB target, final AABB source, final double source_move) {
if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON &&
@@ -2025,7 +2033,10 @@ public final class CollisionUtil {
AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
if (singleAABB != null) {
singleAABB = singleAABB.move((double)blockX, (double)blockY, (double)blockZ);
- if (!voxelShapeIntersect(aabb, singleAABB)) {
+ // Leaves start - Fix incorrect collision behavior for block shape
+ boolean isBlockShape = blockCollision == net.minecraft.world.phys.shapes.Shapes.block();
+ if (isBlockShape && !voxelShapeIntersectVanilla(aabb, singleAABB) || !isBlockShape && !voxelShapeIntersect(aabb, singleAABB)) {
+ // Leaves end - Fix incorrect collision behavior for block shape
continue;
}

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 4 Jun 2025 12:54:22 +0800
Subject: [PATCH] Leaves: Fix chunk reload detector
Co-authored by: Fortern <blueten.ki@gmail.com>
As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/c5f18b7864206cea4411211b51787f10affbcb9c/leaves-server/minecraft-patches/features/0123-Fix-chunk-reload-detector.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index b118e91f1e0b5a8b8c0b2a4a32faabc5a34a5954..663bd286426537daf97ae09dc7e076a9ec6d4c9c 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -376,7 +376,7 @@ public class ServerEntity {
if (!list.isEmpty()) {
consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
}
- ((LivingEntity) this.entity).detectEquipmentUpdates(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
+ if (this.entity.totalEntityAge == 0) ((LivingEntity) this.entity).detectEquipmentUpdates(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending // Leaves - fix chunk reload detector (#492)
}
if (!this.entity.getPassengers().isEmpty()) {

View File

@@ -8,7 +8,7 @@ As part of: Leaves (https://github.com/LeavesMC/Leaves/blob/f553c53e4230aa032e54
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 5564bba66959a2a280f700f6c6a05d292faced88..4492f3ab470c0bf735b6fdc69115c1ebbcd727ef 100644
index bcc0c3cfa5be4daeec6e95a1b63a6cd38890f04d..e83c6a81e318a05580f5c811cb2543a83435f04c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1285,7 +1285,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc

View File

@@ -8,7 +8,7 @@ As part of: Pufferfish (https://github.com/pufferfish-gg/Pufferfish/blob/04bc249
Licensed under: GPL-3.0 (https://github.com/pufferfish-gg/Pufferfish/blob/04bc249f9eee6157338b6884113b6fa3192bf59b/PATCH-LICENSE)
diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java
index ab7f7846d3fc0252c6f71277b3e67d7a785a96b5..0f496f0e1c63bc3d8b17a8f0cb065138cb079334 100644
index 5ae08be75ca01924fc78bdd8d6bb6747ddc21aea..6e0b6304205210cc60293329fe30bd6c99cd263a 100644
--- a/net/minecraft/world/entity/monster/EnderMan.java
+++ b/net/minecraft/world/entity/monster/EnderMan.java
@@ -300,11 +300,18 @@ public class EnderMan extends Monster implements NeutralMob {

View File

@@ -8,7 +8,7 @@ Co-authored by: MrPowerGamerBR <git@mrpowergamerbr.com>
As part of: SparklyPaper (https://github.com/SparklyPower/SparklyPaper/blob/a69d3690472c9967772ffd4d4bd1f41403b7ea6f/sparklypaper-server/minecraft-patches/features/0002-Skip-distanceToSqr-call-in-ServerEntity-sendChanges-.patch)
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index b118e91f1e0b5a8b8c0b2a4a32faabc5a34a5954..8c232ae73a8c6500723994af3a28b36814cccae0 100644
index 663bd286426537daf97ae09dc7e076a9ec6d4c9c..8739b4e806878cf3a4af9ad1aef7f2a55880d503 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -205,6 +205,8 @@ public class ServerEntity {

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Correct player respawn place
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 4492f3ab470c0bf735b6fdc69115c1ebbcd727ef..8b9c823deed1844fa69d1456a91ac65487b8ae69 100644
index e83c6a81e318a05580f5c811cb2543a83435f04c..2f2726ed97658732751032b21752840702da233e 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -492,8 +492,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc

View File

@@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Sat, 31 May 2025 16:28:19 +0800
Subject: [PATCH] Fix mispatched entity custom spawning logic
we should use local players as each tickregion will only foreach their own player entities so that we won't come across async accessing world data
diff --git a/net/minecraft/world/entity/npc/CatSpawner.java b/net/minecraft/world/entity/npc/CatSpawner.java
index f5d27988605d48cdf314f28ba332f33f0a314266..39b506dad0601b2d75acb14989f2758132155d1b 100644
--- a/net/minecraft/world/entity/npc/CatSpawner.java
+++ b/net/minecraft/world/entity/npc/CatSpawner.java
@@ -27,7 +27,7 @@ public class CatSpawner implements CustomSpawner {
worldData.catSpawnerNextTick--; // Folia - region threading
if (worldData.catSpawnerNextTick <= 0) { // Folia - region threading
worldData.catSpawnerNextTick = 1200; // Folia - region threading
- Player randomPlayer = level.getRandomPlayer();
+ Player randomPlayer = level.getRandomLocalPlayer(); // Luminol - Fix mispatched entity custom spawning logic - Use local players for only the players in current tickregion
if (randomPlayer != null) {
RandomSource randomSource = level.random;
int i = (8 + randomSource.nextInt(24)) * (randomSource.nextBoolean() ? -1 : 1);
diff --git a/net/minecraft/world/level/levelgen/PhantomSpawner.java b/net/minecraft/world/level/levelgen/PhantomSpawner.java
index a7f56126a26e1fca86c39d8d656b648f5d6bb4ca..4626ee9f4410d8fd683db17b49ef79ab67eeda5f 100644
--- a/net/minecraft/world/level/levelgen/PhantomSpawner.java
+++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java
@@ -40,7 +40,7 @@ public class PhantomSpawner implements CustomSpawner {
worldData.phantomSpawnerNextTick += (spawnAttemptMinSeconds + randomSource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; // Folia - region threading
// Paper end - Ability to control player's insomnia and phantoms
if (level.getSkyDarken() >= 5 || !level.dimensionType().hasSkyLight()) {
- for (ServerPlayer serverPlayer : level.players()) {
+ for (ServerPlayer serverPlayer : level.getLocalPlayers()) { // Luminol - Fix mispatched entity custom spawning logic - Use local players for only the players in current tickregion
if (!serverPlayer.isSpectator() && (!level.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !serverPlayer.isCreative())) { // Paper - Add phantom creative and insomniac controls
BlockPos blockPos = serverPlayer.blockPosition();
if (!level.dimensionType().hasSkyLight() || blockPos.getY() >= level.getSeaLevel() && level.canSeeSky(blockPos)) {

View File

@@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 5 Jun 2025 21:11:31 +0800
Subject: [PATCH] Fix off region thrown egg new entity creating
should set pos before so that we could correctly modify the entity's other attribute on-region without triggering the async catchers
diff --git a/net/minecraft/world/entity/projectile/ThrownEgg.java b/net/minecraft/world/entity/projectile/ThrownEgg.java
index 73ec34b43f3fb2aa3edc3f1cb48a923d1fa32036..5760ae39e8c452b8291353ed59ce7f8ef4d43dc1 100644
--- a/net/minecraft/world/entity/projectile/ThrownEgg.java
+++ b/net/minecraft/world/entity/projectile/ThrownEgg.java
@@ -97,12 +97,13 @@ public class ThrownEgg extends ThrowableItemProjectile {
for (int i1 = 0; i1 < i; i1++) {
net.minecraft.world.entity.Entity chicken = newEntityType.create(this.level(), net.minecraft.world.entity.EntitySpawnReason.TRIGGERED); // CraftBukkit
if (chicken != null) {
+ chicken.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F); // Luminol - Fix off region thrown egg - move up
// CraftBukkit start
if (chicken.getBukkitEntity() instanceof org.bukkit.entity.Ageable ageable) {
ageable.setBaby();
}
// CraftBukkit end
- chicken.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
+ // chicken.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F); // Luminol - Fix off region thrown egg - move up
// CraftBukkit start
if (chicken instanceof Chicken realChicken) {
Optional.ofNullable(this.getItem().get(DataComponents.CHICKEN_VARIANT))

View File

@@ -1,6 +1,6 @@
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/commands/LuminolConfigCommand.java
@@ -1,0 +_,80 @@
@@ -1,0 +_,121 @@
+package me.earthme.luminol.commands;
+
+import me.earthme.luminol.config.LuminolConfig;
@@ -23,16 +23,26 @@
+ this.setUsage("/luminolconfig");
+ }
+
+ public void wrongUse(CommandSender sender) {
+ sender.sendMessage(
+ Component
+ .text("Wrong use!")
+ .color(TextColor.color(255, 0, 0))
+ );
+ }
+
+ @Override
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args, @Nullable Location location) throws IllegalArgumentException {
+ final List<String> result = new ArrayList<>();
+
+ if (args.length == 1) {
+ result.add("query");
+ // result.add("set"); // TODO Later
+ // result.add("reload"); // TODO Later
+ result.add("set");
+ result.add("reset");
+ result.add("reload");
+ } else if (args.length == 2 && (args[0].equals("query") || args[0].equals("set") || args[0].equals("reset"))) {
+ result.addAll(LuminolConfig.completeConfigPath(args[1]));
+ }
+
+ return result;
+ }
+
@@ -46,11 +56,7 @@
+ }
+
+ if (args.length < 1) {
+ sender.sendMessage(
+ Component
+ .text("Wrong use!\n")
+ .color(TextColor.color(255, 0, 0))
+ );
+ wrongUse(sender);
+ return true;
+ }
+
@@ -62,18 +68,53 @@
+ .color(TextColor.color(0, 255, 0))
+ ));
+ }
+
+ case "set" -> {
+ // TODO Later
+ if (args.length == 2 || args.length > 3) {
+ wrongUse(sender);
+ return true;
+ } else if (LuminolConfig.setConfig(args[1], args[2])) {
+ LuminolConfig.reloadAsync().thenAccept(nullValue -> sender.sendMessage(
+ Component
+ .text("Set Config " + args[1] + " to " + args[2] + " successfully!")
+ .color(TextColor.color(0, 255, 0))
+ ));
+ } else {
+ sender.sendMessage(
+ Component
+ .text("Failed to set config " + args[1] + " to " + args[2] + "!")
+ .color(TextColor.color(255, 0, 0))
+ );
+ }
+ }
+ case "reset" -> {
+ if (args.length != 2) {
+ wrongUse(sender);
+ return true;
+ } else {
+ LuminolConfig.resetConfig(args[1]);
+ LuminolConfig.reloadAsync().thenAccept(nullValue -> sender.sendMessage(
+ Component
+ .text("Reset Config " + args[1] + " to " + LuminolConfig.getConfig(args[1]) + " successfully!")
+ .color(TextColor.color(0, 255, 0))
+ ));
+ }
+ }
+
+ case "query" -> {
+ // TODO Later
+ if (args.length != 2) {
+ wrongUse(sender);
+ return true;
+ } else {
+ sender.sendMessage(
+ Component
+ .text("Config " + args[1] + " is " + LuminolConfig.getConfig(args[1]) + "!")
+ .color(TextColor.color(0, 255, 0))
+ );
+ }
+ }
+
+ default -> sender.sendMessage(
+ Component
+ .text("Unknown action!\n")
+ .text("Unknown action!")
+ .color(TextColor.color(255, 0, 0))
+ );
+ }

View File

@@ -1,9 +1,8 @@
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/config/LuminolConfig.java
@@ -1,0 +_,351 @@
@@ -1,0 +_,378 @@
+package me.earthme.luminol.config;
+
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+import com.electronwill.nightconfig.core.file.CommentedFileConfig;
+import io.papermc.paper.threadedregions.RegionizedServer;
@@ -37,12 +36,14 @@
+ private static final File baseConfigFolder = new File("luminol_config");
+ private static final File baseConfigFile = new File(baseConfigFolder, "luminol_global_config.toml");
+ private static final Set<IConfigModule> allInstanced = new HashSet<>();
+ public static boolean alreadyInited = false;
+ private static final Map<String, Object> stagedConfigMap = new HashMap<>();
+ private static final Map<String, Object> defaultvalueMap = new HashMap<>();
+ public static boolean alreadyInit = false;
+ private static CommentedFileConfig configFileInstance;
+
+ public static void setupLatch() {
+ Bukkit.getCommandMap().register("luminolconfig", "luminol", new LuminolConfigCommand());
+ alreadyInited = true;
+ alreadyInit = true;
+ }
+
+ public static void reload() {
@@ -97,7 +98,7 @@
+ throw new RuntimeException(e);
+ }
+
+ configFileInstance.save();
+ saveConfigs();
+ }
+
+ private static void loadAllModules() throws IllegalAccessException {
@@ -122,7 +123,7 @@
+ for (Field field : fields) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
+ boolean skipLoad = field.getAnnotation(DoNotLoad.class) != null || (alreadyInited && field.getAnnotation(HotReloadUnsupported.class) != null);
+ boolean skipLoad = field.getAnnotation(DoNotLoad.class) != null || (alreadyInit && field.getAnnotation(HotReloadUnsupported.class) != null);
+ ConfigInfo configInfo = field.getAnnotation(ConfigInfo.class);
+
+ if (skipLoad || configInfo == null) {
@@ -133,6 +134,7 @@
+
+ field.setAccessible(true);
+ final Object currentValue = field.get(null);
+ if (!alreadyInit) defaultvalueMap.put(fullConfigKeyName, currentValue);
+ boolean removed = fullConfigKeyName.equals("removed.removed_config.removed");
+
+ if (!configFileInstance.contains(fullConfigKeyName) || removed) {
@@ -180,7 +182,21 @@
+ continue;
+ }
+
+ final Object actuallyValue = configFileInstance.get(fullConfigKeyName);
+ Object actuallyValue;
+ if (stagedConfigMap.containsKey(fullConfigKeyName)) {
+ actuallyValue = stagedConfigMap.get(fullConfigKeyName);
+ if (actuallyValue == null) actuallyValue = defaultvalueMap.get(fullConfigKeyName);
+ stagedConfigMap.remove(fullConfigKeyName);
+ } else {
+ actuallyValue = configFileInstance.get(fullConfigKeyName);
+ }
+ try {
+ actuallyValue = tryTransform(field.get(null).getClass(), actuallyValue);
+ configFileInstance.set(fullConfigKeyName, actuallyValue);
+ } catch (IllegalFormatConversionException e) {
+ resetConfig(fullConfigKeyName);
+ logger.error("Failed to transform config {}, reset to default!", fullConfigKeyName);
+ }
+ field.set(null, actuallyValue);
+ }
+ }
@@ -202,31 +218,43 @@
+ }
+ }
+
+ public static boolean setConfigAndSave(String[] keys, Object value) {
+ return setConfigAndSave(String.join(".", keys), value);
+ }
+
+ public static boolean setConfigAndSave(String key, Object value) {
+ if (setConfig(key, value)) {
+ saveConfig();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean setConfig(String[] keys, Object value) {
+ return setConfig(String.join(".", keys), value);
+ }
+
+ public static boolean setConfig(String key, Object value) {
+ if (configFileInstance.contains(key) && configFileInstance.get(key) != null) {
+ configFileInstance.set(key, value);
+ stagedConfigMap.put(key, value);
+ return true;
+ }
+ return false;
+ }
+
+ public static void saveConfig() {
+ private static Object tryTransform(Class<?> targetType, Object value) {
+ if (!targetType.isAssignableFrom(value.getClass())) {
+ try {
+ if (targetType == Integer.class) {
+ value = Integer.parseInt(value.toString());
+ } else if (targetType == Double.class) {
+ value = Double.parseDouble(value.toString());
+ } else if (targetType == Boolean.class) {
+ value = Boolean.parseBoolean(value.toString());
+ } else if (targetType == Long.class) {
+ value = Long.parseLong(value.toString());
+ } else if (targetType == Float.class) {
+ value = Float.parseFloat(value.toString());
+ } else if (targetType == String.class) {
+ value = value.toString();
+ }
+ } catch (Exception e) {
+ logger.error("Failed to transform value {}!", value);
+ throw new IllegalFormatConversionException((char) 0, targetType);
+ }
+ }
+ return value;
+ }
+
+ public static void saveConfigs() {
+ configFileInstance.save();
+ }
+
@@ -235,9 +263,7 @@
+ }
+
+ public static void resetConfig(String key) {
+ configFileInstance.remove(key);
+ configFileInstance.save();
+ reload();
+ stagedConfigMap.put(key, null);
+ }
+
+ public static String getConfig(String[] keys) {
@@ -245,32 +271,33 @@
+ }
+
+ public static String getConfig(String key) {
+ return configFileInstance.get(key);
+ return configFileInstance.get(key).toString();
+ }
+
+ public List<String> getAllConfigPaths(String prefix) {
+ List<String> configPaths = getAllConfigPaths();
+ return configPaths.stream().filter(path -> path.startsWith(prefix)).toList();
+ }
+ public static List<String> completeConfigPath(String partialPath) {
+ List<String> allPaths = getAllConfigPaths(partialPath);
+ List<String> result = new ArrayList<>();
+
+ public static List<String> getAllConfigPaths() {
+ List<String> configPaths = new ArrayList<>();
+ getAllConfigPathsRecursive(configFileInstance, "", configPaths);
+ return configPaths;
+ }
+ for (String path : allPaths) {
+ String remaining = path.substring(partialPath.length());
+ if (remaining.isEmpty()) continue;
+
+ private static void getAllConfigPathsRecursive(CommentedConfig config, String currentPath, List<String> configPaths) {
+ for (CommentedConfig.Entry entry : config.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ String fullPath = currentPath.isEmpty() ? key : currentPath + "." + key;
+ int dotIndex = remaining.indexOf('.');
+ String suggestion = (dotIndex == -1)
+ ? path
+ : partialPath + remaining.substring(0, dotIndex);
+
+ if (value instanceof CommentedConfig) {
+ getAllConfigPathsRecursive((CommentedConfig) value, fullPath, configPaths);
+ } else {
+ configPaths.add(fullPath);
+ if (!result.contains(suggestion)) {
+ result.add(suggestion);
+ }
+ }
+ return result;
+ }
+
+ private static List<String> getAllConfigPaths(String currentPath) {
+ return defaultvalueMap.keySet().stream()
+ .filter(k -> k.startsWith(currentPath))
+ .toList();
+ }
+
+ public static @NotNull Set<Class<?>> getClasses(String pack) {

View File

@@ -0,0 +1,28 @@
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/CollisionBehaviorConfig.java
@@ -1,0 +_,25 @@
+package me.earthme.luminol.config.modules.misc;
+
+import me.earthme.luminol.config.EnumConfigCategory;
+import me.earthme.luminol.config.IConfigModule;
+import me.earthme.luminol.config.flags.ConfigInfo;
+
+public class CollisionBehaviorConfig implements IConfigModule {
+ @ConfigInfo(baseName = "mode", comments =
+ """
+ Available Value:
+ VANILLA
+ BLOCK_SHAPE_VANILLA
+ PAPER""")
+ public static String behaviorMode = "BLOCK_SHAPE_VANILLA";
+
+ @Override
+ public EnumConfigCategory getCategory() {
+ return EnumConfigCategory.MISC;
+ }
+
+ @Override
+ public String getBaseName() {
+ return "collision_behavior";
+ }
+}