Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forcibly mark chunks as needing saving after pasting contents #51

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,10 @@ tasks.create("play-deploy") {
put(shadowJar.archiveFile.get().getAsFile(), "/home/epic/play/m8/server_config/plugins")
put(shadowJar.archiveFile.get().getAsFile(), "/home/epic/play/m11/server_config/plugins")
put(shadowJar.archiveFile.get().getAsFile(), "/home/epic/play/m13/server_config/plugins")
put(shadowJar.archiveFile.get().getAsFile(), "/home/epic/play/m14/server_config/plugins")
put(shadowJar.archiveFile.get().getAsFile(), "/home/epic/play/m15/server_config/plugins")
execute("cd /home/epic/play/m8/server_config/plugins && rm -f MonumentaStructureManagement.jar && ln -s " + shadowJar.archiveFileName.get() + " MonumentaStructureManagement.jar")
execute("cd /home/epic/play/m11/server_config/plugins && rm -f MonumentaStructureManagement.jar && ln -s " + shadowJar.archiveFileName.get() + " MonumentaStructureManagement.jar")
execute("cd /home/epic/play/m13/server_config/plugins && rm -f MonumentaStructureManagement.jar && ln -s " + shadowJar.archiveFileName.get() + " MonumentaStructureManagement.jar")
execute("cd /home/epic/play/m14/server_config/plugins && rm -f MonumentaStructureManagement.jar && ln -s " + shadowJar.archiveFileName.get() + " MonumentaStructureManagement.jar")
execute("cd /home/epic/play/m15/server_config/plugins && rm -f MonumentaStructureManagement.jar && ln -s " + shadowJar.archiveFileName.get() + " MonumentaStructureManagement.jar")
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/com/playmonumenta/structures/StructuresAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
Expand Down Expand Up @@ -568,6 +571,11 @@ public void run() {
return future;
}

private static boolean ATTEMPTED_REFLECTION = false;
private static @Nullable Object CHUNK_STATUS_FULL_OBJ = null;
private static @Nullable Method GET_HANDLE_METHOD = null;
private static @Nullable Method SET_UNSAVED_METHOD = null;

/*
* Unmarks chunks so that they can be unloaded. (the opposite of markAndLoadChunks)
*
Expand All @@ -585,6 +593,42 @@ public static void unmarkChunksAsync(org.bukkit.World world, Region region) {

for (final BlockVector2 chunkCoords : region.getChunks()) {
final Consumer<Chunk> consumer = (final Chunk chunk) -> {
// Prior to marking the chunk for unloading, reach into paper internals to make sure the chunk is flagged for saving
// For some reason on 1.19 FAWE is not always marking chunks as needing to save, so sometimes on unload with no player interaction they don't save
if (!ATTEMPTED_REFLECTION) {
// First, cache the reflection results for faster use later. This is only attempted once.
ATTEMPTED_REFLECTION = true;
try {
Class<?> chunkStatusClass = Class.forName("net.minecraft.world.level.chunk.ChunkStatus");
Field chunkStatusFullField = chunkStatusClass.getDeclaredField("o"); // "FULL" in 1.19.4
CHUNK_STATUS_FULL_OBJ = chunkStatusFullField.get(null);
GET_HANDLE_METHOD = chunk.getClass().getMethod("getHandle", chunkStatusClass);
Object chunkAccess = GET_HANDLE_METHOD.invoke(chunk, CHUNK_STATUS_FULL_OBJ);
SET_UNSAVED_METHOD = chunkAccess.getClass().getMethod("a", boolean.class); // "setUnsaved" in 1.19.4
SET_UNSAVED_METHOD.invoke(chunkAccess, true);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException | ClassNotFoundException e) {
CHUNK_STATUS_FULL_OBJ = null;
GET_HANDLE_METHOD = null;
SET_UNSAVED_METHOD = null;
plugin.getLogger().warning("Failed to use reflection to access setUnsaved() chunk method. Possibly this is because this is not running on a 1.19+ paper server, in which case you can disregard this warning");
e.printStackTrace();
}
}

// Use the reflection results if they exist to set the chunk as needing to save
if (CHUNK_STATUS_FULL_OBJ != null && GET_HANDLE_METHOD != null && SET_UNSAVED_METHOD != null) {
try {
Object chunkAccess = GET_HANDLE_METHOD.invoke(chunk, CHUNK_STATUS_FULL_OBJ);
SET_UNSAVED_METHOD.invoke(chunkAccess, true);
plugin.getLogger().finer(() -> "Successfully marked chunk at " + chunk.getX() + "," + chunk.getZ() + " as needing saving prior to unloading");
} catch (InvocationTargetException | IllegalAccessException e) {
GET_HANDLE_METHOD = null;
SET_UNSAVED_METHOD = null;
plugin.getLogger().severe("Failed to use reflection to access setUnsaved() chunk method but it previously worked. This is a bug in this plugin.");
e.printStackTrace();
}
}

WorldChunkKey key = new WorldChunkKey(world.getUID(), chunk.getChunkKey());
Integer references = CHUNK_TICKET_REFERENCE_COUNT.remove(key);
if (references == null || references <= 0) {
Expand Down
Loading