Compare commits

..

34 Commits

Author SHA1 Message Date
Helvetica Volubi e5cc20ba9b Update Luminol 2026-02-23 21:23:49 +08:00
Helvetica Volubi 46e7abe59c Update Luminol 2026-02-18 00:25:23 +08:00
Helvetica Volubi 40df6b9dfb Switch to release cannel
Update Luminol
2026-02-13 21:14:41 +08:00
Bacteriawa 5559fa8bcc Update Luminol 2026-02-13 16:21:55 +08:00
Helvetica Volubi 6319ddf78f [ci skip]update qodana (#131)
* qodana update test

use community version

* fix: update bug-report.yml and qodana.yaml for improved configuration clarity

* chore: configure Git user details for CI environment

* [ci skip]fix: update linter image in qodana.yaml to use community version

* fix: update linter and action versions to use latest releases

* Revert "chore: configure Git user details for CI environment"

This reverts commit e53b12ac49.

* fix: update qodana.yaml to exclude all files instead of just JavaFiles

* Revert "qodana update test"

This reverts commit 4aac78cb72.

* [ci skip]fix: update .gitignore and qodana.yaml for project structure changes
2026-02-08 01:20:10 +08:00
Bacteriawa e74300a756 Fix Server crash when using CCE #109 2026-02-08 00:02:31 +08:00
Bacteriawa d9674b4e7c Update Luminol 2026-02-07 23:20:40 +08:00
Helvetica Volubi 6d02f00ac0 Update Luminol to fix #127
fixes #127
2026-01-30 19:47:50 +08:00
Helvetica Volubi 2960e7d24f perf: add cache for replay api 2026-01-28 22:45:50 +08:00
Helvetica Volubi d950c6d3e8 Update Luminol 2026-01-26 23:48:23 +08:00
Helvetica Volubi 357c351b95 fix up version fetcher 2026-01-22 00:59:08 +08:00
Helvetica Volubi a780b13fcc Update Luminol 2026-01-21 21:37:37 +08:00
Helvetica Volubi cf80fc6a86 Update Luminol 2026-01-18 03:31:10 +08:00
Helvetica Volubi 20acc9ccf5 feat: add save-all support 2026-01-16 02:41:01 +08:00
Helvetica Volubi e49ab25111 simply fix for replay api
fixed https://github.com/MC-XiaoHei/ISeeYou/issues/70
2026-01-14 01:57:42 +08:00
Helvetica Volubi eed4457419 add removed check before all checks start in addEffects 2026-01-13 21:06:48 +08:00
Helvetica Volubi 429a0345d9 refactor(bot): delay some logic of extra load in fakeplayer spawn 2026-01-13 20:47:55 +08:00
Helvetica Volubi 670d4e39c9 Update Luminol 2026-01-13 19:30:32 +08:00
Helvetica Volubi 5d4ddb9a6b fix #124 (refix of #121) 2026-01-09 19:10:28 +08:00
Helvetica Volubi 0ad90dc36e Update Luminol 2026-01-07 17:13:18 +08:00
Helvetica Volubi 284810dfb8 fixes #121 2026-01-04 17:51:09 +08:00
Helvetica Volubi 0f67f445b9 Update Luminol 2026-01-04 12:32:42 +08:00
Helvetica Volubi 018cc61204 Update Luminol 2026-01-03 13:59:49 +08:00
Helvetica Volubi b9c3d8deea fix ender dragon caused server crash 2026-01-03 01:40:35 +08:00
Helvetica Volubi 0d5b289d65 Update Luminol 2026-01-03 01:04:31 +08:00
Helvetica Volubi 5b20ad2e2c Update Luminol 2025-12-29 23:39:50 +08:00
Helvetica Volubi 9fcce731d0 Update Luminol 2025-12-29 01:28:30 +08:00
Helvetica Volubi 0783c97224 Update Protocols from Leaves 2025-12-28 00:29:37 +08:00
Helvetica Volubi 300e891906 Update Luminol 2025-12-27 23:42:13 +08:00
Helvetica Volubi a3e0a3ee1a Update Wool Counter & FakePlayer code from Leaves 2025-12-27 20:39:17 +08:00
Helvetica Volubi b3703e2069 fix a bug in way point 2025-12-27 20:36:30 +08:00
Helvetica Volubi d3c8da4edb Update Luminol 2025-12-27 17:16:52 +08:00
Helvetica Volubi bed0bfff06 fix: refix waypoint
related issue: https://github.com/LuminolMC/Lophine/issues/110
2025-12-26 03:38:04 +08:00
Helvetica Volubi 238231b28b Update Luminol 2025-12-26 03:28:41 +08:00
126 changed files with 1532 additions and 1666 deletions
+5 -5
View File
@@ -74,11 +74,11 @@ body:
All config files on your server.
例如以下文件(或者文件夹):
Example files (or folders)
- \spigot.yml
- \bukkit.yml
- \config\\\*
- \lophine_config\\\*
- \luminol_config\\\*
- \spigot.yml
- \bukkit.yml
- \config\\\*
- \lophine_config\\\*
- \luminol_config\\\*
- type: textarea
id: other
attributes:
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2025.3
uses: JetBrains/qodana-action@latest
with:
pr-mode: false
env:
+3 -5
View File
@@ -6,12 +6,10 @@ build
/run
/lightingluminol-server/build.gradle.kts
/lightingluminol-api/build.gradle.kts
/luminol-server/build.gradle.kts
/luminol-server/src/minecraft
/lophine-server/build.gradle.kts
/lophine-api/build.gradle.kts
/lophine-server/src/minecraft
/paper-server
/luminol-api/build.gradle.kts
/paper-api
/paper-api-generator
+2 -2
View File
@@ -4,10 +4,10 @@ mcVersion=1.21.11
# This is the current API version for use in (paper-)plugin.yml files
# During snapshot cycles this should be the anticipated version of the release target
apiVersion=1.21.11
release=1
release=2
# 0 for skip release, 1 for pre-release, 2 for release
luminolRef=db42c568c4fb9addf797d797710fc8ad49daf3ba
luminolRef=78a3b89f5850f71ddb5b1d1adeccda47f5762a1b
org.gradle.configuration-cache=true
org.gradle.caching=true
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -39,9 +39,9 @@ public interface Bot extends Player {
/**
* Gets the fakeplayer name without prefix and suffix
*
* @return fakeplayer real name
* @return fakeplayer raw name
*/
@NotNull String getRealName();
@NotNull String getRawName();
/**
* Gets the creator's UUID of the fakeplayer
@@ -91,4 +91,13 @@ public interface Bot extends Player {
* @return success
*/
boolean remove(boolean save);
/**
* Remove the fakeplayer
*
* @param save should save
* @param resume should resume at next server start
* @return success
*/
boolean remove(boolean save, boolean resume);
}
@@ -27,8 +27,8 @@ import java.util.function.Consumer;
public interface BotCreator {
static BotCreator of(String realName, Location location) {
return Bukkit.getBotManager().botCreator(realName, location);
static BotCreator of(String rawName, Location location) {
return Bukkit.getBotManager().botCreator(rawName, location);
}
BotCreator name(String name);
@@ -61,5 +61,5 @@ public interface BotManager {
*/
<T extends BotAction<T>> T newAction(@NotNull Class<T> type);
BotCreator botCreator(@NotNull String realName, @NotNull Location location);
BotCreator botCreator(@NotNull String rawName, @NotNull Location location);
}
+3 -3
View File
@@ -49,9 +49,9 @@
- implementation(project(":luminol-api")) // Luminol
+ implementation(project(":lophine-api")) // Lophine
// Luminol start - Dependenices insert
implementation("io.netty:netty-all:4.2.9.Final") // used for io_uring
implementation("com.electronwill.night-config:toml:3.8.3") // Night config
implementation("com.github.luben:zstd-jni:1.5.4-1")
@@ -272,14 +_,14 @@
@@ -273,14 +_,14 @@
val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim()
attributes(
"Main-Class" to "org.bukkit.craftbukkit.Main",
@@ -70,7 +70,7 @@
"Build-Number" to (build ?: ""),
"Build-Time" to buildTime.toString(),
"Git-Branch" to gitBranch,
@@ -431,7 +_,7 @@
@@ -432,7 +_,7 @@
}
fill {
@@ -17,15 +17,15 @@ index 0bebc2cc87fb9c1c526e4de4f3bc157552307a72..cb1657939b86c039fcfe6403d3f202c2
}
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
index df5d926fb4401dd93d658fe14ae4d9d58cff68f9..b6d84fea596cfa78032151ab2f206b6b3d5dd197 100644
index e4022494b050b68d54bac3cde87bdfc9e908859c..a4188ef831825764c0756b826651e2e7a9f99c5b 100644
--- a/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
@@ -8,7 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.MISC, name = "server_mod_name")
public class ServerModNameConfig implements IConfigModule {
@ConfigInfo(name = "name")
@ConfigInfo(name = "name", comments = "Decides the server mod name shown in your F3 debug screen.")
- public static String serverModName = "Luminol";
+ public static String serverModName = "Lophine";
@ConfigInfo(name = "vanilla_spoof")
@ConfigInfo(name = "vanilla_spoof", comments = "Ignore any plugin's modification and server mod name set in this config block, only force sending brand name of vanilla")
public static boolean fakeVanilla = false;
@@ -5,26 +5,36 @@ Subject: [PATCH] Transformed Configs
diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
index 4051384e82dba0416838c3c9889a6e368367b601..a5535b479cdcca870d0c8be6ed6fbcb32f266ac2 100644
index ad2ba680bedb2336de24146676e70a1f64fd9c5a..f81581782bf684bbcf4719e9b268efa3c6385325 100644
--- a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
@@ -11,6 +11,7 @@ public class CommandConfig implements IConfigModule {
@TransformedConfig(name = "enable", directory = {"experiment", "force_the_data_command_to_be_enabled"})
@@ -13,6 +13,7 @@ public class CommandConfig implements IConfigModule {
@ConfigInfo(name = "enable_data_command")
@HotReloadUnsupported
public static boolean data = false;
+ @TransformedConfig(name = "command_block_enabled", directory = {"experiment", "command"}, originInstance = "lophine")
@TransformedConfig(name = "enabled", directory = {"experiment", "force_enable_command_block_command_execution"})
@ConfigInfo(name = "enable_command_block", comments = """
Force to enable command blocks.
diff --git a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
index 35e6014f04093050a9a62b895b43e5c5d8c6a28e..11abfb884f972fd73ca4c13a6e309de1bcd23fd5 100644
--- a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
@@ -8,6 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.OPTIMIZATIONS, name = "force_disable_packet_limiter_of_paper")
@@ -20,6 +21,9 @@ public class CommandConfig implements IConfigModule {
DO NOT ENABLE UNLESS YOU KNOW WHAT YOU ARE DOING!!!
""")
public static boolean commandBlock = false;
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint bar"})
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint_bar"})
+ @TransformedConfig(name = "waypoint_command_enabled", directory = {"experiment", "command"})
@ConfigInfo(name = "enable_waypoints_and_waypoint_command", comments = """
Enable waypoint and waypoint command.
WARN: Still under testing
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
index ce190737de22438d5de86b0ee0e8158954fdca18..0a175e87f3cb5b23480abefd70ee2e84e876b0d4 100644
--- a/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
@@ -11,6 +11,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
"has negative impacts on security"
)
public class PaperPacketLimiterConfig implements IConfigModule {
+ @TransformedConfig(name = "unlimit-packet", directory = {"misc", "network"}, originInstance = "lophine")
@TransformedConfig(name = "force_disable", directory = {"misc", "force_disable_packet_limiter_of_paper"})
@TransformedConfig(name = "force_disable", directory = {"optimizations", "force_disable_packet_limiter_of_paper"})
@ConfigInfo(name = "force_disable")
public static boolean forceDisable = false;
@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 18 Feb 2026 00:15:08 +0800
Subject: [PATCH] Leaves: Fakeplayer
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java b/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
index 14cf379decfd46c4719bc192a1467bbbd6835417..20970844de2e64ca213384e53003e80020165770 100644
--- a/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
+++ b/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
@@ -38,6 +38,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
return;
}
+ // Leaves start - fakeplayer
+ if (waypoint instanceof org.leavesmc.leaves.bot.ServerBot bot && !bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.ENABLE_LOCATOR_BAR)) {
+ return;
+ }
+ // Leaves end - fakeplayer
+
this.waypoints.add(waypoint);
for (ServerPlayer toCreateFor : this.trackingPlayers) {
@@ -91,6 +97,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
public void addPlayer(ServerPlayer player) {
this.trackingPlayers.add(player);
+ // Leaves start - fakeplayer
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
+ return;
+ }
+ // Leaves end - fakeplayer
+
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
this.createConnection(player, waypointTransmitter);
}
@@ -1,14 +1,14 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 3 Aug 2025 14:26:37 +0800
Subject: [PATCH] Add configs to disable some check for operators
Subject: [PATCH] Add config to disable some check for operators
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 718a10b442997c8225a7de27ff92c1f36b809a9e..5757832ca91c42b417a95a2536a282a24eef0ef7 100644
index bd344bbebc867f89061694edc0feeab9d679c90c..6d0bb9868e2c2ef516de7630442808c04ba47b39 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -391,7 +391,7 @@ public class ServerGamePacketListenerImpl
@@ -392,7 +392,7 @@ public class ServerGamePacketListenerImpl
this.tickCount++;
this.knownMovePacketCount = this.receivedMovePacketCount;
if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) {
@@ -17,7 +17,7 @@ index 718a10b442997c8225a7de27ff92c1f36b809a9e..5757832ca91c42b417a95a2536a282a2
// LOGGER.warn("{} was kicked for floating too long!", this.player.getPlainTextName()); // Paper - Logging moved to net.minecraft.server.network.ServerCommonPacketListenerImpl.disconnect()
this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_PLAYER); // Paper - use configurable kick message & kick event cause
return true;
@@ -410,7 +410,7 @@ public class ServerGamePacketListenerImpl
@@ -411,7 +411,7 @@ public class ServerGamePacketListenerImpl
this.vehicleLastGoodY = this.lastVehicle.getY();
this.vehicleLastGoodZ = this.lastVehicle.getZ();
if (this.clientVehicleIsFloating && this.lastVehicle.getControllingPassenger() == this.player) {
@@ -26,7 +26,7 @@ index 718a10b442997c8225a7de27ff92c1f36b809a9e..5757832ca91c42b417a95a2536a282a2
// LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getPlainTextName()); // Paper - Logging moved to net.minecraft.server.network.ServerCommonPacketListenerImpl.disconnect()
this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_VEHICLE); // Paper - use configurable kick message & kick event cause
return true;
@@ -661,7 +661,7 @@ public class ServerGamePacketListenerImpl
@@ -662,7 +662,7 @@ public class ServerGamePacketListenerImpl
d5 = d2 - rootVehicle.getZ();
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean flag1 = false;
@@ -35,7 +35,7 @@ index 718a10b442997c8225a7de27ff92c1f36b809a9e..5757832ca91c42b417a95a2536a282a2
flag1 = true; // Paper - diff on change, this should be moved wrongly
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7));
}
@@ -1563,7 +1563,7 @@ public class ServerGamePacketListenerImpl
@@ -1564,7 +1564,7 @@ public class ServerGamePacketListenerImpl
// Paper start - Add fail move event
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
toX, toY, toZ, toYaw, toPitch, true);
@@ -44,7 +44,7 @@ index 718a10b442997c8225a7de27ff92c1f36b809a9e..5757832ca91c42b417a95a2536a282a2
if (event.getLogWarning()) {
LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), d3, d4, d5);
}
@@ -1642,7 +1642,7 @@ public class ServerGamePacketListenerImpl
@@ -1643,7 +1643,7 @@ public class ServerGamePacketListenerImpl
// Paper start - Add fail move event
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY,
toX, toY, toZ, toYaw, toPitch, true);
@@ -5,10 +5,10 @@ Subject: [PATCH] Add config to enable cross region damage trace
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 338ad851814e0d15ac2c20c9f13895f52557e130..93bc76af4d0abbe1c526434991e74c4465764ac6 100644
index 08e0dde0aa5ff94b72127abcd7da2a63d5701db8..e311502c156076842c5dcd91c70a94e414312100 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1447,6 +1447,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -1445,6 +1445,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
this.awardStat(Stats.ENTITY_KILLED_BY.get(killCredit.getType()));
killCredit.awardKillScore(this, damageSource);
this.createWitherRose(killCredit);
@@ -22,7 +22,7 @@ index 338ad851814e0d15ac2c20c9f13895f52557e130..93bc76af4d0abbe1c526434991e74c44
}
this.level().broadcastEntityEvent(this, EntityEvent.DEATH);
@@ -1470,6 +1477,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -1468,6 +1475,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
// Leaves end - exp fix
@@ -48,10 +48,10 @@ index 338ad851814e0d15ac2c20c9f13895f52557e130..93bc76af4d0abbe1c526434991e74c44
AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
this.level()
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 372fb8a7f06e87c5694b972e3b726e52903ef1e3..ad497cc5a01662e86d6c3d0a7d51b86cfc764f53 100644
index c67c29278ea63ab8b936d53bf654089f2dba2eef..88d532d11c29bd8cfe5b6b02847d960b07cf0a38 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1960,6 +1960,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -1975,6 +1975,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
final LivingEntity killer = this.getKillCredit();
if (killer != null) {
killer.awardKillScore(this, damageSource);
@@ -65,7 +65,7 @@ index 372fb8a7f06e87c5694b972e3b726e52903ef1e3..ad497cc5a01662e86d6c3d0a7d51b86c
}
}); // Paper end
this.postDeathDropItems(deathEvent); // Paper
@@ -1970,6 +1977,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -1985,6 +1992,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
return deathEvent; // Paper
}
@@ -84,7 +84,7 @@ index 372fb8a7f06e87c5694b972e3b726e52903ef1e3..ad497cc5a01662e86d6c3d0a7d51b86c
protected void dropEquipment(ServerLevel level) {
}
protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled
@@ -2597,6 +2616,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -2612,6 +2631,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
}
@@ -18,7 +18,7 @@ index bca2c5410aa543d2bb421e53893e9163508b3568..fdccb06c918d387e46c29ede7337159d
double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
if (rangeY != -1) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 532a628de2d5eda29b503274bc323d71a57cf1cb..0f213d1cfab6066f6713c5b2124b76409510454c 100644
index 040c0f8d26554ba583ecf370e2df3690d8f48dd6..22e68cabb4554c3e21e6f4ef316186f60ec77511 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -152,7 +152,7 @@ import org.jetbrains.annotations.Contract;
@@ -30,7 +30,7 @@ index 532a628de2d5eda29b503274bc323d71a57cf1cb..0f213d1cfab6066f6713c5b2124b7640
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
static boolean isLevelAtLeast(ValueInput input, int level) {
@@ -6536,4 +6536,46 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -6442,4 +6442,46 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
// Paper end - Expose entity id counter
public boolean shouldTickHot() { return this.tickCount > 20 * 10 && this.isAlive(); } // KioCG
@@ -92,7 +92,7 @@ index 0590d5723854a03a59fe9471710802f4e64e50b8..4f8ff2c024d6f2e67c5fa38fe2e660ba
public EntityType(
EntityType.EntityFactory<T> factory,
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 5b19dba8c978feafa5d3d956359188e2de2257a5..3a8ac0765b8a988c281a2ded8c40ec044781cda2 100644
index 67c0f4027a1e8b364694b5ed8b71965f2fe9b359..0f6e1428cc71c370af45db3e2a4f47c66e5d8628 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -189,6 +189,25 @@ public abstract class Player extends Avatar implements ContainerUser {
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable function command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index b4976ac6a609c24a43375d6c945e2412b00ab627..ddde2c3e7c8d9105202a7ff32e444f85485cdb7f 100644
index 9c0c3835776bda3bab08f0abaffc89a72bba0bfc..b95d144dd479d339cc4b0f92cc4c3bade9af4927 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -215,7 +215,9 @@ public class Commands {
@@ -0,0 +1,94 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Fri, 16 Jan 2026 02:34:43 +0800
Subject: [PATCH] Add config to enable save-all command
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
index 17108636a5728efe98a064001e00e3ca098b1f51..7748e9ac04fbd3f5259d92fc908d90889cba86c8 100644
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -350,6 +350,8 @@ public final class RegionizedServer {
// tick player ping sample
this.tickPlayerSample();
+ fun.bm.lophine.utils.SaveAllUtil.checkTimeout(); // Lophine - save-all fix
+
// tick worlds
for (final ServerLevel world : this.worlds) {
this.globalTick(world, tickCount);
diff --git a/io/papermc/paper/threadedregions/TickRegions.java b/io/papermc/paper/threadedregions/TickRegions.java
index 02c004bc7f9a0203523ce4b27cd766d6bdc478c0..3d86e21198f2888422330b6f7c27712f40a6aabc 100644
--- a/io/papermc/paper/threadedregions/TickRegions.java
+++ b/io/papermc/paper/threadedregions/TickRegions.java
@@ -190,6 +190,8 @@ public final class TickRegions implements ThreadedRegionizer.RegionCallbacks<Tic
public final ThreadedRegionizer.ThreadedRegion<TickRegionData, TickRegionSectionData> region;
public final ServerLevel world;
+ public long lastSavedTime = 0; // Lophine - save-all fix
+
// generic regionised data
private final Reference2ReferenceOpenHashMap<RegionizedData<?>, Object> regionizedData = new Reference2ReferenceOpenHashMap<>();
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 6cb0d4daf0f275b95d5ca09275e40ab32258eb39..48628174299f8f18170198df1834d6174bd1fafa 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -294,7 +294,11 @@ public class Commands {
PardonCommand.register(this.dispatcher);
PardonIpCommand.register(this.dispatcher);
//PerfCommand.register(this.dispatcher); // Folia - region threading - TODO later
- //SaveAllCommand.register(this.dispatcher); // Folia - region threading - TODO later
+ // Lophine start - Add a config to enable save all command
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.saveAll) {
+ SaveAllCommand.register(this.dispatcher); // Folia - region threading - TODO later
+ }
+ // Lophine end - Add a config to enable save all command
SaveOffCommand.register(this.dispatcher);
SaveOnCommand.register(this.dispatcher);
SetPlayerIdleTimeoutCommand.register(this.dispatcher);
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 4be383909addfebc0ca3b007d4ef2ec30ed445f7..fa60388376e9dafc5e1b71b6e78550bb6cd4009b 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1656,6 +1656,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia start - region threading
public void tickServer(long startTime, long scheduledEnd, long targetBuffer,
io.papermc.paper.threadedregions.TickRegions.TickRegionData region) {
+ // Lophine start - save-all fix
+ if (fun.bm.lophine.utils.SaveAllUtil.isSaving()) {
+ fun.bm.lophine.utils.SaveAllUtil.postRegionSave(region);
+ }
+ // Lophine end - save-all fix
final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle foliaProfiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); // Folia - profiler
// Folia end - region threading
org.spigotmc.WatchdogThread.tick(); // Spigot
diff --git a/net/minecraft/server/commands/SaveAllCommand.java b/net/minecraft/server/commands/SaveAllCommand.java
index e37b71402179c73146ff67babc228ccfb996f8f4..cfdb60bd89d83282f2202a5585bac22a6002e138 100644
--- a/net/minecraft/server/commands/SaveAllCommand.java
+++ b/net/minecraft/server/commands/SaveAllCommand.java
@@ -21,7 +21,9 @@ public class SaveAllCommand {
}
private static int saveAll(CommandSourceStack source, boolean flush) throws CommandSyntaxException {
- source.sendSuccess(() -> Component.translatable("commands.save.saving"), false);
+ // Lophine start - save all
+ fun.bm.lophine.utils.SaveAllUtil.preSaveAll(source, flush);
+/* source.sendSuccess(() -> Component.translatable("commands.save.saving"), false);
MinecraftServer server = source.getServer();
boolean flag = server.saveEverything(true, flush, true);
if (!flag) {
@@ -29,6 +31,12 @@ public class SaveAllCommand {
} else {
source.sendSuccess(() -> Component.translatable("commands.save.success"), true);
return 1;
- }
+ }*/
+ return 1;
}
+
+ public static SimpleCommandExceptionType getErrorFailed() {
+ return ERROR_FAILED;
+ }
+ // Lophine end - save all
}
@@ -5,11 +5,11 @@ Subject: [PATCH] Add config to enable scoreboard command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index c347830fbd8fccbf3b6b536beaba818fb7c6be32..8d77d6f165a5883e997122d1c2f231287e0caebe 100644
index 097e5b6a14fd148c93ca3894295a4a6b1e9aa870..bd8d98aa813b575e83138e79726bd3143268913c 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -241,7 +241,11 @@ public class Commands {
//RotateCommand.register(this.dispatcher); // Folia - region threading - TODO later // Luminol - Disable not correctly disabled unsupported commands
RotateCommand.register(this.dispatcher);
SayCommand.register(this.dispatcher);
//ScheduleCommand.register(this.dispatcher); // Folia - region threading
- //ScoreboardCommand.register(this.dispatcher, context); // Folia - region threading
@@ -1,11 +1,11 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Thu, 1 May 2025 22:43:08 +0800
Date: Wed, 21 Jan 2026 21:28:21 +0800
Subject: [PATCH] Add config to enable tick command
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
index b3bc39ebe8d2f48d976de9cd4fcae46095523514..6df2b8cf3b902bb157ed5fa9eaf9ca062d9e5a39 100644
index 7748e9ac04fbd3f5259d92fc908d90889cba86c8..80b5d151e0dd6fa44794187875b4b80519839d81 100644
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -332,6 +332,11 @@ public final class RegionizedServer {
@@ -20,7 +20,7 @@ index b3bc39ebe8d2f48d976de9cd4fcae46095523514..6df2b8cf3b902bb157ed5fa9eaf9ca06
// expire invalid click command callbacks
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
@@ -355,6 +360,13 @@ public final class RegionizedServer {
@@ -357,6 +362,13 @@ public final class RegionizedServer {
this.globalTick(world, tickCount);
}
@@ -34,20 +34,20 @@ index b3bc39ebe8d2f48d976de9cd4fcae46095523514..6df2b8cf3b902bb157ed5fa9eaf9ca06
// tick connections
this.tickConnections();
@@ -488,7 +500,7 @@ public final class RegionizedServer {
@@ -492,7 +504,7 @@ public final class RegionizedServer {
}
private void tickTime(final ServerLevel world, final int tickCount) {
private void tickTime(final ServerLevel world, final long tickCount) {
- if (world.tickTime) {
+ if ((!fun.bm.lophine.config.modules.experiment.CommandConfig.tick || world.tickRateManager().runsNormally()) && world.tickTime) { // Luminol - Add a config to enable tick command
if (world.getGameRules().get(GameRules.ADVANCE_TIME)) {
world.setDayTime(world.levelData.getDayTime() + (long)tickCount);
world.setDayTime(world.levelData.getDayTime() + tickCount);
}
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index e3df94bfd26b118cb8a7bdece75d611aaf27c606..bae2385b725c0ce551768f22945393ccf6e1e432 100644
index 0f4e8ee9438135e24e0f61a1390c59573b9e9c3f..65699d2ad66c686a8b7d1b951787ddd766800a94 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -39,8 +39,8 @@ public final class TickRegionScheduler {
@@ -40,8 +40,8 @@ public final class TickRegionScheduler {
}
}
@@ -58,14 +58,14 @@ index e3df94bfd26b118cb8a7bdece75d611aaf27c606..bae2385b725c0ce551768f22945393cc
// Folia start - watchdog
public static final FoliaWatchdogThread WATCHDOG_THREAD = new FoliaWatchdogThread();
static {
@@ -447,8 +447,23 @@ public final class TickRegionScheduler {
@@ -448,8 +448,23 @@ public final class TickRegionScheduler {
final long cpuStart = MEASURE_CPU_TIME ? THREAD_MX_BEAN.getCurrentThreadCpuTime() : 0L;
final long tickStart = System.nanoTime();
- // use max(), don't assume that tickStart >= scheduledStart
- final int tickCount = Math.max(1, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
- final long tickCount = Math.max(1L, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
+ // Luminol start - Add a config to enable tick command
+ final int tickCount;
+ final long tickCount;
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
+ if (MinecraftServer.tickRateManager.isSprinting() && MinecraftServer.tickRateManager.checkShouldSprintThisTick()) {
+ TICK_RATE = net.minecraft.server.commands.TickCommand.MAX_TICKRATE;
@@ -74,17 +74,17 @@ index e3df94bfd26b118cb8a7bdece75d611aaf27c606..bae2385b725c0ce551768f22945393cc
+ } else {
+ TICK_RATE = MinecraftServer.tickRateManager.tickrate();
+ TIME_BETWEEN_TICKS = (long) (1_000_000_000L / TICK_RATE);
+ tickCount = Math.max(1, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
+ tickCount = Math.max(1L, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
+ }
+ } else {
+ // use max(), don't assume that tickStart >= scheduledStart
+ tickCount = Math.max(1, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
+ tickCount = Math.max(1L, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
+ }
+ // Luminol end - Add tick command support
if (!this.tryMarkTicking()) {
if (!this.cancelled.get()) {
@@ -497,6 +512,11 @@ public final class TickRegionScheduler {
@@ -498,6 +513,11 @@ public final class TickRegionScheduler {
try {
// next start isn't updated until the end of this tick
this.tickRegion(tickCount, tickStart, scheduledEnd);
@@ -97,7 +97,7 @@ index e3df94bfd26b118cb8a7bdece75d611aaf27c606..bae2385b725c0ce551768f22945393cc
this.scheduler.regionFailed(this, false, thr);
// regionFailed will schedule a shutdown, so we should avoid letting this region tick further
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 8d77d6f165a5883e997122d1c2f231287e0caebe..23f047a47cdccc90a96153cc52bdedf91cd15db9 100644
index bd8d98aa813b575e83138e79726bd3143268913c..65f200381f486146ce8d893378f0591a82f6076b 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -262,7 +262,11 @@ public class Commands {
@@ -114,7 +114,7 @@ index 8d77d6f165a5883e997122d1c2f231287e0caebe..23f047a47cdccc90a96153cc52bdedf9
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 1627a8f3ebf7bf1a750a62802f1187ad8c1a6a0f..6e18d988104c121d9683eb57e77a78acfdee3907 100644
index 76098a4f44f8d82bebf832fa5e240adf25fbe80a..644c49730fc08a7c680f8114136db00ee8f81378 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -270,7 +270,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1,170 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 7 Jul 2025 18:19:00 +0800
Subject: [PATCH] Add config to enable waypoint command & bar
use concurrent method to fix it (now, they are thread safe)
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 23f047a47cdccc90a96153cc52bdedf91cd15db9..9cfcac44b4d51cb5f122d7e1fa980791a23dd38f 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -270,7 +270,11 @@ public class Commands {
TimeCommand.register(this.dispatcher);
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
- //WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ // Lophine start - unsafe waypoint bar
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) {
+ WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ }
+ // Lophine end - unsafe waypoint bar
WeatherCommand.register(this.dispatcher);
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 1cbc46e480cc7d109c6128f109a1a9e38bf1a193..d2c0b532200f274e44f18147d294dfe5d3f52d1d 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -16,22 +16,31 @@ import net.minecraft.world.waypoints.WaypointManager;
import net.minecraft.world.waypoints.WaypointTransmitter;
public class ServerWaypointManager implements WaypointManager<WaypointTransmitter> {
- private final Set<WaypointTransmitter> waypoints = new HashSet<>();
- private final Set<ServerPlayer> players = new HashSet<>();
- private final Table<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = HashBasedTable.create();
+ private final Set<WaypointTransmitter> waypoints = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final Set<ServerPlayer> players = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final fun.bm.lophine.utils.concurrent.AbstractConcurrentTable<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = fun.bm.lophine.config.modules.optimizations.WayPointOptimizedTableConfig.optimizedTable ? new fun.bm.lophine.utils.concurrent.OptimizedConcurrentTable<>() : new fun.bm.lophine.utils.concurrent.ConcurrentTable<>(); // Lophine - concurrent
@Override
public void trackWaypoint(WaypointTransmitter waypoint) {
- // Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.waypoints.add(waypoint);
+
+ for (ServerPlayer serverPlayer : this.players) {
+ this.createConnection(serverPlayer, waypoint);
+ }
+ // Lophine end - unsafe waypoint bar
}
@Override
public void updateWaypoint(WaypointTransmitter waypoint) {
if (this.waypoints.contains(waypoint)) {
- Map<ServerPlayer, WaypointTransmitter.Connection> map = Tables.transpose(this.connections).row(waypoint);
- SetView<ServerPlayer> set = Sets.difference(this.players, map.keySet());
+ java.util.List<Entry<ServerPlayer, WaypointTransmitter.Connection>> map = this.connections.getXZ(waypoint); // Lophine - concurrent
+ Set<ServerPlayer> players1 = new HashSet<>(); // Lophine - concurrent
+ map.forEach((entry) -> players1.add(entry.getKey())); // Lophine - concurrent
+ SetView<ServerPlayer> set = Sets.difference(this.players, players1); // Lophine - concurrent
- for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
+ for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
this.updateConnection(entry.getKey(), waypoint, entry.getValue());
}
@@ -43,26 +52,60 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void untrackWaypoint(WaypointTransmitter waypoint) {
- this.connections.column(waypoint).forEach((serverPlayer, connection) -> connection.disconnect());
- Tables.transpose(this.connections).row(waypoint).clear();
+ this.connections.getXZ(waypoint).forEach((entry) -> entry.getKey().disconnect()); // Lophine - concurrent
+ this.connections.clearXZ(waypoint); // Lophine - concurrent
this.waypoints.remove(waypoint);
}
public void addPlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.players.add(player);
+
+ for (WaypointTransmitter waypointTransmitter : this.waypoints) {
+ this.createConnection(player, waypointTransmitter);
+ }
+
+ if (player.isTransmittingWaypoint()) {
+ this.trackWaypoint((WaypointTransmitter)player);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void updatePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ java.util.List<Entry<WaypointTransmitter, WaypointTransmitter.Connection>> map = this.connections.getYZ(player); // Lophine - concurrent
+ Set<WaypointTransmitter> waypoints1 = new HashSet<>();// Lophine - concurrent
+ map.forEach((entry) -> waypoints1.add(entry.getKey()));// Lophine - concurrent
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, waypoints1); // Lophine - concurrent
+
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
+ this.updateConnection(player, entry.getKey(), entry.getValue());
+ }
+
+ for (WaypointTransmitter waypointTransmitter : set) {
+ this.createConnection(player, waypointTransmitter);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void removePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.connections.getYZ(player).forEach((entry) -> entry.getValue().disconnect()); // Lophine - concurrent
+ this.connections.clearYZ(player); // Lophine - concurrent
+ this.untrackWaypoint((WaypointTransmitter)player);
+ this.players.remove(player);
+ // Lophine end - unsafe waypoint bar
}
public void breakAllConnections() {
- this.connections.values().forEach(WaypointTransmitter.Connection::disconnect);
- this.connections.clear();
+ this.connections.getAllZ().forEach(WaypointTransmitter.Connection::disconnect); // Lophine - concurrent
+ this.connections.clearAll(); // Lophine - concurrent
}
public void remakeConnections(WaypointTransmitter waypoint) {
@@ -83,13 +126,18 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
if (player != waypoint) {
if (isLocatorBarEnabledFor(player)) {
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection -> {
- this.connections.put(player, waypoint, connection);
+ this.connections.putOrUpdate(player, waypoint, connection);
connection.connect();
}, () -> {
- WaypointTransmitter.Connection connection = this.connections.remove(player, waypoint);
- if (connection != null) {
- connection.disconnect();
- }
+ // Lophine start - concurrent
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint);
+ c.forEach((connection) -> {
+ this.connections.remove(player, waypoint, connection);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ });
+ // Lophine end - concurrent
});
}
}
@@ -103,10 +151,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
} else {
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection1 -> {
connection1.connect();
- this.connections.put(player, waypoint, connection1);
+ this.connections.putOrUpdate(player, waypoint, connection1);
}, () -> {
connection.disconnect();
- this.connections.remove(player, waypoint);
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint); // Lophine - concurrent
+ c.forEach((connection1) -> this.connections.remove(player, waypoint, connection1)); // Lophine - concurrent
});
}
}
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix datapack command save function
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 2f91e80f8c3df2775a793aa3c648c50007543192..6230c08d001aebc119900906aa557247261985a3 100644
index d7d22806c010636d8c1471f0fd1d64ef54e92c97..8d96d2130cf3383427047fee97fa49155164e337 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2430,6 +2430,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2431,6 +2431,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
@@ -8,10 +8,10 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc1
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
index 6df2b8cf3b902bb157ed5fa9eaf9ca062d9e5a39..057f60cd24876bf44cf3efccbc8a61b030a2a381 100644
index 80b5d151e0dd6fa44794187875b4b80519839d81..8d32d906f87a1cb866a7497ee0de3045e9adce2c 100644
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
@@ -367,6 +367,8 @@ public final class RegionizedServer {
@@ -369,6 +369,8 @@ public final class RegionizedServer {
}
// Luminol end - Add a config to enable tick command
@@ -129,7 +129,7 @@ index 0bf2173c5445de021090c2d0264afc440b005bdc..076bfe7a7b57f8d3b29ef0aaad117707
this.connection.setReadOnly();
// CraftBukkit - Don't wait
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index fe58993c3fb98189141120f849c7750ce1e24ebd..d9b20f3b099eea4862774da79929bb71f8dede53 100644
index d2b2bf35b3f5704f3bca94d0fe3b70206c2fbf96..c8be04b8a50bac52150a1fdd238634e630e1765e 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -315,6 +315,8 @@ public abstract class PlayerList {
@@ -8,10 +8,10 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a60c70550c217bccda6695dc7b7165de8e97e5cd..6aad6024dc121eae63b7fde92a89cbea29fcafb3 100644
index d0b5bc9da450d881534abdbd8ff786e1fbc7d418..674450fb2e600236f99550febf276d093308b8d8 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -3015,7 +3015,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3030,7 +3030,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Spigot start
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
// Paper start - Fix merchant inventory not closing on entity removal
@@ -21,7 +21,7 @@ index a60c70550c217bccda6695dc7b7165de8e97e5cd..6aad6024dc121eae63b7fde92a89cbea
}
// Paper end - Fix merchant inventory not closing on entity removal
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index a8a034ad5895ad1b833754c91c3a370c3b9b3744..35d51c727ce9229a174c45e9ea838fa9e3c3d80a 100644
index c8be04b8a50bac52150a1fdd238634e630e1765e..2eb551390aa66d89988df9ca23cf0dd7ccfd6577 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -555,7 +555,7 @@ public abstract class PlayerList {
@@ -41,10 +41,10 @@ index 1281eadb2d0c5b4ce5e3afd51d8205f9a4ef011c..b1446dfb4689174ee9f55cec6080622e
if (count > i) {
source.sendFailure(Component.translatable("commands.give.failed.toomanyitems", i, itemStack.getDisplayName()));
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 5757832ca91c42b417a95a2536a282a24eef0ef7..df6ad5831f83fcc869b0b4ed844a73b3cb81c469 100644
index 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1e48bf102 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3126,7 +3126,7 @@ public class ServerGamePacketListenerImpl
@@ -3162,7 +3162,7 @@ public class ServerGamePacketListenerImpl
} else if (slot.mayPlace(cursor)) {
if (ItemStack.isSameItemSameComponents(clickedItem, cursor)) {
int toPlace = packet.buttonNum() == 0 ? cursor.getCount() : 1;
@@ -53,7 +53,7 @@ index 5757832ca91c42b417a95a2536a282a24eef0ef7..df6ad5831f83fcc869b0b4ed844a73b3
toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount());
if (toPlace == 1) {
action = InventoryAction.PLACE_ONE;
@@ -3162,7 +3162,7 @@ public class ServerGamePacketListenerImpl
@@ -3198,7 +3198,7 @@ public class ServerGamePacketListenerImpl
}
} else if (ItemStack.isSameItemSameComponents(cursor, clickedItem)) {
if (clickedItem.getCount() >= 0) {
@@ -62,7 +62,7 @@ index 5757832ca91c42b417a95a2536a282a24eef0ef7..df6ad5831f83fcc869b0b4ed844a73b3
// As of 1.5, this is result slots only
action = InventoryAction.PICKUP_ALL;
}
@@ -3379,6 +3379,7 @@ public class ServerGamePacketListenerImpl
@@ -3415,6 +3415,7 @@ public class ServerGamePacketListenerImpl
this.player.containerMenu.broadcastFullState();
} else {
this.player.containerMenu.broadcastChanges();
@@ -70,7 +70,7 @@ index 5757832ca91c42b417a95a2536a282a24eef0ef7..df6ad5831f83fcc869b0b4ed844a73b3
}
if (packet.buttonNum() == Inventory.SLOT_OFFHAND && this.player.containerMenu != this.player.inventoryMenu) this.player.containerSynchronizer.sendOffHandSlotChange(); // Paper - update offhand data when the player is clicking in an inventory not their own as the sychronizer does not include offhand slots
if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
@@ -3490,7 +3491,7 @@ public class ServerGamePacketListenerImpl
@@ -3526,7 +3527,7 @@ public class ServerGamePacketListenerImpl
}
boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45;
@@ -79,7 +79,7 @@ index 5757832ca91c42b417a95a2536a282a24eef0ef7..df6ad5831f83fcc869b0b4ed844a73b3
if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot
// CraftBukkit start - Call click event
org.bukkit.inventory.InventoryView inventory = this.player.inventoryMenu.getBukkitView();
@@ -3532,6 +3533,7 @@ public class ServerGamePacketListenerImpl
@@ -3568,6 +3569,7 @@ public class ServerGamePacketListenerImpl
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemStack);
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemStack);
this.player.inventoryMenu.broadcastChanges();
@@ -229,7 +229,7 @@ index 44276ba9c7aab306cfd12833a834ebe61cf64e7c..0a19a7782f3fa27c41e02393e887ba7c
default @Nullable SlotAccess getChestVehicleSlot(final int slot) {
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6fd57a4574 100644
index 3b1c6a2397950ceb167890961b635cc69aaf8950..e7ea8849e8fa942709f505145e8076f064476206 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -292,6 +292,13 @@ public abstract class AbstractContainerMenu {
@@ -255,7 +255,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
int min1 = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemStack) + i2, min);
count -= min1 - i2;
// slot1.setByPlayer(itemStack.copyWithCount(min1));
@@ -560,7 +567,7 @@ public abstract class AbstractContainerMenu {
@@ -556,7 +563,7 @@ public abstract class AbstractContainerMenu {
slot.setByPlayer(carried2);
}
} else if (ItemStack.isSameItemSameComponents(carried, carried2)) {
@@ -264,7 +264,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
optional1.ifPresent(itemStack2 -> {
carried2.grow(itemStack2.getCount());
slot.onTake(player, itemStack2);
@@ -622,7 +629,7 @@ public abstract class AbstractContainerMenu {
@@ -618,7 +625,7 @@ public abstract class AbstractContainerMenu {
Slot slot2 = this.slots.get(slotIndex);
if (slot2.hasItem()) {
ItemStack itemStack = slot2.getItem();
@@ -273,7 +273,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
}
} else if (clickType == ClickType.THROW && this.getCarried().isEmpty() && slotIndex >= 0) {
Slot slot2 = this.slots.get(slotIndex);
@@ -657,15 +664,15 @@ public abstract class AbstractContainerMenu {
@@ -653,15 +660,15 @@ public abstract class AbstractContainerMenu {
int maxStackSize = button == 0 ? 1 : -1;
for (int i3 = 0; i3 < 2; i3++) {
@@ -292,7 +292,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
itemStack.grow(itemStack1.getCount());
}
}
@@ -767,7 +774,7 @@ public abstract class AbstractContainerMenu {
@@ -763,7 +770,7 @@ public abstract class AbstractContainerMenu {
i = endIndex - 1;
}
@@ -301,7 +301,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
while (!stack.isEmpty() && (reverseDirection ? i >= startIndex : i < endIndex)) {
Slot slot = this.slots.get(i);
ItemStack item = slot.getItem();
@@ -868,7 +875,7 @@ public abstract class AbstractContainerMenu {
@@ -864,7 +871,7 @@ public abstract class AbstractContainerMenu {
public static boolean canItemQuickReplace(@Nullable Slot slot, ItemStack stack, boolean stackSizeMatters) {
boolean flag = slot == null || !slot.hasItem();
return !flag && ItemStack.isSameItemSameComponents(stack, slot.getItem())
@@ -310,7 +310,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
: flag;
}
@@ -876,7 +883,7 @@ public abstract class AbstractContainerMenu {
@@ -872,7 +879,7 @@ public abstract class AbstractContainerMenu {
return switch (type) {
case 0 -> Mth.floor((float)stack.getCount() / slots.size());
case 1 -> 1;
@@ -319,7 +319,7 @@ index b090b74c845cd5944c9a8e35e899ad35063770d3..8e28afcfda7b180def81122e35920f6f
default -> stack.getCount();
};
}
@@ -899,7 +906,7 @@ public abstract class AbstractContainerMenu {
@@ -895,7 +902,7 @@ public abstract class AbstractContainerMenu {
for (int i = 0; i < container.getContainerSize(); i++) {
ItemStack item = container.getItem(i);
if (!item.isEmpty()) {
@@ -355,7 +355,7 @@ index 2b7e245ebf3bb471a1d817c4b5d56a64790cbab7..00083df87439ff0fa56e09853931b605
public @Nullable Identifier getNoItemIcon() {
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index 5489302acb5bceac2cae71079a8356b455cf1304..a688da5bc71799c13548d390ab58645da9128229 100644
index ed2971b54afbebe37a7ff61798778a4249b79856..73a97f4f62fe7d27b441cab58c6c86ffce011060 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -174,7 +174,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
@@ -420,7 +420,7 @@ index c5fe15844d405a27cdae18c903dd481c25b437de..3faa541c4a4623e915269d6241e1a3a3
org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack);
diff --git a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
index 8176c0b35852fb0753339a92f1bbae41a5ae6782..9ada04588b8d30d307a91d93c48e6dca25b8d484 100644
index 9346f2deca8eeca29fa2fced416247fd1609249e..07236e86638faa3231917af507cdd37ac49b1270 100644
--- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
@@ -412,7 +412,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/3e96b237749a960f2
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index c8fa9a9ae29ecf9b2d6234e7efee61d05c8bc0ae..336daa3f7e2c957286d245746af47603a98438bc 100644
index 08a1be57e1057a9e961eeb8f59cd12e98ed52bab..33ee967d8a7d4ce2c455db9ffaac740cd589e6f3 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -717,6 +717,7 @@ public class ServerExplosion implements Explosion {
@@ -24,7 +24,7 @@ index 80f17f33f670018240c854df589cf90cdeab6e70..c9af615ff9120c07787f436dbb6024e5
if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) {
serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier));
diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java
index 4deb9e4688a04b25e862572b70bf91e3ace9841e..2f31fae204dbeeeec1d188b6908aec5ad937e62e 100644
index dcdd6f230ff153a7b58d13b963d32376b1905b72..be9c78f2cf121e8ae55c1b18685877637a25ec80 100644
--- a/net/minecraft/world/entity/raid/Raid.java
+++ b/net/minecraft/world/entity/raid/Raid.java
@@ -264,7 +264,7 @@ public class Raid {
@@ -167,7 +167,7 @@ index 4deb9e4688a04b25e862572b70bf91e3ace9841e..2f31fae204dbeeeec1d188b6908aec5a
+ // Leaves end - old FindSpawnPosition
}
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
index 29ddff87216069cdf423f96f3fe835e0802d9ab5..18c4dffb5942b17dc1f2e9d9124c4f3ba3271360 100644
index 5e8245e6dd8a07ece83dd1bc20e351a92044e7a0..2b7894a4104d4cb237746a3e66d4368b6f8f3c1e 100644
--- a/net/minecraft/world/entity/raid/Raider.java
+++ b/net/minecraft/world/entity/raid/Raider.java
@@ -127,6 +127,43 @@ public abstract class Raider extends PatrollingMonster {
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/item/ShearsItem.java b/net/minecraft/world/item/ShearsItem.java
index 8cf3e51e12f9cf98836657e722edb23943f9e866..5fd5e9fd4e1a79dd1a9d62a5cf0c308805979420 100644
index 5e25ddf78f72ada318ef28d29199de9b93fe667e..6f48162a25e20d2439935e8995cede9497626322 100644
--- a/net/minecraft/world/item/ShearsItem.java
+++ b/net/minecraft/world/item/ShearsItem.java
@@ -80,7 +80,10 @@ public class ShearsItem extends Item {
@@ -23,10 +23,10 @@ index 95f6b1cbd5e33151c326689bc72292ff38a48803..e1974bbebbd10b75ea02955d9ced198d
+ // Leaves end - servux
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 6aad6024dc121eae63b7fde92a89cbea29fcafb3..90e363a310936830788aee6484a63a989aa316e7 100644
index 674450fb2e600236f99550febf276d093308b8d8..f5bf8ed6e741823078ab93eac47636fcebcdfe2d 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2442,6 +2442,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2463,6 +2463,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.server.updateEffectiveRespawnData();
}
// Paper end
@@ -5,7 +5,7 @@ Subject: [PATCH] LeavesHooks
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
index d9fd093f59987f471627ff20cfd2eb1a3c0dea64..78c6d425589b8147c33583ac46d6f21ec5dbefaa 100644
index faf62f8c9453f8e7a21b1e141577f1f4539cd2f9..4e5985a3e4969f466980558190324350019ba8af 100644
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
@@ -29,7 +29,7 @@ import net.minecraft.world.phys.AABB;
@@ -6,10 +6,10 @@ Subject: [PATCH] Compatibility fix for Raid Revert and Cross Region Damage
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index ad497cc5a01662e86d6c3d0a7d51b86cfc764f53..67faf1ed52806ee2c1e2ea080b39959ad9ae986d 100644
index 5bae274f5ee869e540e4330e03af671b6b18313d..3fe9afe55b4d61db8030479d3d74b8656679cb10 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1225,6 +1225,29 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -1240,6 +1240,29 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
}
@@ -40,7 +40,7 @@ index ad497cc5a01662e86d6c3d0a7d51b86cfc764f53..67faf1ed52806ee2c1e2ea080b39959a
if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) {
return !effectInstance.is(MobEffects.INFESTED);
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
index 18c4dffb5942b17dc1f2e9d9124c4f3ba3271360..2a2078ff7e353f5c94145ae700d58771a602a7ff 100644
index 2b7894a4104d4cb237746a3e66d4368b6f8f3c1e..03e3290367790add702a5856aaff3be465ea548c 100644
--- a/net/minecraft/world/entity/raid/Raider.java
+++ b/net/minecraft/world/entity/raid/Raider.java
@@ -157,7 +157,7 @@ public abstract class Raider extends PatrollingMonster {
@@ -8,7 +8,7 @@ As part of: Purpur (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18
Licensed under: MIT (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/LICENSE)
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 3c27af5cf768eaa0591846923590a88606dee589..66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85 100644
index 2eb551390aa66d89988df9ca23cf0dd7ccfd6577..0d322ef4aa3e70c9edbb9fa8ba368bef916eb93d 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -963,6 +963,10 @@ public abstract class PlayerList {
@@ -23,7 +23,7 @@ index 3c27af5cf768eaa0591846923590a88606dee589..66e7b62a57eada0f0db7d9cc1be6362a
// Paper start - whitelist verify event / login event
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 3a8ac0765b8a988c281a2ded8c40ec044781cda2..1e3ac806c3f111ee0a4f8ef47134941ec85fbe6e 100644
index 0f6e1428cc71c370af45db3e2a4f47c66e5d8628..83612969b6058f444fc17e360cdcc4a8846f79aa 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -179,6 +179,7 @@ public abstract class Player extends Avatar implements ContainerUser {
@@ -26,7 +26,7 @@ index 7b9e2a1a208b46a69c16e6afd8b502259893574f..c300ae4363298fecef8fa9bf739f01c0
loadFromJson(resourceAsStream, output);
} catch (JsonParseException | IOException var7) {
diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java
index 2d5309cc9386ef1bddf0bd4e153269fea1e75d60..ffa479f3743571bd198b1e3694301d73953a4eaa 100644
index f41c70c6cb2e9fae43bad9dd66546417fe38f14a..0bb0877011cac3184b881bfa35f10ef83d7341de 100644
--- a/net/minecraft/server/Main.java
+++ b/net/minecraft/server/Main.java
@@ -151,6 +151,8 @@ public class Main {
@@ -5,7 +5,7 @@ Subject: [PATCH] Old zombie reinforcement
diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java
index 1f359cf31dfd1a7bb9e1a9c8c830a4a4aa88ed00..206b725c9d1dffb185a466746544324135b14f23 100644
index 7a9394d4555464312418964f1a7a5d2581496774..3fcd32cc77458ce196ce0e468c1a99f456a57604 100644
--- a/net/minecraft/world/entity/monster/zombie/Zombie.java
+++ b/net/minecraft/world/entity/monster/zombie/Zombie.java
@@ -314,7 +314,7 @@ public class Zombie extends Monster {
@@ -5,14 +5,14 @@ Subject: [PATCH] Spawn invulnerable time
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 93bc76af4d0abbe1c526434991e74c4465764ac6..1a43bdcb4c37df4ad1c4dfe6ebb84470832e910f 100644
index ec34282744c71c881392ff278a8b03af90de8151..f0d45cb70af62b747c9235941f3fc45221b3b8e6 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -240,6 +240,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private int lastSentFood = -99999999;
private boolean lastFoodSaturationZero = true;
public int lastSentExp = -99999999;
+ private int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
+ protected int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
private ChatVisiblity chatVisibility = ChatVisiblity.FULL;
public ParticleStatus particleStatus = ParticleStatus.ALL;
private boolean canChatColor = true;
@@ -24,7 +24,7 @@ index 93bc76af4d0abbe1c526434991e74c4465764ac6..1a43bdcb4c37df4ad1c4dfe6ebb84470
if (this.invulnerableTime > 0) {
this.invulnerableTime--;
}
@@ -1538,6 +1540,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -1536,6 +1538,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
if (this.isInvulnerableTo(level, damageSource)) {
return false;
} else {
@@ -20,7 +20,7 @@ index 368a9d33f93c2e85abe4cf10df5a85c09fb03dd6..2b78040daeb3b09c08494cbc1d4a9d36
Set<CriterionTrigger.Listener<T>> set = (Set) advancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak
if (set != null && !set.isEmpty()) {
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index d5e604bf18a223aa434bc9f78274e1f3ff7f203d..715976a49ca621ba40d9353b18d41d342315afd4 100644
index 9f444988d40208a1bfa9f147ef80489f81115e93..ab2fa0b86564622501238305714fff014e2b2a56 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -74,7 +74,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -33,7 +33,7 @@ index d5e604bf18a223aa434bc9f78274e1f3ff7f203d..715976a49ca621ba40d9353b18d41d34
private boolean encrypted;
private final java.util.concurrent.atomic.AtomicBoolean disconnectionHandled = new java.util.concurrent.atomic.AtomicBoolean(false); // Folia - region threading - may be called concurrently during configuration stage
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4d2197da3 100644
index 8d96d2130cf3383427047fee97fa49155164e337..dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -425,6 +425,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -45,7 +45,7 @@ index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -1052,6 +1054,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1050,6 +1052,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia end - region threading
public void stopServer() {
@@ -57,7 +57,7 @@ index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4
// Folia start - region threading
// halt scheduler
// don't wait, we may be on a scheduler thread
@@ -1665,7 +1672,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1668,7 +1675,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
int i = this.pauseWhenEmptySeconds() * 20;
this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
if (false && i > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
@@ -66,7 +66,15 @@ index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4
this.emptyTicks++;
} else {
this.emptyTicks = 0;
@@ -2020,6 +2027,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1801,6 +1808,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.isSaving = true;
if (playerSaveInterval > 0) {
this.playerList.saveAll(playerSaveInterval);
+ org.leavesmc.leaves.bot.BotList.INSTANCE.saveAllResumeBots(playerSaveInterval); // Leaves - resident fakeplayer
}
for (final ServerLevel level : (region == null ? this.getAllLevels() : Arrays.asList(region.world))) { // Folia - region threading
if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
@@ -2021,6 +2029,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void tickConnection() {
this.getConnection().tick();
@@ -74,7 +82,7 @@ index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4
}
private void synchronizeTime(ServerLevel level) {
@@ -3154,6 +3162,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -3155,6 +3164,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.debugSubscribers;
}
@@ -92,7 +100,7 @@ index e4d1f0ae66aec2cdc625e3140da094c99350687d..324f9d259c6e0fd814ffe11b62d07fb4
@Override
public void close() {
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index 98004b77b7f40d315f41be82105df6d4b9000e33..cd968c6971d054d9b66a0eaea4753a51ebb75b02 100644
index c9e6cd428e4c74b14a992de9ffe85a5a707c4e6b..5bf7eb5b906cefa57a1dfcf828ff7872dbceac4f 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -166,6 +166,11 @@ public class PlayerAdvancements {
@@ -125,7 +133,7 @@ index d92492bca62fb02fe7a9e4e220cb8651c4d1e16d..7d0752f9a6a90cf389a9cb5829ab0260
return banIp(source, playerByName.getIpAddress(), reason);
} else {
diff --git a/net/minecraft/server/commands/BanPlayerCommands.java b/net/minecraft/server/commands/BanPlayerCommands.java
index 978ef2e996a78e907f959bfe99d47de832427011..b34a077449f1c7bb8a0b5c94fdeb072dcc103797 100644
index 978ef2e996a78e907f959bfe99d47de832427011..9644e68ba2c295fcda2d4a3034277520ade91bca 100644
--- a/net/minecraft/server/commands/BanPlayerCommands.java
+++ b/net/minecraft/server/commands/BanPlayerCommands.java
@@ -45,7 +45,15 @@ public class BanPlayerCommands {
@@ -135,7 +143,7 @@ index 978ef2e996a78e907f959bfe99d47de832427011..b34a077449f1c7bb8a0b5c94fdeb072d
+ boolean hasBot = false; // Leaves - disable kick
for (NameAndId nameAndId : players) {
+ // Leaves start - disable ban
+ if (org.leavesmc.leaves.bot.BotList.INSTANCE.getBot(nameAndId.id()) != null) {
+ if (nameAndId.isBot()) {
+ source.sendFailure(Component.literal("Permission denied"));
+ hasBot = true;
+ continue;
@@ -197,19 +205,19 @@ index 5f78d629bab84acb2fa43a9fcc1cec52b4e91ae3..1c6059bd23afdd555d2dc8c533a5b027
return i;
}
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
index 07332a86e6e0441bafacb020f27ec36a277f1486..3798c1ea92ead20b3b7e67d88d2405f29e324d2c 100644
index 07332a86e6e0441bafacb020f27ec36a277f1486..590891d70a71ab30ceae3a3cf1923745e7c20b26 100644
--- a/net/minecraft/server/commands/OpCommand.java
+++ b/net/minecraft/server/commands/OpCommand.java
@@ -43,6 +43,7 @@ public class OpCommand {
int i = 0;
for (NameAndId nameAndId : players) {
+ if (org.leavesmc.leaves.bot.BotList.INSTANCE.getBot(nameAndId.id()) != null) continue; // Leaves - disable op
+ if (nameAndId.isBot()) continue; // Leaves - disable op
if (!playerList.isOp(nameAndId)) {
playerList.op(nameAndId);
i++;
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 5fbcb5eac1d5449247da77b6120edbe2f2dd1d58..00285e2e68b00d627e91f292da46e9058255ea52 100644
index 5fbcb5eac1d5449247da77b6120edbe2f2dd1d58..69cc67b7618d38e65ccb1f4c58013ef116886ad2 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -252,6 +252,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -224,7 +232,7 @@ index 5fbcb5eac1d5449247da77b6120edbe2f2dd1d58..00285e2e68b00d627e91f292da46e905
// Paper end - initialize global and world-defaults configuration
me.earthme.luminol.config.ConfigManager.loadConfigFiles(); // Luminol - load config file
fun.bm.lophine.utils.ServerI18nUtil.init(); // Lophine - I18n support
+ this.getBotList().loadBotInfo(); // Leaves - load resident bot info
+ this.getBotList().loadResumeBotInfo(); // Leaves - load resident bot info
if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
if (this.convertOldUsers()) {
@@ -247,7 +255,7 @@ index fdccb06c918d387e46c29ede7337159db8c5d9ca..4cd5c5659ac098c3b7ff95efb94b1987
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 90e363a310936830788aee6484a63a989aa316e7..c09af6b81987df8e681689ecce9932a2e39af1a3 100644
index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc851f34230b 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -220,6 +220,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -266,7 +274,7 @@ index 90e363a310936830788aee6484a63a989aa316e7..c09af6b81987df8e681689ecce9932a2
}
// Folia start - region threading
@@ -2469,6 +2471,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2490,6 +2492,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return this.players;
}
@@ -279,7 +287,7 @@ index 90e363a310936830788aee6484a63a989aa316e7..c09af6b81987df8e681689ecce9932a2
@Override
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldState);
@@ -2942,6 +2950,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2963,6 +2971,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
@@ -291,7 +299,7 @@ index 90e363a310936830788aee6484a63a989aa316e7..c09af6b81987df8e681689ecce9932a2
if (serverPlayer.isReceivingWaypoints()) {
ServerLevel.this.getWaypointManager().addPlayer(serverPlayer);
}
@@ -3029,6 +3042,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3044,6 +3057,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
@@ -304,7 +312,7 @@ index 90e363a310936830788aee6484a63a989aa316e7..c09af6b81987df8e681689ecce9932a2
ServerLevel.this.updateSleepingPlayerList();
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 1a43bdcb4c37df4ad1c4dfe6ebb84470832e910f..daff493e4ea944afa91ce99a3596931a487304a3 100644
index f0d45cb70af62b747c9235941f3fc45221b3b8e6..132645758827ee4d332909e701dde24e69699beb 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -227,7 +227,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -316,15 +324,6 @@ index 1a43bdcb4c37df4ad1c4dfe6ebb84470832e910f..daff493e4ea944afa91ce99a3596931a
private final PlayerAdvancements advancements;
private final ServerStatsCounter stats;
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
@@ -240,7 +240,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private int lastSentFood = -99999999;
private boolean lastFoodSaturationZero = true;
public int lastSentExp = -99999999;
- private int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
+ public int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
private ChatVisiblity chatVisibility = ChatVisiblity.FULL;
public ParticleStatus particleStatus = ParticleStatus.ALL;
private boolean canChatColor = true;
@@ -248,7 +248,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private @Nullable Entity camera;
public boolean isChangingDimension;
@@ -334,26 +333,16 @@ index 1a43bdcb4c37df4ad1c4dfe6ebb84470832e910f..daff493e4ea944afa91ce99a3596931a
private @Nullable Vec3 levitationStartPos;
private int levitationStartTime;
private boolean disconnected;
@@ -440,6 +440,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// Paper start - rewrite chunk system
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
+ // Leaves start - player operation limiter
+ private int instaBreakCountPerTick = 0;
+ private int placeBlockCountPerTick = 0;
+ // Leaves end - player operation limiter
@@ -1495,7 +1495,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
// Luminol End - Cross Region Damage trace
@Override
public final boolean moonrise$isRealPlayer() {
@@ -897,6 +901,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
// CraftBukkit end
this.connection.tickClientLoadTimeout();
+ this.resetOperationCountPerTick(); // Leaves - player operation limiter
this.gameMode.tick();
this.wardenSpawnTracker.tick();
if (this.spawnInvulnerableTime > 0) --this.spawnInvulnerableTime; // Lophine - spawn invulnerable time
@@ -2134,6 +2139,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
- private void tellNeutralMobsThatIDied() {
+ protected void tellNeutralMobsThatIDied() { // Leaves private -> protected
AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
this.level()
.getEntitiesOfClass(Mob.class, aabb, EntitySelector.NO_SPECTATORS)
@@ -2132,6 +2132,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
this.lastSentHealth = -1.0F;
this.lastSentFood = -1;
this.teleportSpectators(teleportTransition, serverLevel);
@@ -365,40 +354,73 @@ index 1a43bdcb4c37df4ad1c4dfe6ebb84470832e910f..daff493e4ea944afa91ce99a3596931a
// CraftBukkit start
org.bukkit.event.player.PlayerChangedWorldEvent changeEvent = new org.bukkit.event.player.PlayerChangedWorldEvent(this.getBukkitEntity(), serverLevel.getWorld());
this.level().getCraftServer().getPluginManager().callEvent(changeEvent);
@@ -3728,4 +3738,31 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return (org.bukkit.craftbukkit.entity.CraftPlayer) super.getBukkitEntity();
diff --git a/net/minecraft/server/players/CachedUserNameToIdResolver.java b/net/minecraft/server/players/CachedUserNameToIdResolver.java
index 345bdb0ff82b8cd8c63e5ff1f2dabb68a1e54b77..c9a7c1c37155e0d339b880179a5988c9a6cb3797 100644
--- a/net/minecraft/server/players/CachedUserNameToIdResolver.java
+++ b/net/minecraft/server/players/CachedUserNameToIdResolver.java
@@ -122,6 +122,12 @@ public class CachedUserNameToIdResolver implements UserNameToIdResolver {
@Override
public Optional<NameAndId> get(String name) {
+ // Leaves start - fix bot
+ org.leavesmc.leaves.bot.ServerBot bot = org.leavesmc.leaves.bot.BotList.INSTANCE.getBotByName(name);
+ if (bot != null) {
+ return Optional.of(bot.nameAndId());
+ }
+ // Leaves end - fix bot
String string = name.toLowerCase(Locale.ROOT);
boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
CachedUserNameToIdResolver.GameProfileInfo gameProfileInfo = this.profilesByName.get(string);
diff --git a/net/minecraft/server/players/NameAndId.java b/net/minecraft/server/players/NameAndId.java
index 05b4a75f6597e8613c91344d100bb7f005d169a7..9a5006362c12e972d031c48be4d30362d88d1c00 100644
--- a/net/minecraft/server/players/NameAndId.java
+++ b/net/minecraft/server/players/NameAndId.java
@@ -8,18 +8,22 @@ import java.util.UUID;
import net.minecraft.core.UUIDUtil;
import org.jspecify.annotations.Nullable;
-public record NameAndId(UUID id, String name) {
+public record NameAndId(UUID id, String name, boolean isBot) { // Leaves - fakeplayer
public static final Codec<NameAndId> CODEC = RecordCodecBuilder.create(
instance -> instance.group(UUIDUtil.STRING_CODEC.fieldOf("id").forGetter(NameAndId::id), Codec.STRING.fieldOf("name").forGetter(NameAndId::name))
.apply(instance, NameAndId::new)
);
public NameAndId(GameProfile profile) {
- this(profile.id(), profile.name());
+ this(profile.id(), profile.name(), com.google.common.collect.Iterables.getFirst(profile.properties().get("is_bot"), "false").equals("true")); // Leaves - fakeplayer
}
// CraftBukkit end
+
+ // Leaves start - player operation limiter
+ protected void resetOperationCountPerTick() {
+ instaBreakCountPerTick = 0;
+ placeBlockCountPerTick = 0;
public NameAndId(com.mojang.authlib.yggdrasil.response.NameAndId nameAndId) {
- this(nameAndId.id(), nameAndId.name());
+ this(nameAndId.id(), nameAndId.name(), false); // Leaves - fakeplayer
+ }
+
+ public int getInstaBreakCountPerTick() {
+ return instaBreakCountPerTick;
+ }
+
+ public int getPlaceBlockCountPerTick() {
+ return placeBlockCountPerTick;
+ }
+
+ public void addInstaBreakCountPerTick() {
+ ++instaBreakCountPerTick;
+ }
+
+ public void addPlaceBlockCountPerTick() {
+ ++placeBlockCountPerTick;
+ }
+
+ public boolean allowOperation() {
+ return (instaBreakCountPerTick == 0 || placeBlockCountPerTick == 0) && (instaBreakCountPerTick <= 1 && placeBlockCountPerTick <= 2);
+ }
+ // Leaves end - player operation limiter
}
+ public NameAndId(UUID uuid, String name) {
+ this(uuid, name, false); // Leaves - fakeplayer
}
public static @Nullable NameAndId fromJson(JsonObject json) {
@@ -33,7 +37,7 @@ public record NameAndId(UUID id, String name) {
return null;
}
- return new NameAndId(uuid, json.get("name").getAsString());
+ return new NameAndId(uuid, json.get("name").getAsString(), false); // Leaves - fakeplayer
} else {
return null;
}
@@ -46,7 +50,7 @@ public record NameAndId(UUID id, String name) {
public static NameAndId createOffline(String name) {
UUID uuid = UUIDUtil.createOfflinePlayerUUID(name);
- return new NameAndId(uuid, name);
+ return new NameAndId(uuid, name, false); // Leaves - fakeplayer
}
// Paper start - utility method for common conversion back to the game profile
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3c96739a6 100644
index 0d322ef4aa3e70c9edbb9fa8ba368bef916eb93d..3a5319739f0687fc6f09b2ac8ee31716593fe0de 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -317,6 +317,19 @@ public abstract class PlayerList {
@@ -409,7 +431,7 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
+ if (bot != null) {
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false, true);
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false, false);
+ }
+ this.server.getBotList().bots.forEach(bot1 -> {
+ bot1.sendPlayerInfo(player);
@@ -421,7 +443,7 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
@@ -791,6 +804,13 @@ public abstract class PlayerList {
@@ -791,6 +804,12 @@ public abstract class PlayerList {
).callEvent();
// Paper end
@@ -430,12 +452,11 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
+ this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(serverPlayer, true)); // Leaves - render bot
+ }
+ // Leaves end - bot support
+
+
return serverPlayer;
}
@@ -892,11 +912,16 @@ public abstract class PlayerList {
@@ -892,11 +911,16 @@ public abstract class PlayerList {
public String[] getPlayerNamesArray() {
List<ServerPlayer> players = new java.util.ArrayList<>(this.players); // Folia - region threading
@@ -453,7 +474,7 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
return strings;
}
@@ -1016,7 +1041,14 @@ public abstract class PlayerList {
@@ -1016,7 +1040,14 @@ public abstract class PlayerList {
}
public @Nullable ServerPlayer getPlayerByName(String username) {
@@ -469,7 +490,7 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
}
public void broadcast(@Nullable Player except, double x, double y, double z, double radius, ResourceKey<Level> dimension, Packet<?> packet) {
@@ -1353,7 +1385,13 @@ public abstract class PlayerList {
@@ -1353,7 +1384,13 @@ public abstract class PlayerList {
}
public @Nullable ServerPlayer getPlayer(UUID playerUUID) {
@@ -484,39 +505,11 @@ index 66e7b62a57eada0f0db7d9cc1be6362ac1e3ee85..95e7d3245bb38bf33a401648143522d3
}
public @Nullable ServerPlayer getPlayer(String name) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index d2c0b532200f274e44f18147d294dfe5d3f52d1d..55133071b97400dfb3ed6615c30006003de492c5 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -24,6 +24,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
public void trackWaypoint(WaypointTransmitter waypoint) {
// Lophine start - unsafe waypoint bar
if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ // Leaves start - fakeplayer
+ if (waypoint instanceof org.leavesmc.leaves.bot.ServerBot bot && !bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.ENABLE_LOCATOR_BAR)) {
+ return;
+ }
+ // Leaves end - fakeplayer
this.waypoints.add(waypoint);
for (ServerPlayer serverPlayer : this.players) {
@@ -61,6 +66,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
// Folia - region threading
// Lophine start - unsafe waypoint bar
if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ // Leaves start - fakeplayer
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
+ return;
+ }
+ // Leaves end - fakeplayer
this.players.add(player);
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 0f213d1cfab6066f6713c5b2124b76409510454c..5bcf646ef600a2aae7633453d3331fde2f141aff 100644
index 32299faedd89a848ca00288672ce85cad707dca0..3e44a61bd982dc822437f647a02a95affd988cdb 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1252,7 +1252,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -1235,7 +1235,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
BlockPos onPosLegacy = this.getOnPosLegacy();
BlockState blockState = this.level().getBlockState(onPosLegacy);
@@ -525,7 +518,7 @@ index 0f213d1cfab6066f6713c5b2124b76409510454c..5bcf646ef600a2aae7633453d3331fde
this.checkFallDamage(vec3.y, this.onGround(), blockState, onPosLegacy);
}
@@ -1577,7 +1577,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -1570,7 +1570,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
}
// Paper start - optimise collisions
@@ -535,10 +528,10 @@ index 0f213d1cfab6066f6713c5b2124b76409510454c..5bcf646ef600a2aae7633453d3331fde
final boolean yZero = movement.y == 0.0;
final boolean zZero = movement.z == 0.0;
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 67faf1ed52806ee2c1e2ea080b39959ad9ae986d..f80f5533c1c3c64ee1b8b2c43e2bc2dea8ab567f 100644
index 3fe9afe55b4d61db8030479d3d74b8656679cb10..c0ebd700caa950948c58846eaf164afc14f87f09 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3304,7 +3304,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -3319,7 +3319,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
private void travelRidden(Player player, Vec3 travelVector) {
Vec3 riddenInput = this.getRiddenInput(player, travelVector);
this.tickRidden(player, riddenInput);
@@ -547,16 +540,16 @@ index 67faf1ed52806ee2c1e2ea080b39959ad9ae986d..f80f5533c1c3c64ee1b8b2c43e2bc2de
this.setSpeed(this.getRiddenSpeed(player));
this.travel(riddenInput);
} else {
@@ -4213,7 +4213,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -4228,7 +4228,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
// Paper start - lag compensate eating
// we add 1 to the expected time to avoid lag compensating when we should not
final boolean shouldLagCompensate = this.useItem.has(DataComponents.FOOD) && this.eatStartTime != -1 && (System.nanoTime() - this.eatStartTime) > ((1L + this.totalEatTimeTicks) * 50L * (1000L * 1000L));
- if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide() && !usingItem.useOnRelease()) {
+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !(this instanceof org.leavesmc.leaves.bot.ServerBot) && !this.level().isClientSide() && !usingItem.useOnRelease()) { // Leaves - Fakeplayer skip this check
+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !(this instanceof org.leavesmc.leaves.bot.ServerBot) && !this.level().isClientSide() && !usingItem.useOnRelease()) {
this.useItemRemaining = 0;
// Paper end - lag compensate eating
this.completeUsingItem();
@@ -4397,6 +4397,23 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -4413,6 +4413,23 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.stopUsingItem();
}
@@ -581,7 +574,7 @@ index 67faf1ed52806ee2c1e2ea080b39959ad9ae986d..f80f5533c1c3c64ee1b8b2c43e2bc2de
if (!this.level().isClientSide()) {
boolean isUsingItem = this.isUsingItem();
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 1e3ac806c3f111ee0a4f8ef47134941ec85fbe6e..52c41d1e526dc0d42dde3773ab20ee7252737e5d 100644
index 83612969b6058f444fc17e360cdcc4a8846f79aa..29ca31b05ec5636243b13fee8b3399d5e21808b4 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -168,7 +168,7 @@ public abstract class Player extends Avatar implements ContainerUser {
@@ -668,7 +661,7 @@ index fc1b3501114108d0166256c514a225337955e461..56bc87fefc82c77077bfed50accbde77
return switch (this.getStatus()) {
case IN_WATER, UNDER_WATER, UNDER_FLOWING_WATER -> SoundEvents.BOAT_PADDLE_WATER;
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index 8e28afcfda7b180def81122e35920f6fd57a4574..f42697374461ab2e1c610b8c3250d3c03c0821b4 100644
index e7ea8849e8fa942709f505145e8076f064476206..4935234fba5977246aa303bf8162404813b5491e 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -404,6 +404,7 @@ public abstract class AbstractContainerMenu {
@@ -679,7 +672,7 @@ index 8e28afcfda7b180def81122e35920f6fd57a4574..f42697374461ab2e1c610b8c3250d3c0
if (clickType == ClickType.QUICK_CRAFT) {
int i = this.quickcraftStatus;
this.quickcraftStatus = getQuickcraftHeader(button);
@@ -682,6 +683,22 @@ public abstract class AbstractContainerMenu {
@@ -678,6 +679,22 @@ public abstract class AbstractContainerMenu {
}
}
@@ -703,7 +696,7 @@ index 8e28afcfda7b180def81122e35920f6fd57a4574..f42697374461ab2e1c610b8c3250d3c0
FeatureFlagSet featureFlagSet = player.level().enabledFeatures();
return carriedItem.isItemEnabled(featureFlagSet) && carriedItem.overrideStackedOnOther(slot, action, player)
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index a688da5bc71799c13548d390ab58645da9128229..e63cca6f7f63a2b5f249ae01033167186e710fdb 100644
index 73a97f4f62fe7d27b441cab58c6c86ffce011060..ea6285b7f53f391c84519aa7ae39164cf5740b81 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -456,7 +456,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
@@ -737,7 +730,7 @@ index a688da5bc71799c13548d390ab58645da9128229..e63cca6f7f63a2b5f249ae0103316718
return this.getItem().useOnRelease(this);
}
diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
index adc8c8a7b8a02cf395e919eb51aefc47ff6ed9ab..225fedb32b100a0f24e001e442c0bae44acb0284 100644
index 557ea70254a8d6dd87c656098c5632bdcbe3df8f..1d60054146f970ec24054d2c76a27e2d8620ec67 100644
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -145,7 +145,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
@@ -778,16 +771,3 @@ index bef794c3f58c41d910aa0bcc63fbdeea7225fddf..a601da588e6973cc5b87d3e3eeba49b5
this.id = id;
}
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
index b8ef50bc3d07890c9da2c98d5f009a3adc52f4b0..f37de9384005f53631cc3e122c74e4793dff4be0 100644
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -19,7 +19,7 @@ import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.player.Player;
import org.slf4j.Logger;
-public class PlayerDataStorage {
+public class PlayerDataStorage implements org.leavesmc.leaves.bot.IPlayerDataStorage {
private static final Logger LOGGER = LogUtils.getLogger();
private final File playerDir;
protected final DataFixer fixerUpper;
@@ -9,7 +9,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index df6ad5831f83fcc869b0b4ed844a73b3cb81c469..5c81be29f7ecc3389b5b578f85774035def96e26 100644
index 1a749d9bee139975fa249839df93c3a1e48bf102..d81e22fe65cded98c201a7bbae8a51b22c6a20b4 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -338,9 +338,12 @@ public class ServerGamePacketListenerImpl
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 173a55715b9c459eeb93cdc065c69e64fc704b43..b2694cb60505766877398fa526b9bfe60e6b6404 100644
index 435acf0fe5e80ca371d1145befc2cfaa917a89e3..77577d0df9b14a7ad55b2866c2ac31863226458b 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -758,6 +758,11 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -1,6 +1,6 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:33:19 +0800
From: Bacteriawa <A3167717663@hotmail.com>
Date: Fri, 13 Feb 2026 16:17:36 +0800
Subject: [PATCH] Leaves: Jade Protocol
This patch is Powered by Jade(https://github.com/Snownee/Jade)
@@ -9,10 +9,10 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
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 daff493e4ea944afa91ce99a3596931a487304a3..f79735abf21b6eecc02e5b5f1ce3140dfef3ede9 100644
index 132645758827ee4d332909e701dde24e69699beb..3efce8ae3cba85019aea7453f97a72939baef7f6 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1458,7 +1458,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -1451,7 +1451,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
} else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
final LivingEntity entitylivingnew = this.getKillCreditOrigin();
if (entitylivingnew != null) {
@@ -22,18 +22,18 @@ index daff493e4ea944afa91ce99a3596931a487304a3..f79735abf21b6eecc02e5b5f1ce3140d
// Luminol End - Cross Region Damage trace
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 5c81be29f7ecc3389b5b578f85774035def96e26..98d641b2e8ca340cb10fe0448f8a8b4f8446f616 100644
index d81e22fe65cded98c201a7bbae8a51b22c6a20b4..43e9fd5684d15f8643524132e60266071c6dc454 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3798,7 +3798,7 @@ public class ServerGamePacketListenerImpl
} else if (this.clientLoadedTimeoutTimer == 1) markClientLoaded(); // Paper - Add PlayerLoadedWorldEvent - decrement by calling #markClientLoaded which will set the value to 0 *and* call the event.
@@ -3835,7 +3835,7 @@ public class ServerGamePacketListenerImpl
}
@Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add PlayerLoadedWorldEvent
- private void markClientLoaded() {
+ public void markClientLoaded() { // Lophine - private -> public
// Paper start - Add PlayerLoadedWorldEvent
if (!hasClientLoaded()) {
final io.papermc.paper.event.player.PlayerClientLoadedWorldEvent event = new io.papermc.paper.event.player.PlayerClientLoadedWorldEvent(this.player.getBukkitEntity(), true);
this.markClientLoaded(false);
}
diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
index 901e4e9897e76cd4158d7f8bb8ec829df8ff8196..59b9373c7a14e42d9e5f691354a7616d16856215 100644
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
@@ -8,13 +8,13 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 95e7d3245bb38bf33a401648143522d3c96739a6..98f2ced8e6afdf0074ade94e0bbf5498bd6c505f 100644
index 3a5319739f0687fc6f09b2ac8ee31716593fe0de..36cd1a0307ff1a734eb5f4f3cc013781692e7ad8 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1127,6 +1127,7 @@ public abstract class PlayerList {
player.connection.send(new ClientboundSetChunkCacheRadiusPacket(level.spigotConfig.viewDistance));
player.connection.send(new ClientboundSetSimulationDistancePacket(level.spigotConfig.simulationDistance));
// Paper end
@@ -1126,6 +1126,7 @@ public abstract class PlayerList {
//player.connection.send(new ClientboundSetChunkCacheRadiusPacket(io.papermc.paper.FeatureHooks.getViewDistance(level))); // Paper - rewrite chunk system
//player.connection.send(new ClientboundSetSimulationDistancePacket(io.papermc.paper.FeatureHooks.getSimulationDistance(level))); // Paper - rewrite chunk system
// Paper end - view distances
+ org.leavesmc.leaves.protocol.XaeroMapProtocol.onSendWorldInfo(player); // Leaves - xaero map protocol
if (level.isRaining()) {
// CraftBukkit start - handle player weather
@@ -10,7 +10,7 @@ Licensed under: MIT
This patch is Powered by fabric-carpet(https://github.com/gnembon/fabric-carpet)
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 6a51bace3151646afae9de820d9c8977a0221df4..8dd50507bbc9f61c9ae019eecc5e9338d20addff 100644
index 6a51bace3151646afae9de820d9c8977a0221df4..474ca0ce0d3095fc586f8f9e9a584ed4f7c91471 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -229,8 +229,30 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -72,7 +72,7 @@ index 6a51bace3151646afae9de820d9c8977a0221df4..8dd50507bbc9f61c9ae019eecc5e9338
+ for (int i = 0; i < container.getContainerSize(); ++i) {
+ if (!container.getItem(i).isEmpty()) {
+ ItemStack itemstack = container.getItem(i);
+ org.leavesmc.leaves.util.HopperCounter.getCounter(woolColor).add(level, itemstack);
+ org.leavesmc.leaves.util.HopperCounter.getCounter(woolColor).add(level.getServer(), itemstack);
+ container.setItem(i, ItemStack.EMPTY);
+ }
+ }
@@ -13,10 +13,10 @@ MasaGadget(https://github.com/plusls/MasaGadget)
litematica(https://github.com/maruohon/litematica)
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 98d641b2e8ca340cb10fe0448f8a8b4f8446f616..878f5c5801b68e3a6d01d63caf44737841387600 100644
index 43e9fd5684d15f8643524132e60266071c6dc454..62db0ba9c824959f953d452ab7aafcde01b10bcd 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2128,7 +2128,7 @@ public class ServerGamePacketListenerImpl
@@ -2134,7 +2134,7 @@ public class ServerGamePacketListenerImpl
if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.player.level(), blockPos.getX() >> 4, blockPos.getZ() >> 4, 8) && this.player.isWithinBlockInteractionRange(blockPos, 1.0)) { // Folia - do not allow players to interact with blocks outside the current region
Vec3 vec3 = location.subtract(Vec3.atCenterOf(blockPos));
double d = 1.0000001;
@@ -60,7 +60,7 @@ index d75c95d728efc0b4064f562ca80b68b23883feaf..be685383b8e7c8111e976fc560f16d43
blockState = blockState1;
break;
diff --git a/net/minecraft/world/level/block/Block.java b/net/minecraft/world/level/block/Block.java
index 13f06f1fbdf77b88493333b481b951d729579f11..e0ab4f7372ed11c9d17b341c8a24a82350c714fa 100644
index 492b1615d96133c5082e0f16015f114299f8082f..fb47a2acb0e4232359f5da061c1620a7b1d6c476 100644
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -489,6 +489,32 @@ public class Block extends BlockBehaviour implements ItemLike {
@@ -84,10 +84,10 @@ index af71e0e1eb1c93745f3e4954ecc8fbd2ad4808a3..f3829564681faff3ae8971ed90b947b4
players.add(serverPlayer1);
if (players.size() >= resultLimit) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a465408bf341a 100644
index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374bba22eee 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1894,7 +1894,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1896,7 +1896,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
private ServerStatus.Players buildPlayerStatus() {
@@ -96,7 +96,7 @@ index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a4654
int maxPlayers = this.getMaxPlayers();
if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
@@ -2103,7 +2103,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2105,7 +2105,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public int getPlayerCount() {
@@ -105,7 +105,7 @@ index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a4654
}
public String[] getPlayerNames() {
@@ -2307,7 +2307,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2309,7 +2309,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} else {
int i = 0;
@@ -114,7 +114,7 @@ index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a4654
serverPlayer.getBukkitEntity().taskScheduler.schedule((ServerPlayer player) -> { // Folia - region threading
// Paper start - Expand PlayerGameModeChangeEvent
org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null); // Folia - region threading
@@ -2492,7 +2492,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2494,7 +2494,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (Thread.currentThread() != this.serverThread) return; // Paper
// Paper start - we don't need to save everything, just advancements
// this.getPlayerList().saveAll();
@@ -123,7 +123,7 @@ index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a4654
player.getAdvancements().save();
}
// Paper end - we don't need to save everything, just advancements
@@ -2630,7 +2630,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2632,7 +2632,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
PlayerList playerList = this.getPlayerList();
UserWhiteList whiteList = playerList.getWhiteList();
@@ -133,7 +133,7 @@ index 324f9d259c6e0fd814ffe11b62d07fb4d2197da3..9d334ed3307f3c4c82bb5300219a4654
serverPlayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause
}
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index cd968c6971d054d9b66a0eaea4753a51ebb75b02..9525d77c379264289db4256e8275378d9cfa9f63 100644
index 5bf7eb5b906cefa57a1dfcf828ff7872dbceac4f..85923708c32a0597e98e028eeec501f2ea07b63b 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -167,7 +167,7 @@ public class PlayerAdvancements {
@@ -146,7 +146,7 @@ index cd968c6971d054d9b66a0eaea4753a51ebb75b02..9525d77c379264289db4256e8275378d
}
// Leaves end - bot can't get advancement
diff --git a/net/minecraft/server/ServerScoreboard.java b/net/minecraft/server/ServerScoreboard.java
index a938ae0f340aaece1fdbe3f6d517809563b1b0f5..e9f2c50c0e87b32627ad16910e1f4a816f4efc5f 100644
index 04a62e5ab9c0d0b9b7959280b0e0ea3ff388c017..e4260ec1938a43d41e007ad67a2b5be96a00227c 100644
--- a/net/minecraft/server/ServerScoreboard.java
+++ b/net/minecraft/server/ServerScoreboard.java
@@ -249,7 +249,7 @@ public class ServerScoreboard extends Scoreboard {
@@ -181,7 +181,7 @@ index e7c778a7f292fd0749cbd5e6586ad1b93ac7f29e..024b56405bb58801b17a6adad7fec7a4
org.bukkit.entity.Player sender = (org.bukkit.entity.Player) source.getBukkitSender();
playersTemp = playersTemp.stream().filter((ep) -> sender.canSee(ep.getBukkitEntity())).collect(java.util.stream.Collectors.toList());
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
index 3798c1ea92ead20b3b7e67d88d2405f29e324d2c..922383a284c8fb3c183d8a15dc41d813451adfb3 100644
index 590891d70a71ab30ceae3a3cf1923745e7c20b26..0ede1e5c73a0071e854de70f81ae1fc6a1d6dc59 100644
--- a/net/minecraft/server/commands/OpCommand.java
+++ b/net/minecraft/server/commands/OpCommand.java
@@ -25,7 +25,7 @@ public class OpCommand {
@@ -284,10 +284,10 @@ index 5a8cd3e6b448a4472092690cf589bca10b142126..158593b1a6384db635a1f224919b24c1
this.setListData(list);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index c09af6b81987df8e681689ecce9932a2e39af1a3..b3c593474e889d254f59813e7f42299b7b77d9ce 100644
index 14516c3ebd092e6f230804145a60dc851f34230b..bd33a381490ec3f03af9d22c9337f5d686014d85 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2951,7 +2951,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2972,7 +2972,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
// Leaves start - skip
@@ -296,7 +296,7 @@ index c09af6b81987df8e681689ecce9932a2e39af1a3..b3c593474e889d254f59813e7f42299b
ServerLevel.this.realPlayers.add(serverPlayer);
}
// Leaves end - skip
@@ -3043,7 +3043,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3058,7 +3058,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
// Leaves start - skip
@@ -306,7 +306,7 @@ index c09af6b81987df8e681689ecce9932a2e39af1a3..b3c593474e889d254f59813e7f42299b
}
// Leaves end - skip
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index f79735abf21b6eecc02e5b5f1ce3140dfef3ede9..04a600d96f4ae7c08d06dc4fb8f71b3f0eb1be07 100644
index 3efce8ae3cba85019aea7453f97a72939baef7f6..0741dc695c0b22a6edd9a51a9dcdec0cdd545326 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -202,7 +202,7 @@ import org.jspecify.annotations.Nullable;
@@ -318,7 +318,7 @@ index f79735abf21b6eecc02e5b5f1ce3140dfef3ede9..04a600d96f4ae7c08d06dc4fb8f71b3f
public static final long LAST_SAVE_ABSENT = Long.MIN_VALUE; public long lastSave = LAST_SAVE_ABSENT; // Paper // Folia - threaded regions - changed to nanoTime
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
@@ -673,7 +673,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -669,7 +669,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// Paper start - Expand PlayerGameModeChangeEvent
this.loadGameTypes(input);
}
@@ -328,7 +328,7 @@ index f79735abf21b6eecc02e5b5f1ce3140dfef3ede9..04a600d96f4ae7c08d06dc4fb8f71b3f
if (new org.bukkit.event.player.PlayerGameModeChangeEvent(this.getBukkitEntity(), org.bukkit.GameMode.getByValue(this.server.getDefaultGameType().getId()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null).callEvent()) {
this.gameMode.setGameModeForPlayer(this.server.getForcedGameType(), GameType.DEFAULT_MODE);
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 98f2ced8e6afdf0074ade94e0bbf5498bd6c505f..4dcb90709200b33fc9155f6dea4281ed32427889 100644
index 36cd1a0307ff1a734eb5f4f3cc013781692e7ad8..0c3e6c1f70289c63bfe424a49374602f25ff5c0b 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -130,6 +130,7 @@ public abstract class PlayerList {
@@ -356,7 +356,7 @@ index 98f2ced8e6afdf0074ade94e0bbf5498bd6c505f..4dcb90709200b33fc9155f6dea4281ed
+
+ LevelData worlddata = worldserver1.getLevelData();
+
+ player.loadGameTypes(null);
+ //player.loadGameTypes(null); // simply fix for https://github.com/MC-XiaoHei/ISeeYou/issues/70
+ ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, CommonListenerCookie.createInitial(player.gameProfile, false));
+ GameRules gamerules = worldserver1.getGameRules();
+ boolean flag = gamerules.get(GameRules.IMMEDIATE_RESPAWN);
@@ -405,7 +405,7 @@ index 98f2ced8e6afdf0074ade94e0bbf5498bd6c505f..4dcb90709200b33fc9155f6dea4281ed
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
+ if (bot != null) {
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false);
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false, false);
+ }
+ this.server.getBotList().bots.forEach(bot1 -> {
+ bot1.sendPlayerInfo(player);
@@ -532,7 +532,7 @@ index 98f2ced8e6afdf0074ade94e0bbf5498bd6c505f..4dcb90709200b33fc9155f6dea4281ed
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
this.server.getCustomBossEvents().onPlayerDisconnect(player);
UUID uuid = player.getUUID();
@@ -911,15 +1070,15 @@ public abstract class PlayerList {
@@ -910,15 +1069,15 @@ public abstract class PlayerList {
}
public String[] getPlayerNamesArray() {
@@ -553,7 +553,7 @@ index 98f2ced8e6afdf0074ade94e0bbf5498bd6c505f..4dcb90709200b33fc9155f6dea4281ed
// Leaves end - fakeplayer support
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index f80f5533c1c3c64ee1b8b2c43e2bc2dea8ab567f..aa9cbb5f02b87789fd94521792aefac5c840c95a 100644
index c0ebd700caa950948c58846eaf164afc14f87f09..e4ae4354e81f34f0db410333fc9b8e17383a5f41 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -262,7 +262,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -1,6 +1,6 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 15 Nov 2025 01:06:20 +0800
Date: Sat, 7 Feb 2026 23:57:50 +0800
Subject: [PATCH] Leaves: Catch update suppression crash
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/network/PacketProcessor.java b/net/minecraft/network/PacketProcessor.java
index e07e5eced7e32d1c3a0ac14e8639c1f19b0f641a..fe7483581e5c3f5cb523601b394750a94a178e10 100644
index cf162ec136309d05c5aa03042652ab3932935917..469ffd54eb5af980f49061bbec0a72f4e888f8a3 100644
--- a/net/minecraft/network/PacketProcessor.java
+++ b/net/minecraft/network/PacketProcessor.java
@@ -107,7 +107,20 @@ public class PacketProcessor implements AutoCloseable {
@@ -46,10 +46,10 @@ index ab80a66646a7ebe2862221ac342b06c14fcfc734..7834f3da4d0ff4a5cdec46b023d73483
fillCrashReport(reportedException.getReport(), packetListener, packet);
return reportedException;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 9d334ed3307f3c4c82bb5300219a465408bf341a..f2f57cfcd7739a951123c81f82cac1c2bbd3a3bd 100644
index 9130f1fbf15500f2149418b73ae2a374bba22eee..eef40103bcec3b836ffb50154fb05926b9757723 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1973,8 +1973,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1975,11 +1975,27 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
profiler.startTimer(serverLevel.tickTimerId); try { // Folia - profiler
serverLevel.tick(hasTimeLeft, region); // Folia - region threading
@@ -59,19 +59,32 @@ index 9d334ed3307f3c4c82bb5300219a465408bf341a..f2f57cfcd7739a951123c81f82cac1c2
+ exception.consume();
} finally { profiler.stopTimer(serverLevel.tickTimerId); } // Folia - profiler
} catch (Throwable var7) {
+ if (var7.getCause() instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideLevel(serverLevel);
+ exception.consume();
- CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
- serverLevel.fillReportDetails(crashReport);
- throw new ReportedException(crashReport);
+ org.leavesmc.leaves.util.UpdateSuppressionException updateSuppressionException = null;
+ if (var7 instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ updateSuppressionException = exception;
+ } else if (var7.getCause() instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ updateSuppressionException = exception;
+ }
+ // Leaves end - update suppression crash fix
CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
serverLevel.fillReportDetails(crashReport);
throw new ReportedException(crashReport);
+ if (updateSuppressionException != null) {
+ updateSuppressionException.provideLevel(serverLevel);
+ updateSuppressionException.consume();
+ } else {
+ // Leaves end - update suppression crash fix
+ CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
+ serverLevel.fillReportDetails(crashReport);
+ throw new ReportedException(crashReport);
+ }
}
profilerFiller.pop();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index b3c593474e889d254f59813e7f42299b7b77d9ce..b878f5a8316dfee582180a6bbe7122a78581fae2 100644
index bd33a381490ec3f03af9d22c9337f5d686014d85..284bd9703b11804f51273ecfaa5cd44925726f5f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -878,7 +878,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -894,7 +894,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Kaiiju end
profilerFiller.push("tick");
@@ -79,7 +92,7 @@ index b3c593474e889d254f59813e7f42299b7b77d9ce..b878f5a8316dfee582180a6bbe7122a7
+ // Lophine start - update suppression crash fix
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ try {
+ this.tickNonPassenger(entity); // Lophine changed
+ this.guardEntityTick(this::tickNonPassenger, entity); // Lophine changed
+ // Leaves start - update suppression crash fix - for dragon dupe
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideLevel(this);
@@ -94,12 +107,12 @@ index b3c593474e889d254f59813e7f42299b7b77d9ce..b878f5a8316dfee582180a6bbe7122a7
}
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 04a600d96f4ae7c08d06dc4fb8f71b3f0eb1be07..45e4e3243c72b01d94dc440b26230a48b3909367 100644
index 0741dc695c0b22a6edd9a51a9dcdec0cdd545326..3fc09529159d202ce6201626be535d791818fa4a 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1115,6 +1115,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
((org.bukkit.craftbukkit.CraftWorldBorder) this.getBukkitEntity().getWorldBorder()).getHandle().tick();
}
@@ -1108,6 +1108,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// Folia - region threading - move to main tick loop where we can detect duplicates
// CraftBukkit end
+ // Leaves start - update suppression crash fix
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
@@ -111,10 +124,10 @@ index 04a600d96f4ae7c08d06dc4fb8f71b3f0eb1be07..45e4e3243c72b01d94dc440b26230a48
CrashReport crashReport = CrashReport.forThrowable(var4, "Ticking player");
CrashReportCategory crashReportCategory = crashReport.addCategory("Player being ticked");
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 5bcf646ef600a2aae7633453d3331fde2f141aff..5d5c3694e821954e6ede4d5c3de8844a84f71f45 100644
index 3e44a61bd982dc822437f647a02a95affd988cdb..28ae21b08e42edcc031ace2404b9d213589c28e8 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1434,9 +1434,19 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -1427,9 +1427,19 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
this.walkingStepSound(pos, state);
}
@@ -181,7 +194,7 @@ index c020d1944a25e2cd247e30a97c35bf93731d694a..74967f20099ea58cea1f9fc0f2387d8c
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index b2694cb60505766877398fa526b9bfe60e6b6404..7c354894f48a5540eb2554d8b1428306e8929066 100644
index 77577d0df9b14a7ad55b2866c2ac31863226458b..5551d80a81c2878136f96b3e695c4888c7105a49 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -383,7 +383,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -5,10 +5,10 @@ Subject: [PATCH] Instant Block Updater
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 56e09b99c2901882483c5f2b3216bf0a11406e2e..1480bd04597001cd7c54e53ded46d7f75908a65a 100644
index c7f578c117baccb10c0c15b05a6c04e073f15da8..aee3f98173de1b2c5a5278919a5d21b4bca03b7b 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -468,7 +468,7 @@ public final class RegionizedWorldData {
@@ -472,7 +472,7 @@ public final class RegionizedWorldData {
public long lastMidTickExecuteFailure;
// From Level
public boolean populating;
@@ -17,7 +17,7 @@ index 56e09b99c2901882483c5f2b3216bf0a11406e2e..1480bd04597001cd7c54e53ded46d7f7
public boolean captureBlockStates = false;
public boolean captureTreeGeneration = false;
public final Map<BlockPos, CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
@@ -527,7 +527,7 @@ public final class RegionizedWorldData {
@@ -531,7 +531,7 @@ public final class RegionizedWorldData {
this.world = world;
this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false);
@@ -27,10 +27,10 @@ index 56e09b99c2901882483c5f2b3216bf0a11406e2e..1480bd04597001cd7c54e53ded46d7f7
this.wireHandler = new alternate.current.wire.WireHandler(world);
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index b878f5a8316dfee582180a6bbe7122a78581fae2..a906e7a34d048369f9357b0371339b4cccac5f25 100644
index 284bd9703b11804f51273ecfaa5cd44925726f5f..9756caff0b7403c212987e58c08a2da09bdeb962 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -911,12 +911,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -927,12 +927,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper - rewrite chunk system
profilerFiller.pop();
profilerFiller.push("debugSynchronizers");
@@ -9,7 +9,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc1
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index 8d28b29348c255c901375af161210a2041de1985..86f7cfd5816917bd03aca0a796f1eaca938d65aa 100644
index 3301e521a79adbc6dd76be05e69cce1c97f8697e..8dc1db0e6bafba19e07a7fa78f3920d572d38466 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -386,7 +386,18 @@ public class ServerPlayerGameMode {
@@ -553,7 +553,7 @@ index d1dbfc821f068f14bf975842f66251816ea5c985..fe13504a7d2d685e43183dd7b3219f16
protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
if (!movedByPiston) {
diff --git a/net/minecraft/world/level/block/SculkSensorBlock.java b/net/minecraft/world/level/block/SculkSensorBlock.java
index 5ef593a61c74af7077d02f4b18d936bf9b2507b9..a94d69927b78089b2e9eb0ee74f693821c3d93fc 100644
index 80e6ba8f026a6565696f18304507b2d0a347650c..593fab3d2a70bcd6832dd777afa4973f22abbaeb 100644
--- a/net/minecraft/world/level/block/SculkSensorBlock.java
+++ b/net/minecraft/world/level/block/SculkSensorBlock.java
@@ -128,6 +128,18 @@ public class SculkSensorBlock extends BaseEntityBlock implements SimpleWaterlogg
@@ -732,7 +732,7 @@ index b369e80a4fc371a3762e34e7d4d180b86d1d190f..0b412e360b090dc85d1726eb8f51c69b
protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite());
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index 10f9f617123e7ef582707038230f43350d5c5810..d96385dc19b7d6409d65ae37549b6d8f3f955cd2 100644
index 418517faeaf40ae2f7dea5635e4232cd89db3ead..0de2f6627fb6295d7927ac3e37026f4daf68cfd3 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -171,6 +171,15 @@ public abstract class BlockBehaviour implements FeatureElement, org.leavesmc.lea
@@ -765,7 +765,7 @@ index 10f9f617123e7ef582707038230f43350d5c5810..d96385dc19b7d6409d65ae37549b6d8f
this.getBlock().affectNeighborsAfterRemoval(this.asState(), level, pos, movedByPiston);
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 7c354894f48a5540eb2554d8b1428306e8929066..de82e9a7f0cc190f8cf8c66eee9dc178241f6a27 100644
index 5551d80a81c2878136f96b3e695c4888c7105a49..73dd8e67938fbfff745b189c06a93174976ba1bf 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -406,22 +406,28 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -46,7 +46,7 @@ index a5d3caab2072b2c9bf8fdcbdb7c52c2dee16cfaf..4bb36d2965785fadf628a9bfac4fa2aa
// CraftBukkit start
if (bukkitState != null) {
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index de82e9a7f0cc190f8cf8c66eee9dc178241f6a27..f5b128a993fab7af71a098b2128537ac749edc2f 100644
index 73dd8e67938fbfff745b189c06a93174976ba1bf..8e56c2237f9d49d0b09b62c0933283f77aaae679 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1001,12 +1001,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -5,10 +5,10 @@ Subject: [PATCH] Global Entities Counter
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 1480bd04597001cd7c54e53ded46d7f75908a65a..c49be29a91422bea393f0acd6daa6ef948bd77ec 100644
index aee3f98173de1b2c5a5278919a5d21b4bca03b7b..24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -521,6 +521,9 @@ public final class RegionizedWorldData {
@@ -525,6 +525,9 @@ public final class RegionizedWorldData {
public final Map<Long, ChunkSectionItemEntityMovementTracker> itemEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>();
// Luminol end
@@ -18,7 +18,7 @@ index 1480bd04597001cd7c54e53ded46d7f75908a65a..c49be29a91422bea393f0acd6daa6ef9
public final TickRegions.TickRegionData regionData;
public RegionizedWorldData(final ServerLevel world, final TickRegions.TickRegionData regionData) {
@@ -533,6 +536,15 @@ public final class RegionizedWorldData {
@@ -537,6 +540,15 @@ public final class RegionizedWorldData {
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
this.regionData = regionData;
@@ -97,10 +97,10 @@ index f4c9c551fc39bae38e283ccbbc3cd116f0536a64..db13211b24208d8addd3c210155bc244
NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a906e7a34d048369f9357b0371339b4cccac5f25..b5be74243607fb34812dcbad26aad76e44bf0b19 100644
index 9756caff0b7403c212987e58c08a2da09bdeb962..4acd4718628b160c482b04da6ba905687b61bbad 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -763,6 +763,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -765,6 +765,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
public void tick(BooleanSupplier hasTimeLeft, io.papermc.paper.threadedregions.TickRegions.TickRegionData region) { // Folia - regionised ticking
@@ -6,7 +6,7 @@ Subject: [PATCH] Add null check in RegionizedWorldData >> conections
some of our function patches will crash because of get null in conn.getPlayer(), then when we process player.chunkPosition() will get NPE, so we need to fix it.
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index c49be29a91422bea393f0acd6daa6ef948bd77ec..9b25483472a606168e6860e0a96f05b78d3e5b02 100644
index 24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff..35f8bcb5569987e4096ea06bab4e6693dfb97695 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -229,14 +229,17 @@ public final class RegionizedWorldData {
@@ -41,7 +41,7 @@ index c49be29a91422bea393f0acd6daa6ef948bd77ec..9b25483472a606168e6860e0a96f05b7
}
for (final Entity entity : from.allEntities) {
final ChunkPos pos = entity.chunkPosition();
@@ -581,6 +591,7 @@ public final class RegionizedWorldData {
@@ -585,6 +595,7 @@ public final class RegionizedWorldData {
private static void cleanUpConnection(final Connection conn) {
// note: ALL connections HERE have a player
final ServerPlayer player = conn.getPlayer();
@@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 13 Jan 2026 21:04:59 +0800
Subject: [PATCH] Add removed check before all checks start in addEffect
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index e4ae4354e81f34f0db410333fc9b8e17383a5f41..fc8833655102ca98bb331782a624af79cbef414f 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1186,6 +1186,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) {
// Paper end - Don't fire sync event during generation
// org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
+ if (this.isRemoved()) return false; // Lophine - shouldn't be possible to add effects to removed entity
if (!this.hasNullCallback()) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add effects to entities asynchronously"); // Folia - region threading
if (this.isTickingEffects) {
this.effectsToProcess.add(new ProcessableEffect(effectInstance, cause));
@@ -35,27 +35,20 @@ index fcdfd4f6de5e1c2d5e80c7f3d8cb0b33a62d257d..6eaa4e9bd1ecbfa531313ce1f470d55e
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();
diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
index 22125dbe1765f930fe10fe420be1c275ec553139..d5b070d1a3d84e228960dacd614e0f1abe968e79 100644
index a5f2e4454258886536251288c3694f58386c7420..28371ecf31d5fc280aaa03ed9a86f5b565cf32f6 100644
--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
@@ -35,7 +35,7 @@ public class PaperVersionFetcher implements VersionFetcher {
private static final Logger LOGGER = LogUtils.getClassLogger();
@@ -40,8 +40,8 @@ public class PaperVersionFetcher implements VersionFetcher {
private static final ComponentLogger COMPONENT_LOGGER = ComponentLogger.logger(LogManager.getRootLogger().getName());
private static final int DISTANCE_ERROR = -1;
private static final int DISTANCE_UNKNOWN = -2;
- private static final String DOWNLOAD_PAGE = "https://github.com/LuminolMC/Luminol"; //Luminol
+ private static final String DOWNLOAD_PAGE = "https://github.com/LuminolMC/Lophine"; //Luminol // Lophine
@Override
public long getCacheTime() {
@@ -49,7 +49,7 @@ public class PaperVersionFetcher implements VersionFetcher {
if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) {
updateMessage = text("You are running a development version without access to version information", color(0xFF5300));
} else {
- updateMessage = getUpdateStatusMessage("LuminolMC/Luminol", build); // Folia // Luminol
+ updateMessage = getUpdateStatusMessage("LuminolMC/Lophine", build); // Folia // Luminol // Lophine
}
final @Nullable Component history = this.getHistory();
- private static final String DOWNLOAD_PAGE = "https://github.com/LuminolMC/Luminol"; // Luminol
- private static final String REPOSITORY = "LuminolMC/Luminol"; // Luminol
+ private static final String DOWNLOAD_PAGE = "https://github.com/LuminolMC/Lophine"; // Luminol // Lophine
+ private static final String REPOSITORY = "LuminolMC/Lophine"; // Luminol // Lophine
private static final ServerBuildInfo BUILD_INFO = ServerBuildInfo.buildInfo();
private static final String USER_AGENT = BUILD_INFO.brandName() + "/" + BUILD_INFO.asString(VERSION_SIMPLE) + " (https://papermc.io)";
private static final Gson GSON = new Gson();
diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
index 4ada790812957657d580cb748077c4961987d0cb..3a539df860305e777e85ed6dc72b5effe76734b4 100644
--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc1
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 92de192c7b420ebb4510354f347003127595e141..e28c7043c04a4df34733b9bfc5fd55ee6082d010 100644
index 507204301b5e3cd545e66c510842c0a6391f0241..dfcfc83474443638e30f87d54472e5a38947dfad 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -485,6 +485,7 @@ public final class CraftServer implements Server {
@@ -53,7 +53,7 @@ index 21d594a39d09b2d17f05c40ce5323d57949d0b0b..46859fc70795097366f02dba48fdaab8
if (nmsEntity.level() != this.getHandle().getLevel()) {
throw new IllegalArgumentException(entity + " wasn't created with this world, you must create the entity with the world you want to add it to.");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index e28c7043c04a4df34733b9bfc5fd55ee6082d010..3b179b11441a9295698fa55cc4a020b4c1652616 100644
index dfcfc83474443638e30f87d54472e5a38947dfad..973f064fc5e089dc892d4b4077a807517fd199b5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -305,6 +305,7 @@ public final class CraftServer implements Server {
@@ -96,10 +96,10 @@ index e28c7043c04a4df34733b9bfc5fd55ee6082d010..3b179b11441a9295698fa55cc4a020b4
/**
* Gets the TPS from the region which owns the specified location, or {@code null} if no region owns
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index a1a79a18673c94218288f32345195b80139ad879..acddc62380f65f50779786296dec265de4a647db 100644
index dcd32c1e32fd67ec13bb7fca8af20aa665407594..b7f83e66227b5a1f5ad9b424e425eee118cc0692 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -249,7 +249,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -247,7 +247,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getPlayerCount() {
@@ -108,7 +108,7 @@ index a1a79a18673c94218288f32345195b80139ad879..acddc62380f65f50779786296dec265d
}
@Override
@@ -1122,9 +1122,9 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -1121,9 +1121,9 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public List<Player> getPlayers() {
@@ -120,7 +120,7 @@ index a1a79a18673c94218288f32345195b80139ad879..acddc62380f65f50779786296dec265d
HumanEntity bukkitEntity = human.getBukkitEntity();
if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) {
@@ -1626,7 +1626,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -1622,7 +1622,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public void playSound(final net.kyori.adventure.sound.Sound sound) {
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
final long seed = sound.seed().orElseGet(this.world.getRandom()::nextLong);
@@ -129,7 +129,7 @@ index a1a79a18673c94218288f32345195b80139ad879..acddc62380f65f50779786296dec265d
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null));
}
}
@@ -1654,7 +1654,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -1650,7 +1650,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong);
if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) {
@@ -138,7 +138,7 @@ index a1a79a18673c94218288f32345195b80139ad879..acddc62380f65f50779786296dec265d
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null));
}
} else if (emitter instanceof CraftEntity craftEntity) {
@@ -1795,7 +1795,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -1791,7 +1791,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
}
this.getHandle().sendParticlesSource(
@@ -161,7 +161,7 @@ index e6cd1c4a50b32b74f70a264e869b1ab4ac9b9eb0..6867064de52d810f1fd3869463ae2786
if (entity instanceof EnderDragonPart complexPart) {
if (complexPart.parentMob instanceof EnderDragon) {
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 3d465c857b3560a2cd57825c9cbd0a3c9029e6d3..a41b5eeb1e82725948c448c7c3c4f5b2d1e06af6 100644
index 0429ffda4aa4410f6f53bd163be8dee71b134637..9274f4ef639ed9b3028321566173d6612663082b 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1014,7 +1014,11 @@ public class CraftEventFactory {
@@ -21,7 +21,7 @@ index e1f456b31ffda9370b63e51f343847d6f9f62263..8a0f85241cc1530aacccc8b6ea24b9de
}
// Leaves end - skip bot
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 401f977483b6e809b9b10c601900e35110ae7396..030a9cf9a95fcea1410e825df76f817f73d4db78 100644
index 973f064fc5e089dc892d4b4077a807517fd199b5..06766a9058595aeee25e8fd3e3d01aeec2a55116 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -306,6 +306,7 @@ public final class CraftServer implements Server {
@@ -66,10 +66,10 @@ index 6867064de52d810f1fd3869463ae27868ed0fb07..69a0de762cfde089e8aaa6e6be79a8c8
// Special case complex part, since there is no extra entity type for them
if (entity instanceof EnderDragonPart complexPart) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index a8838e45dd7d44c25442c14d49b53583f9f2a2d2..effb56daac89b7cc3106ed4b394a6c583b654735 100644
index 6e745c7ddf417ec2fdcf102edd6a592047b5f73e..2f59107642d43719159872d5df37107d8a1a5b97 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2091,7 +2091,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
@@ -2085,7 +2085,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
@Override
public boolean canSee(Player player) {
@@ -79,10 +79,10 @@ index a8838e45dd7d44c25442c14d49b53583f9f2a2d2..effb56daac89b7cc3106ed4b394a6c58
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index dcec69554213bfb8608b77341819b09e31a96e09..5dce7416f42902de34bff6398cf402c610be600c 100644
index 6f608ad5d1151925ad0f38596dfe15290d20d753..9c1a48dd4a8957200d8c05cf7e142c621132bb74 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -338,7 +338,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
@@ -344,7 +344,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
Bukkit.getLogger().log(Level.SEVERE, "Error saving advancement " + key, ex);
}
@@ -4,5 +4,5 @@ import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
public class LophineLogger {
public static final Logger LOGGER = LogUtils.getClassLogger(); // only provided for some Logger can not be initialized
public static final Logger LOGGER = LogUtils.getLogger();
}
@@ -1,6 +1,6 @@
package fun.bm.lophine.command.counter;
import net.minecraft.commands.CommandSourceStack;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.LiteralNode;
@@ -25,7 +25,7 @@ public class DisplayCommand extends CounterSubCommand {
}
public static void displayCounter(CommandContext context, @NotNull HopperCounter counter, boolean realTime) {
for (Component component : counter.format(MinecraftServer.getServer(), context.getSource().getLevel(), realTime)) {
for (Component component : counter.format(MinecraftServer.getServer(), realTime)) {
context.getSender().sendMessage(component);
}
}
@@ -9,6 +9,7 @@ import fun.bm.lophine.command.counter.CounterSubCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.TextColor;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.item.DyeColor;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.ArgumentNode;
@@ -27,7 +28,7 @@ public class ResetCommand extends CounterSubCommand {
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
HopperCounter.resetAll(context.getSource().getLevel(), false);
HopperCounter.resetAll(MinecraftServer.getServer(), false);
context.getSender().sendMessage(Component.text("Restarted all counters."));
return true;
}
@@ -41,14 +42,14 @@ public class ResetCommand extends CounterSubCommand {
protected boolean execute(@NotNull CommandContext context) {
String color0 = context.getArgument(DyeColorArg.class);
if (color0.equals("all")) {
HopperCounter.resetAll(context.getSource().getLevel(), false);
HopperCounter.resetAll(MinecraftServer.getServer(), false);
context.getSender().sendMessage(Component.text("Restarted all counters."));
return true;
}
DyeColor color = DyeColor.byName(color0, null);
if (color == null) return true;
HopperCounter counter = HopperCounter.getCounter(color);
counter.reset(context.getSource().getLevel());
counter.reset(MinecraftServer.getServer());
context.getSender().sendMessage(Component.join(JoinConfiguration.noSeparators(),
Component.text("Restarted "),
Component.text(color.getName(), TextColor.color(color.getTextColor())),
@@ -22,11 +22,21 @@ public class CommandConfig implements IConfigModule {
@TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint_bar"})
@ConfigInfo(name = "waypoint_command_enabled", comments =
"""
Allow to use waypoint command""")
Allow to use waypoint command and locator bar""")
public static boolean waypoint = false;
@ConfigInfo(name = "scoreboard_command_enabled", comments =
"""
Allow to use scoreboard command""")
public static boolean scoreboard = false;
@ConfigInfo(name = "enabled", directory = {"save_all_command"}, comments =
"""
Allow to use save-all command""")
public static boolean saveAll = false;
@ConfigInfo(name = "log_all_process", directory = {"save_all_command"}, comments =
"""
Log all process of save-all command to console""")
public static boolean logAllProcess = false;
}
@@ -0,0 +1,34 @@
package fun.bm.lophine.config.modules.function;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import fun.bm.lophine.utils.RandomProfilePool;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.config.flags.HotReloadUnsupported;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "replay-api")
public class ReplayAPIConfig implements IConfigModule {
@HotReloadUnsupported
@ConfigInfo(name = "enable-cache")
public static boolean enableCache = true;
@HotReloadUnsupported
@ConfigInfo(name = "cache-photographer-time", comments = """
Time to cache photographer profile(in seconds)""")
public static int cachePhotographerTime = 3600;
@HotReloadUnsupported
@ConfigInfo(name = "cache-photographer-size", comments = """
Maximum size of cache photographer profile""")
public static int cachePhotographerSize = 100;
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> e) {
RandomProfilePool.init();
}
}
@@ -1,17 +0,0 @@
package fun.bm.lophine.config.modules.optimizations;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.config.flags.HotReloadUnsupported;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.OPTIMIZATIONS, name = "waypoint")
public class WayPointOptimizedTableConfig implements IConfigModule {
@HotReloadUnsupported
@ConfigInfo(name = "optimizedTable", comments = """
Should use optimized table instead of normal concurrent table for waypoints.
May improve performance when there are many waypoints and players.
When enabled, more memory is needed to store data.""")
public static boolean optimizedTable = false;
}
@@ -8,6 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.REMOVED, name = "removed_config")
public class RemovedConfig implements IConfigModule {
@TransformedConfig(name = "optimizedTable", directory = {"optimizations", "waypoint"}, transform = false)
@TransformedConfig(name = "enabled", directory = {"fixes", "end-void-ring"}, transform = false)
@TransformedConfig(name = "enabled", directory = {"gameplay", "end_void_rings"}, transform = false)
@TransformedConfig(name = "datapack_command_enabled", directory = {"experiment", "command"}, transform = false)
@@ -0,0 +1,59 @@
package fun.bm.lophine.utils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mojang.authlib.GameProfile;
import fun.bm.lophine.config.modules.function.ReplayAPIConfig;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RandomProfilePool {
private static int lastUsedId = 0;
private static final Object lock = new Object();
private static Cache<Integer, GameProfile> cache;
private static final Object lockCache = new Object();
public static void init() {
if (ReplayAPIConfig.enableCache) {
cache = CacheBuilder.newBuilder()
.maximumSize(ReplayAPIConfig.cachePhotographerSize)
.expireAfterWrite(ReplayAPIConfig.cachePhotographerTime, TimeUnit.SECONDS)
.build();
}
}
public static GameProfile getRandomProfile(String id) {
if (ReplayAPIConfig.enableCache) {
synchronized (lockCache) {
for (Map.Entry<Integer, GameProfile> entry : cache.asMap().entrySet()) {
int key = entry.getKey();
cache.invalidate(key);
GameProfile gp = entry.getValue();
return new GameProfile(gp.id(), id, gp.properties());
}
}
}
return new GameProfile(UUID.randomUUID(), id);
}
public static void putProfile(GameProfile profile) {
if (ReplayAPIConfig.enableCache) {
synchronized (lockCache) {
cache.put(getNextId(), profile);
}
}
}
private static int getNextId() {
synchronized (lock) {
int newId = lastUsedId + 1;
while (cache.getIfPresent(newId) != null) {
newId++;
}
lastUsedId = newId;
return newId;
}
}
}
@@ -0,0 +1,91 @@
package fun.bm.lophine.utils;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import fun.bm.lophine.config.modules.experiment.CommandConfig;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.SaveAllCommand;
import net.minecraft.server.level.ServerLevel;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class SaveAllUtil {
private static final long TIMEOUT = 1000 * 30; // 30 seconds
public static final Object lock = new Object();
public static volatile long lastSaveAllTime = 0;
private static volatile Pair<CommandSourceStack, Boolean> currentSaveAll = null;
private static volatile int regionCount = 0;
private static final AtomicInteger savedRegionCount = new AtomicInteger(0);
private static final AtomicBoolean withError = new AtomicBoolean(false);
public static void preSaveAll(CommandSourceStack source, boolean flush) {
synchronized (lock) {
if (isSaving()) {
source.sendFailure(Component.literal("Server is already in saving! Please wait..."));
} else {
source.sendSuccess(() -> Component.translatable("commands.save.saving"), false);
currentSaveAll = Pair.of(source, flush);
// we need to count how many regions we need to save
AtomicInteger count = new AtomicInteger();
for (ServerLevel world : source.getServer().getAllLevels()) {
world.regioniser.computeForAllRegions(unused -> {
count.getAndIncrement();
});
}
regionCount = count.get();
savedRegionCount.set(0);
withError.set(false);
lastSaveAllTime = System.currentTimeMillis();
}
}
}
public static void postRegionSave(io.papermc.paper.threadedregions.TickRegions.TickRegionData region) {
Pair<CommandSourceStack, Boolean> currentSaveAll = SaveAllUtil.currentSaveAll;
synchronized (lock) {
if (lastSaveAllTime < region.lastSavedTime) return; // already saved
}
try {
region.world.moonrise$getChunkTaskScheduler().chunkHolderManager.saveAllChunksRegionised(currentSaveAll.getSecond(), false, CommandConfig.logAllProcess, false, false, true, false);
currentSaveAll.getFirst().getServer().getPlayerList().saveAll();
MinecraftServer.LOGGER.info("Saved chunks in region around chunk {} in world '{}'", region.region.getCenterChunk(), region.region.regioniser.world.getWorld().getName());
} catch (final Throwable thr) {
CommandSyntaxException error = SaveAllCommand.getErrorFailed().create();
currentSaveAll.getFirst().sendFailure(Component.literal(error.getMessage()));
MinecraftServer.LOGGER.error(error.getMessage(), thr);
withError.set(true);
}
region.lastSavedTime = System.currentTimeMillis();
int saved;
synchronized (savedRegionCount) {
saved = savedRegionCount.incrementAndGet();
}
if (saved >= regionCount) {
if (withError.get()) {
currentSaveAll.getFirst().sendFailure(Component.literal("At least one region failed to save!"));
} else if (saved == regionCount) {
currentSaveAll.getFirst().sendSuccess(() -> Component.translatable("commands.save.success"), true);
SaveAllUtil.currentSaveAll = null;
}
}
}
public static void checkTimeout() {
Pair<CommandSourceStack, Boolean> currentSaveAll = SaveAllUtil.currentSaveAll;
if (!isSaving()) return;
if (System.currentTimeMillis() - lastSaveAllTime > TIMEOUT) {
currentSaveAll.getFirst().sendFailure(Component.literal("At least one region save data timeout!"));
currentSaveAll.getFirst().sendFailure(Component.literal("Regions need to save expect is " + regionCount + ", but only " + savedRegionCount.get() + " saved!"));
SaveAllUtil.currentSaveAll = null;
}
}
public static boolean isSaving() {
synchronized (lock) {
return currentSaveAll != null;
}
}
}
@@ -62,7 +62,7 @@ public class ServerI18nUtil {
}
langPath = BASE_PATH + "lang/" + LanguageConfig.lang + ".json";
langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json";
lophineLangPath = "/assets/Lophine/lang/" + LanguageConfig.lang + ".json";
lophineLangPath = "/assets/lophine/lang/" + LanguageConfig.lang + ".json";
logger.info("Starting load language: {}", LanguageConfig.lang);
preloadTask.thenAcceptAsync(v -> loadI18n(LanguageConfig.lang, 2));
}
@@ -1,38 +0,0 @@
package fun.bm.lophine.utils.concurrent;
import java.util.List;
import java.util.Map;
public abstract class AbstractConcurrentTable<X, Y, Z> {
public abstract void putOrUpdate(X x, Y y, Z z);
public abstract void put(X x, Y y, Z z);
public abstract void remove(X x, Y y, Z z);
public abstract List<Z> getZ(X x, Y y);
public abstract List<Y> getY(X x, Z z);
public abstract List<X> getX(Y y, Z z);
public abstract List<Map.Entry<X, Y>> getXY(Z z);
public abstract List<Map.Entry<Y, Z>> getYZ(X x);
public abstract List<Map.Entry<X, Z>> getXZ(Y y);
public abstract List<X> getAllX();
public abstract List<Y> getAllY();
public abstract List<Z> getAllZ();
public abstract void clearXY(Z z);
public abstract void clearYZ(X x);
public abstract void clearXZ(Y y);
public abstract void clearAll();
}
@@ -1,150 +0,0 @@
package fun.bm.lophine.utils.concurrent;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Predicate;
public class ConcurrentTable<X, Y, Z> extends AbstractConcurrentTable<X, Y, Z> {
protected final ConcurrentLinkedDeque<TableEntry<X, Y, Z>> data = new ConcurrentLinkedDeque<>();
@Override
public void putOrUpdate(X x, Y y, Z z) {
for (TableEntry<X, Y, Z> entry : data) {
if (entry.getX().equals(x) && entry.getY().equals(y)) {
entry.setZ(z);
return;
}
}
this.put(x, y, z);
}
@Override
public void put(X x, Y y, Z z) {
data.add(new TableEntry<>(x, y, z));
}
@Override
public void remove(X x, Y y, Z z) {
data.removeIf(entry -> entry.getX().equals(x) && entry.getY().equals(y) && entry.getZ().equals(z));
}
@Override
public List<Z> getZ(X x, Y y) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getY().equals(y),
TableEntry::getZ
);
}
@Override
public List<Y> getY(X x, Z z) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getZ().equals(z),
TableEntry::getY
);
}
@Override
public List<X> getX(Y y, Z z) {
return filterAndCollect(
entry -> entry.getY().equals(y) && entry.getZ().equals(z),
TableEntry::getX
);
}
@Override
public List<Map.Entry<X, Y>> getXY(Z z) {
return filterAndMap(
entry -> entry.getZ().equals(z),
TableEntry::getX,
TableEntry::getY
);
}
@Override
public List<Map.Entry<Y, Z>> getYZ(X x) {
return filterAndMap(
entry -> entry.getX().equals(x),
TableEntry::getY,
TableEntry::getZ
);
}
@Override
public List<Map.Entry<X, Z>> getXZ(Y y) {
return filterAndMap(
entry -> entry.getY().equals(y),
TableEntry::getX,
TableEntry::getZ
);
}
@Override
public List<X> getAllX() {
return collectAll(TableEntry::getX);
}
@Override
public List<Y> getAllY() {
return collectAll(TableEntry::getY);
}
@Override
public List<Z> getAllZ() {
return collectAll(TableEntry::getZ);
}
@Override
public void clearXY(Z z) {
data.removeIf(entry -> entry.getZ().equals(z));
}
@Override
public void clearYZ(X x) {
data.removeIf(entry -> entry.getX().equals(x));
}
@Override
public void clearXZ(Y y) {
data.removeIf(entry -> entry.getY().equals(y));
}
@Override
public void clearAll() {
data.clear();
}
private <T> List<T> filterAndCollect(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
result.add(mapper.apply(entry));
}
}
return result;
}
private <K, V> List<Map.Entry<K, V>> filterAndMap(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, K> keyMapper,
java.util.function.Function<TableEntry<X, Y, Z>, V> valueMapper) {
List<Map.Entry<K, V>> list = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
list.add(new AbstractMap.SimpleEntry<>(keyMapper.apply(entry), valueMapper.apply(entry)));
}
}
return list;
}
private <T> List<T> collectAll(java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
result.add(mapper.apply(entry));
}
return result;
}
}
@@ -1,197 +0,0 @@
package fun.bm.lophine.utils.concurrent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Predicate;
public class OptimizedConcurrentTable<X, Y, Z> extends ConcurrentTable<X, Y, Z> {
private final ConcurrentHashMap<X, ConcurrentHashMap<Y, Set<Z>>> xyIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Y, ConcurrentHashMap<Z, Set<X>>> yzIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Z, ConcurrentHashMap<X, Set<Y>>> zxIndex = new ConcurrentHashMap<>();
@Override
public void putOrUpdate(X x, Y y, Z z) {
boolean updated = false;
for (TableEntry<X, Y, Z> entry : data) {
if (entry.getX().equals(x) && entry.getY().equals(y)) {
removeFromIndex(xyIndex, entry.getX(), entry.getY(), entry.getZ());
removeFromIndex(yzIndex, entry.getY(), entry.getZ(), entry.getX());
removeFromIndex(zxIndex, entry.getZ(), entry.getX(), entry.getY());
entry.setZ(z);
putData(x, y, z);
updated = true;
break;
}
}
if (!updated) {
this.put(x, y, z);
}
}
private void putData(X x, Y y, Z z) {
xyIndex.computeIfAbsent(x, k -> new ConcurrentHashMap<>())
.computeIfAbsent(y, k -> ConcurrentHashMap.newKeySet()).add(z);
yzIndex.computeIfAbsent(y, k -> new ConcurrentHashMap<>())
.computeIfAbsent(z, k -> ConcurrentHashMap.newKeySet()).add(x);
zxIndex.computeIfAbsent(z, k -> new ConcurrentHashMap<>())
.computeIfAbsent(x, k -> ConcurrentHashMap.newKeySet()).add(y);
}
@Override
public void put(X x, Y y, Z z) {
super.put(x, y, z);
putData(x, y, z);
}
@Override
public void remove(X x, Y y, Z z) {
super.remove(x, y, z);
removeFromIndex(xyIndex, x, y, z);
removeFromIndex(yzIndex, y, z, x);
removeFromIndex(zxIndex, z, x, y);
}
private <K, V, T> void removeFromIndex(ConcurrentHashMap<K, ConcurrentHashMap<V, Set<T>>> index,
K key1, V key2, T value) {
index.computeIfPresent(key1, (k, map) -> {
map.computeIfPresent(key2, (k2, set) -> {
set.remove(value);
return set.isEmpty() ? null : set;
});
return map.isEmpty() ? null : map;
});
}
public void removeAll(Predicate<TableEntry<X, Y, Z>> predicate) {
data.removeIf(entry -> {
boolean shouldRemove = predicate.test(entry);
if (shouldRemove) {
removeFromIndex(xyIndex, entry.getX(), entry.getY(), entry.getZ());
removeFromIndex(yzIndex, entry.getY(), entry.getZ(), entry.getX());
removeFromIndex(zxIndex, entry.getZ(), entry.getX(), entry.getY());
}
return shouldRemove;
});
}
public boolean putIfAbsent(X x, Y y, Z z) {
if (data.stream().anyMatch(entry ->
Objects.equals(entry.getX(), x) &&
Objects.equals(entry.getY(), y) &&
Objects.equals(entry.getZ(), z))) {
return false;
}
put(x, y, z);
return true;
}
@Override
public List<Z> getZ(X x, Y y) {
Set<Z> result = xyIndex.getOrDefault(x, new ConcurrentHashMap<>()).get(y);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public List<Y> getY(X x, Z z) {
Set<Y> result = zxIndex.getOrDefault(z, new ConcurrentHashMap<>()).get(x);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
public List<X> getX(Y y, Z z) {
Set<X> result = yzIndex.getOrDefault(y, new ConcurrentHashMap<>()).get(z);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public List<Map.Entry<X, Y>> getXY(Z z) {
return buildDataFromIndex(zxIndex.get(z));
}
@Override
public List<Map.Entry<Y, Z>> getYZ(X x) {
return buildDataFromIndex(xyIndex.get(x));
}
@Override
public List<Map.Entry<X, Z>> getXZ(Y y) {
return reverseDataFromIndex(yzIndex.get(y));
}
@Override
public List<X> getAllX() {
Set<X> resultSet = new HashSet<>(xyIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Y> getAllY() {
Set<Y> resultSet = new HashSet<>(yzIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Z> getAllZ() {
Set<Z> resultSet = new HashSet<>(zxIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public void clearXY(Z z) {
super.clearXY(z);
zxIndex.remove(z);
}
@Override
public void clearYZ(X x) {
super.clearYZ(x);
xyIndex.remove(x);
}
@Override
public void clearXZ(Y y) {
super.clearXZ(y);
yzIndex.remove(y);
}
@Override
public void clearAll() {
super.clearAll();
xyIndex.clear();
yzIndex.clear();
zxIndex.clear();
}
private <K, V, R> List<R> buildEntriesFromIndex(ConcurrentHashMap<K, Set<V>> indexMap,
BiFunction<K, V, R> entryCreator) {
List<R> result = new ArrayList<>();
if (indexMap != null) {
for (Map.Entry<K, Set<V>> entry : indexMap.entrySet()) {
K key = entry.getKey();
Set<V> valueSet = entry.getValue();
if (valueSet != null && !valueSet.isEmpty()) {
for (V value : valueSet) {
result.add(entryCreator.apply(key, value));
}
}
}
}
return result;
}
private <K, V> List<Map.Entry<K, V>> buildDataFromIndex(ConcurrentHashMap<K, Set<V>> indexMap) {
return buildEntriesFromIndex(indexMap, AbstractMap.SimpleEntry::new);
}
private <K, V> List<Map.Entry<V, K>> reverseDataFromIndex(ConcurrentHashMap<K, Set<V>> indexMap) {
return buildEntriesFromIndex(indexMap, (key, value) -> new AbstractMap.SimpleEntry<>(value, key));
}
}
@@ -1,29 +0,0 @@
package fun.bm.lophine.utils.concurrent;
public class TableEntry<X, Y, Z> {
private final X x;
private final Y y;
private Z z;
public TableEntry(X x, Y y, Z z) {
this.x = x;
this.y = y;
this.z = z;
}
public X getX() {
return x;
}
public Y getY() {
return y;
}
public Z getZ() {
return z;
}
public void setZ(Z z) {
this.z = z;
}
}
@@ -17,7 +17,6 @@
package org.leavesmc.leaves.bot;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -33,7 +32,7 @@ import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
import java.util.Objects;
import java.util.function.Consumer;
public record BotCreateState(String realName, String name, String skinName, String[] skin, Location location,
public record BotCreateState(String rawName, String fullName, String skinName, String[] skin, Location location,
BotCreateEvent.CreateReason createReason, CommandSender creator) {
private static final MinecraftServer server = MinecraftServer.getServer();
@@ -43,15 +42,15 @@ public record BotCreateState(String realName, String name, String skinName, Stri
}
@NotNull
public static Builder builder(@NotNull String realName, @Nullable Location location) {
return new Builder(realName, location);
public static Builder builder(@NotNull String rawName, @Nullable Location location) {
return new Builder(rawName, location);
}
public static class Builder implements BotCreator {
private final String realName;
private final String rawName; // For internal calculation, use it as little as possible
private String name;
private String fullName;
private Location location;
private String skinName;
@@ -60,14 +59,14 @@ public record BotCreateState(String realName, String name, String skinName, Stri
private BotCreateEvent.CreateReason createReason;
private CommandSender creator;
private Builder(@NotNull String realName, @Nullable Location location) {
Objects.requireNonNull(realName);
private Builder(@NotNull String rawName, @Nullable Location location) {
Objects.requireNonNull(rawName);
this.realName = realName;
this.rawName = rawName;
this.location = location;
this.name = FakeplayerConfig.prefix + realName + FakeplayerConfig.suffix;
this.skinName = this.realName;
this.fullName = BotUtil.getFullName(rawName);
this.skinName = this.rawName;
this.skin = null;
this.createReason = BotCreateEvent.CreateReason.UNKNOWN;
this.creator = null;
@@ -75,7 +74,7 @@ public record BotCreateState(String realName, String name, String skinName, Stri
public Builder name(@NotNull String name) {
Objects.requireNonNull(name);
this.name = name;
this.fullName = name;
return this;
}
@@ -113,31 +112,24 @@ public record BotCreateState(String realName, String name, String skinName, Stri
}
public BotCreateState build() {
return new BotCreateState(realName, name, skinName, skin, location, createReason, creator);
return new BotCreateState(rawName, fullName, skinName, skin, location, createReason, creator);
}
public void spawnWithSkin(Consumer<Bot> consumer) {
Bukkit.getRegionScheduler().execute(
MinecraftInternalPlugin.INSTANCE,
location.getWorld(),
location.getBlockX() >> 4,
location.getBlockZ() >> 4,
() -> {
this.mojangAPISkin();
Bukkit.getRegionScheduler().execute(
MinecraftInternalPlugin.INSTANCE,
location.getWorld(),
location.getBlockX() >> 4,
location.getBlockZ() >> 4,
() -> {
CraftBot bot = this.spawn();
if (bot != null && consumer != null) {
consumer.accept(bot);
}
}
);
}
);
Bukkit.getAsyncScheduler().runNow(MinecraftInternalPlugin.INSTANCE, (task0) -> {
this.mojangAPISkin();
Bukkit.getRegionScheduler().execute(
MinecraftInternalPlugin.INSTANCE,
location.getWorld(),
location.getBlockX() >> 4,
location.getBlockZ() >> 4,
() -> {
CraftBot bot = this.spawn();
if (bot != null && consumer != null) {
consumer.accept(bot);
}
});
});
}
@Nullable
@@ -36,10 +36,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Optional;
public class BotDataStorage implements IPlayerDataStorage {
private static final LevelResource BOT_DATA_DIR = new LevelResource("fakeplayerdata");
private static final LevelResource BOT_LIST_FILE = new LevelResource("fakeplayer.dat");
public class BotDataStorage {
private static final Logger LOGGER = LogUtils.getLogger();
private final File botDir;
@@ -47,9 +44,9 @@ public class BotDataStorage implements IPlayerDataStorage {
private CompoundTag savedBotList;
public BotDataStorage(LevelStorageSource.@NotNull LevelStorageAccess session) {
this.botDir = session.getLevelPath(BOT_DATA_DIR).toFile();
this.botListFile = session.getLevelPath(BOT_LIST_FILE).toFile();
public BotDataStorage(LevelStorageSource.@NotNull LevelStorageAccess session, String dataDir, String listFileName) {
this.botDir = session.getLevelPath(new LevelResource(dataDir)).toFile();
this.botListFile = session.getLevelPath(new LevelResource(listFileName)).toFile();
this.botDir.mkdirs();
this.savedBotList = new CompoundTag();
@@ -62,7 +59,6 @@ public class BotDataStorage implements IPlayerDataStorage {
}
}
@Override
public void save(Player player) {
boolean flag = true;
try {
@@ -85,22 +81,27 @@ public class BotDataStorage implements IPlayerDataStorage {
if (flag && player instanceof ServerBot bot) {
CompoundTag nbt = new CompoundTag();
nbt.putString("name", bot.createState.name());
nbt.putString("name", bot.createState.fullName());
nbt.store("uuid", UUIDUtil.CODEC, bot.getUUID());
nbt.putBoolean("resume", bot.resume);
this.savedBotList.put(bot.createState.realName(), nbt);
this.savedBotList.put(bot.createState.fullName(), nbt);
this.saveBotList();
}
}
public Optional<ValueInput> load(Player player, ProblemReporter reporter) {
return this.load(player.getScoreboardName(), player.getStringUUID()).map(nbt -> {
ValueInput valueInput = TagValueInput.create(reporter, player.registryAccess(), nbt);
player.load(valueInput);
public Optional<ValueInput> load(@NotNull ServerBot bot, ProblemReporter reporter) {
return this.load(bot.nameAndId().name(), bot.nameAndId().id().toString()).map(nbt -> {
ValueInput valueInput = TagValueInput.create(reporter, bot.registryAccess(), nbt);
bot.load(valueInput);
return valueInput;
});
}
public void removeSavedData(@NotNull ServerBot bot) {
this.load(bot.nameAndId().name(), bot.nameAndId().id().toString());
}
private Optional<CompoundTag> load(String name, String uuid) {
File file = new File(this.botDir, uuid + ".dat");
@@ -21,13 +21,14 @@ import ca.spottedleaf.moonrise.common.util.TickThread;
import com.google.common.collect.Maps;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import fun.bm.lophine.config.modules.function.OldFeatureConfig;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.profile.MutablePropertyMap;
import io.papermc.paper.threadedregions.RegionizedServer;
import io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler;
import io.papermc.paper.util.MCUtil;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.minecraft.nbt.CompoundTag;
@@ -46,10 +47,10 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.ValueInput;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.event.entity.EntityRemoveEvent;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.event.bot.*;
@@ -68,23 +69,48 @@ public class BotList {
private final MinecraftServer server;
public final List<ServerBot> bots = new CopyOnWriteArrayList<>();
private final BotDataStorage dataStorage;
private final BotDataStorage manualSaveDataStorage;
private final BotDataStorage resumeDataStorage;
private final Map<UUID, ServerBot> botsByUUID = Maps.newHashMap();
private final Map<String, ServerBot> botsByName = Maps.newHashMap();
private final Map<String, Set<String>> botsNameByWorldUuid = Maps.newHashMap();
private final Map<String, Set<String>> legacyBotsNameByWorldUuid = Maps.newHashMap();
public boolean forceShutdown = false;
public BotList(MinecraftServer server) {
public BotList(@NotNull MinecraftServer server) {
this.server = server;
this.dataStorage = new BotDataStorage(server.storageSource);
this.manualSaveDataStorage = new BotDataStorage(server.storageSource, "fakeplayerdata", "fakeplayer.dat");
this.resumeDataStorage = new BotDataStorage(server.storageSource, "resume_fakeplayerdata", "resume_fakeplayer.dat");
INSTANCE = this;
}
public ServerBot createNewBot(BotCreateState state) {
BotCreateEvent event = new BotCreateEvent(state.name(), state.skinName(), state.location(), state.createReason(), state.creator());
event.setCancelled(!BotUtil.isCreateLegal(state.name()));
public void saveAllResumeBots(final int interval) {
MCUtil.ensureMain("Save Bots", () -> {
final long now = System.currentTimeMillis() / 50;
for (ServerBot bot : bots) {
if (interval == -1 || now - bot.lastSave >= interval) {
this.resumeDataStorage.save(bot);
bot.lastSave = now;
}
}
return null;
});
}
public void saveAllResumeBots() {
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
return;
}
for (ServerBot bot : this.bots) {
this.resumeDataStorage.save(bot);
}
}
public ServerBot createNewBot(@NotNull BotCreateState state) {
BotCreateEvent event = new BotCreateEvent(state.fullName(), state.skinName(), state.location(), state.createReason(), state.creator());
event.setCancelled(!BotUtil.isCreateLegal(state.fullName()));
this.server.server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
@@ -94,7 +120,7 @@ public class BotList {
Location location = event.getCreateLocation();
ServerLevel world = ((CraftWorld) location.getWorld()).getHandle();
GameProfile profile = setProfile(BotUtil.getBotUUID(state), state.name(), state.skin());
GameProfile profile = createBotProfile(BotUtil.getBotUUID(state), state.fullName(), state.skin());
ServerBot bot = new ServerBot(this.server, world, profile);
bot.createState = state;
if (event.getCreator() instanceof org.bukkit.entity.Player player) {
@@ -104,56 +130,58 @@ public class BotList {
return this.placeNewBot(bot, world, location, null);
}
public ServerBot loadNewBot(String realName) {
try {
return this.loadNewBot(realName, this.dataStorage);
} catch (Exception e) {
LOGGER.error("Failed to load bot {}", realName, e);
return null;
}
public ServerBot loadNewManualSavedBot(String fullName) {
return this.loadNewBot(fullName, this.manualSaveDataStorage);
}
public ServerBot loadNewBot(String realName, IPlayerDataStorage playerIO) {
UUID uuid = BotUtil.getBotUUID(realName);
public ServerBot loadNewResumeBot(String fullName) {
return this.loadNewBot(fullName, this.resumeDataStorage);
}
BotLoadEvent event = new BotLoadEvent(realName, uuid);
this.server.server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
public ServerBot loadNewBot(String fullName, BotDataStorage storage) {
if (botsByName.containsKey(fullName)) {
return null;
}
try {
UUID uuid = BotUtil.getBotUUID(fullName);
ServerBot bot = new ServerBot(this.server, this.server.getLevel(Level.OVERWORLD), new GameProfile(uuid, realName));
bot.connection = new ServerBotPacketListenerImpl(this.server, bot, new ServerBotPacketListenerImpl.BotConnection());
bot.connection.markClientLoaded();
Optional<ValueInput> optional;
if (playerIO instanceof BotDataStorage botIO) {
BotLoadEvent event = new BotLoadEvent(fullName, uuid);
this.server.server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return null;
}
ServerBot bot = new ServerBot(this.server, this.server.getLevel(Level.OVERWORLD), new GameProfile(uuid, fullName));
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
Optional<ValueInput> optional;
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(bot.problemPath(), LOGGER)) {
optional = botIO.load(bot, scopedCollector);
optional = storage.load(bot, scopedCollector);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
optional = Optional.empty();
}
if (optional.isEmpty()) {
return null;
}
ValueInput nbt = optional.get();
ResourceKey<Level> resourcekey = null;
if (nbt.getLong("WorldUUIDMost").isPresent() && nbt.getLong("WorldUUIDLeast").isPresent()) {
World bWorld = Bukkit.getServer().getWorld(new UUID(nbt.getLong("WorldUUIDMost").orElseThrow(), nbt.getLong("WorldUUIDLeast").orElseThrow()));
if (bWorld != null) {
resourcekey = ((CraftWorld) bWorld).getHandle().dimension();
if (optional.isEmpty()) {
return null;
}
}
if (resourcekey == null) {
ValueInput nbt = optional.get();
ResourceKey<Level> resourcekey = null;
if (nbt.getLong("WorldUUIDMost").isPresent() && nbt.getLong("WorldUUIDLeast").isPresent()) {
org.bukkit.World bWorld = Bukkit.getServer().getWorld(new UUID(nbt.getLong("WorldUUIDMost").orElseThrow(), nbt.getLong("WorldUUIDLeast").orElseThrow()));
if (bWorld != null) {
resourcekey = ((CraftWorld) bWorld).getHandle().dimension();
}
}
if (resourcekey == null) {
return null;
}
ServerLevel world = this.server.getLevel(resourcekey);
return this.placeNewBot(bot, world, bot.getLocation(), nbt);
} catch (Exception e) {
LOGGER.error("Failed to load bot {}", fullName, e);
return null;
}
ServerLevel world = this.server.getLevel(resourcekey);
return this.placeNewBot(bot, world, bot.getLocation(), nbt);
}
public ServerBot placeNewBot(@NotNull ServerBot bot, ServerLevel world, Location location, ValueInput save) {
@@ -161,7 +189,7 @@ public class BotList {
bot.isRealPlayer = true;
bot.loginTime = System.currentTimeMillis();
bot.connection = new ServerBotPacketListenerImpl(this.server, bot, new ServerBotPacketListenerImpl.BotConnection());
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
bot.connection.markClientLoaded();
bot.setServerLevel(world);
@@ -183,65 +211,60 @@ public class BotList {
bot.suppressTrackerForLogin = true;
optional.ifPresent(nbt -> {
bot.loadAndSpawnEnderPearls(nbt);
bot.loadAndSpawnParentVehicle(nbt);
});
Runnable task = () -> {
optional.ifPresent(nbt -> {
bot.loadAndSpawnEnderPearls(nbt);
bot.loadAndSpawnParentVehicle(nbt);
});
world.getCurrentWorldData().connections.add(bot.connection.connection);
world.addNewPlayer(bot);
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
this.server.server.getPluginManager().callEvent(event1);
net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
}
bot.renderInfo();
bot.suppressTrackerForLogin = false;
bot.level().getChunkSource().chunkMap.addEntity(bot);
bot.renderData();
bot.initInventoryMenu();
botsNameByWorldUuid
.computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>())
.add(bot.getBukkitEntity().getName());
BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
};
if (TickThread.isTickThreadFor(world, location.blockX() >> 4, location.blockZ() >> 4)) {
summonBot(bot, world);
task.run();
} else {
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
world, location.getBlockX() >> 4, location.blockZ() >> 4,
() -> summonBot(bot, world));
task);
}
return bot;
}
private ServerBot summonBot(ServerBot bot, ServerLevel world) {
world.getCurrentWorldData().connections.add(bot.connection.connection);
world.addNewPlayer(bot);
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
this.server.server.getPluginManager().callEvent(event1);
net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
}
bot.renderInfo();
bot.suppressTrackerForLogin = false;
bot.level().getChunkSource().chunkMap.addEntity(bot);
bot.renderData();
bot.initInventoryMenu();
botsNameByWorldUuid
.computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>())
.add(bot.getBukkitEntity().getRealName());
BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
return bot;
}
/*
* return true if async
*/
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved, boolean async) {
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean save, boolean resume, boolean async) {
if (async && !TickThread.isTickThreadFor(bot.level(), bot.getX(), bot.getZ())) {
bot.getBukkitEntity().taskScheduler.schedule((Entity unused) -> this.removeBot(bot, reason, remover, saved), null, 1L);
return true;
bot.getBukkitEntity().taskScheduler.schedule((Entity unused) -> this.removeBot(bot, reason, remover, save, resume), null, 1L);
return true; // async always return true
}
this.removeBot(bot, reason, remover, saved);
return false;
return this.removeBot(bot, remover, reason, save, resume);
}
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved) {
return this.removeBot(bot, reason, remover, saved, this.dataStorage);
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean save, boolean resume) {
return this.removeBot(bot, reason, remover, save, resume, true);
}
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved, IPlayerDataStorage playerIO) {
BotRemoveEvent event = new BotRemoveEvent(bot.getBukkitEntity(), reason, remover, PaperAdventure.asAdventure(Component.translatable("multiplayer.player.left", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)), saved);
public boolean removeBot(@NotNull ServerBot bot, @Nullable CommandSender remover, @NotNull BotRemoveEvent.RemoveReason reason, boolean save, boolean resume) {
BotRemoveEvent event = new BotRemoveEvent(bot.getBukkitEntity(), reason, remover, PaperAdventure.asAdventure(Component.translatable("multiplayer.player.left", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)), save);
this.server.server.getPluginManager().callEvent(event);
if (event.isCancelled() && event.getReason() != BotRemoveEvent.RemoveReason.INTERNAL) {
@@ -255,11 +278,16 @@ public class BotList {
bot.disconnect();
this.resumeDataStorage.removeSavedData(bot);
if (event.shouldSave()) {
playerIO.save(bot);
if (resume) {
this.resumeDataStorage.save(bot);
} else {
this.manualSaveDataStorage.save(bot);
}
} else {
bot.dropAll(true);
botsNameByWorldUuid.getOrDefault(bot.level().uuid.toString(), new HashSet<>()).remove(bot.getBukkitEntity().getRealName());
botsNameByWorldUuid.getOrDefault(bot.level().uuid.toString(), new HashSet<>()).remove(bot.getBukkitEntity().getName());
}
if (bot.isPassenger() && event.shouldSave()) {
@@ -267,7 +295,7 @@ public class BotList {
if (entity.hasExactlyOnePlayerPassenger()) {
bot.stopRiding();
entity.getPassengersAndSelf().forEach((entity1) -> {
if (entity1 instanceof AbstractVillager villager) {
if (!OldFeatureConfig.villagerVoidTrade && entity1 instanceof AbstractVillager villager) {
final Player human = villager.getTradingPlayer();
if (human != null) {
villager.setTradingPlayer(null);
@@ -312,15 +340,14 @@ public class BotList {
if (removeMessage != null && !removeMessage.equals(net.kyori.adventure.text.Component.empty())) {
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(removeMessage), false);
}
return true;
}
public void removeAllIn(String worldUuid) {
for (String realName : this.botsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())) {
ServerBot bot = this.getBotByName(realName);
for (String fullName : this.botsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())) {
ServerBot bot = this.getBotByName(fullName);
if (bot != null) {
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident, FakeplayerConfig.canResident);
}
}
}
@@ -332,7 +359,7 @@ public class BotList {
for (ServerBot bot : this.bots) {
bot.resume = FakeplayerConfig.canResident;
if (TickThread.isTickThreadFor(bot.level(), bot.getX(), bot.getZ())) {
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident, FakeplayerConfig.canResident);
} else {
finished = false;
check.getAndIncrement();
@@ -349,7 +376,7 @@ public class BotList {
}
counter.getAndIncrement();
try {
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident, FakeplayerConfig.canResident);
received.getAndIncrement();
} catch (Exception e) {
this.removeBot(bot, check, received, counter);
@@ -361,24 +388,39 @@ public class BotList {
}, null, 1L);
}
public void loadBotInfo() {
public void loadResumeBotInfo() {
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
return;
}
CompoundTag savedBotList = this.getSavedBotList().copy();
for (String realName : savedBotList.keySet()) {
CompoundTag nbt = savedBotList.getCompound(realName).orElseThrow();
if (!nbt.getBoolean("resume").orElse(false)) {
continue;
}
UUID levelUuid = BotUtil.getBotLevel(realName, this.dataStorage);
CompoundTag savedBotList = this.getResumeBotList().copy();
for (String fullName : savedBotList.keySet()) {
UUID levelUuid = BotUtil.getBotLevel(fullName, this.resumeDataStorage);
if (levelUuid == null) {
LOGGER.warn("Bot {} has no world UUID, skipping loading.", realName);
LOGGER.warn("Bot {} has no world UUID, skipping loading.", fullName);
continue;
}
this.botsNameByWorldUuid
.computeIfAbsent(levelUuid.toString(), (k) -> new HashSet<>())
.add(realName);
.add(fullName);
}
loadLegacyResumeBotInfo();
}
private void loadLegacyResumeBotInfo() {
CompoundTag savedBotList = this.getManualSavedBotList().copy();
for (String fullName : savedBotList.keySet()) {
CompoundTag nbt = savedBotList.getCompound(fullName).orElseThrow();
if (!nbt.getBoolean("resume").orElse(false)) {
continue;
}
UUID levelUuid = BotUtil.getBotLevel(fullName, this.manualSaveDataStorage);
if (levelUuid == null) {
LOGGER.warn("Bot {} has no world UUID, skipping loading.", fullName);
continue;
}
this.legacyBotsNameByWorldUuid
.computeIfAbsent(levelUuid.toString(), (k) -> new HashSet<>())
.add(fullName);
}
}
@@ -386,23 +428,19 @@ public class BotList {
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
return;
}
Set<String> bots = this.botsNameByWorldUuid.get(worldUuid);
if (bots == null) {
return;
}
Set<String> botsCopy = new HashSet<>(bots);
botsCopy.forEach(this::loadNewBot);
new HashSet<>(this.botsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())).forEach(this::loadNewResumeBot);
new HashSet<>(this.legacyBotsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())).forEach(this::loadNewManualSavedBot);
}
public void updateBotLevel(ServerBot bot, ServerLevel level) {
public void updateBotLevel(@NotNull ServerBot bot, @NotNull ServerLevel level) {
String prevUuid = bot.level().uuid.toString();
String newUuid = level.uuid.toString();
this.botsNameByWorldUuid
.computeIfAbsent(newUuid, (k) -> new HashSet<>())
.add(bot.getBukkitEntity().getRealName());
.add(bot.getBukkitEntity().getName());
this.botsNameByWorldUuid
.computeIfAbsent(prevUuid, (k) -> new HashSet<>())
.remove(bot.getBukkitEntity().getRealName());
.remove(bot.getBukkitEntity().getName());
}
public void networkTick() {
@@ -419,15 +457,25 @@ public class BotList {
return this.botsByName.get(name.toLowerCase(Locale.ROOT));
}
public CompoundTag getSavedBotList() {
return this.dataStorage.getSavedBotList();
public CompoundTag getManualSavedBotList() {
return this.getSavedBotList(this.manualSaveDataStorage);
}
public GameProfile setProfile(UUID uuid, String name, String[] skin) {
GameProfile profile = new GameProfile(uuid, name);
if (skin == null) return profile;
PropertyMap profileMap = new MutablePropertyMap(profile.properties());
profileMap.put("textures", new Property("textures", skin[0], skin[1]));
return new GameProfile(uuid, name, profileMap);
public CompoundTag getResumeBotList() {
return this.getSavedBotList(this.resumeDataStorage);
}
public CompoundTag getSavedBotList(@NotNull BotDataStorage storage) {
return storage.getSavedBotList();
}
@Contract("_, _, _ -> new")
public static @NotNull GameProfile createBotProfile(UUID uuid, String name, String[] skin) {
GameProfile profile = new GameProfile(uuid, name, new MutablePropertyMap());
profile.properties().put("is_bot", new Property("is_bot", "true"));
if (skin != null) {
profile.properties().put("textures", new Property("textures", skin[0], skin[1]));
}
return profile;
}
}
@@ -24,6 +24,7 @@ import net.minecraft.stats.ServerStatsCounter;
import net.minecraft.stats.Stat;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NonNull;
import java.io.File;
@@ -44,7 +45,7 @@ public class BotStatsCounter extends ServerStatsCounter {
}
@Override
public void parse(@NotNull DataFixer dataFixer, @NotNull JsonElement json) {
public void parse(@NonNull DataFixer fixerUpper, @NonNull JsonElement json) {
}
@Override
@@ -85,15 +85,15 @@ public class BotUtil {
@NotNull
public static UUID getBotUUID(@NotNull BotCreateState state) {
return getBotUUID(state.realName());
return getBotUUID(state.fullName());
}
public static UUID getBotUUID(@NotNull String realName) {
return UUID.nameUUIDFromBytes(("Fakeplayer:" + realName).getBytes(Charsets.UTF_8));
public static UUID getBotUUID(@NotNull String fullName) {
return UUID.nameUUIDFromBytes(("Fakeplayer:" + fullName).getBytes(Charsets.UTF_8));
}
public static UUID getBotLevel(@NotNull String realName, BotDataStorage botDataStorage) {
UUID uuid = BotUtil.getBotUUID(realName);
public static UUID getBotLevel(@NotNull String fullName, BotDataStorage botDataStorage) {
UUID uuid = getBotUUID(fullName);
Optional<CompoundTag> tagOptional = botDataStorage.read(uuid.toString());
if (tagOptional.isEmpty()) {
return null;
@@ -1,27 +0,0 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.bot;
import net.minecraft.world.entity.player.Player;
public interface IPlayerDataStorage {
void save(Player player);
// Optional<ValueInput> load(Player player, ProblemReporter reporter);
}
@@ -19,7 +19,7 @@ package org.leavesmc.leaves.bot;
import com.google.common.collect.ImmutableMap;
import com.mojang.authlib.GameProfile;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.LophineLogger;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.event.entity.EntityKnockbackEvent;
@@ -77,16 +77,16 @@ import org.leavesmc.leaves.entity.bot.CraftBot;
import org.leavesmc.leaves.event.bot.*;
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
import org.leavesmc.leaves.util.MathUtils;
import org.slf4j.Logger;
import java.util.*;
import java.util.function.Predicate;
import static net.minecraft.server.MinecraftServer.getServer;
public class ServerBot extends ServerPlayer {
private final List<AbstractBotAction<?>> actions;
private final Map<String, AbstractBotConfig<?, ?, ?>> configs;
private static final Logger LOGGER = LogUtils.getClassLogger();
private final Map<String, AbstractBotConfig<?, ?>> configs;
public boolean resume = false;
public BotCreateState createState;
@@ -108,8 +108,8 @@ public class ServerBot extends ServerPlayer {
this.gameMode = new ServerBotGameMode(this);
this.actions = new ArrayList<>();
ImmutableMap.Builder<String, AbstractBotConfig<?, ?, ?>> configBuilder = ImmutableMap.builder();
for (AbstractBotConfig<?, ?, ?> config : Configs.getConfigs()) {
ImmutableMap.Builder<String, AbstractBotConfig<?, ?>> configBuilder = ImmutableMap.builder();
for (AbstractBotConfig<?, ?> config : Configs.getConfigs()) {
configBuilder.put(config.getName(), config.create().setBot(this));
}
this.configs = configBuilder.build();
@@ -139,7 +139,7 @@ public class ServerBot extends ServerPlayer {
this.joining = false;
}
this.resetOperationCountPerTick(); // Leaves - player operation limiter
// this.resetOperationCountPerTick(); // Leaves - player operation limiter
this.wardenSpawnTracker.tick();
if (this.invulnerableTime > 0) {
this.invulnerableTime--;
@@ -213,17 +213,7 @@ public class ServerBot extends ServerPlayer {
this.updateIsUnderwater();
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.NETWORK) {
try {
Bukkit.getRegionScheduler().execute(
MinecraftInternalPlugin.INSTANCE,
this.level().getWorld(),
this.getBlockX() >> 4,
this.getBlockZ() >> 4,
this::runAction
);
} catch (Exception e) {
this.runAction();
}
getServer().scheduleOnMain(this::runAction);
}
this.livingEntityTick();
@@ -305,6 +295,18 @@ public class ServerBot extends ServerPlayer {
teleportTransition.postTeleportTransition().onTransition(this);
this.isChangingDimension = false;
// Lophine - We don't have this
/* if (LeavesConfig.modify.netherPortalFix) {
final ResourceKey<Level> fromDim = fromLevel.dimension();
final ResourceKey<Level> toDim = level().dimension();
if (!((fromDim != Level.OVERWORLD || toDim != Level.NETHER) && (fromDim != Level.NETHER || toDim != Level.OVERWORLD))) {
BlockPos fromPortal = org.leavesmc.leaves.util.ReturnPortalManager.findPortalAt(this, fromDim, lastPos);
BlockPos toPos = this.blockPosition();
if (fromPortal != null) {
org.leavesmc.leaves.util.ReturnPortalManager.storeReturnPortal(this, toDim, toPos, fromPortal);
}
}
}*/
if (this.isBlocking()) {
this.stopUsingItem();
}
@@ -350,7 +352,7 @@ public class ServerBot extends ServerPlayer {
if (FakeplayerConfig.canOpenInventory) {
if (player instanceof ServerPlayer player1 && player.getMainHandItem().isEmpty()) {
BotInventoryOpenEvent event = new BotInventoryOpenEvent(this.getBukkitEntity(), player1.getBukkitEntity());
MinecraftServer.getServer().server.getPluginManager().callEvent(event);
getServer().server.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
player.openMenu(new SimpleMenuProvider((i, inventory, p) -> ChestMenu.sixRows(i, inventory, this.container), this.getDisplayName()));
return InteractionResult.SUCCESS;
@@ -372,8 +374,8 @@ public class ServerBot extends ServerPlayer {
nbt.putBoolean("isShiftKeyDown", this.isShiftKeyDown());
CompoundTag createNbt = new CompoundTag();
createNbt.putString("realName", this.createState.realName());
createNbt.putString("name", this.createState.name());
createNbt.putString("rawName", this.createState.rawName());
createNbt.putString("name", this.createState.fullName());
createNbt.putString("skinName", this.createState.skinName());
if (this.createState.skin() != null) {
@@ -395,7 +397,7 @@ public class ServerBot extends ServerPlayer {
if (!this.configs.isEmpty()) {
ValueOutput.TypedOutputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC);
for (AbstractBotConfig<?, ?, ?> config : this.configs.values()) {
for (AbstractBotConfig<?, ?> config : this.configs.values()) {
configNbt.add(config.save(new CompoundTag()));
}
}
@@ -407,7 +409,11 @@ public class ServerBot extends ServerPlayer {
this.setShiftKeyDown(nbt.getBooleanOr("isShiftKeyDown", false));
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC).orElseThrow();
BotCreateState.Builder createBuilder = BotCreateState.builder(createNbt.getString("realName").orElseThrow(), null).name(createNbt.getString("name").orElseThrow());
BotCreateState.Builder createBuilder = BotCreateState
.builder(createNbt.getString("rawName")
.orElseGet(() -> createNbt.getString("realName")
.orElseThrow()), null) // Convert from legacy version, consider to use ca.spottedleaf.dataconverter.minecraft.MCDataConverter instead for release version
.name(createNbt.getString("name").orElseThrow());
String[] skin = null;
if (createNbt.contains("skin")) {
@@ -422,7 +428,7 @@ public class ServerBot extends ServerPlayer {
createBuilder.createReason(BotCreateEvent.CreateReason.INTERNAL).creator(null);
this.createState = createBuilder.build();
this.gameProfile = BotList.INSTANCE.setProfile(this.getUUID(), this.createState.name(), this.createState.skin());
this.gameProfile = BotList.createBotProfile(this.getUUID(), this.createState.fullName(), this.createState.skin());
if (nbt.list("actions", CompoundTag.CODEC).isPresent()) {
@@ -440,8 +446,9 @@ public class ServerBot extends ServerPlayer {
if (nbt.list("configs", CompoundTag.CODEC).isPresent()) {
ValueInput.TypedInputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC).orElseThrow();
for (CompoundTag configTag : configNbt) {
AbstractBotConfig<?, ?, ?> config = Configs.getConfig(configTag.getString("configName").orElseThrow());
AbstractBotConfig<?, ?> config = Configs.getConfig(configTag.getString("configName").orElseThrow());
if (config != null) {
config.setBot(this);
config.load(configTag);
}
}
@@ -466,30 +473,30 @@ public class ServerBot extends ServerPlayer {
ChunkMap.TrackedEntity entityTracker = this.moonrise$getTrackedEntity();
if (entityTracker == null) {
LOGGER.warn("Fakeplayer cant get entity tracker for {}", this.getId());
LophineLogger.LOGGER.warn("Fakeplayer cant get entity tracker for {}", this.getId());
return;
}
playerConnection.send(this.getAddEntityPacket(entityTracker.serverEntity));
if (login) {
Bukkit.getGlobalRegionScheduler().runDelayed(MinecraftInternalPlugin.INSTANCE, (unused) -> playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f))), 10);
Bukkit.getGlobalRegionScheduler().runDelayed(MinecraftInternalPlugin.INSTANCE, (task) -> playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f))), 10);
} else {
playerConnection.send(new ClientboundRotateHeadPacket(this, (byte) ((getYRot() * 256f) / 360f)));
}
}
public void renderInfo() {
MinecraftServer.getServer().getPlayerList().getPlayers().forEach(this::sendPlayerInfo);
getServer().getPlayerList().getPlayers().forEach(this::sendPlayerInfo);
}
public void renderData() {
MinecraftServer.getServer().getPlayerList().getPlayers().forEach(
getServer().getPlayerList().getPlayers().forEach(
player -> this.sendFakeDataIfNeed(player, false)
);
}
private void sendPacket(Packet<?> packet) {
MinecraftServer.getServer().getPlayerList().getPlayers().forEach(player -> player.connection.send(packet));
getServer().getPlayerList().getPlayers().forEach(player -> player.connection.send(packet));
}
@Override
@@ -498,7 +505,7 @@ public class ServerBot extends ServerPlayer {
Component defaultMessage = this.getCombatTracker().getDeathMessage();
BotDeathEvent event = new BotDeathEvent(this.getBukkitEntity(), PaperAdventure.asAdventure(defaultMessage), flag);
MinecraftServer.getServer().server.getPluginManager().callEvent(event);
getServer().server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
if (this.getHealth() <= 0) {
@@ -511,18 +518,23 @@ public class ServerBot extends ServerPlayer {
net.kyori.adventure.text.Component deathMessage = event.deathMessage();
if (event.isSendDeathMessage() && deathMessage != null && !deathMessage.equals(net.kyori.adventure.text.Component.empty())) {
MinecraftServer.getServer().getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(deathMessage), false);
getServer().getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(deathMessage), false);
}
MinecraftServer.getServer().getBotList().removeBot(this, BotRemoveEvent.RemoveReason.DEATH, null, false);
// TODO: separate die and remove logic, call super.die here
this.removeEntitiesOnShoulder();
if (this.level().getGameRules().get(GameRules.FORGIVE_DEAD_PLAYERS)) {
this.tellNeutralMobsThatIDied();
}
getServer().getBotList().removeBot(this, BotRemoveEvent.RemoveReason.DEATH, null, false, false);
}
@Override
public boolean startRiding(Entity entity, boolean force, boolean triggerEvents) {
if (super.startRiding(entity, force, triggerEvents)) {
if (entity.getControllingPassenger() == this) { // see net.minecraft.server.networkServerGamePacketListenerImpl#handleMoveVehicle
public boolean startRiding(@NotNull Entity vehicle, boolean force, boolean sendGameEvent) {
if (super.startRiding(vehicle, force, sendGameEvent)) {
if (vehicle.getControllingPassenger() == this) { // see net.minecraft.server.networkServerGamePacketListenerImpl#handleMoveVehicle
this.setDeltaMovement(Vec3.ZERO);
this.setYRot(entity.yRotO);
this.setYRot(vehicle.yRotO);
}
return true;
} else {
@@ -669,15 +681,15 @@ public class ServerBot extends ServerPlayer {
}
@SuppressWarnings("unchecked")
public <O, I, E extends AbstractBotConfig<O, I, E>> AbstractBotConfig<O, I, E> getConfig(@NotNull AbstractBotConfig<O, I, E> config) {
return (AbstractBotConfig<O, I, E>) Objects.requireNonNull(this.configs.get(config.getName()));
public <T, E extends AbstractBotConfig<T, E>> AbstractBotConfig<T, E> getConfig(@NotNull AbstractBotConfig<T, E> config) {
return (AbstractBotConfig<T, E>) Objects.requireNonNull(this.configs.get(config.getName()));
}
public Collection<AbstractBotConfig<?, ?, ?>> getAllConfigs() {
public Collection<AbstractBotConfig<?, ?>> getAllConfigs() {
return configs.values();
}
public <O, I, E extends AbstractBotConfig<O, I, E>> O getConfigValue(@NotNull AbstractBotConfig<O, I, E> config) {
public <T, E extends AbstractBotConfig<T, E>> T getConfigValue(@NotNull AbstractBotConfig<T, E> config) {
return this.getConfig(config).getValue();
}
@@ -79,7 +79,7 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
this.level.sendBlockUpdated(pos, blockState, blockState, 3);
return false;
} else {
BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player); // Leaves - no block update
BlockState blockState1 = /*isNoBlockUpdate() ? blockState : */block.playerWillDestroy(this.level, pos, blockState, this.player); // Leaves - no block update
boolean flag = this.level.removeBlock(pos, false);
if (flag) {
block.destroy(this.level, pos, blockState1);
@@ -29,12 +29,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
public final Connection fakeConnection;
public ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot, BotConnection botConnection) {
super(server, botConnection, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
this.fakeConnection = botConnection;
this.fakeConnection.packetListener = this;
public ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot) {
super(server, BotConnection.INSTANCE, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
}
@Override
@@ -56,6 +53,8 @@ public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
public static class BotConnection extends Connection {
private static final BotConnection INSTANCE = new BotConnection();
public BotConnection() {
super(PacketFlow.SERVERBOUND);
}
@@ -28,7 +28,7 @@ import java.util.Map;
@SuppressWarnings({"unused"})
public class Configs {
private static final Map<Class<?>, AbstractBotConfig<?, ?, ?>> configs = new HashMap<>();
private static final Map<Class<?>, AbstractBotConfig<?, ?>> configs = new HashMap<>();
public static final SkipSleepConfig SKIP_SLEEP = register(new SkipSleepConfig());
public static final AlwaysSendDataConfig ALWAYS_SEND_DATA = register(new AlwaysSendDataConfig());
@@ -38,7 +38,7 @@ public class Configs {
public static final LocatorBarConfig ENABLE_LOCATOR_BAR = register(new LocatorBarConfig());
@Nullable
public static AbstractBotConfig<?, ?, ?> getConfig(String name) {
public static AbstractBotConfig<?, ?> getConfig(String name) {
return configs.values().stream()
.filter(config -> config.getName().equals(name))
.findFirst()
@@ -47,12 +47,12 @@ public class Configs {
@NotNull
@Contract(pure = true)
public static Collection<AbstractBotConfig<?, ?, ?>> getConfigs() {
public static Collection<AbstractBotConfig<?, ?>> getConfigs() {
return configs.values();
}
@SuppressWarnings("unchecked")
private static <Value, Type, E extends AbstractBotConfig<Value, Type, E>> @NotNull E register(AbstractBotConfig<Value, Type, E> instance) {
private static <T, E extends AbstractBotConfig<T, E>> @NotNull E register(AbstractBotConfig<T, E> instance) {
configs.put(instance.getClass(), instance);
return (E) instance;
}
@@ -0,0 +1,11 @@
package org.leavesmc.leaves.bot.agent;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
public record ExtraData(List<Pair<String, String>> raw) {
public void add(String key, String value) {
raw.add(Pair.of(key, value));
}
}
@@ -19,18 +19,18 @@ package org.leavesmc.leaves.bot.agent.actions;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.LophineLogger;
import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.WrappedArgument;
import org.leavesmc.leaves.event.bot.BotActionExecuteEvent;
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
import org.leavesmc.leaves.util.UpdateSuppressionException;
import org.slf4j.Logger;
import java.util.*;
import java.util.function.Consumer;
@@ -56,7 +56,6 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
private Consumer<E> onFail;
private Consumer<E> onSuccess;
private Consumer<E> onStop;
private static final Logger LOGGER = LogUtils.getClassLogger();
public AbstractBotAction(String name, Supplier<E> creator) {
this.name = name;
@@ -74,13 +73,12 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
public abstract Object asCraft();
public void provideActionData(@NotNull ActionData data) {
public String getActionDataString() {
return getActionDataString(new ExtraData(new ArrayList<>()));
}
public String getActionDataString() {
ActionData data = new ActionData(new ArrayList<>());
provideActionData(data);
return data.raw.stream()
public String getActionDataString(@NotNull ExtraData data) {
return data.raw().stream()
.map(pair -> pair.getLeft() + "=" + pair.getRight())
.reduce((a, b) -> a + ", " + b)
.orElse("No arguments");
@@ -141,7 +139,7 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
e.providePlayer(bot);
e.consume();
} catch (Exception e) {
LOGGER.warn("An error occurred while executing bot " + bot.displayName + ", action " + this.name, e);
LophineLogger.LOGGER.error("An error occurred while executing bot {}, action {}", bot.displayName, this.name, e);
}
if (result) {
@@ -179,12 +177,12 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
public void load(@NotNull CompoundTag nbt) {
this.uuid = nbt.read("actionUUID", UUIDUtil.CODEC).orElse(UUID.randomUUID());
this.initialTickDelay = nbt.getInt("initialTickDelay").orElse(0);
this.initialTickInterval = nbt.getInt("initialTickInterval").orElse(0);
this.initialNumber = nbt.getInt("initialNumber").orElse(0);
this.initialTickDelay = nbt.getIntOr("initialTickDelay", 0);
this.initialTickInterval = nbt.getIntOr("initialTickInterval", 0);
this.initialNumber = nbt.getIntOr("initialNumber", 0);
this.tickToNext = nbt.getInt("tickToNext").orElse(0);
this.numberRemaining = nbt.getInt("numberRemaining").orElse(0);
this.tickToNext = nbt.getIntOr("tickToNext", 0);
this.numberRemaining = nbt.getIntOr("numberRemaining", 0);
}
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
@@ -263,12 +261,4 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
public void setOnStop(Consumer<E> onStop) {
this.onStop = onStop;
}
public record ActionData(
List<Pair<String, String>> raw
) {
public void add(String key, String value) {
raw.add(Pair.of(key, value));
}
}
}
@@ -20,7 +20,6 @@ package org.leavesmc.leaves.bot.agent.actions;
import java.util.function.Supplier;
public abstract class AbstractStateBotAction<E extends AbstractStateBotAction<E>> extends AbstractBotAction<E> {
public AbstractStateBotAction(String name, Supplier<E> creator) {
super(name, creator);
this.setDoNumber(-1);
@@ -19,6 +19,7 @@ package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import java.util.function.Supplier;
@@ -30,12 +31,8 @@ public abstract class AbstractTimerBotAction<E extends AbstractTimerBotAction<E>
public AbstractTimerBotAction(String name, Supplier<E> creator) {
super(name, creator);
this.addArgument("delay", integer(0))
.suggests(strings("0", "5", "10", "20"))
.setOptional(true);
this.addArgument("interval", integer(0))
.suggests(strings("20", "0", "5", "10"))
.setOptional(true);
this.addArgument("delay", integer(0)).suggests(strings("0", "5", "10", "20")).setOptional(true);
this.addArgument("interval", integer(0)).suggests(strings("20", "0", "5", "10")).setOptional(true);
this.addArgument("do_number", integer(-1))
.suggests(((context, builder) -> builder.suggest("-1", Component.literal("do infinite times"))))
.setOptional(true);
@@ -49,11 +46,11 @@ public abstract class AbstractTimerBotAction<E extends AbstractTimerBotAction<E>
}
@Override
public void provideActionData(@NotNull ActionData data) {
super.provideActionData(data);
public String getActionDataString(@NotNull ExtraData data) {
data.add("delay", String.valueOf(this.getStartDelayTick()));
data.add("interval", String.valueOf(this.getDoIntervalTick()));
data.add("do_number", String.valueOf(this.getDoNumber()));
data.add("remaining_do_number", String.valueOf(this.getDoNumberRemaining()));
return super.getActionDataString(data);
}
}
@@ -22,6 +22,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionResult;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
@@ -115,10 +116,10 @@ public abstract class AbstractUseBotAction<T extends AbstractUseBotAction<T>> ex
}
@Override
public void provideActionData(@NotNull ActionData data) {
super.provideActionData(data);
public String getActionDataString(@NotNull ExtraData data) {
data.add("use_timeout", String.valueOf(this.useTickTimeout));
data.add("already_used_tick", String.valueOf(this.alreadyUsedTick));
return super.getActionDataString(data);
}
@Override
@@ -133,9 +134,9 @@ public abstract class AbstractUseBotAction<T extends AbstractUseBotAction<T>> ex
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.useTickTimeout = nbt.getInt("useTick").orElseThrow();
this.useTickTimeout = nbt.getIntOr("useTick", this.useTickTimeout);
this.alreadyUsedTick = nbt.getInt("alreadyUsedTick").orElseGet(
() -> this.useTickTimeout - nbt.getInt("tickToRelease").orElseThrow()
() -> nbt.getInt("tickToRelease").map(tickToRelease -> this.useTickTimeout - tickToRelease).orElse(this.alreadyUsedTick)
);
}
@@ -55,8 +55,8 @@ public class ServerFishAction extends AbstractTimerBotAction<ServerFishAction> {
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.initialFishInterval = nbt.getInt("initialFishInterval").orElseThrow();
this.tickToNextFish = nbt.getInt("tickToNextFish").orElseThrow();
this.initialFishInterval = nbt.getIntOr("initialFishInterval", this.initialFishInterval);
this.tickToNextFish = nbt.getIntOr("tickToNextFish", this.tickToNextFish);
}
@Override
@@ -18,15 +18,15 @@
package org.leavesmc.leaves.bot.agent.actions;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.arguments.coordinates.Coordinates;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.commands.arguments.selector.EntitySelector;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver;
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
@@ -42,35 +42,28 @@ public class ServerLookAction extends AbstractBotAction<ServerLookAction> {
public ServerLookAction() {
super("look", ServerLookAction::new);
declarePlayerBranch();
declareLocationBranch();
}
private void declarePlayerBranch() {
this.fork(0);
this.addArgument("player", EntityArgument.player())
.setOptional(true);
}
private void declareLocationBranch() {
this.addArgument("player", ArgumentTypes.player()).setOptional(true);
this.fork(1);
this.addArgument("location", Vec3Argument.vec3(false));
this.addArgument("location", ArgumentTypes.finePosition());
}
@Override
public void loadCommand(@NotNull CommandContext context) throws CommandSyntaxException {
EntitySelector selector = context.getArgumentOrDefault("player", EntitySelector.class, null);
Coordinates location = context.getArgumentOrDefault("location", Coordinates.class, null);
PlayerSelectorArgumentResolver playerSelectorResolver = context.getArgumentOrDefault("player", PlayerSelectorArgumentResolver.class, null);
FinePositionResolver positionResolver = context.getArgumentOrDefault("location", FinePositionResolver.class, null);
CommandSourceStack source = context.getSource();
if (selector == null && location == null) {
Entity sender = source.getEntityOrException();
this.setPos(new Vector(sender.getX(), sender.getY(), sender.getZ()));
} else if (selector != null) {
ServerPlayer player = selector.findSinglePlayer(source);
this.setTarget(player);
if (playerSelectorResolver == null && positionResolver == null) {
CommandSender sender = context.getSender();
if (sender instanceof Entity entity) {
this.setPos(entity.getLocation().toVector());
} else {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().create();
}
} else if (playerSelectorResolver != null) {
CraftPlayer player = (CraftPlayer) playerSelectorResolver.resolve(source).getFirst();
this.setTarget(player.getHandle());
} else {
Vec3 vector = location.getPosition(source);
this.setPos(new Vector(vector.x, vector.y, vector.z));
this.setPos(positionResolver.resolve(source).toVector());
}
}
@@ -115,16 +108,6 @@ public class ServerLookAction extends AbstractBotAction<ServerLookAction> {
return target;
}
@Override
public void provideActionData(@NotNull ActionData data) {
super.provideActionData(data);
if (target != null) {
data.add("target", target.getName().getString());
} else {
data.add("position", String.format("(%.2f, %.2f, %.2f)", pos.getX(), pos.getY(), pos.getZ()));
}
}
@Override
public boolean doTick(@NotNull ServerBot bot) {
if (target != null) {
@@ -17,9 +17,8 @@
package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.world.entity.Entity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftVehicle;
import org.bukkit.entity.Vehicle;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
@@ -39,14 +38,17 @@ public class ServerMountAction extends AbstractBotAction<ServerMountAction> {
Location center = bot.getBukkitEntity().getLocation();
List<Vehicle> vehicles = center.getNearbyEntitiesByType(
Vehicle.class,
4,
vehicle -> manhattanDistance(bot, ((CraftEntity) vehicle).getHandle()) <= 3
bot.entityInteractionRange()
).stream().sorted(Comparator.comparingDouble(
(vehicle) -> center.distanceSquared(vehicle.getLocation())
)).toList();
for (Vehicle vehicle : vehicles) {
if (bot.startRiding(((CraftEntity) vehicle).getHandle(), false, true)) {
CraftVehicle craftVehicle = (CraftVehicle) vehicle;
if (!bot.hasLineOfSight(craftVehicle.getHandle())) {
continue;
}
if (bot.startRiding(craftVehicle.getHandle())) {
return true;
}
}
@@ -58,10 +60,4 @@ public class ServerMountAction extends AbstractBotAction<ServerMountAction> {
public Object asCraft() {
return new CraftMountAction(this);
}
private double manhattanDistance(@NotNull Entity entity1, @NotNull Entity entity2) {
return Math.abs(entity1.getX() - entity2.getX()) +
Math.abs(entity1.getY() - entity2.getY()) +
Math.abs(entity1.getZ() - entity2.getZ());
}
}
@@ -17,46 +17,26 @@
package org.leavesmc.leaves.bot.agent.actions;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.arguments.EnumArgumentType;
import org.leavesmc.leaves.entity.bot.action.MoveAction.MoveDirection;
import org.leavesmc.leaves.entity.bot.actions.CraftMoveAction;
import org.leavesmc.leaves.event.bot.BotActionStopEvent;
import java.util.Arrays;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
import static org.leavesmc.leaves.command.ArgumentNode.ArgumentSuggestions.strings;
public class ServerMoveAction extends AbstractStateBotAction<ServerMoveAction> {
private static final Map<String, MoveDirection> NAME_TO_DIRECTION = Arrays.stream(MoveDirection.values()).collect(toMap(
it -> it.name,
it -> it
));
private MoveDirection direction = MoveDirection.FORWARD;
public ServerMoveAction() {
super("move", ServerMoveAction::new);
this.addArgument("direction", StringArgumentType.word())
.suggests(strings(
Arrays.stream(MoveDirection.values())
.map((it) -> it.name)
.toList()
));
this.addArgument("direction", EnumArgumentType.fromEnum(MoveDirection.class));
}
@Override
public void loadCommand(@NotNull CommandContext context) throws CommandSyntaxException {
String raw = context.getArgument("direction", String.class);
MoveDirection direction = NAME_TO_DIRECTION.get(raw);
if (direction == null) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
}
this.direction = direction;
public void loadCommand(@NotNull CommandContext context) {
this.direction = context.getArgument("direction", MoveDirection.class);
}
@Override
@@ -82,9 +62,9 @@ public class ServerMoveAction extends AbstractStateBotAction<ServerMoveAction> {
}
@Override
public void provideActionData(@NotNull ActionData data) {
super.provideActionData(data);
public String getActionDataString(@NotNull ExtraData data) {
data.add("direction", direction.name);
return super.getActionDataString(data);
}
public MoveDirection getDirection() {
@@ -18,11 +18,13 @@
package org.leavesmc.leaves.bot.agent.actions;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.entity.bot.actions.CraftRotationAction;
@@ -35,16 +37,26 @@ public class ServerRotationAction extends AbstractBotAction<ServerRotationAction
public ServerRotationAction() {
super("rotation", ServerRotationAction::new);
this.addArgument("yaw", FloatArgumentType.floatArg(-180, 180))
.suggests((context, builder) -> builder.suggest(
DF.format(context.getSource().getEntityOrException().getYRot()),
Component.literal("current player yaw")
))
.suggests((context, builder) -> {
CommandSender sender = context.getSender();
if (sender instanceof Entity entity) {
builder.suggest(
DF.format(entity.getYaw()),
Component.literal("current player yaw")
);
}
})
.setOptional(true);
this.addArgument("pitch", FloatArgumentType.floatArg(-90, 90))
.suggests((context, builder) -> builder.suggest(
DF.format(context.getSource().getEntityOrException().getXRot()),
Component.literal("current player pitch")
))
.suggests((context, builder) -> {
CommandSender sender = context.getSender();
if (sender instanceof Entity entity) {
builder.suggest(
DF.format(entity.getPitch()),
Component.literal("current player pitch")
);
}
})
.setOptional(true);
}
@@ -52,9 +64,14 @@ public class ServerRotationAction extends AbstractBotAction<ServerRotationAction
private float pitch = 0.0f;
@Override
public void loadCommand(@NotNull CommandContext context) throws CommandSyntaxException {
this.yaw = context.getFloatOrDefault("yaw", context.getSource().getEntityOrException().getYRot());
this.pitch = context.getFloatOrDefault("pitch", context.getSource().getEntityOrException().getXRot());
public void loadCommand(@NotNull CommandContext context) {
CommandSender sender = context.getSender();
if (sender instanceof Entity entity) {
this.yaw = entity.getYaw();
this.pitch = entity.getPitch();
}
this.yaw = context.getFloatOrDefault("yaw", this.yaw);
this.pitch = context.getFloatOrDefault("pitch", this.pitch);
}
public void setYaw(float yaw) {
@@ -74,10 +91,10 @@ public class ServerRotationAction extends AbstractBotAction<ServerRotationAction
}
@Override
public void provideActionData(@NotNull ActionData data) {
super.provideActionData(data);
public String getActionDataString(@NotNull ExtraData data) {
data.add("yaw", DF.format(this.yaw));
data.add("pitch", DF.format(this.pitch));
return super.getActionDataString(data);
}
@Override
@@ -21,29 +21,27 @@ import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.nbt.CompoundTag;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.WrappedArgument;
import java.lang.reflect.Method;
import java.util.List;
import java.util.ArrayList;
import java.util.function.Supplier;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.HoverEvent.showText;
import static net.kyori.adventure.text.format.NamedTextColor.AQUA;
public abstract class AbstractBotConfig<Value, Type, E extends AbstractBotConfig<Value, Type, E>> {
public abstract class AbstractBotConfig<T, E extends AbstractBotConfig<T, E>> {
private final String name;
private final WrappedArgument<Type> argument;
private final WrappedArgument<T> argument;
private final Supplier<E> creator;
protected ServerBot bot;
public AbstractBotConfig(String name, ArgumentType<Type> type, Supplier<E> creator) {
public AbstractBotConfig(String name, ArgumentType<T> type, Supplier<E> creator) {
this.name = name;
this.argument = new WrappedArgument<>(name, type);
if (shouldApplySuggestions()) {
@@ -56,7 +54,7 @@ public abstract class AbstractBotConfig<Value, Type, E extends AbstractBotConfig
public void applySuggestions(final CommandContext context, final SuggestionsBuilder builder) throws CommandSyntaxException {
}
public AbstractBotConfig<Value, Type, E> setBot(ServerBot bot) {
public AbstractBotConfig<T, E> setBot(ServerBot bot) {
this.bot = bot;
return this;
}
@@ -65,34 +63,32 @@ public abstract class AbstractBotConfig<Value, Type, E extends AbstractBotConfig
return creator.get();
}
public abstract Value getValue();
public abstract T getValue();
public abstract void setValue(Value value) throws CommandSyntaxException;
public abstract void setValue(T value) throws CommandSyntaxException;
public abstract Value loadFromCommand(@NotNull CommandContext context) throws CommandSyntaxException;
public List<Pair<String, String>> getExtraData() {
return List.of();
}
public abstract T loadFromCommand(@NotNull CommandContext context) throws CommandSyntaxException;
public String getName() {
return name;
}
public Component getNameComponent() {
Component result = text(getName(), AQUA);
if (!getExtraData().isEmpty()) {
result = result.hoverEvent(showText(
getExtraData().stream()
.map(pair -> text(pair.getKey() + "=" + pair.getValue()))
.reduce((a, b) -> a.append(text(", ")).append(b))
.orElseGet(() -> text(""))
));
}
return result;
return Component.text(getName(), NamedTextColor.AQUA).hoverEvent(HoverEvent.showText(Component.text(getExtraDataString())));
}
public WrappedArgument<Type> getArgument() {
public String getExtraDataString() {
return getExtraDataString(new ExtraData(new ArrayList<>()));
}
public String getExtraDataString(@NotNull ExtraData data) {
return data.raw().stream()
.map(pair -> pair.getLeft() + "=" + pair.getRight())
.reduce((a, b) -> a + ", " + b)
.orElse("No data");
}
public WrappedArgument<T> getArgument() {
return argument;
}
@@ -23,7 +23,7 @@ import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.CommandContext;
public class AlwaysSendDataConfig extends AbstractBotConfig<Boolean, Boolean, AlwaysSendDataConfig> {
public class AlwaysSendDataConfig extends AbstractBotConfig<Boolean, AlwaysSendDataConfig> {
private boolean value;
public AlwaysSendDataConfig() {
@@ -18,17 +18,18 @@
package org.leavesmc.leaves.bot.agent.configs;
import com.mojang.brigadier.arguments.BoolArgumentType;
import fun.bm.lophine.LophineLogger;
import fun.bm.lophine.config.modules.experiment.CommandConfig;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import me.earthme.luminol.utils.FoliaServerWaypointManager;
import me.earthme.luminol.utils.NullPlugin;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.waypoints.ServerWaypointManager;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandContext;
public class LocatorBarConfig extends AbstractBotConfig<Boolean, Boolean, LocatorBarConfig> {
public class LocatorBarConfig extends AbstractBotConfig<Boolean, LocatorBarConfig> {
private boolean value;
public LocatorBarConfig() {
@@ -43,20 +44,24 @@ public class LocatorBarConfig extends AbstractBotConfig<Boolean, Boolean, Locato
@Override
public void setValue(@NotNull Boolean value) throws IllegalArgumentException {
if (bot == null) {
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task) -> setValue(value), 20);
} else {
setValue(value, this.bot);
}
setValue(value, 0);
}
public void setValue(@NotNull Boolean value, ServerBot bot) throws IllegalArgumentException {
this.value = value;
ServerWaypointManager manager = bot.level().getWaypointManager();
if (value) {
manager.trackWaypoint(bot);
public void setValue(@NotNull Boolean value, int count) throws IllegalArgumentException {
if (count > 60) {
LophineLogger.LOGGER.error("Failed to set locator bar for a fakeplayer after 60 attempts");
return;
}
if (this.bot != null) {
this.value = value;
FoliaServerWaypointManager manager = this.bot.level().getWaypointManager(); // Lophine - waypoint for adapt of luminol ver
if (value) {
manager.trackWaypoint(this.bot);
} else {
manager.untrackWaypoint(this.bot);
}
} else {
manager.untrackWaypoint(bot);
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task0) -> setValue(value, count + 1), 20);
}
}
@@ -19,6 +19,7 @@ package org.leavesmc.leaves.bot.agent.configs;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import fun.bm.lophine.LophineLogger;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import me.earthme.luminol.utils.NullPlugin;
import net.minecraft.nbt.CompoundTag;
@@ -28,7 +29,7 @@ import org.leavesmc.leaves.command.CommandContext;
import static net.minecraft.network.chat.Component.literal;
public class SimulationDistanceConfig extends AbstractBotConfig<Integer, Integer, SimulationDistanceConfig> {
public class SimulationDistanceConfig extends AbstractBotConfig<Integer, SimulationDistanceConfig> {
public SimulationDistanceConfig() {
super("simulation_distance", IntegerArgumentType.integer(2, 32), SimulationDistanceConfig::new);
@@ -68,10 +69,18 @@ public class SimulationDistanceConfig extends AbstractBotConfig<Integer, Integer
@Override
public void load(@NotNull CompoundTag nbt) {
if (this.bot == null) {
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task) -> load(nbt), 20);
} else {
load(nbt, 0);
}
public void load(@NotNull CompoundTag nbt, int count) {
if (count > 60) {
LophineLogger.LOGGER.error("Failed to load simulation distance for a fakeplayer after 60 attempts");
return;
}
if (this.bot != null) {
this.setValue(nbt.getIntOr(getName(), FakeplayerConfig.getSimulationDistance(this.bot)));
} else {
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task0) -> load(nbt, count + 1), 20);
}
}
}
@@ -18,15 +18,15 @@
package org.leavesmc.leaves.bot.agent.configs;
import com.mojang.brigadier.arguments.BoolArgumentType;
import fun.bm.lophine.LophineLogger;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import me.earthme.luminol.utils.NullPlugin;
import net.minecraft.nbt.CompoundTag;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandContext;
public class SkipSleepConfig extends AbstractBotConfig<Boolean, Boolean, SkipSleepConfig> {
public class SkipSleepConfig extends AbstractBotConfig<Boolean, SkipSleepConfig> {
public SkipSleepConfig() {
super("skip_sleep", BoolArgumentType.bool(), SkipSleepConfig::new);
@@ -38,16 +38,20 @@ public class SkipSleepConfig extends AbstractBotConfig<Boolean, Boolean, SkipSle
}
@Override
public void setValue(@NotNull Boolean value) throws IllegalArgumentException {
if (bot == null) {
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task) -> setValue(value), 20);
} else {
setValue(value, this.bot);
}
public void setValue(Boolean value) throws IllegalArgumentException {
setValue(value, 0);
}
public void setValue(@NotNull Boolean value, ServerBot bot) throws IllegalArgumentException {
bot.fauxSleeping = value;
public void setValue(Boolean value, int count) throws IllegalArgumentException {
if (count > 60) {
LophineLogger.LOGGER.error("Failed to set skip sleep config for a fakeplayer after 60 attempts.");
return;
}
if (this.bot != null) {
bot.fauxSleeping = value;
} else {
Bukkit.getGlobalRegionScheduler().runDelayed(new NullPlugin(), (task0) -> setValue(value, count + 1), 20);
}
}
@Override
@@ -20,13 +20,11 @@ package org.leavesmc.leaves.bot.agent.configs;
import com.mojang.brigadier.arguments.BoolArgumentType;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import net.minecraft.nbt.CompoundTag;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.agent.ExtraData;
import org.leavesmc.leaves.command.CommandContext;
import java.util.List;
public class SpawnPhantomConfig extends AbstractBotConfig<Boolean, Boolean, SpawnPhantomConfig> {
public class SpawnPhantomConfig extends AbstractBotConfig<Boolean, SpawnPhantomConfig> {
private boolean value;
public SpawnPhantomConfig() {
@@ -45,8 +43,9 @@ public class SpawnPhantomConfig extends AbstractBotConfig<Boolean, Boolean, Spaw
}
@Override
public List<Pair<String, String>> getExtraData() {
return List.of(Pair.of("not_sleeping_ticks", String.valueOf(bot.notSleepTicks)));
public String getExtraDataString(@NotNull ExtraData data) {
data.add("not_sleeping_ticks", String.valueOf(bot.notSleepTicks));
return super.getExtraDataString(data);
}
@Override
@@ -17,37 +17,26 @@
package org.leavesmc.leaves.bot.agent.configs;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.arguments.EnumArgumentType;
public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType, String, TickTypeConfig> {
import java.util.Locale;
public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType, TickTypeConfig> {
private ServerBot.TickType value;
public TickTypeConfig() {
super("tick_type", StringArgumentType.word(), TickTypeConfig::new);
super("tick_type", EnumArgumentType.fromEnum(ServerBot.TickType.class), TickTypeConfig::new);
this.value = FakeplayerConfig.tickType;
}
@Override
public void applySuggestions(CommandContext context, @NotNull SuggestionsBuilder builder) {
builder.suggest("network");
builder.suggest("entity_list");
}
@Override
public ServerBot.TickType loadFromCommand(@NotNull CommandContext context) throws CommandSyntaxException {
String raw = context.getString(getName());
return switch (raw) {
case "network" -> ServerBot.TickType.NETWORK;
case "entity_list" -> ServerBot.TickType.ENTITY_LIST;
default -> throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
};
public ServerBot.TickType loadFromCommand(@NotNull CommandContext context) {
return context.getArgument("tick_type", ServerBot.TickType.class);
}
@Override
@@ -64,14 +53,14 @@ public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType, String
@NotNull
public CompoundTag save(@NotNull CompoundTag nbt) {
super.save(nbt);
nbt.putString(getName(), this.getValue().toString());
nbt.putString(getName(), this.getValue().toString().toLowerCase(Locale.ROOT));
return nbt;
}
@Override
public void load(@NotNull CompoundTag nbt) {
String raw = nbt.getStringOr(getName(), FakeplayerConfig.tickType.name());
this.setValue(switch (raw.toLowerCase()) {
this.setValue(switch (raw.toLowerCase(Locale.ROOT)) {
case "network" -> ServerBot.TickType.NETWORK;
case "entity_list" -> ServerBot.TickType.ENTITY_LIST;
default -> throw new IllegalStateException("Unexpected bot tick type value: " + raw);
@@ -0,0 +1,71 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.command.arguments;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.ServerBot;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public class BotArgumentType implements CustomArgumentType.Converted<@NotNull ServerBot, @NotNull String> {
private BotArgumentType() {
}
public static @NotNull BotArgumentType bot() {
return new BotArgumentType();
}
@Override
public <S> @NotNull CompletableFuture<Suggestions> listSuggestions(com.mojang.brigadier.context.@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
Collection<ServerBot> bots = BotList.INSTANCE.bots;
if (bots.isEmpty()) {
return builder
.suggest("<NO BOT EXISTS>", Component.literal("There are no bots in the server, create one first."))
.buildFuture();
}
bots.stream().map(ServerBot::getScoreboardName).forEach(builder::suggest);
return builder.buildFuture();
}
@Override
public ServerBot convert(String nativeType) throws CommandSyntaxException {
ServerBot bot = BotList.INSTANCE.getBotByName(nativeType);
if (bot == null) {
throw new CommandSyntaxException(
CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(),
Component.literal("Bot with name '" + nativeType + "' does not exist")
);
}
return bot;
}
@Override
public @NotNull ArgumentType<@NotNull String> getNativeType() {
return StringArgumentType.word();
}
}
@@ -0,0 +1,98 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.command.arguments;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("ClassCanBeRecord")
public final class EnumArgumentType<T extends Enum<T>> implements CustomArgumentType.Converted<@NotNull T, @NotNull String> {
private final Class<T> enumClass;
@Contract(value = "_ -> new", pure = true)
public static <T extends Enum<T>> @NotNull EnumArgumentType<T> fromEnum(Class<T> enumClass) {
return new EnumArgumentType<>(enumClass);
}
private EnumArgumentType(Class<T> enumClass) {
this.enumClass = enumClass;
}
@Override
public @NotNull T convert(@NotNull String nativeType) throws CommandSyntaxException {
try {
return Enum.valueOf(enumClass, nativeType.toUpperCase());
} catch (IllegalArgumentException e) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
}
}
@Override
public <S> @NotNull CompletableFuture<Suggestions> listSuggestions(@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
for (Enum<?> value : enumClass.getEnumConstants()) {
String name = value.name().toLowerCase();
if (name.startsWith(builder.getRemainingLowerCase())) {
builder.suggest(name);
}
}
return builder.buildFuture();
}
@Override
public @NotNull ArgumentType<@NotNull String> getNativeType() {
return StringArgumentType.word();
}
public Class<T> enumClass() {
return enumClass;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
EnumArgumentType<?> that = (EnumArgumentType<?>) obj;
return Objects.equals(this.enumClass, that.enumClass);
}
@Override
public int hashCode() {
return Objects.hash(enumClass);
}
@Override
public String toString() {
return "EnumArgumentType[" +
"enumClass=" + enumClass + ']';
}
}
@@ -1,46 +0,0 @@
package org.leavesmc.leaves.command.bot;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.network.chat.Component;
import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.CustomArgumentType;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public class BotArgument implements CustomArgumentType<ServerBot, String> {
@Override
public ArgumentType<String> getBaseArgumentType() {
return StringArgumentType.word();
}
@Override
public ServerBot transform(String value) throws CommandSyntaxException {
ServerBot bot = BotList.INSTANCE.getBotByName(value);
if (bot == null) {
throw new CommandSyntaxException(
CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(),
Component.literal("Bot with name '" + value + "' does not exist")
);
}
return bot;
}
@Override
public CompletableFuture<Suggestions> getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException {
Collection<ServerBot> bots = BotList.INSTANCE.bots;
if (bots.isEmpty()) {
return builder
.suggest("<NO BOT EXISTS>", Component.literal("There are no bots in the server, create one first."))
.buildFuture();
}
bots.stream().map(ServerBot::getScoreboardName).forEach(builder::suggest);
return builder.buildFuture();
}
}
@@ -18,7 +18,7 @@
package org.leavesmc.leaves.command.bot;
import com.mojang.brigadier.builder.ArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.RootNode;
@@ -52,7 +52,7 @@ public class BotCommand extends RootNode {
return sender.hasPermission(PERM_BASE);
}
public static boolean hasPermission(@NotNull CommandSender sender, String... subcommand) {
return hasPermission(PERM_BASE, sender, subcommand);
public static boolean hasPermission(@NotNull CommandSender sender, String subcommand) {
return sender.hasPermission(PERM_BASE) || sender.hasPermission(PERM_BASE + "." + subcommand);
}
}

Some files were not shown because too many files have changed in this diff Show More