mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
262 lines
12 KiB
Diff
262 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
|
Date: Thu, 25 Jan 2024 01:16:49 +0800
|
|
Subject: [PATCH] Fast resume
|
|
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
index 58d3d1a47e9f2423c467bb329c2d5f4b58a8b5ef..ea1ffe6b5e49ccf2b472829ed97e977b4938f3a5 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
@@ -582,6 +582,49 @@ public final class ChunkHolderManager {
|
|
}
|
|
}
|
|
|
|
+ // Leaves start - add custom ticket
|
|
+ public <T> void addTicketAtLevelCustom(final Ticket<T> ticket, final long chunk, final boolean lock) {
|
|
+ final long removeDelay = ticket.moonrise$getRemoveDelay();
|
|
+
|
|
+ final int chunkX = CoordinateUtils.getChunkX(chunk);
|
|
+ final int chunkZ = CoordinateUtils.getChunkZ(chunk);
|
|
+
|
|
+ final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null;
|
|
+ try {
|
|
+ final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> SortedArraySet.create(4));
|
|
+
|
|
+ final int levelBefore = getTicketLevelAt(ticketsAtChunk);
|
|
+ final Ticket<T> current = (Ticket<T>)((ChunkSystemSortedArraySet<Ticket<?>>)ticketsAtChunk).moonrise$replace(ticket);
|
|
+ final int levelAfter = getTicketLevelAt(ticketsAtChunk);
|
|
+
|
|
+ if (current != ticket) {
|
|
+ final long oldRemoveDelay = ((ChunkSystemTicket<T>) current).moonrise$getRemoveDelay();
|
|
+ if (removeDelay != oldRemoveDelay) {
|
|
+ if (oldRemoveDelay != NO_TIMEOUT_MARKER && removeDelay == NO_TIMEOUT_MARKER) {
|
|
+ this.removeExpireCount(chunkX, chunkZ);
|
|
+ } else if (oldRemoveDelay == NO_TIMEOUT_MARKER) {
|
|
+ // since old != new, we have that NO_TIMEOUT_MARKER != new
|
|
+ this.addExpireCount(chunkX, chunkZ);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (removeDelay != NO_TIMEOUT_MARKER) {
|
|
+ this.addExpireCount(chunkX, chunkZ);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (levelBefore != levelAfter) {
|
|
+ this.updateTicketLevel(chunk, levelAfter);
|
|
+ }
|
|
+
|
|
+ } finally {
|
|
+ if (ticketLock != null) {
|
|
+ this.ticketLockArea.unlock(ticketLock);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Leaves end - add custom ticket
|
|
+
|
|
public <T> boolean removeTicketAtLevel(final TicketType<T> type, final ChunkPos chunkPos, final int level, final T identifier) {
|
|
return this.removeTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, identifier);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 0c43170b31363beca32407bddeee33ef9404d09a..3b4d1bd023904ead8b340021acd1d74b5aa53a87 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -749,6 +749,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
this.getBotList().loadResume(); // Leaves - load resident bot
|
|
|
|
+ org.leavesmc.leaves.util.TicketHelper.tryToLoadTickets(); // Leaves - load ticket
|
|
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
|
this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark
|
|
this.server.spark.enableAfterPlugins(this.server); // Paper - spark
|
|
@@ -1020,6 +1021,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
// CraftBukkit end
|
|
|
|
public void stopServer() {
|
|
+ org.leavesmc.leaves.util.TicketHelper.tryToSaveTickets(); // Leaves - save ticket
|
|
// CraftBukkit start - prevent double stopping on multiple threads
|
|
synchronized(this.stopLock) {
|
|
if (this.hasStopped) return;
|
|
diff --git a/src/main/java/org/leavesmc/leaves/util/TicketHelper.java b/src/main/java/org/leavesmc/leaves/util/TicketHelper.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..36e9da7e68de086bc8572138eb14d0e00dc56879
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/leavesmc/leaves/util/TicketHelper.java
|
|
@@ -0,0 +1,175 @@
|
|
+package org.leavesmc.leaves.util;
|
|
+
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
|
+import com.google.gson.Gson;
|
|
+import com.google.gson.JsonArray;
|
|
+import com.google.gson.JsonElement;
|
|
+import com.google.gson.JsonObject;
|
|
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceKey;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.DistanceManager;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.Ticket;
|
|
+import net.minecraft.server.level.TicketType;
|
|
+import net.minecraft.util.SortedArraySet;
|
|
+import net.minecraft.world.level.ChunkPos;
|
|
+import net.minecraft.world.level.storage.LevelResource;
|
|
+import org.leavesmc.leaves.LeavesConfig;
|
|
+import org.leavesmc.leaves.LeavesLogger;
|
|
+
|
|
+import java.io.BufferedReader;
|
|
+import java.io.BufferedWriter;
|
|
+import java.io.File;
|
|
+import java.io.IOException;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.nio.file.Files;
|
|
+import java.util.Set;
|
|
+
|
|
+public class TicketHelper {
|
|
+
|
|
+ private static final Set<TicketType<?>> NEED_SAVED = Set.of(TicketType.PLAYER, TicketType.PORTAL, RegionizedPlayerChunkLoader.PLAYER_TICKET);
|
|
+
|
|
+ public static void tryToLoadTickets() {
|
|
+ if (!LeavesConfig.fastResume) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("chunk_tickets.leaves.json").toFile();
|
|
+ if (file.isFile()) {
|
|
+ try (BufferedReader bfr = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
|
+ JsonObject json = new Gson().fromJson(bfr, JsonObject.class);
|
|
+ loadSavedChunkTickets(json);
|
|
+ if (!file.delete()) {
|
|
+ throw new IOException();
|
|
+ }
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.severe("Failed to load saved chunk tickets file", e);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void tryToSaveTickets() {
|
|
+ if (!LeavesConfig.fastResume) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ File file = MinecraftServer.getServer().getWorldPath(LevelResource.ROOT).resolve("chunk_tickets.leaves.json").toFile();
|
|
+ try (BufferedWriter bfw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
|
|
+ bfw.write(new Gson().toJson(getSavedChunkTickets()));
|
|
+ } catch (IOException e) {
|
|
+ LeavesLogger.LOGGER.severe("Failed to save chunk tickets file", e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void loadSavedChunkTickets(JsonObject json) {
|
|
+ MinecraftServer server = MinecraftServer.getServer();
|
|
+ for (String worldKey : json.keySet()) {
|
|
+ ResourceLocation dimensionKey = ResourceLocation.tryParse(worldKey);
|
|
+ if (dimensionKey == null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ServerLevel level = server.getLevel(ResourceKey.create(Registries.DIMENSION, dimensionKey));
|
|
+ if (level == null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager;
|
|
+ for (JsonElement chunkElement : json.get(worldKey).getAsJsonArray()) {
|
|
+ JsonObject chunkJson = (JsonObject) chunkElement;
|
|
+ long chunkKey = chunkJson.get("key").getAsLong();
|
|
+
|
|
+ for (JsonElement ticketElement : chunkJson.get("tickets").getAsJsonArray()) {
|
|
+ Ticket<?> ticket = tickFormJson((JsonObject) ticketElement);
|
|
+ chunkDistanceManager.getChunkHolderManager().addTicketAtLevelCustom(ticket, chunkKey, true);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static JsonObject getSavedChunkTickets() {
|
|
+ JsonObject json = new JsonObject();
|
|
+
|
|
+ for (ServerLevel level : MinecraftServer.getServer().getAllLevels()) {
|
|
+ JsonArray levelArray = new JsonArray();
|
|
+ DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager;
|
|
+
|
|
+ for (Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> chunkTickets : chunkDistanceManager.getChunkHolderManager().getTicketsCopy().long2ObjectEntrySet()) {
|
|
+ long chunkKey = chunkTickets.getLongKey();
|
|
+ JsonArray ticketArray = new JsonArray();
|
|
+ SortedArraySet<Ticket<?>> tickets = chunkTickets.getValue();
|
|
+
|
|
+ for (Ticket<?> ticket : tickets) {
|
|
+ if (!NEED_SAVED.contains(ticket.getType())) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ticketArray.add(ticketToJson(ticket));
|
|
+ }
|
|
+
|
|
+ if (!ticketArray.isEmpty()) {
|
|
+ JsonObject chunkJson = new JsonObject();
|
|
+ chunkJson.addProperty("key", chunkKey);
|
|
+ chunkJson.add("tickets", ticketArray);
|
|
+ levelArray.add(chunkJson);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!levelArray.isEmpty()) {
|
|
+ json.add(level.dimension().location().toString(), levelArray);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return json;
|
|
+ }
|
|
+
|
|
+ private static JsonObject ticketToJson(Ticket<?> ticket) {
|
|
+ JsonObject json = new JsonObject();
|
|
+ json.addProperty("type", ticket.getType().toString());
|
|
+ json.addProperty("ticketLevel", ticket.getTicketLevel());
|
|
+ json.addProperty("removeDelay", ticket.moonrise$getRemoveDelay());
|
|
+ if (ticket.key instanceof BlockPos pos) {
|
|
+ json.addProperty("key", pos.asLong());
|
|
+ } else if (ticket.key instanceof ChunkPos pos) {
|
|
+ json.addProperty("key", pos.toLong());
|
|
+ } else if (ticket.key instanceof Long l) {
|
|
+ json.addProperty("key", l);
|
|
+ }
|
|
+ return json;
|
|
+ }
|
|
+
|
|
+ private static <T> Ticket<T> tickFormJson(JsonObject json) {
|
|
+ TicketType<?> ticketType = null;
|
|
+ Object key = null;
|
|
+ switch (json.get("type").getAsString()) {
|
|
+ case "player" -> {
|
|
+ ticketType = TicketType.PLAYER;
|
|
+ key = new ChunkPos(json.get("key").getAsLong());
|
|
+ }
|
|
+ case "portal" -> {
|
|
+ ticketType = TicketType.PORTAL;
|
|
+ key = BlockPos.of(json.get("key").getAsLong());
|
|
+ }
|
|
+ case "chunk_system:player_ticket" -> {
|
|
+ ticketType = RegionizedPlayerChunkLoader.PLAYER_TICKET;
|
|
+ key = json.get("key").getAsLong();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ticketType == null) {
|
|
+ throw new IllegalArgumentException("Cant convert " + json.get("type").getAsString() + ", report it ???");
|
|
+ }
|
|
+
|
|
+ int ticketLevel = json.get("ticketLevel").getAsInt();
|
|
+ long removeDelay = json.get("removeDelay").getAsLong();
|
|
+ @SuppressWarnings("unchecked")
|
|
+ Ticket<T> ticket = new Ticket<>((TicketType<T>) ticketType, ticketLevel, (T) key);
|
|
+ ticket.moonrise$setRemoveDelay(removeDelay);
|
|
+
|
|
+ return ticket;
|
|
+ }
|
|
+}
|