First idea implementation

This commit is contained in:
mastermc05 2022-03-29 23:33:46 +03:00
parent c8801dbb8c
commit b0f0a4deb5
13 changed files with 243 additions and 79 deletions

View File

@ -37,12 +37,14 @@ import net.minecraft.server.v1_16_R2.MinecraftServer;
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot116_2 extends BukkitVersionHelperGeneric { public class BukkitVersionHelperSpigot116_2 extends BukkitVersionHelperGeneric {
private Field watercolorfield; private final boolean unsafeAsync;
private Field watercolorfield;
public BukkitVersionHelperSpigot116_2() { public BukkitVersionHelperSpigot116_2() {
Class biomefog = getNMSClass("net.minecraft.server.BiomeFog"); Class biomefog = getNMSClass("net.minecraft.server.BiomeFog");
watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class); watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class);
} this.unsafeAsync = true;
}
/** /**
* Get block short name list * Get block short name list
@ -69,7 +71,13 @@ public class BukkitVersionHelperSpigot116_2 extends BukkitVersionHelperGeneric {
} }
private Object[] biomelist; private Object[] biomelist;
/**
@Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get list of defined biomebase objects * Get list of defined biomebase objects
*/ */
@Override @Override

View File

@ -37,12 +37,14 @@ import net.minecraft.server.v1_16_R2.BlockPosition;
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot116_3 extends BukkitVersionHelperGeneric { public class BukkitVersionHelperSpigot116_3 extends BukkitVersionHelperGeneric {
private Field watercolorfield; private final boolean unsafeAsync;
private Field watercolorfield;
public BukkitVersionHelperSpigot116_3() { public BukkitVersionHelperSpigot116_3() {
Class biomefog = getNMSClass("net.minecraft.server.BiomeFog"); Class biomefog = getNMSClass("net.minecraft.server.BiomeFog");
watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class); watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class);
} this.unsafeAsync = true;
}
/** /**
* Get block short name list * Get block short name list
@ -69,7 +71,13 @@ public class BukkitVersionHelperSpigot116_3 extends BukkitVersionHelperGeneric {
} }
private Object[] biomelist; private Object[] biomelist;
/**
@Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get list of defined biomebase objects * Get list of defined biomebase objects
*/ */
@Override @Override

View File

@ -26,12 +26,14 @@ import java.util.List;
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot116_4 extends BukkitVersionHelperGeneric { public class BukkitVersionHelperSpigot116_4 extends BukkitVersionHelperGeneric {
private Field watercolorfield; private final boolean unsafeAsync;
private Field watercolorfield;
public BukkitVersionHelperSpigot116_4() { public BukkitVersionHelperSpigot116_4() {
Class biomefog = getNMSClass("net.minecraft.server.BiomeFog"); Class biomefog = getNMSClass("net.minecraft.server.BiomeFog");
watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class); watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class);
} this.unsafeAsync = true;
}
/** /**
* Get block short name list * Get block short name list
@ -58,7 +60,13 @@ public class BukkitVersionHelperSpigot116_4 extends BukkitVersionHelperGeneric {
} }
private Object[] biomelist; private Object[] biomelist;
/**
@Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get list of defined biomebase objects * Get list of defined biomebase objects
*/ */
@Override @Override

View File

@ -36,12 +36,14 @@ import net.minecraft.server.v1_16_R1.BlockPosition;
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot116 extends BukkitVersionHelperGeneric { public class BukkitVersionHelperSpigot116 extends BukkitVersionHelperGeneric {
private final boolean unsafeAsync;
private Field watercolorfield; private Field watercolorfield;
public BukkitVersionHelperSpigot116() { public BukkitVersionHelperSpigot116() {
Class biomefog = getNMSClass("net.minecraft.server.BiomeFog"); Class biomefog = getNMSClass("net.minecraft.server.BiomeFog");
watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class); watercolorfield = getPrivateField(biomefog, new String[] { "c" }, int.class);
} this.unsafeAsync = true;
}
/** /**
* Get block short name list * Get block short name list
@ -59,7 +61,13 @@ public class BukkitVersionHelperSpigot116 extends BukkitVersionHelperGeneric {
} }
private Object[] biomelist; private Object[] biomelist;
/**
@Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get list of defined biomebase objects * Get list of defined biomebase objects
*/ */
@Override @Override

View File

@ -58,11 +58,19 @@ import java.util.Map;
/** /**
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot117 extends BukkitVersionHelper { public class BukkitVersionHelperSpigot117 extends BukkitVersionHelper {
public BukkitVersionHelperSpigot117() { private final boolean unsafeAsync;
public BukkitVersionHelperSpigot117() {
this.unsafeAsync = true;
} }
/** @Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get block short name list * Get block short name list
*/ */
@Override @Override

View File

@ -0,0 +1,56 @@
package org.dynmap.bukkit.helper.v118_2;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.level.World;
import net.minecraft.world.level.chunk.Chunk;
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.function.Consumer;
import java.util.function.Predicate;
/**
* The provider used to work with paper libs
* Because paper libs need java 17 we can't interact with them directly
*/
public class AsyncChunkProvider118_2 {
private static final Thread ioThread;
private static final Method getChunk;
private static final Predicate<NBTTagCompound> ifFailed;
static {
try {
Predicate<NBTTagCompound> ifFailed1 = null;
Method getChunk1 = null;
Thread ioThread1 = null;
try {
Class<?> threadClass = Class.forName("com.destroystokyo.paper.io.PaperFileIOThread");
Class<?>[] classes = threadClass.getClasses();
Class<?> holder = Arrays.stream(classes).filter(aClass -> aClass.getSimpleName().equals("Holder")).findAny().orElseThrow(RuntimeException::new);
ioThread1 = (Thread) holder.getField("INSTANCE").get(null);
getChunk1 = threadClass.getMethod("loadChunkDataAsync", WorldServer.class, int.class, int.class, int.class, Consumer.class, boolean.class, boolean.class, boolean.class);
NBTTagCompound failure = (NBTTagCompound) threadClass.getField("FAILURE_VALUE").get(null);
ifFailed1 = nbtTagCompound -> nbtTagCompound == failure;
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
ifFailed = Objects.requireNonNull(ifFailed1);
getChunk = Objects.requireNonNull(getChunk1);
ioThread = Objects.requireNonNull(ioThread1);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public NBTTagCompound getChunk(WorldServer world, int x, int y) throws InvocationTargetException, IllegalAccessException, NoSuchFieldException {
CompletableFuture<Object> future = new CompletableFuture<>();
getChunk.invoke(ioThread,world,x,y,5,(Consumer<Object>) future::complete, false, true, true);
Object resultFuture = future.join();
if (resultFuture == null) return null;
NBTTagCompound result = (NBTTagCompound) resultFuture.getClass().getField("chunkData").get(resultFuture);
return ifFailed.test(result) ? null : result;
}
}

View File

@ -64,12 +64,25 @@ import java.util.Set;
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot118_2 extends BukkitVersionHelper { public class BukkitVersionHelperSpigot118_2 extends BukkitVersionHelper {
private final boolean unsafeAsync;
public BukkitVersionHelperSpigot118_2() { public BukkitVersionHelperSpigot118_2() {
boolean unsafeAsync1;
} try {
Class.forName("com.destroystokyo.paper.io.PaperFileIOThread");
/** unsafeAsync1 = false;
} catch (ClassNotFoundException e) {
unsafeAsync1 = true;
}
this.unsafeAsync = unsafeAsync1;
}
@Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get block short name list * Get block short name list
*/ */
@Override @Override

View File

@ -1,8 +1,11 @@
package org.dynmap.bukkit.helper.v118_2; package org.dynmap.bukkit.helper.v118_2;
import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.dynmap.DynmapChunk; import org.dynmap.DynmapChunk;
import org.dynmap.bukkit.helper.BukkitVersionHelper;
import org.dynmap.bukkit.helper.BukkitWorld; import org.dynmap.bukkit.helper.BukkitWorld;
import org.dynmap.common.chunk.GenericChunk; import org.dynmap.common.chunk.GenericChunk;
import org.dynmap.common.chunk.GenericChunkCache; import org.dynmap.common.chunk.GenericChunkCache;
@ -14,12 +17,15 @@ import net.minecraft.world.level.chunk.storage.ChunkRegionLoader;
import net.minecraft.world.level.chunk.Chunk; import net.minecraft.world.level.chunk.Chunk;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
/** /**
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread
*/ */
public class MapChunkCache118_2 extends GenericMapChunkCache { public class MapChunkCache118_2 extends GenericMapChunkCache {
private final AsyncChunkProvider118_2 provider = BukkitVersionHelper.helper.isUnsafeAsync() ? null : new AsyncChunkProvider118_2();
private World w; private World w;
/** /**
* Construct empty cache * Construct empty cache
@ -30,20 +36,24 @@ public class MapChunkCache118_2 extends GenericMapChunkCache {
// Load generic chunk from existing and already loaded chunk // Load generic chunk from existing and already loaded chunk
protected GenericChunk getLoadedChunk(DynmapChunk chunk) { protected GenericChunk getLoadedChunk(DynmapChunk chunk) {
CraftWorld cw = (CraftWorld) w; CraftWorld cw = (CraftWorld) w;
NBTTagCompound nbt = null; NBTTagCompound nbt = null;
GenericChunk gc = null; GenericChunk gc = null;
if (cw.isChunkLoaded(chunk.x, chunk.z)) { if (cw.isChunkLoaded(chunk.x, chunk.z)) {
Chunk c = cw.getHandle().getChunkIfLoaded(chunk.x, chunk.z); Chunk c = cw.getHandle().getChunkIfLoaded(chunk.x, chunk.z); //already safe async on vanilla
if ((c != null) && c.o) { // c.loaded if ((c != null) && c.o) { // c.loaded
nbt = ChunkRegionLoader.a(cw.getHandle(), c); if (provider == null) { //idk why, but paper uses this only sync, so I won't be smarter
} nbt = ChunkRegionLoader.a(cw.getHandle(), c);
if (nbt != null) { } else {
gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); nbt = CompletableFuture.supplyAsync(() -> ChunkRegionLoader.a(cw.getHandle(), c), ((CraftServer) Bukkit.getServer()).getServer()).join();
}
} }
return gc; }
} if (nbt != null) {
gc = parseChunkFromNBT(new NBT.NBTCompound(nbt));
}
}
return gc;
}
// Load generic chunk from unloaded chunk // Load generic chunk from unloaded chunk
protected GenericChunk loadChunk(DynmapChunk chunk) { protected GenericChunk loadChunk(DynmapChunk chunk) {
CraftWorld cw = (CraftWorld) w; CraftWorld cw = (CraftWorld) w;
@ -51,8 +61,12 @@ public class MapChunkCache118_2 extends GenericMapChunkCache {
ChunkCoordIntPair cc = new ChunkCoordIntPair(chunk.x, chunk.z); ChunkCoordIntPair cc = new ChunkCoordIntPair(chunk.x, chunk.z);
GenericChunk gc = null; GenericChunk gc = null;
try { try {
nbt = cw.getHandle().k().a.f(cc); // playerChunkMap if (provider == null){
} catch (IOException iox) { nbt = cw.getHandle().k().a.f(cc); // playerChunkMap
} else {
nbt = provider.getChunk(cw.getHandle(),chunk.x, chunk.z);
}
} catch (IOException | InvocationTargetException | IllegalAccessException | NoSuchFieldException ignored) {
} }
if (nbt != null) { if (nbt != null) {
gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); gc = parseChunkFromNBT(new NBT.NBTCompound(nbt));

View File

@ -63,13 +63,20 @@ import java.util.Set;
/** /**
* Helper for isolation of bukkit version specific issues * Helper for isolation of bukkit version specific issues
*/ */
public class BukkitVersionHelperSpigot118 extends BukkitVersionHelper { public class BukkitVersionHelperSpigot118 extends BukkitVersionHelper {
public BukkitVersionHelperSpigot118() { private final boolean unsafeAsync;
public BukkitVersionHelperSpigot118() {
this.unsafeAsync = true;
} }
/** @Override
public boolean isUnsafeAsync() {
return unsafeAsync;
}
/**
* Get block short name list * Get block short name list
*/ */
@Override @Override

View File

@ -34,6 +34,10 @@ public abstract class BukkitVersionHelper {
protected BukkitVersionHelper() { protected BukkitVersionHelper() {
} }
/**
* Get if it's unsafe to load chunks async
*/
public abstract boolean isUnsafeAsync();
/** /**
* Get list of defined biomebase objects * Get list of defined biomebase objects
*/ */

View File

@ -54,6 +54,12 @@ public class BukkitVersionHelperCB extends BukkitVersionHelperGeneric {
isBadUnload = HDBlockModels.checkVersionRange(mcver, "1.9-"); isBadUnload = HDBlockModels.checkVersionRange(mcver, "1.9-");
Log.verboseinfo("MCVER=" + mcver + ", isBadUnload=" + isBadUnload); Log.verboseinfo("MCVER=" + mcver + ", isBadUnload=" + isBadUnload);
} }
@Override
public boolean isUnsafeAsync() {
return true;
}
@Override @Override
protected String getNMSPackage() { protected String getNMSPackage() {
Server srv = Bukkit.getServer(); Server srv = Bukkit.getServer();

View File

@ -32,7 +32,12 @@ public class BukkitVersionHelperGlowstone extends BukkitVersionHelper {
throw new IllegalArgumentException("Error initializing dynmap - Glowstone version incompatible!"); throw new IllegalArgumentException("Error initializing dynmap - Glowstone version incompatible!");
} }
} }
@Override
public boolean isUnsafeAsync() {
return true;
}
@Override @Override
public Object[] getBiomeBaseList() { public Object[] getBiomeBaseList() {
return new Object[0]; return new Object[0];

View File

@ -481,6 +481,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
boolean rslt = permissions.hasOfflinePermission(player, perm); boolean rslt = permissions.hasOfflinePermission(player, perm);
return rslt; return rslt;
} }
private final Object[] lock = {};
/** /**
* Render processor helper - used by code running on render threads to request chunk snapshot cache from server/sync thread * Render processor helper - used by code running on render threads to request chunk snapshot cache from server/sync thread
*/ */
@ -514,46 +515,64 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
final MapChunkCache cc = c; final MapChunkCache cc = c;
while(!cc.isDoneLoading()) { while(!cc.isDoneLoading()) {
Future<Boolean> f = core.getServer().callSyncMethod(new Callable<Boolean>() { if (BukkitVersionHelper.helper.isUnsafeAsync()) {
public Boolean call() throws Exception { Future<Boolean> f = core.getServer().callSyncMethod(new Callable<Boolean>() {
boolean exhausted = true; public Boolean call() throws Exception {
boolean exhausted = true;
if (prev_tick != cur_tick) {
prev_tick = cur_tick; if (prev_tick != cur_tick) {
cur_tick_starttime = System.nanoTime(); prev_tick = cur_tick;
} cur_tick_starttime = System.nanoTime();
if(chunks_in_cur_tick > 0) {
boolean done = false;
while (!done) {
int cnt = chunks_in_cur_tick;
if (cnt > 5) cnt = 5;
chunks_in_cur_tick -= cc.loadChunks(cnt);
exhausted = (chunks_in_cur_tick == 0) || ((System.nanoTime() - cur_tick_starttime) > perTickLimit);
done = exhausted || cc.isDoneLoading();
} }
if (chunks_in_cur_tick > 0) {
boolean done = false;
while (!done) {
int cnt = chunks_in_cur_tick;
if (cnt > 5) cnt = 5;
chunks_in_cur_tick -= cc.loadChunks(cnt);
exhausted = (chunks_in_cur_tick == 0) || ((System.nanoTime() - cur_tick_starttime) > perTickLimit);
done = exhausted || cc.isDoneLoading();
}
}
return exhausted;
} }
return exhausted; });
if (f == null) {
return null;
}
Boolean delay;
try {
delay = f.get();
} catch (CancellationException cx) {
return null;
} catch (InterruptedException cx) {
return null;
} catch (ExecutionException ex) {
Log.severe("Exception while fetching chunks: ", ex.getCause());
return null;
} catch (Exception ix) {
Log.severe(ix);
return null;
}
if ((delay != null) && delay.booleanValue()) {
try {
Thread.sleep(25);
} catch (InterruptedException ix) {
}
}
} else {
try {
synchronized (lock) {
if (prev_tick != cur_tick) {
prev_tick = cur_tick;
cur_tick_starttime = System.nanoTime();
}
cc.loadChunks(Integer.MAX_VALUE);
}
} catch (Exception e) {
e.printStackTrace();
} }
});
if (f == null) {
return null;
}
Boolean delay;
try {
delay = f.get();
} catch (CancellationException cx) {
return null;
} catch (InterruptedException cx) {
return null;
} catch (ExecutionException ex) {
Log.severe("Exception while fetching chunks: ", ex.getCause());
return null;
} catch (Exception ix) {
Log.severe(ix);
return null;
}
if((delay != null) && delay.booleanValue()) {
try { Thread.sleep(25); } catch (InterruptedException ix) {}
} }
} }
/* If cancelled due to world unload return nothing */ /* If cancelled due to world unload return nothing */