Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47bc8042d2 | |||
| 03849ba73c | |||
| ef3d1625b8 | |||
| c4acc049d8 | |||
| 3dc7b1edb7 | |||
| ab2992a2ef | |||
| 5622d49d49 | |||
| 7fdce6aedd | |||
| c26d8d1ee3 | |||
| 7bc12d2487 | |||
| 2c3aa4b68e | |||
| 91312b7a15 | |||
| 77c412bfe8 | |||
| c06d875df7 | |||
| 5e96851513 | |||
| 91b3ad527c | |||
| 959a1c346c | |||
| b594382a6a | |||
| 37f1b4e5f1 | |||
| af2af944bf | |||
| 54688d8e34 | |||
| 7b7f0b6d1f | |||
| 0ab3983145 | |||
| 24a9d5cea8 | |||
| 8ee77c3354 | |||
| 98df254bad | |||
| 19f001ad1b | |||
| 106dfeaa38 | |||
| 403ae2b2d0 | |||
| 96c99519d5 | |||
| 5c4031b836 | |||
| 376857ff97 | |||
| fa1ab90579 | |||
| fc9c6937b6 |
@@ -25,7 +25,7 @@ body:
|
||||
LophineMC服务端发行版本 LophineMC server release version (contains the git commit's hash)<br>
|
||||
通过执行`/version`命令将获取到的内容复制到这里。 Run `/version` on your server and paste the full, unmodified output here.
|
||||
placeholder: >
|
||||
e.g. git-Lophine-"0635a89" (MC: 1.20.4)
|
||||
e.g. git-Lophine-"fa1ab90" (MC: 1.21.8)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -65,6 +65,20 @@ body:
|
||||
To list plugins, run `/plugins`. For datapacks, run `/datapack list`.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: config-file-list
|
||||
attributes:
|
||||
label: 配置文件列表(如果可以)。 Config File List (if possible).
|
||||
description:
|
||||
服务器上的配置文件列表。
|
||||
All config files on your server.
|
||||
例如以下文件(或者文件夹):
|
||||
Example files (or folders):
|
||||
- \spigot.yml
|
||||
- \bukkit.yml
|
||||
- \config\\\*
|
||||
- \lophine_config\\\*
|
||||
- \luminol_config\\\*
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
|
||||
@@ -14,7 +14,7 @@ on:
|
||||
required: false
|
||||
default: ""
|
||||
force-push:
|
||||
description: "Force disable push to repo if you enter 2, push to repo if you enter 1, default push if not released else skip push repo"
|
||||
description: "Force disable push to repo if you enter 2, force push to repo if you enter 1, default push if not released else skip push repo"
|
||||
required: false
|
||||
default: ""
|
||||
comments:
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ env.project_id_b }} CI Artifacts
|
||||
path: lophine-server/build/libs/*-paperclip-*-mojmap.jar
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Publish To Repo
|
||||
if: (( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !( inputs.force-release == '2' )) || inputs.force-push == '1') && false # do not push to any repo
|
||||
if: ( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !( inputs.force-release == '2' )) || inputs.force-push == '1'
|
||||
continue-on-error: true
|
||||
run: ./gradlew --refresh-dependencies generateDevelopmentBundle publish -PpublishDevBundle=true
|
||||
env:
|
||||
@@ -106,11 +106,12 @@ jobs:
|
||||
- name: Create Release - Pre Process
|
||||
if: ( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !(inputs.force-release == '2')) || inputs.force-release == '1'
|
||||
run: |
|
||||
if [ ${{ inputs.comments }} == "" ]; then
|
||||
echo "No comments provided"
|
||||
echo "flag_comment=true" >> $GITHUB_ENV
|
||||
if [ "${{ inputs.comments }}" == "" ]; then
|
||||
echo "No comments provided"
|
||||
echo "flag_comment=false" >> $GITHUB_ENV
|
||||
else
|
||||
echo "flag_comment=false" >> $GITHUB_ENV
|
||||
echo "Comments provided: ${{ inputs.comments }}"
|
||||
echo "flag_comment=true" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Create Release - No Comment
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: # Specify your branches here
|
||||
- ver/* # Stable branch
|
||||
- exp/* # Experimental branch
|
||||
- dev/* # Development Branch
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
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
|
||||
with:
|
||||
pr-mode: false
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_1658551725 }}
|
||||
QODANA_ENDPOINT: 'https://qodana.cloud'
|
||||
@@ -62,7 +62,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("me.earthme.luminol:luminol-api:$VERSION")
|
||||
compileOnly("fun.bm.lophine:lophine-api:$VERSION")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ dependencies {
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.earthme.luminol</groupId>
|
||||
<groupId>fun.bm.lophine</groupId>
|
||||
<artifactId>luminol-api</artifactId>
|
||||
<version>$VERSION</version>
|
||||
</dependency>
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("me.earthme.luminol:luminol-api:$VERSION")
|
||||
compileOnly("fun.bm.lophine:lophine-api:$VERSION")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ dependencies {
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.earthme.luminol</groupId>
|
||||
<groupId>fun.bm.lophine</groupId>
|
||||
<artifactId>luminol-api</artifactId>
|
||||
<version>$VERSION</version>
|
||||
<scope>provided</scope>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ mcVersion=1.21.8
|
||||
release=2
|
||||
# 0 for skip release, 1 for pre-release, 2 for release
|
||||
|
||||
luminolRef=1f72de21ac9002070bea969d73f16d8e23c9c745
|
||||
luminolRef=e324edacf6651633674a0abce91ffbd290943dd5
|
||||
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.caching=true
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Rebrand to Lophine
|
||||
|
||||
|
||||
diff --git a/src/main/java/me/earthme/luminol/config/ConfigManager.java b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
index a5208f5426ea2e5d8f50c51fe17718ea1d63eef5..c80c578f1a3786bd7413c9a7af8ae125fe7975bb 100644
|
||||
index 0bebc2cc87fb9c1c526e4de4f3bc157552307a72..cb1657939b86c039fcfe6403d3f202c2c5879e88 100644
|
||||
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
@@ -21,6 +21,7 @@ public class ConfigManager {
|
||||
@@ -23,6 +23,7 @@ public class ConfigManager {
|
||||
|
||||
public static void initConfigs() {
|
||||
configfiles.put("luminol", ConfigsInstance.of("luminol", "me.earthme.luminol.config.modules"));
|
||||
|
||||
+94
@@ -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 e1842ab8f20271a8b20ab8462b09a4745a612a9a..6deaeaf95a43e217f65d3e0764b844ff4573d4ca 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
@@ -317,6 +317,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 bfb39cbf1ba58d02da7e35300eb95872747506e8..25e8f26f708b68bd90f57863e7833e6502d981e2 100644
|
||||
--- a/io/papermc/paper/threadedregions/TickRegions.java
|
||||
+++ b/io/papermc/paper/threadedregions/TickRegions.java
|
||||
@@ -168,6 +168,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 25ccbfa12d57d540195ecc16aa54a144f8ecc0e8..c00e0e74cb4c820a5c8b3722ebc1ff56318dae02 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -277,7 +277,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 75027e73859c955a54034640f905cb5d91a6a2f4..1fa4110b61e49ee9527b65ecafb96b0dcd2c8ce2 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1581,6 +1581,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 15e0fce7500dc2dc2a712bf61d1d5506fd8f317a..71f53f057527192f8f830c7d21bd72233479fe55 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
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable scoreboard command
|
||||
|
||||
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index 25ccbfa12d57d540195ecc16aa54a144f8ecc0e8..22d211bee83e3ef34d6ac81dd89c0e73924bee90 100644
|
||||
index c00e0e74cb4c820a5c8b3722ebc1ff56318dae02..2be7e5de57f474a3600ea4990899c1955faa512e 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -229,7 +229,11 @@ public class Commands {
|
||||
+5
-5
@@ -5,7 +5,7 @@ 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 e1842ab8f20271a8b20ab8462b09a4745a612a9a..8405157ec35a443e238fa6866772c839621d73d7 100644
|
||||
index 6deaeaf95a43e217f65d3e0764b844ff4573d4ca..1cefe9d83788dafc293aeb6d1b5e635211094b2a 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
@@ -299,6 +299,11 @@ public final class RegionizedServer {
|
||||
@@ -20,7 +20,7 @@ index e1842ab8f20271a8b20ab8462b09a4745a612a9a..8405157ec35a443e238fa6866772c839
|
||||
// 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
|
||||
@@ -322,6 +327,13 @@ public final class RegionizedServer {
|
||||
@@ -324,6 +329,13 @@ public final class RegionizedServer {
|
||||
this.globalTick(world, tickCount);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ index e1842ab8f20271a8b20ab8462b09a4745a612a9a..8405157ec35a443e238fa6866772c839
|
||||
// tick connections
|
||||
this.tickConnections();
|
||||
|
||||
@@ -455,7 +467,7 @@ public final class RegionizedServer {
|
||||
@@ -457,7 +469,7 @@ public final class RegionizedServer {
|
||||
}
|
||||
|
||||
private void tickTime(final ServerLevel world, final int tickCount) {
|
||||
@@ -97,7 +97,7 @@ index c485d5b3ba95f2a969bdf0dab9654ac284976f8d..b957585cf7cf7f845e20f59c7b355722
|
||||
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 22d211bee83e3ef34d6ac81dd89c0e73924bee90..1fb359073342a657e9e493403263b56be5b3393f 100644
|
||||
index 2be7e5de57f474a3600ea4990899c1955faa512e..8d332f8e99ceb5a20b3b54bc327b024968719017 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -249,7 +249,11 @@ public class Commands {
|
||||
@@ -114,7 +114,7 @@ index 22d211bee83e3ef34d6ac81dd89c0e73924bee90..1fb359073342a657e9e493403263b56b
|
||||
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 75027e73859c955a54034640f905cb5d91a6a2f4..2f4970242c754cfba8ff5656a63af07d9d0a723c 100644
|
||||
index 1fa4110b61e49ee9527b65ecafb96b0dcd2c8ce2..df5ff1cc7b289c3f52723d4e7b418e79ae6c3082 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -267,7 +267,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
-92
@@ -1,92 +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
|
||||
|
||||
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index 1fb359073342a657e9e493403263b56be5b3393f..451a685782da46f5683ea6707289bbaf3193771f 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -257,7 +257,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 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254b9b656d7 100644
|
||||
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
@@ -22,7 +22,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
@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
|
||||
@@ -50,14 +57,48 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
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;
|
||||
+ Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
|
||||
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
|
||||
+
|
||||
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
|
||||
+ 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.row(player).values().removeIf(connection -> {
|
||||
+ connection.disconnect();
|
||||
+ return true;
|
||||
+ });
|
||||
+ this.untrackWaypoint((WaypointTransmitter)player);
|
||||
+ this.players.remove(player);
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
}
|
||||
|
||||
public void breakAllConnections() {
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
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 8d332f8e99ceb5a20b3b54bc327b024968719017..d4357499cbfd0474dae3c66b4473b81367f284d1 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -257,7 +257,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 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..65a4956ba2f0e38da7ae4f8cd7ed6e4b02c015a9 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
|
||||
+ if (!map.isEmpty()) 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,65 @@ 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.getValue().disconnect();
|
||||
+ this.connections.remove(entry.getKey(), waypoint, entry.getValue());
|
||||
+ }); // 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
|
||||
+ if (!map.isEmpty()) 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();
|
||||
+ this.connections.remove(player, entry.getKey(), entry.getValue());
|
||||
+ }); // 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();
|
||||
+ java.util.List<WaypointTransmitter.Connection> map = this.connections.getAllZ(); // Lophine - concurrent
|
||||
+ if (!map.isEmpty()) map.forEach(WaypointTransmitter.Connection::disconnect); // Lophine - concurrent
|
||||
+ this.connections.clearAll(); // Lophine - concurrent
|
||||
}
|
||||
|
||||
public void remakeConnections(WaypointTransmitter waypoint) {
|
||||
@@ -83,13 +131,19 @@ 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);
|
||||
+ if (c.isEmpty()) return; // Lophine - concurrent
|
||||
+ c.forEach((connection) -> {
|
||||
+ this.connections.remove(player, waypoint, connection);
|
||||
+ if (connection != null) {
|
||||
+ connection.disconnect();
|
||||
+ }
|
||||
+ });
|
||||
+ // Lophine end - concurrent
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -103,10 +157,12 @@ 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
|
||||
+ if (c.isEmpty()) return; // Lophine - concurrent
|
||||
+ c.forEach((connection1) -> this.connections.remove(player, waypoint, connection1)); // Lophine - concurrent
|
||||
});
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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 2f4970242c754cfba8ff5656a63af07d9d0a723c..b156b5c635f931a3d4abc0584591f90476d011a6 100644
|
||||
index df5ff1cc7b289c3f52723d4e7b418e79ae6c3082..8f74af03cf0af0960b1016c8304ca0e440969215 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -2353,6 +2353,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2358,6 +2358,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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 8405157ec35a443e238fa6866772c839621d73d7..67f3454e6479abb241a7d94699d4f7bec22da56b 100644
|
||||
index 1cefe9d83788dafc293aeb6d1b5e635211094b2a..e423d5c0614823ce9a8fbd32b672232e5758e3c1 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
@@ -334,6 +334,8 @@ public final class RegionizedServer {
|
||||
@@ -336,6 +336,8 @@ public final class RegionizedServer {
|
||||
}
|
||||
// Luminol end - Add a config to enable tick command
|
||||
|
||||
+1
-1
@@ -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/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 923aca94520f456a13d4e4eb23e1499b3dbc920f..0ee9e51c5abfd9269bcf0bb29c4b8394b27af02a 100644
|
||||
index 7584ba257d61bf650d83a2316ff012df928d54f3..4d71cc3b643f6f594c90f2f8f9e5b820ebdff080 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2901,7 +2901,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
+1
-1
@@ -23,7 +23,7 @@ index d0d90a25a10bbecfffceee1992af88c60d14fd87..187e48e78e69c80b25710f193813335e
|
||||
+ // Leaves end - servux
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 0ee9e51c5abfd9269bcf0bb29c4b8394b27af02a..d1a799199a68771ae3130a30a59625587c11b0b0 100644
|
||||
index 4d71cc3b643f6f594c90f2f8f9e5b820ebdff080..7fb095c544294796768ce19c4409416e2848e296 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2368,6 +2368,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
+9
-9
@@ -20,7 +20,7 @@ index a82d84283632342bd30bc3449983431ba43583e0..f59526f6bfa1b4af5b474f0b438513c9
|
||||
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 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..04ae8de63af0a8abe578f14c8ef85fd4beca0969 100644
|
||||
index 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..58694e28b3ff85ef038fe2ada2672e6d3b074fa4 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -96,7 +96,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -28,12 +28,12 @@ index 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..04ae8de63af0a8abe578f14c8ef85fd4
|
||||
private volatile PacketListener disconnectListener;
|
||||
@Nullable
|
||||
- private volatile PacketListener packetListener;
|
||||
+ protected volatile PacketListener packetListener; // Leaves - private -> protected
|
||||
+ public volatile PacketListener packetListener; // Leaves - private -> protected // Lophine protected -> public
|
||||
@Nullable
|
||||
private DisconnectionDetails disconnectionDetails;
|
||||
private boolean encrypted;
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786e46148b1 100644
|
||||
index 8f74af03cf0af0960b1016c8304ca0e440969215..c33757d3e53a97148c11f601cde0b6127d663f11 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -347,6 +347,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -57,7 +57,7 @@ index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786
|
||||
// Folia start - region threading
|
||||
// halt scheduler
|
||||
// don't wait, we may be on a scheduler thread
|
||||
@@ -1588,7 +1595,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1593,7 +1600,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
int i = this.pauseWhileEmptySeconds() * 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,7 @@ index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786
|
||||
this.emptyTicks++;
|
||||
} else {
|
||||
this.emptyTicks = 0;
|
||||
@@ -1912,6 +1919,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1917,6 +1924,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
public void tickConnection() {
|
||||
this.getConnection().tick();
|
||||
@@ -74,7 +74,7 @@ index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786
|
||||
}
|
||||
|
||||
private void synchronizeTime(ServerLevel level) {
|
||||
@@ -2983,6 +2991,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2988,6 +2996,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ index 70740381c6501c1a518c52b24381edd16792507f..8ad6d5a2522cfbe316fec2c3b918e2e1
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 89d72f242d0fc224701bdcdf245d948415e7c820..5efe7d94a364cec1bd18c4d8d62c19075d52f3e8 100644
|
||||
index 7fb095c544294796768ce19c4409416e2848e296..3393fb585747aaf3fa270c4bfe06ed4e9174d519 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -207,6 +207,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -511,7 +511,7 @@ index 8e2775e873c92beed6a0e4e8ce1304ab8ca6e8d1..580f114fd99b017403c61e994d0c947d
|
||||
|
||||
public boolean canBypassPlayerLimit(GameProfile profile) {
|
||||
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd13444f4ad 100644
|
||||
index 65a4956ba2f0e38da7ae4f8cd7ed6e4b02c015a9..044ded5ec81905e38efe8812ff6d50259d761385 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
|
||||
@@ -526,7 +526,7 @@ index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd1
|
||||
this.waypoints.add(waypoint);
|
||||
|
||||
for (ServerPlayer serverPlayer : this.players) {
|
||||
@@ -59,6 +64,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
@@ -63,6 +68,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;
|
||||
+1
-1
@@ -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 f8bc2679d9eeaa5d179ab70a4cc5c97522bcc674..a536dc4e09b22b69bcdf59871a9cccfcf8129395 100644
|
||||
index 0d906776e10605ecfe7c681dbf313cf25080d2e8..1f44a59e46f86ca055e89631d831a38f4c3e238e 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -754,6 +754,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
+15
-13
@@ -84,10 +84,10 @@ index 514f8fbdeb776087608665c35de95294aadf5cf0..b75772897cabc3e7c59301d451685378
|
||||
players.add(serverPlayer1);
|
||||
if (players.size() >= resultLimit) {
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 81726e5782d25ff35a2bea8820a62786e46148b1..3c94631f468f4ee1b46de9981c17171e5901d745 100644
|
||||
index c33757d3e53a97148c11f601cde0b6127d663f11..c67fed7b35f273a2eb6809897a891988fe30558c 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1800,7 +1800,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1805,7 +1805,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
private ServerStatus.Players buildPlayerStatus() {
|
||||
@@ -96,7 +96,7 @@ index 81726e5782d25ff35a2bea8820a62786e46148b1..3c94631f468f4ee1b46de9981c17171e
|
||||
int maxPlayers = this.getMaxPlayers();
|
||||
if (this.hidesOnlinePlayers()) {
|
||||
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
|
||||
@@ -2001,7 +2001,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2006,7 +2006,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
@@ -105,7 +105,7 @@ index 81726e5782d25ff35a2bea8820a62786e46148b1..3c94631f468f4ee1b46de9981c17171e
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2415,7 +2415,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2420,7 +2420,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();
|
||||
@@ -114,7 +114,7 @@ index 81726e5782d25ff35a2bea8820a62786e46148b1..3c94631f468f4ee1b46de9981c17171e
|
||||
player.getAdvancements().save();
|
||||
}
|
||||
// Paper end - we don't need to save everything, just advancements
|
||||
@@ -2554,7 +2554,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2559,7 +2559,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
if (!playerList.isUsingWhitelist()) return; // Paper - whitelist not enabled
|
||||
UserWhiteList whiteList = playerList.getWhiteList();
|
||||
|
||||
@@ -288,7 +288,7 @@ index f5ba0c9a4c3f9eaa38eeb689de915c25c7165433..24bbc32bc17802edbd9cc14310fe8141
|
||||
|
||||
this.setListData(list);
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 5efe7d94a364cec1bd18c4d8d62c19075d52f3e8..a4ee2bcdb31f889c7edbbc422636782c0d5d0af6 100644
|
||||
index 3393fb585747aaf3fa270c4bfe06ed4e9174d519..c58b88f09e6f08d178fcb17a13dd790c3cb62aae 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2836,7 +2836,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -323,7 +323,7 @@ index 4b2dba8b30b72fb3acc00957b3e19d6fc178c99e..afd607df83fda3de6f53f55ad305c8f6
|
||||
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
|
||||
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c947965938 100644
|
||||
index 11c1eb6995ec3aa896815efa59477096c39089d1..762728e490a152d159684324e200ab0d2852ac7f 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -130,6 +130,7 @@ public abstract class PlayerList {
|
||||
@@ -334,7 +334,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
|
||||
// CraftBukkit start
|
||||
private org.bukkit.craftbukkit.CraftServer cserver;
|
||||
@@ -213,6 +214,122 @@ public abstract class PlayerList {
|
||||
@@ -213,6 +214,123 @@ public abstract class PlayerList {
|
||||
|
||||
abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor
|
||||
|
||||
@@ -384,6 +384,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
+ this.playersByUUID.put(player.getUUID(), player);
|
||||
+
|
||||
+ player.supressTrackerForLogin = true;
|
||||
+ worldserver1.getCurrentWorldData().connections.add(player.connection.connection);
|
||||
+ worldserver1.addNewPlayer(player);
|
||||
+ this.server.getCustomBossEvents().onPlayerConnect(player);
|
||||
+ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity();
|
||||
@@ -457,7 +458,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
public void loadSpawnForNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie, org.apache.commons.lang3.mutable.MutableObject<net.minecraft.util.ProblemReporter.ScopedCollector> scopedCollectorStore, org.apache.commons.lang3.mutable.MutableObject<ValueInput> data, org.apache.commons.lang3.mutable.MutableObject<String> lastKnownName, ca.spottedleaf.concurrentutil.completable.CallbackCompletable<org.bukkit.Location> toComplete) { // Folia - region threading - rewrite login process
|
||||
player.isRealPlayer = true; // Paper
|
||||
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
|
||||
@@ -398,6 +515,7 @@ public abstract class PlayerList {
|
||||
@@ -398,6 +516,7 @@ public abstract class PlayerList {
|
||||
|
||||
// player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below
|
||||
this.players.add(player);
|
||||
@@ -465,7 +466,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
|
||||
this.playersByUUID.put(player.getUUID(), player);
|
||||
// this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
|
||||
@@ -601,6 +719,7 @@ public abstract class PlayerList {
|
||||
@@ -601,6 +720,7 @@ public abstract class PlayerList {
|
||||
}
|
||||
|
||||
protected void save(ServerPlayer player) {
|
||||
@@ -473,7 +474,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
|
||||
player.lastSave = System.nanoTime(); // Folia - region threading - changed to nanoTime tracking
|
||||
this.playerIo.save(player);
|
||||
@@ -615,6 +734,43 @@ public abstract class PlayerList {
|
||||
@@ -615,6 +735,44 @@ public abstract class PlayerList {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,6 +498,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ worldserver.getCurrentWorldData().connections.remove(entityplayer.connection.connection);
|
||||
+ worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
+ entityplayer.retireScheduler();
|
||||
+ entityplayer.getAdvancements().stopListening();
|
||||
@@ -517,7 +519,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player) { // CraftBukkit - return string // Paper - return Component
|
||||
// Paper start - Fix kick event leave message not being sent
|
||||
return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName())));
|
||||
@@ -688,6 +844,7 @@ public abstract class PlayerList {
|
||||
@@ -688,6 +846,7 @@ public abstract class PlayerList {
|
||||
player.retireScheduler(); // Paper - Folia schedulers
|
||||
player.getAdvancements().stopListening();
|
||||
this.players.remove(player);
|
||||
@@ -525,7 +527,7 @@ index 11c1eb6995ec3aa896815efa59477096c39089d1..54e7ddf0b3949e02d2705ff7626010c9
|
||||
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
|
||||
this.server.getCustomBossEvents().onPlayerDisconnect(player);
|
||||
UUID uuid = player.getUUID();
|
||||
@@ -1044,15 +1201,15 @@ public abstract class PlayerList {
|
||||
@@ -1044,15 +1203,15 @@ public abstract class PlayerList {
|
||||
}
|
||||
|
||||
public String[] getPlayerNamesArray() {
|
||||
+4
-4
@@ -33,10 +33,10 @@ index 7280ca2250ade4166a1a883b205bbc89f4e00d1a..06268ac3420fc03a798a20bb617ba89a
|
||||
throw makeReportedException(var4, packet, processor);
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 3c94631f468f4ee1b46de9981c17171e5901d745..f7a2a19c0832ca90536d09de23f4ec9a7d4a1521 100644
|
||||
index c67fed7b35f273a2eb6809897a891988fe30558c..1f1e23d9cd73e3ada89367a080de445a18823d7f 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1878,8 +1878,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1883,8 +1883,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
try {
|
||||
profiler.startTimer(serverLevel.tickTimerId); try { // Folia - profiler
|
||||
serverLevel.tick(hasTimeLeft, region); // Folia - region threading
|
||||
@@ -55,7 +55,7 @@ index 3c94631f468f4ee1b46de9981c17171e5901d745..f7a2a19c0832ca90536d09de23f4ec9a
|
||||
serverLevel.fillReportDetails(crashReport);
|
||||
throw new ReportedException(crashReport);
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index a4ee2bcdb31f889c7edbbc422636782c0d5d0af6..42bf4f0430a232a10cffb47d0d33f586afdc2a4d 100644
|
||||
index c58b88f09e6f08d178fcb17a13dd790c3cb62aae..c0ba36849da0983117901f0cd04b2c785df0010b 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -840,7 +840,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -168,7 +168,7 @@ index 9b9842462b210b0407279ee8a8e9ccecf3ead005..1057f3bd32ff4c8f93f09251341c2439
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index a536dc4e09b22b69bcdf59871a9cccfcf8129395..a557045888b153bb046a25d276d811b9146a7874 100644
|
||||
index 1f44a59e46f86ca055e89631d831a38f4c3e238e..39c075173b77b5197af5d4ad3fe235ea0d2df756 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -378,7 +378,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
+1
-1
@@ -765,7 +765,7 @@ index e9749b66239d7562ecf22002bfbaa95df197050f..6e3841ed012eee16dcaa61f70edfc1d8
|
||||
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 a557045888b153bb046a25d276d811b9146a7874..568427a952a24012c17f64f62b6545a9d89c8c8a 100644
|
||||
index 39c075173b77b5197af5d4ad3fe235ea0d2df756..b8a2ac505d121b46c33db2fbf4d02c5bc60c6c0f 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -401,20 +401,26 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
+1
-1
@@ -46,7 +46,7 @@ index 8f112f382190d606a87f38da8bcdf8dd5f31d1c5..f27915e43df8e627000cda1cb213a953
|
||||
// CraftBukkit start
|
||||
if (bukkitState != null) {
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 568427a952a24012c17f64f62b6545a9d89c8c8a..49a740bf17b2bed8010e347747b170b69ac26929 100644
|
||||
index b8a2ac505d121b46c33db2fbf4d02c5bc60c6c0f..7a8ceb00eb5845a1d5011a518f63a9cabf131f3c 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -971,12 +971,14 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
@@ -0,0 +1,136 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 3 Dec 2025 16:28:34 +0800
|
||||
Subject: [PATCH] Global Entities Counter
|
||||
|
||||
|
||||
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
index 3b39b8f7ccb278cf910e3cc4e00da29790465a81..7cc41630a39c1e29840d70eee78473d06b7c11cd 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
@@ -520,6 +520,9 @@ public final class RegionizedWorldData {
|
||||
public final Map<Long, ChunkSectionItemEntityMovementTracker> itemEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>();
|
||||
// Luminol end
|
||||
|
||||
+ public int uniqueId = -1; // Lophine - Global Entities Counter
|
||||
+ public boolean underGlobalEntitiesCounter = false; // Lophine - Global Entities Counter
|
||||
+
|
||||
public RegionizedWorldData(final ServerLevel world) {
|
||||
this.world = world;
|
||||
this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
|
||||
@@ -529,6 +532,15 @@ public final class RegionizedWorldData {
|
||||
this.wireHandler = new alternate.current.wire.WireHandler(world);
|
||||
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
|
||||
|
||||
+ // Lophine start - Global Entities Counter
|
||||
+ if (fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter.enabled) {
|
||||
+ uniqueId = fun.bm.lophine.utils.EntitiesCounterUtil.generateUniqueId();
|
||||
+ fun.bm.lophine.utils.EntitiesCounterUtil.reportAreaMap(world, spawnChunkTracker, uniqueId);
|
||||
+ fun.bm.lophine.utils.EntitiesCounterUtil.addDataToLoaded(world, loadedEntities, uniqueId);
|
||||
+ underGlobalEntitiesCounter = true;
|
||||
+ }
|
||||
+ // Lophine end - Global Entities Counter
|
||||
+
|
||||
// tasks may be drained before the region ticks, so we must set up the tick data early just in case
|
||||
this.updateTickData();
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 05bad0366bd36b8134290a0cceb4d4dfcbbd0544..604681c8fda696dcc824b1b36b53c0d8434ec5c2 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -516,6 +516,25 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
this.chunkMap.tick(hasTimeLeft);
|
||||
profilerFiller.pop();
|
||||
this.clearCache();
|
||||
+ // Lophine start - Global Entities Counter
|
||||
+ if (fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter.enabled) {
|
||||
+ io.papermc.paper.threadedregions.RegionizedWorldData regionizedWorldData = this.level.getCurrentWorldData();
|
||||
+ if (regionizedWorldData != null) {
|
||||
+ if (regionizedWorldData.underGlobalEntitiesCounter) {
|
||||
+ if (regionizedWorldData.getLocalPlayers().isEmpty()) {
|
||||
+ fun.bm.lophine.utils.EntitiesCounterUtil.onWorldDataUnload(level, regionizedWorldData.uniqueId);
|
||||
+ regionizedWorldData.underGlobalEntitiesCounter = false;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (!regionizedWorldData.getLocalPlayers().isEmpty()) {
|
||||
+ fun.bm.lophine.utils.EntitiesCounterUtil.reportAreaMap(level, regionizedWorldData.spawnChunkTracker, regionizedWorldData.uniqueId);
|
||||
+ fun.bm.lophine.utils.EntitiesCounterUtil.addDataToLoaded(level, (ca.spottedleaf.moonrise.common.list.ReferenceList<Entity>) regionizedWorldData.getLoadedEntities(), regionizedWorldData.uniqueId);
|
||||
+ regionizedWorldData.underGlobalEntitiesCounter = true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - Global Entities Counter
|
||||
}
|
||||
|
||||
private void tickChunks() {
|
||||
@@ -580,7 +599,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
}
|
||||
spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, regionizedWorldData.getLoadedEntities(), this::getFullChunk, null, true); // Folia - region threading - note: function only cares about loaded entities, doesn't need all
|
||||
} else {
|
||||
+ // Lophine start - Global Entities Counter
|
||||
+ if (fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter.enabled) {
|
||||
+ spawnState = fun.bm.lophine.utils.EntitiesCounterUtil.runRemainingTasks(level, regionizedWorldData.getLoadedEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
|
||||
+ } else {
|
||||
spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, regionizedWorldData.getLoadedEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); // Folia - region threading - note: function only cares about loaded entities, doesn't need all
|
||||
+ }
|
||||
+ // Lophine end - Global Entities Counter
|
||||
}
|
||||
} finally { foliaProfiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.MOB_SPAWN_ENTITY_COUNT); } // Folia - profiler
|
||||
// Paper end - Optional per player mob spawns
|
||||
@@ -589,7 +614,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.getLocalPlayers().isEmpty(); // CraftBukkit // Folia - region threading
|
||||
int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
||||
List<MobCategory> filteredSpawningCategories;
|
||||
- if (_boolean && (this.spawnEnemies || this.spawnFriendlies)) {
|
||||
+ if (spawnState != null && _boolean && (this.spawnEnemies || this.spawnFriendlies)) { // Lophine - Global Entities Counter
|
||||
// Paper start - PlayerNaturallySpawnCreaturesEvent
|
||||
for (ServerPlayer entityPlayer : this.level.getLocalPlayers()) { // Folia - region threading
|
||||
int chunkRange = Math.min(level.spigotConfig.mobSpawnRange, entityPlayer.getBukkitEntity().getViewDistance());
|
||||
@@ -646,7 +671,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
this.level.tickThunder(chunk);
|
||||
}
|
||||
|
||||
- if (!spawnCategories.isEmpty()) {
|
||||
+ if (spawnState != null && !spawnCategories.isEmpty()) { // Lophine - Global Entities Counter
|
||||
if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system
|
||||
NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index c0ba36849da0983117901f0cd04b2c785df0010b..0b0d9322632ef1a751add633bdcb87ec84f82704 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -729,6 +729,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
public void tick(BooleanSupplier hasTimeLeft, io.papermc.paper.threadedregions.TickRegions.TickRegionData region) { // Folia - regionised ticking
|
||||
+ // Lophine start - Global Entities Counter
|
||||
+ if (fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter.enabled) {
|
||||
+ if (fun.bm.lophine.utils.EntitiesCounterUtil.canRunNewTask(this)) fun.bm.lophine.utils.EntitiesCounterUtil.tick(this);
|
||||
+ }
|
||||
+ // Lophine end - Global Entities Counter
|
||||
final io.papermc.paper.threadedregions.RegionizedWorldData regionizedWorldData = this.getCurrentWorldData(); // Folia - regionised ticking
|
||||
final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); // Folia - profiler
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 81f4707cf1a7cbdfcfc519cfbe1052cde77aa755..5c53e2d61ce8483d5d9e93e9f3112074c704a546 100644
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -113,7 +113,7 @@ public final class NaturalSpawner {
|
||||
return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator);
|
||||
}
|
||||
|
||||
- static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
|
||||
+ public static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) { // Lophine - protected -> public
|
||||
return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
|
||||
}
|
||||
|
||||
@@ -584,7 +584,7 @@ public final class NaturalSpawner {
|
||||
private EntityType<?> lastCheckedType;
|
||||
private double lastCharge;
|
||||
|
||||
- SpawnState(
|
||||
+ public SpawnState( // Lophine - protected -> public
|
||||
int spawnableChunkCount,
|
||||
Object2IntOpenHashMap<MobCategory> mobCategoryCounts,
|
||||
PotentialCalculator spawnPotential,
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 17 Dec 2025 00:20:57 +0800
|
||||
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 7cc41630a39c1e29840d70eee78473d06b7c11cd..d0ab09a77953c1ccd309637ba80c47a37114f082 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
|
||||
@@ -230,14 +230,17 @@ public final class RegionizedWorldData {
|
||||
regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift)).itemEntityMovementTrackerMap.put(key, tracker);
|
||||
}
|
||||
// Luminol end
|
||||
+ Set<Connection> cons = new java.util.HashSet<>(from.connections.size()); // Lophine - save all connections we have processed in connection process
|
||||
// connections
|
||||
for (final Connection conn : from.connections) {
|
||||
final ServerPlayer player = conn.getPlayer();
|
||||
+ if (player == null) continue; // Lophine - skip null player connections
|
||||
final ChunkPos pos = player.chunkPosition();
|
||||
// Note: It is impossible for an entity in the world to _not_ be in an entity chunk, which means
|
||||
// the chunk holder must _exist_, and so the region section exists.
|
||||
regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
|
||||
.connections.add(conn);
|
||||
+ cons.add(conn); // Lophine - save all connections we have processed in connection process
|
||||
}
|
||||
// entities
|
||||
for (final ServerPlayer player : from.localPlayers) {
|
||||
@@ -247,6 +250,13 @@ public final class RegionizedWorldData {
|
||||
final RegionizedWorldData into = regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift));
|
||||
into.localPlayers.add(player);
|
||||
into.nearbyPlayers.addPlayer(player);
|
||||
+ // Lophine start - we need to process the connection if player's connection is not processed, if connection.player is null, next tick will clean up it
|
||||
+ final Connection conn = player.connection.connection;
|
||||
+ if (!cons.contains(conn)) {
|
||||
+ regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift))
|
||||
+ .connections.add(conn);
|
||||
+ }
|
||||
+ // Lophine end
|
||||
}
|
||||
for (final Entity entity : from.allEntities) {
|
||||
final ChunkPos pos = entity.chunkPosition();
|
||||
@@ -577,6 +587,7 @@ public final class RegionizedWorldData {
|
||||
private static void cleanUpConnection(final Connection conn) {
|
||||
// note: ALL connections HERE have a player
|
||||
final ServerPlayer player = conn.getPlayer();
|
||||
+ if (player == null) return; // Lophine - skip null player connections
|
||||
// now that the connection is removed, we can allow this region to die
|
||||
player.level().moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(
|
||||
ServerGamePacketListenerImpl.DISCONNECT_TICKET, player.connection.disconnectPos,
|
||||
+18
@@ -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 205e2648569b7c18f488aa4d3d9ec944bb1babf4..c6ee14f14e051b30012e608b84a055ff2468850d 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1174,6 +1174,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));
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: mrhua269 <mrhua269@gmail.com>
|
||||
Date: Sat, 3 Jan 2026 10:42:39 +0800
|
||||
Subject: [PATCH] Luminol - 1.21.11: Sync dragon part when teleportation or
|
||||
firstly created
|
||||
|
||||
A simple fix of https://github.com/PaperMC/Folia/issues/371
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
index e3a49083e75116622bd95fb17533730a6fb36752..545697d9c081a270ff47b332baee68372c0dd67d 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
@@ -96,6 +96,7 @@ public final class ServerEntityLookup extends EntityLookup {
|
||||
if (entity instanceof ThrownEnderpearl enderpearl) {
|
||||
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()), enderpearl.getId()); // Folia - region threading
|
||||
}
|
||||
+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon dragon) dragon.syncDragonPartsAfterTeleportTransform(); // Luminol - Sync dragon part when teleportation or firstly created
|
||||
entity.registerScheduler(); // Paper - optimise Folia entity scheduler
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index f63252c580c313ccf89304866cfc6503f4b26052..f0ddf0bf462a316f82f4babac17af6860b6ba143 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -4325,6 +4325,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
Entity copy = this.getType().create(destination, EntitySpawnReason.DIMENSION_TRAVEL);
|
||||
copy.restoreFrom(this);
|
||||
copy.transform(pos, yaw, pitch, velocity);
|
||||
+ if (copy instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon dragon) dragon.syncDragonPartsAfterTeleportTransform(); // Luminol - Sync dragon part when teleportation or firstly created
|
||||
if (toSameWorld) copy.notifyLithiumTrackerIfNeeded();// Luminol - region threading for sleeping block entity
|
||||
// vanilla code used to call remove _after_ copying, and some stuff is required to be after copy - so add hook here
|
||||
// for example, clearing of inventory after switching dimensions
|
||||
diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
||||
index 84d215a498d9bf6f1232bf4af5a4a98d3ba9b131..2aa57b995d9fcfbd335655ef383314ce11d93707 100644
|
||||
--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
||||
+++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
|
||||
@@ -1003,4 +1003,12 @@ public class EnderDragon extends Mob implements Enemy {
|
||||
return flag ? i : 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+
|
||||
+ // Luminol start - Sync dragon part when teleportation or firstly created
|
||||
+ public void syncDragonPartsAfterTeleportTransform() {
|
||||
+ for (EnderDragonPart part : this.subEntities) {
|
||||
+ this.tickPart(part, 0.0, 0.0, 0.0); // offset -> 0.0
|
||||
+ }
|
||||
+ }
|
||||
+ // Luminol end
|
||||
}
|
||||
@@ -79,7 +79,7 @@ index 7883b7973570b5f275a03c6aac4d3b4392e7fa6e..18d066c605f8027d9b1170b64ef1428f
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
||||
index 679529d94c16960527e5d76adb61c7e536f55d3e..503e11640bd2b6f7c854747723d26fe9cd67f490 100644
|
||||
index 7334422a6e0e7faec675cf038ccbca0e284f61b5..7fee749197b6c6a8890567a7a7819f45b290012b 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 {
|
||||
|
||||
+11
-1
@@ -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;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package fun.bm.lophine.config.modules.experiment;
|
||||
|
||||
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(name = "global_entities_counter", category = EnumConfigCategory.EXPERIMENT)
|
||||
public class GlobalEntitiesCounter implements IConfigModule {
|
||||
@HotReloadUnsupported
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable global entities counter.
|
||||
You need to set per-player-mob-spawns to false on paper-world-defaults.yml or paper-world.yml""")
|
||||
public static boolean enabled = false;
|
||||
|
||||
@HotReloadUnsupported
|
||||
@ConfigInfo(name = "async", comments = "Enable Asynchronous(maybe cause bugs)")
|
||||
public static boolean async = false;
|
||||
}
|
||||
+34
@@ -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();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package fun.bm.lophine.utils;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
|
||||
import fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LocalMobCapCalculator;
|
||||
import net.minecraft.world.level.NaturalSpawner;
|
||||
import net.minecraft.world.level.PotentialCalculator;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static net.minecraft.world.level.NaturalSpawner.getRoughBiome;
|
||||
|
||||
public class EntitiesCounterUtil {
|
||||
private static final WeakHashMap<ServerLevel, WeakHashMap<Integer, ReferenceList<Entity>>> globalLoadedEntities = new WeakHashMap<>();
|
||||
private static final WeakHashMap<ServerLevel, Object2IntOpenHashMap<MobCategory>> mobsMap = new WeakHashMap<>();
|
||||
private static final WeakHashMap<ServerLevel, WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>>> mobsAreaMap = new WeakHashMap<>();
|
||||
private static final WeakHashMap<ServerLevel, Integer> spawnableChunkCount = new WeakHashMap<>();
|
||||
private static final WeakHashMap<ServerLevel, CompletableFuture<Void>> tasks = new WeakHashMap<>();
|
||||
private static final Set<Integer> UniqueIds = new HashSet<>();
|
||||
|
||||
private static int lastUsedId = 0;
|
||||
|
||||
public static int generateUniqueId() {
|
||||
synchronized (UniqueIds) {
|
||||
int id = lastUsedId;
|
||||
while (UniqueIds.contains(id)) {
|
||||
id++;
|
||||
}
|
||||
|
||||
lastUsedId = id;
|
||||
UniqueIds.add(id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public static void onWorldDataUnload(ServerLevel level, int uniqueId) {
|
||||
synchronized (UniqueIds) {
|
||||
UniqueIds.remove(uniqueId);
|
||||
}
|
||||
synchronized (globalLoadedEntities) {
|
||||
globalLoadedEntities.get(level).remove(uniqueId);
|
||||
}
|
||||
synchronized (mobsAreaMap) {
|
||||
mobsAreaMap.get(level).remove(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addDataToLoaded(ServerLevel level, ReferenceList<Entity> data, int uniqueId) {
|
||||
WeakHashMap<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.computeIfAbsent(level, k -> new WeakHashMap<>());
|
||||
if (data0.containsKey(uniqueId)) return;
|
||||
data0.put(uniqueId, data);
|
||||
}
|
||||
|
||||
public static void reportAreaMap(ServerLevel level, PositionCountingAreaMap<ServerPlayer> areaMap, int uniqueId) {
|
||||
WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>> areaMap0 = mobsAreaMap.computeIfAbsent(level, k -> new WeakHashMap<>());
|
||||
if (areaMap0.containsKey(uniqueId)) return;
|
||||
areaMap0.put(uniqueId, areaMap);
|
||||
}
|
||||
|
||||
public static int getTotalChunkCount(ServerLevel level) {
|
||||
return spawnableChunkCount.getOrDefault(level, 0);
|
||||
}
|
||||
|
||||
public static @Nullable Object2IntOpenHashMap<MobCategory> getMobsMap(ServerLevel level) {
|
||||
return mobsMap.get(level);
|
||||
}
|
||||
|
||||
public static boolean canRunNewTask(ServerLevel level) {
|
||||
CompletableFuture<Void> task = tasks.get(level);
|
||||
return task == null || task.isDone();
|
||||
}
|
||||
|
||||
public static void tick(ServerLevel level) {
|
||||
Runnable task = () -> {
|
||||
WeakHashMap<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.get(level);
|
||||
Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
|
||||
for (ReferenceList<Entity> data : data0.values()) {
|
||||
for (Entity entity : GlobalEntitiesCounter.enabled ? data.copy() : data) {
|
||||
if (entity == null || entity.isRemoved() || !entity.isAlive()) continue;
|
||||
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
|
||||
MobCategory category = entity.getType().getCategory();
|
||||
if (category != MobCategory.MISC) {
|
||||
// Paper start - Only count natural spawns
|
||||
if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
|
||||
!(entity.spawnReason == CreatureSpawnEvent.SpawnReason.NATURAL ||
|
||||
entity.spawnReason == CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
|
||||
continue;
|
||||
}
|
||||
// Paper end - Only count natural spawns
|
||||
map.addTo(category, 1);
|
||||
}
|
||||
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
|
||||
}
|
||||
}
|
||||
mobsMap.put(level, map);
|
||||
for (ServerLevel world : mobsAreaMap.keySet()) {
|
||||
int count = 0;
|
||||
WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>> collection = mobsAreaMap.get(level);
|
||||
if (collection == null) continue;
|
||||
for (PositionCountingAreaMap<ServerPlayer> areaMap : collection.values()) {
|
||||
count += areaMap.getTotalPositions();
|
||||
}
|
||||
spawnableChunkCount.put(world, count);
|
||||
}
|
||||
};
|
||||
if (GlobalEntitiesCounter.async) {
|
||||
tasks.put(level, CompletableFuture.runAsync(task));
|
||||
} else {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
public static NaturalSpawner.SpawnState runRemainingTasks(
|
||||
ServerLevel level, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
|
||||
) {
|
||||
Object2IntOpenHashMap<MobCategory> map = getMobsMap(level);
|
||||
if (map == null) return null; // skip if no data
|
||||
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
|
||||
PotentialCalculator potentialCalculator = new PotentialCalculator();
|
||||
for (Entity entity : entities) {
|
||||
if (entity == null || entity.isRemoved() || !entity.isAlive()) continue;
|
||||
// Paper start - Only count natural spawns
|
||||
if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
|
||||
!(entity.spawnReason == CreatureSpawnEvent.SpawnReason.NATURAL ||
|
||||
entity.spawnReason == CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
|
||||
continue;
|
||||
}
|
||||
// Paper end - Only count natural spawns
|
||||
BlockPos blockPos = entity.blockPosition();
|
||||
chunkGetter.query(ChunkPos.asLong(blockPos), chunk -> {
|
||||
MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType());
|
||||
if (mobSpawnCost != null) {
|
||||
potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
|
||||
}
|
||||
|
||||
if (calculator != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
|
||||
calculator.addMob(chunk.getPos(), entity.getType().getCategory());
|
||||
}
|
||||
|
||||
// Paper start - Optional per player mob spawns
|
||||
if (countMobs) {
|
||||
chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
|
||||
}
|
||||
// Paper end - Optional per player mob spawns
|
||||
});
|
||||
}
|
||||
return new NaturalSpawner.SpawnState(getTotalChunkCount(level), map, potentialCalculator, calculator);
|
||||
// Lophine end - Copy from net/minecraft/world/level/NaturalSpawner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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 key0 = entry.getKey();
|
||||
cache.invalidate(key0);
|
||||
GameProfile gp = entry.getValue();
|
||||
GameProfile ret = new GameProfile(gp.getId(), id);
|
||||
gp.getProperties().asMap().forEach(
|
||||
(key, values) ->
|
||||
values.forEach((value) ->
|
||||
ret.getProperties().put(key, value)
|
||||
)
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
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, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.leavesmc.leaves;
|
||||
|
||||
public class LeavesConfig {
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class BotList {
|
||||
}
|
||||
|
||||
ServerBot bot = new ServerBot(this.server, this.server.getLevel(Level.OVERWORLD), new GameProfile(uuid, realName));
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot, new ServerBotPacketListenerImpl.BotConnection());
|
||||
Optional<ValueInput> optional;
|
||||
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(bot.problemPath(), LOGGER)) {
|
||||
optional = playerIO.load(bot, scopedCollector);
|
||||
@@ -154,7 +154,7 @@ public class BotList {
|
||||
|
||||
bot.isRealPlayer = true;
|
||||
bot.loginTime = System.currentTimeMillis();
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
|
||||
bot.connection = new ServerBotPacketListenerImpl(this.server, bot, new ServerBotPacketListenerImpl.BotConnection());
|
||||
bot.setServerLevel(world);
|
||||
|
||||
BotSpawnLocationEvent event = new BotSpawnLocationEvent(bot.getBukkitEntity(), location);
|
||||
@@ -175,22 +175,24 @@ public class BotList {
|
||||
|
||||
bot.supressTrackerForLogin = true;
|
||||
|
||||
if (TickThread.isTickThreadFor(world, net.minecraft.util.Mth.floor(location.getX()) >> 4, net.minecraft.util.Mth.floor(location.getZ()) >> 4)) {
|
||||
summonBot(bot, world);
|
||||
} else {
|
||||
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
world, net.minecraft.util.Mth.floor(location.getX()) >> 4, net.minecraft.util.Mth.floor(location.getZ()) >> 4,
|
||||
() -> summonBot(bot, world),
|
||||
ca.spottedleaf.concurrentutil.util.Priority.HIGHER);
|
||||
}
|
||||
optional.ifPresent(nbt -> {
|
||||
bot.loadAndSpawnEnderPearls(nbt);
|
||||
bot.loadAndSpawnParentVehicle(nbt);
|
||||
});
|
||||
|
||||
if (TickThread.isTickThreadFor(world, location.blockX() >> 4, location.blockZ() >> 4)) {
|
||||
summonBot(bot, world);
|
||||
} else {
|
||||
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
world, location.getBlockX() >> 4, location.blockZ() >> 4,
|
||||
() -> summonBot(bot, world));
|
||||
}
|
||||
|
||||
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)));
|
||||
@@ -277,7 +279,10 @@ public class BotList {
|
||||
}
|
||||
}
|
||||
|
||||
bot.level().getCurrentWorldData().connections.remove(bot.connection.connection);
|
||||
bot.level().removePlayerImmediately(bot, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
bot.retireScheduler();
|
||||
|
||||
this.bots.remove(bot);
|
||||
this.botsByName.remove(bot.getScoreboardName().toLowerCase(Locale.ROOT));
|
||||
|
||||
@@ -299,6 +304,7 @@ public class BotList {
|
||||
if (removeMessage != null && !removeMessage.equals(net.kyori.adventure.text.Component.empty())) {
|
||||
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(removeMessage), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -29,9 +29,12 @@ 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) {
|
||||
super(server, BotConnection.INSTANCE, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
|
||||
public ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot, BotConnection botConnection) {
|
||||
super(server, botConnection, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
|
||||
this.fakeConnection = botConnection;
|
||||
this.fakeConnection.packetListener = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,8 +56,6 @@ public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
|
||||
|
||||
public static class BotConnection extends Connection {
|
||||
|
||||
private static final BotConnection INSTANCE = new BotConnection();
|
||||
|
||||
public BotConnection() {
|
||||
super(PacketFlow.SERVERBOUND);
|
||||
}
|
||||
|
||||
-3
@@ -61,7 +61,6 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
|
||||
private static final Table<DataLogger.Type, ServerPlayer, Tag> DATA = HashBasedTable.create();
|
||||
|
||||
public static boolean refreshSpawnMetadata = false;
|
||||
private long lastAcceptTime = 0;
|
||||
|
||||
@ProtocolHandler.Init
|
||||
private static void initializeLoggers() {
|
||||
@@ -240,8 +239,6 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis() / 50;
|
||||
if (currentTime == lastAcceptTime) return;
|
||||
lastAcceptTime = currentTime;
|
||||
|
||||
if (currentTime % ServuxProtocolConfig.hudUpdateInterval == 0) {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ public class ServuxStructuresProtocol implements LeavesProtocol {
|
||||
final ChunkPos pos = chunk.getPos();
|
||||
|
||||
if (chunkHasStructureReferences(pos.x, pos.z, chunk.getLevel())) {
|
||||
final Map<ChunkPos, Timeout> map = timeouts.computeIfAbsent(uuid, (u) -> new HashMap<>());
|
||||
final Map<ChunkPos, Timeout> map = timeouts.computeIfAbsent(uuid, (u) -> new ConcurrentHashMap<>());
|
||||
map.computeIfAbsent(pos, (p) -> new Timeout(tickCounter - ServuxProtocolConfig.maxDelay));
|
||||
}
|
||||
}
|
||||
@@ -304,7 +304,7 @@ public class ServuxStructuresProtocol implements LeavesProtocol {
|
||||
}
|
||||
|
||||
public static void addOrRefreshTimeouts(final UUID uuid, final Map<Structure, LongSet> references, final long tickCounter) {
|
||||
Map<ChunkPos, Timeout> map = timeouts.computeIfAbsent(uuid, (u) -> new HashMap<>());
|
||||
Map<ChunkPos, Timeout> map = timeouts.computeIfAbsent(uuid, (u) -> new ConcurrentHashMap<>());
|
||||
|
||||
for (LongSet chunks : references.values()) {
|
||||
for (Long chunkPosLong : chunks) {
|
||||
|
||||
@@ -26,7 +26,7 @@ public class RecordMetaData {
|
||||
public static final int CURRENT_FILE_FORMAT_VERSION = 14;
|
||||
|
||||
public boolean singleplayer = false;
|
||||
public String serverName = "Leaves";
|
||||
public String serverName = "Lophine";
|
||||
public int duration = 0;
|
||||
public long date;
|
||||
public String mcversion;
|
||||
@@ -37,4 +37,22 @@ public class RecordMetaData {
|
||||
public int selfId = -1;
|
||||
|
||||
public Set<UUID> players = new HashSet<>();
|
||||
|
||||
public RecordMetaData copy() {
|
||||
RecordMetaData ret = new RecordMetaData();
|
||||
synchronized (this) {
|
||||
ret.singleplayer = this.singleplayer;
|
||||
ret.serverName = this.serverName;
|
||||
ret.duration = this.duration;
|
||||
ret.date = this.date;
|
||||
ret.mcversion = this.mcversion;
|
||||
ret.fileFormat = this.fileFormat;
|
||||
ret.fileFormatVersion = this.fileFormatVersion;
|
||||
ret.protocol = this.protocol;
|
||||
ret.generator = this.generator;
|
||||
ret.selfId = this.selfId;
|
||||
ret.players = new HashSet<>(this.players);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,9 @@ public class Recorder extends Connection {
|
||||
}
|
||||
case ClientboundAddEntityPacket packet1 -> {
|
||||
if (packet1.getType() == EntityType.PLAYER) {
|
||||
metaData.players.add(packet1.getUUID());
|
||||
synchronized (metaData) {
|
||||
metaData.players.add(packet1.getUUID());
|
||||
}
|
||||
saveMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
public class RecorderOption {
|
||||
|
||||
public int recordDistance = -1;
|
||||
public String serverName = "Leaves";
|
||||
public String serverName = "Lophine";
|
||||
public RecordWeather forceWeather = null;
|
||||
public int forceDayTime = -1;
|
||||
public boolean ignoreChat = false;
|
||||
|
||||
@@ -123,9 +123,10 @@ public class ReplayFile {
|
||||
data.fileFormatVersion = RecordMetaData.CURRENT_FILE_FORMAT_VERSION;
|
||||
data.protocol = SharedConstants.getCurrentVersion().protocolVersion();
|
||||
data.generator = ProtocolUtils.buildProtocolVersion("replay");
|
||||
RecordMetaData dataCopy = data.copy();
|
||||
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(metaFile), StandardCharsets.UTF_8)) {
|
||||
writer.write(META_GSON.toJson(data));
|
||||
writer.write(META_GSON.toJson(dataCopy));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.leavesmc.leaves.replay;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import fun.bm.lophine.utils.RandomProfilePool;
|
||||
import io.papermc.paper.threadedregions.RegionizedServer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
@@ -70,7 +71,7 @@ public class ServerPhotographer extends ServerPlayer {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
ServerLevel world = ((CraftWorld) state.loc.getWorld()).getHandle();
|
||||
GameProfile profile = new GameProfile(UUID.randomUUID(), state.id);
|
||||
GameProfile profile = RandomProfilePool.getRandomProfile(state.id); // Lophine - add cache
|
||||
|
||||
ServerPhotographer photographer = new ServerPhotographer(server, world, profile);
|
||||
photographer.absSnapTo(state.loc.x(), state.loc.y(), state.loc.z(), state.loc.getYaw(), state.loc.getPitch());
|
||||
@@ -84,9 +85,8 @@ public class ServerPhotographer extends ServerPlayer {
|
||||
placePhotographer(server, photographer, world, state);
|
||||
} else {
|
||||
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
world, net.minecraft.util.Mth.floor(state.loc.getX()) >> 4, net.minecraft.util.Mth.floor(state.loc.getZ()) >> 4,
|
||||
() -> placePhotographer(server, photographer, world, state),
|
||||
ca.spottedleaf.concurrentutil.util.Priority.HIGHER);
|
||||
world, state.loc.blockX() >> 4, state.loc.blockZ() >> 4,
|
||||
() -> placePhotographer(server, photographer, world, state));
|
||||
}
|
||||
|
||||
photographers.add(photographer);
|
||||
@@ -116,11 +116,13 @@ public class ServerPhotographer extends ServerPlayer {
|
||||
|
||||
if (this.followPlayer != null) {
|
||||
if (this.getCamera() == this || this.getCamera().level() != this.level()) {
|
||||
this.getBukkitPlayer().teleportAsync(this.getCamera().getBukkitEntity().getLocation());
|
||||
this.setCamera(followPlayer);
|
||||
}
|
||||
|
||||
if (lastPosVec3.distanceToSqr(this.position()) > 1024D) {
|
||||
this.getBukkitPlayer().teleportAsync(this.getCamera().getBukkitEntity().getLocation());
|
||||
((CraftPhotographer) this.getBukkitPlayer()).taskScheduler.schedule(ent -> {
|
||||
this.getBukkitPlayer().teleportAsync(this.getCamera().getBukkitEntity().getLocation());
|
||||
}, null, 1L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,13 +160,13 @@ public class ServerPhotographer extends ServerPlayer {
|
||||
}
|
||||
|
||||
public void remove(boolean async, boolean save) {
|
||||
super.remove(RemovalReason.KILLED);
|
||||
photographers.remove(this);
|
||||
this.recorder.stop();
|
||||
this.getServer().getPlayerList().removePhotographer(this);
|
||||
|
||||
LOGGER.info("Photographer {} removed", createState.id);
|
||||
|
||||
this.recorder.stop();
|
||||
photographers.remove(this);
|
||||
|
||||
MinecraftServer.getServer().getPlayerList().removePhotographer(this);
|
||||
RandomProfilePool.putProfile(this.gameProfile); // Lophine - add cache
|
||||
if (!recorder.isSaved()) {
|
||||
CompletableFuture<Void> future = recorder.saveRecording(saveFile, save);
|
||||
if (!async) {
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
####################################################################################################################
|
||||
# WARNING: Do not store sensitive information in this file, as its contents will be included in the Qodana report. #
|
||||
####################################################################################################################
|
||||
|
||||
version: "1.0"
|
||||
linter: jetbrains/qodana-jvm:2025.3
|
||||
profile:
|
||||
name: qodana.recommended
|
||||
include:
|
||||
- name: CheckDependencyLicenses
|
||||
Reference in New Issue
Block a user