-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4120 from ashduino101/v3.0
1.21 support for Bukkit/Spigot
- Loading branch information
Showing
9 changed files
with
861 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
eclipse { | ||
project { | ||
name = "Dynmap(Spigot-1.21)" | ||
} | ||
} | ||
|
||
description = 'bukkit-helper-1.21' | ||
|
||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = JavaLanguageVersion.of(17) // Need this here so eclipse task generates correctly. | ||
|
||
dependencies { | ||
implementation project(':bukkit-helper') | ||
implementation project(':dynmap-api') | ||
implementation project(path: ':DynmapCore', configuration: 'shadow') | ||
compileOnly group: 'org.spigotmc', name: 'spigot-api', version:'1.21-R0.1-SNAPSHOT' | ||
compileOnly group: 'org.spigotmc', name: 'spigot', version:'1.21-R0.1-SNAPSHOT' | ||
} |
130 changes: 130 additions & 0 deletions
130
bukkit-helper-121/src/main/java/org/dynmap/bukkit/helper/v121/AsyncChunkProvider121.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package org.dynmap.bukkit.helper.v121; | ||
|
||
import net.minecraft.nbt.NBTTagCompound; | ||
import net.minecraft.server.MinecraftServer; | ||
import net.minecraft.server.level.WorldServer; | ||
import net.minecraft.world.level.chunk.Chunk; | ||
import net.minecraft.world.level.chunk.IChunkAccess; | ||
import net.minecraft.world.level.chunk.storage.ChunkRegionLoader; | ||
import org.bukkit.Bukkit; | ||
import org.bukkit.craftbukkit.v1_21_R1.CraftServer; | ||
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; | ||
import org.dynmap.MapManager; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* The provider used to work with paper libs | ||
* Because paper libs need java 17 we can't interact with them directly | ||
*/ | ||
@SuppressWarnings({"JavaReflectionMemberAccess"}) //java don't know about paper | ||
public class AsyncChunkProvider121 { | ||
private final Method getChunk; | ||
private final Method getAsyncSaveData; | ||
private final Method save; | ||
private final Enum<?> data; | ||
private final Enum<?> priority; | ||
private int currTick = MinecraftServer.currentTick; | ||
private int currChunks = 0; | ||
|
||
AsyncChunkProvider121() { | ||
try { | ||
Method getChunk1 = null; | ||
Method getAsyncSaveData1 = null; | ||
Method save1 = null; | ||
Enum<?> priority1 = null; | ||
Enum<?> data1 = null; | ||
try { | ||
Class<?> threadClass = Class.forName("io.papermc.paper.chunk.system.io.RegionFileIOThread"); | ||
|
||
Class<?> dataclass = Arrays.stream(threadClass.getDeclaredClasses()) | ||
.filter(c -> c.getSimpleName().equals("RegionFileType")) | ||
.findAny() | ||
.orElseThrow(NullPointerException::new); | ||
data1 = Enum.valueOf(cast(dataclass), "CHUNK_DATA"); | ||
|
||
Class<?> priorityClass = Arrays.stream(Class.forName("ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor").getClasses()) | ||
.filter(c -> c.getSimpleName().equals("Priority")) | ||
.findAny() | ||
.orElseThrow(NullPointerException::new); | ||
//Almost lowest priority, but not quite so low as to be considered idle | ||
//COMPLETING->BLOCKING->HIGHEST->HIGHER->HIGH->NORMAL->LOW->LOWER->LOWEST->IDLE | ||
priority1 = Enum.valueOf(cast(priorityClass), "LOWEST"); | ||
|
||
getAsyncSaveData1 = ChunkRegionLoader.class.getMethod("getAsyncSaveData", WorldServer.class, IChunkAccess.class); | ||
save1 = ChunkRegionLoader.class.getMethod("saveChunk", WorldServer.class, IChunkAccess.class, getAsyncSaveData1.getReturnType()); | ||
getChunk1 = threadClass.getMethod("loadDataAsync", WorldServer.class, int.class, int.class, data1.getClass(), BiConsumer.class, boolean.class, priority1.getClass()); | ||
} catch (ClassNotFoundException | NoSuchMethodException e) { | ||
e.printStackTrace(); | ||
} | ||
getAsyncSaveData = Objects.requireNonNull(getAsyncSaveData1); | ||
save = Objects.requireNonNull(save1); | ||
getChunk = Objects.requireNonNull(getChunk1); | ||
data = Objects.requireNonNull(data1); | ||
priority = Objects.requireNonNull(priority1); | ||
} catch (Throwable e) { | ||
e.printStackTrace(); | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private <T> T cast(Object o) { | ||
return (T) o; | ||
} | ||
public CompletableFuture<NBTTagCompound> getChunk(WorldServer world, int x, int y) throws InvocationTargetException, IllegalAccessException { | ||
CompletableFuture<NBTTagCompound> future = new CompletableFuture<>(); | ||
getChunk.invoke(null, world, x, y, data, (BiConsumer<NBTTagCompound, Throwable>) (nbt, exception) -> future.complete(nbt), true, priority); | ||
return future; | ||
} | ||
|
||
public synchronized Supplier<NBTTagCompound> getLoadedChunk(CraftWorld world, int x, int z) { | ||
if (!world.isChunkLoaded(x, z)) return () -> null; | ||
Chunk c = world.getHandle().getChunkIfLoaded(x, z); //already safe async on vanilla | ||
if ((c == null) || !c.q) return () -> null; // c.loaded | ||
if (currTick != MinecraftServer.currentTick) { | ||
currTick = MinecraftServer.currentTick; | ||
currChunks = 0; | ||
} | ||
//prepare data synchronously | ||
CompletableFuture<?> future = CompletableFuture.supplyAsync(() -> { | ||
//Null will mean that we save with spigot methods, which may be risky on async | ||
//Since we're not in main thread, it now refuses new tasks because of shutdown, the risk is lower | ||
if (!Bukkit.isPrimaryThread()) return null; | ||
try { | ||
return getAsyncSaveData.invoke(null, world.getHandle(), c); | ||
} catch (ReflectiveOperationException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}, ((CraftServer) Bukkit.getServer()).getServer()); | ||
//we shouldn't stress main thread | ||
if (++currChunks > MapManager.mapman.getMaxChunkLoadsPerTick()) { | ||
try { | ||
Thread.sleep(25); //hold the lock so other threads also won't stress main thread | ||
} catch (InterruptedException ignored) {} | ||
} | ||
//save data asynchronously | ||
return () -> { | ||
Object o = null; | ||
try { | ||
o = future.get(); | ||
return (NBTTagCompound) save.invoke(null, world.getHandle(), c, o); | ||
} catch (InterruptedException e) { | ||
return null; | ||
} catch (InvocationTargetException e) { | ||
//We tried to use simple spigot methods at shutdown and failed, hopes for reading from disk | ||
if (o == null) return null; | ||
throw new RuntimeException(e); | ||
} catch (ReflectiveOperationException | ExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}; | ||
} | ||
} |
Oops, something went wrong.