From 8ca0da72f1c044534f3cab5412af490986072a41 Mon Sep 17 00:00:00 2001 From: Justin Su <3387175+injust@users.noreply.github.com> Date: Mon, 22 Jul 2019 07:54:07 -0400 Subject: [PATCH 01/26] Remove duplicate of onblockfade trigger --- spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java index be7e73e1..69393802 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java +++ b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java @@ -1194,7 +1194,6 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { onblockfromto = core.isTrigger("blockfromto"); onblockphysics = core.isTrigger("blockphysics"); onpiston = core.isTrigger("pistonmoved"); - onblockfade = core.isTrigger("blockfaded"); onblockredstone = core.isTrigger("blockredstone"); if(onplace) { From 8fc95bfc96949b6286bfc03e678407c3ebaf453f Mon Sep 17 00:00:00 2001 From: Tiago Dias Date: Wed, 14 Aug 2019 12:24:38 +0100 Subject: [PATCH 02/26] Fix for disappearing sign-markers on 1.14 (Issue #2533) --- .../dynmap/common/DynmapServerInterface.java | 11 +++++++++++ .../dynmap/markers/impl/MarkerSignManager.java | 15 ++++++--------- .../org/dynmap/forge_1_10_2/DynmapPlugin.java | 17 +++++++++++++++++ .../org/dynmap/forge_1_11_2/DynmapPlugin.java | 17 +++++++++++++++++ .../org/dynmap/forge_1_12_2/DynmapPlugin.java | 17 +++++++++++++++++ .../org/dynmap/forge_1_13_2/DynmapPlugin.java | 8 ++++++++ .../org/dynmap/forge_1_8_9/DynmapPlugin.java | 17 +++++++++++++++++ .../org/dynmap/forge_1_9_4/DynmapPlugin.java | 17 +++++++++++++++++ .../java/org/dynmap/bukkit/DynmapPlugin.java | 17 +++++++++++++++++ 9 files changed, 127 insertions(+), 9 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java b/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java index 3271300a..7fd4f584 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java +++ b/DynmapCore/src/main/java/org/dynmap/common/DynmapServerInterface.java @@ -178,6 +178,17 @@ public abstract class DynmapServerInterface { * @return block ID, or -1 if chunk at given coordinate isn't loaded */ public abstract int getBlockIDAt(String wname, int x, int y, int z); + + /** + * Checks if a sign is at a given coordinate in a given world (if chunk is loaded) + * @param wname - world name + * @param x - X coordinate + * @param y - Y coordinate + * @param z - Z coordinate + * @return 1 if a sign is at the location, 0 if it's not, -1 if the chunk isn't loaded + */ + public abstract int isSignAt(String wname, int x, int y, int z); + /** * Get current TPS for server (20.0 is nominal) * @return ticks per second diff --git a/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java b/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java index 123cdc8c..48274b6e 100644 --- a/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java +++ b/DynmapCore/src/main/java/org/dynmap/markers/impl/MarkerSignManager.java @@ -20,9 +20,6 @@ public class MarkerSignManager { private static DynmapCore plugin = null; private static String defSignSet = null; - private static final int SIGNPOST_ID = 63; - private static final int WALLSIGN_ID = 68; - private static class SignRec { String wname; int x, y, z; @@ -33,10 +30,12 @@ public class MarkerSignManager { @Override public void signChangeEvent(int blkid, String wname, int x, int y, int z, String[] lines, DynmapPlayer p) { if(mgr == null) - return; + return; + if(!lines[0].equalsIgnoreCase("[dynmap]")) { /* If not dynmap sign, quit */ return; } + /* If allowed to do marker signs */ if((p == null) || ((plugin != null) && (plugin.checkPlayerPermission(p, "marker.sign")))) { String id = getSignMarkerID(wname, x, y, z); /* Get marker ID */ @@ -143,17 +142,15 @@ public class MarkerSignManager { for(Iterator> iter = sign_cache.entrySet().iterator(); iter.hasNext(); ) { Entry ent = iter.next(); SignRec r = ent.getValue(); - /* If deleted marker, remote */ + /* If deleted marker, remove */ if(r.m.getMarkerSet() == null) { iter.remove(); } else { - /* Get block ID */ - int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); - if((blkid >= 0) && (blkid != WALLSIGN_ID) && (blkid != SIGNPOST_ID)) { + if(plugin.getServer().isSignAt(r.wname, r.x, r.y, r.z) == 0) { r.m.deleteMarker(); iter.remove(); - } + } } } plugin.getServer().scheduleServerTask(sl, 60*20); diff --git a/forge-1.10.2/src/main/java/org/dynmap/forge_1_10_2/DynmapPlugin.java b/forge-1.10.2/src/main/java/org/dynmap/forge_1_10_2/DynmapPlugin.java index e53e1165..22464dad 100644 --- a/forge-1.10.2/src/main/java/org/dynmap/forge_1_10_2/DynmapPlugin.java +++ b/forge-1.10.2/src/main/java/org/dynmap/forge_1_10_2/DynmapPlugin.java @@ -145,6 +145,9 @@ public class DynmapPlugin private boolean isMCPC = false; private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; @@ -509,6 +512,20 @@ public class DynmapPlugin } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); + + if (blkid == -1) + return -1; + + if((blkid == WALLSIGN_ID) || (blkid == SIGNPOST_ID)) { + return 1; + } else { + return 0; + } + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/forge-1.11.2/src/main/java/org/dynmap/forge_1_11_2/DynmapPlugin.java b/forge-1.11.2/src/main/java/org/dynmap/forge_1_11_2/DynmapPlugin.java index 95b6310a..affba5f4 100644 --- a/forge-1.11.2/src/main/java/org/dynmap/forge_1_11_2/DynmapPlugin.java +++ b/forge-1.11.2/src/main/java/org/dynmap/forge_1_11_2/DynmapPlugin.java @@ -146,6 +146,9 @@ public class DynmapPlugin private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; + private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; private static final Pattern patternControlCode = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); @@ -517,6 +520,20 @@ public class DynmapPlugin } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); + + if (blkid == -1) + return -1; + + if((blkid == WALLSIGN_ID) || (blkid == SIGNPOST_ID)) { + return 1; + } else { + return 0; + } + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/forge-1.12.2/src/main/java/org/dynmap/forge_1_12_2/DynmapPlugin.java b/forge-1.12.2/src/main/java/org/dynmap/forge_1_12_2/DynmapPlugin.java index ffbcadc5..e1b20341 100644 --- a/forge-1.12.2/src/main/java/org/dynmap/forge_1_12_2/DynmapPlugin.java +++ b/forge-1.12.2/src/main/java/org/dynmap/forge_1_12_2/DynmapPlugin.java @@ -145,6 +145,9 @@ public class DynmapPlugin private boolean isMCPC = false; private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; @@ -522,6 +525,20 @@ public class DynmapPlugin } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); + + if (blkid == -1) + return -1; + + if((blkid == WALLSIGN_ID) || (blkid == SIGNPOST_ID)) { + return 1; + } else { + return 0; + } + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/forge-1.13.2/src/main/java/org/dynmap/forge_1_13_2/DynmapPlugin.java b/forge-1.13.2/src/main/java/org/dynmap/forge_1_13_2/DynmapPlugin.java index b08d367d..01c83663 100644 --- a/forge-1.13.2/src/main/java/org/dynmap/forge_1_13_2/DynmapPlugin.java +++ b/forge-1.13.2/src/main/java/org/dynmap/forge_1_13_2/DynmapPlugin.java @@ -156,6 +156,9 @@ public class DynmapPlugin private boolean isMCPC = false; private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; @@ -513,6 +516,11 @@ public class DynmapPlugin public int getBlockIDAt(String wname, int x, int y, int z) { return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + return -1; + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/forge-1.8.9/src/main/java/org/dynmap/forge_1_8_9/DynmapPlugin.java b/forge-1.8.9/src/main/java/org/dynmap/forge_1_8_9/DynmapPlugin.java index d6522c45..91024fd0 100644 --- a/forge-1.8.9/src/main/java/org/dynmap/forge_1_8_9/DynmapPlugin.java +++ b/forge-1.8.9/src/main/java/org/dynmap/forge_1_8_9/DynmapPlugin.java @@ -140,6 +140,9 @@ public class DynmapPlugin private boolean isMCPC = false; private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; @@ -493,6 +496,20 @@ public class DynmapPlugin } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); + + if (blkid == -1) + return -1; + + if((blkid == WALLSIGN_ID) || (blkid == SIGNPOST_ID)) { + return 1; + } else { + return 0; + } + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/forge-1.9.4/src/main/java/org/dynmap/forge_1_9_4/DynmapPlugin.java b/forge-1.9.4/src/main/java/org/dynmap/forge_1_9_4/DynmapPlugin.java index c1210eab..1f408598 100644 --- a/forge-1.9.4/src/main/java/org/dynmap/forge_1_9_4/DynmapPlugin.java +++ b/forge-1.9.4/src/main/java/org/dynmap/forge_1_9_4/DynmapPlugin.java @@ -138,6 +138,9 @@ public class DynmapPlugin private boolean isMCPC = false; private boolean useSaveFolder = true; private Field displayName = null; // MCPC+ display name + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; @@ -496,6 +499,20 @@ public class DynmapPlugin } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + int blkid = plugin.getServer().getBlockIDAt(r.wname, r.x, r.y, r.z); + + if (blkid == -1) + return -1; + + if((blkid == WALLSIGN_ID) || (blkid == SIGNPOST_ID)) { + return 1; + } else { + return 0; + } + } @Override public void scheduleServerTask(Runnable run, long delay) diff --git a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java index be7e73e1..ecbed6a6 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java +++ b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java @@ -28,6 +28,7 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -212,6 +213,22 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { } return -1; } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + World w = getServer().getWorld(wname); + if((w != null) && w.isChunkLoaded(x >> 4, z >> 4)) { + Block b = w.getBlockAt(x, y, z); + BlockState s = b.getState(); + + if (s instanceof Sign) { + return 1; + } else { + return 0; + } + } + return -1; + } @Override public void scheduleServerTask(Runnable run, long delay) { From 7f84262bfb1c05a782495d8a4427f4afa90b1095 Mon Sep 17 00:00:00 2001 From: Eric_Lian Date: Wed, 14 Aug 2019 23:28:20 +0800 Subject: [PATCH 03/26] HD skins support --- .../src/main/java/org/dynmap/PlayerFaces.java | 86 ++++++++++++++----- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java b/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java index 02bb5fa1..de0f591c 100644 --- a/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java +++ b/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java @@ -11,6 +11,7 @@ import org.dynmap.utils.DynmapBufferedImage; import org.dynmap.utils.ImageIOManager; import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -96,6 +97,13 @@ public class PlayerFaces { } } } + + private void copyLayersToTarget(BufferedImage srcimg, int layer1x, int layer1y, int layer2x, int layer2y, int w, int h, BufferedImage dest, int destoff, int destscansize) + { + int[] tmp = new int[w*h]; + copyLayersToTarget(srcimg,layer1x,layer1y,layer2x,layer2y,w,h,tmp,0,w); + dest.setRGB(0,0,w,h,tmp,destoff,destscansize); + } private class LoadPlayerImages implements Runnable { public final String playername; @@ -145,14 +153,24 @@ public class PlayerFaces { img.flush(); return; } - else if(img.getHeight() == 32) { + else if( (img.getWidth() / img.getHeight()) == 2 ) { /* Is single layer skin? */ is_64x32_skin = true; } + + /* Get buffered image for face at original size */ + int scale = img.getWidth()/8 /8; + BufferedImage faceOriginal = new BufferedImage(8*scale, 8*scale, BufferedImage.TYPE_INT_ARGB); + // Copy face and overlay to icon + copyLayersToTarget(img, 8*scale, 8*scale, 40*scale, 8*scale, 8*scale, 8*scale, faceOriginal, 0, 8*scale); + int[] faceaccessory = new int[64]; /* 8x8 of face accessory */ /* Get buffered image for face at 8x8 */ DynmapBufferedImage face8x8 = DynmapBufferedImage.allocateBufferedImage(8, 8); - // Copy face and overlay to icon - copyLayersToTarget(img, 8, 8, 40, 8, 8, 8, face8x8.argb_buf, 0, 8); + Image face8x8_image = faceOriginal.getScaledInstance(8,8,BufferedImage.SCALE_SMOOTH); + BufferedImage face8x8_buff = new BufferedImage(8, 8, BufferedImage.TYPE_INT_ARGB); + + face8x8_buff.getGraphics().drawImage(face8x8_image,0,0,null); + face8x8_buff.getRGB(0,0,8,8,face8x8.argb_buf,0,8); /* Write 8x8 file */ if(refreshskins || (!has_8x8)) { BufferOutputStream bos = ImageIOManager.imageIOEncode(face8x8.buf_img, ImageFormat.FORMAT_PNG); @@ -164,36 +182,60 @@ public class PlayerFaces { if(refreshskins || (!has_16x16)) { /* Make 16x16 version */ DynmapBufferedImage face16x16 = DynmapBufferedImage.allocateBufferedImage(16, 16); - for(int i = 0; i < 16; i++) { - for(int j = 0; j < 16; j++) { - face16x16.argb_buf[i*16+j] = face8x8.argb_buf[(i/2)*8 + (j/2)]; - } - } + Image face16x16_image = faceOriginal.getScaledInstance(16,16,BufferedImage.SCALE_SMOOTH); + BufferedImage face16x16_buff = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + + face16x16_buff.getGraphics().drawImage(face16x16_image,0,0,null); + face16x16_buff.getRGB(0,0,16,16,face16x16.argb_buf,0,16); + BufferOutputStream bos = ImageIOManager.imageIOEncode(face16x16.buf_img, ImageFormat.FORMAT_PNG); if (bos != null) { storage.setPlayerFaceImage(playername, FaceType.FACE_16X16, bos); } DynmapBufferedImage.freeBufferedImage(face16x16); + face16x16_buff.flush(); } /* Write 32x32 file */ if(refreshskins || (!has_32x32)) { /* Make 32x32 version */ DynmapBufferedImage face32x32 = DynmapBufferedImage.allocateBufferedImage(32, 32); - for(int i = 0; i < 32; i++) { - for(int j = 0; j < 32; j++) { - face32x32.argb_buf[i*32+j] = face8x8.argb_buf[(i/4)*8 + (j/4)]; - } - } + Image face32x32_image = faceOriginal.getScaledInstance(32,32,BufferedImage.SCALE_SMOOTH); + BufferedImage face32x32_buff = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB); + + face32x32_buff.getGraphics().drawImage(face32x32_image,0,0,null); + face32x32_buff.getRGB(0,0,32,32,face32x32.argb_buf,0,32); + BufferOutputStream bos = ImageIOManager.imageIOEncode(face32x32.buf_img, ImageFormat.FORMAT_PNG); if (bos != null) { storage.setPlayerFaceImage(playername, FaceType.FACE_32X32, bos); } DynmapBufferedImage.freeBufferedImage(face32x32); + face32x32_buff.flush(); } /* Write body file */ if(refreshskins || (!has_body)) { + + Image skin_image = null; + BufferedImage skin_buff = null; + + if (is_64x32_skin){ + + skin_image = img.getScaledInstance(64,32,BufferedImage.SCALE_SMOOTH); + skin_buff = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB); + + skin_buff.getGraphics().drawImage(skin_image,0,0,null); + + } else { + + skin_image = img.getScaledInstance(64,64,BufferedImage.SCALE_SMOOTH); + skin_buff = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + + skin_buff.getGraphics().drawImage(skin_image,0,0,null); + + } + /* Make 32x32 version */ DynmapBufferedImage body32x32 = DynmapBufferedImage.allocateBufferedImage(32, 32); /* Copy face at 12,0 to 20,8 (already handled accessory) */ @@ -203,34 +245,38 @@ public class PlayerFaces { } } /* Copy body at 20,20 and chest at 20,36 to 8,12 */ - copyLayersToTarget(img, 20, 20, 20, 36, 8, 12, body32x32.argb_buf, 8*32+12, 32); + copyLayersToTarget(skin_buff, 20, 20, 20, 36, 8, 12, body32x32.argb_buf, 8*32+12, 32); /* Copy right leg at 4,20 and 4,36 to 20,12 */ - copyLayersToTarget(img, 4, 20, 4, 36, 4, 12, body32x32.argb_buf, 20*32+12, 32); + copyLayersToTarget(skin_buff, 4, 20, 4, 36, 4, 12, body32x32.argb_buf, 20*32+12, 32); /* Copy left leg at 4,20 if old format or 20,52 and 4,53 to 20,16 */ if(is_64x32_skin) { - img.getRGB(4, 20, 4, 12, body32x32.argb_buf, 20*32+16, 32); + skin_buff.getRGB(4, 20, 4, 12, body32x32.argb_buf, 20*32+16, 32); } else { - copyLayersToTarget(img, 20, 52, 4, 52, 4, 12, body32x32.argb_buf, 20 * 32 + 16, 32); + copyLayersToTarget(skin_buff, 20, 52, 4, 52, 4, 12, body32x32.argb_buf, 20 * 32 + 16, 32); } /* Copy right arm at 44,20 and 44,36 to 8,8 */ - copyLayersToTarget(img, 44, 20, 44, 36, 4, 12, body32x32.argb_buf, 8*32+8, 32); + copyLayersToTarget(skin_buff, 44, 20, 44, 36, 4, 12, body32x32.argb_buf, 8*32+8, 32); /* Copy left arm at 44,20 if old format or 36,52 and 52,52 to 8,20 */ if(is_64x32_skin) { - img.getRGB(44, 20, 4, 12, body32x32.argb_buf, 8*32+20, 32); + skin_buff.getRGB(44, 20, 4, 12, body32x32.argb_buf, 8*32+20, 32); } else { - copyLayersToTarget(img, 36, 52, 52, 52, 4, 12, body32x32.argb_buf, 8 * 32 + 20, 32); + copyLayersToTarget(skin_buff, 36, 52, 52, 52, 4, 12, body32x32.argb_buf, 8 * 32 + 20, 32); } BufferOutputStream bos = ImageIOManager.imageIOEncode(body32x32.buf_img, ImageFormat.FORMAT_PNG); if (bos != null) { storage.setPlayerFaceImage(playername, FaceType.BODY_32X32, bos); } + DynmapBufferedImage.freeBufferedImage(body32x32); + skin_buff.flush(); } DynmapBufferedImage.freeBufferedImage(face8x8); + face8x8_buff.flush(); + faceOriginal.flush(); img.flush(); } } From 8522015edc408703824319e3fd98aaf6114c5683 Mon Sep 17 00:00:00 2001 From: baconwaifu Date: Thu, 15 Aug 2019 00:51:21 -0400 Subject: [PATCH 04/26] Add initial postgres support; internal webserver works perfectly --- .../src/main/java/org/dynmap/DynmapCore.java | 4 + .../postgresql/PostgreSQLMapStorage.java | 1052 +++++++++++++++++ 2 files changed, 1056 insertions(+) create mode 100644 DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java index f4259be6..b844cb53 100644 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java @@ -57,6 +57,7 @@ import org.dynmap.storage.filetree.FileTreeMapStorage; import org.dynmap.storage.mysql.MySQLMapStorage; import org.dynmap.storage.mariadb.MariaDBMapStorage; import org.dynmap.storage.sqllte.SQLiteMapStorage; +import org.dynmap.storage.postgresql.PostgreSQLMapStorage; import org.dynmap.utils.BlockStep; import org.dynmap.utils.ImageIOManager; import org.dynmap.web.BanIPFilter; @@ -388,6 +389,9 @@ public class DynmapCore implements DynmapCommonAPI { else if (storetype.equals("mariadb")) { defaultStorage = new MariaDBMapStorage(); } + else if (storetype.equals("postgres") || storetype.equals("postgresql")) { + defaultStorage = new PostgreSQLMapStorage(); + } else { Log.severe("Invalid storage type for map data: " + storetype); return false; diff --git a/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java new file mode 100644 index 00000000..25b083dc --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java @@ -0,0 +1,1052 @@ +package org.dynmap.storage.postgresql; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import org.dynmap.DynmapCore; +import org.dynmap.DynmapWorld; +import org.dynmap.Log; +import org.dynmap.MapType; +import org.dynmap.WebAuthManager; +import org.dynmap.MapType.ImageVariant; +import org.dynmap.PlayerFaces.FaceType; +import org.dynmap.storage.MapStorage; +import org.dynmap.storage.MapStorageTile; +import org.dynmap.storage.MapStorageTileEnumCB; +import org.dynmap.utils.BufferInputStream; +import org.dynmap.utils.BufferOutputStream; + +public class PostgreSQLMapStorage extends MapStorage { + private String connectionString; + private String userid; + private String password; + private String database; + private String hostname; + private String prefix = ""; + private String flags; + private String tableTiles; + private String tableMaps; + private String tableFaces; + private String tableMarkerIcons; + private String tableMarkerFiles; + private String tableStandaloneFiles; + private String tableSchemaVersion; + + private int port; + private static final int POOLSIZE = 5; + private Connection[] cpool = new Connection[POOLSIZE]; + private int cpoolCount = 0; + private static final Charset UTF8 = Charset.forName("UTF-8"); + + private HashMap mapKey = new HashMap(); + + public class StorageTile extends MapStorageTile { + private Integer mapkey; + private String uri; + protected StorageTile(DynmapWorld world, MapType map, int x, int y, + int zoom, ImageVariant var) { + super(world, map, x, y, zoom, var); + + mapkey = getMapKey(world, map, var); + + if (zoom > 0) { + uri = map.getPrefix() + var.variantSuffix + "/"+ (x >> 5) + "_" + (y >> 5) + "/" + "zzzzzzzzzzzzzzzz".substring(0, zoom) + "_" + x + "_" + y + "." + map.getImageFormat().getFileExt(); + } + else { + uri = map.getPrefix() + var.variantSuffix + "/"+ (x >> 5) + "_" + (y >> 5) + "/" + x + "_" + y + "." + map.getImageFormat().getFileExt(); + } + } + + @Override + public boolean exists() { + if (mapkey == null) return false; + boolean rslt = false; + Connection c = null; + boolean err = false; + try { + c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT HashCode FROM " + tableTiles + " WHERE MapID=" + mapkey + " AND x=" + x + " AND y=" + y + " AND zoom=" + zoom + ";"); + rslt = rs.next(); + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Tile exists error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return rslt; + } + + @Override + public boolean matchesHashCode(long hash) { + if (mapkey == null) return false; + boolean rslt = false; + Connection c = null; + boolean err = false; + try { + c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT HashCode FROM " + tableTiles + " WHERE MapID=" + mapkey + " AND x=" + x + " AND y=" + y + " AND zoom=" + zoom + ";"); + if (rs.next()) { + long v = rs.getLong("HashCode"); + rslt = (v == hash); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Tile matches hash error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return rslt; + } + + @Override + public TileRead read() { + if (mapkey == null) return null; + TileRead rslt = null; + Connection c = null; + boolean err = false; + try { + c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT HashCode,LastUpdate,Format,Image FROM " + tableTiles + " WHERE MapID=" + mapkey + " AND x=" + x + " AND y=" + y + " AND zoom=" + zoom + ";"); + if (rs.next()) { + rslt = new TileRead(); + rslt.hashCode = rs.getLong("HashCode"); + rslt.lastModified = rs.getLong("LastUpdate"); + rslt.format = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); + byte[] img = rs.getBytes("Image"); + rslt.image = new BufferInputStream(img); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Tile read error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return rslt; + } + + @Override + public boolean write(long hash, BufferOutputStream encImage) { + if (mapkey == null) return false; + Connection c = null; + boolean err = false; + boolean exists = exists(); + // If delete, and doesn't exist, quit + if ((encImage == null) && (!exists)) return false; + + try { + c = getConnection(); + PreparedStatement stmt; + if (encImage == null) { // If delete + stmt = c.prepareStatement("DELETE FROM " + tableTiles + " WHERE MapID=? AND x=? and y=? AND zoom=?;"); + stmt.setInt(1, mapkey); + stmt.setInt(2, x); + stmt.setInt(3, y); + stmt.setInt(4, zoom); + } + else if (exists) { + stmt = c.prepareStatement("UPDATE " + tableTiles + " SET HashCode=?, LastUpdate=?, Format=?, Image=? WHERE MapID=? AND x=? and y=? AND zoom=?;"); + stmt.setLong(1, hash); + stmt.setLong(2, System.currentTimeMillis()); + stmt.setInt(3, map.getImageFormat().getEncoding().ordinal()); + stmt.setBinaryStream(4, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + stmt.setInt(5, mapkey); + stmt.setInt(6, x); + stmt.setInt(7, y); + stmt.setInt(8, zoom); + } + else { + stmt = c.prepareStatement("INSERT INTO " + tableTiles + " (MapID,x,y,zoom,HashCode,LastUpdate,Format,Image) VALUES (?,?,?,?,?,?,?,?);"); + stmt.setInt(1, mapkey); + stmt.setInt(2, x); + stmt.setInt(3, y); + stmt.setInt(4, zoom); + stmt.setLong(5, hash); + stmt.setLong(6, System.currentTimeMillis()); + stmt.setInt(7, map.getImageFormat().getEncoding().ordinal()); + stmt.setBinaryStream(8, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + } + stmt.executeUpdate(); + stmt.close(); + // Signal update for zoom out + if (zoom == 0) { + world.enqueueZoomOutUpdate(this); + } + } catch (SQLException x) { + Log.severe("Tile write error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return !err; + } + + @Override + public boolean getWriteLock() { + return PostgreSQLMapStorage.this.getWriteLock(uri); + } + + @Override + public void releaseWriteLock() { + PostgreSQLMapStorage.this.releaseWriteLock(uri); + } + + @Override + public boolean getReadLock(long timeout) { + return PostgreSQLMapStorage.this.getReadLock(uri, timeout); + } + + @Override + public void releaseReadLock() { + PostgreSQLMapStorage.this.releaseReadLock(uri); + } + + @Override + public void cleanup() { + } + + @Override + public String getURI() { + return uri; + } + + @Override + public void enqueueZoomOutUpdate() { + world.enqueueZoomOutUpdate(this); + } + + @Override + public MapStorageTile getZoomOutTile() { + int xx, yy; + int step = 1 << zoom; + if(x >= 0) + xx = x - (x % (2*step)); + else + xx = x + (x % (2*step)); + yy = -y; + if(yy >= 0) + yy = yy - (yy % (2*step)); + else + yy = yy + (yy % (2*step)); + yy = -yy; + return new StorageTile(world, map, xx, yy, zoom+1, var); + } + + @Override + public boolean equals(Object o) { + if (o instanceof StorageTile) { + StorageTile st = (StorageTile) o; + return uri.equals(st.uri); + } + return false; + } + + @Override + public int hashCode() { + return uri.hashCode(); + } + } + + public PostgreSQLMapStorage(){ + } + + @Override + public boolean init(DynmapCore core) { + if (!super.init(core)) { + return false; + } + database = core.configuration.getString("storage/database", "dynmap"); + hostname = core.configuration.getString("storage/hostname", "localhost"); + port = core.configuration.getInteger("storage/port", 5432); + userid = core.configuration.getString("storage/userid", "dynmap"); + password = core.configuration.getString("storage/password", "dynmap"); + prefix = core.configuration.getString("storage/prefix", ""); + flags = core.configuration.getString("storage/flags", "?allowReconnect=true"); + tableTiles = prefix + "Tiles"; + tableMaps = prefix + "Maps"; + tableFaces = prefix + "Faces"; + tableMarkerIcons = prefix + "MarkerIcons"; + tableMarkerFiles = prefix + "MarkerFiles"; + tableStandaloneFiles = prefix + "StandaloneFiles"; + tableSchemaVersion = prefix + "SchemaVersion"; + + connectionString = "jdbc:postgresql://" + hostname + ":" + port + "/" + database + flags; + Log.info("Opening PostgreSQL database " + hostname + ":" + port + "/" + database + " as map store"); + try { + Class.forName("org.postgresql.Driver"); + // Initialize/update tables, if needed + if(!initializeTables()) { + return false; + } + } catch (ClassNotFoundException cnfx) { + Log.severe("PostgreSQL-JDBC classes not found - PostgreSQL data source not usable"); + return false; + } + return writeConfigPHP(core); + } + private boolean writeConfigPHP(DynmapCore core) { + FileWriter fw = null; + try { + fw = new FileWriter(new File(baseStandaloneDir, "PostgreSQL_config.php")); + fw.write("\n"); + } catch (IOException iox) { + Log.severe("Error writing PostgreSQL_config.php", iox); + return false; + } finally { + if (fw != null) { + try { fw.close(); } catch (IOException x) {} + } + } + return true; + } + + private int getSchemaVersion() { + int ver = 0; + boolean err = false; + Connection c = null; + try { + c = getConnection(); // Get connection (create DB if needed) + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery( "SELECT level FROM " + tableSchemaVersion + ";"); + if (rs.next()) { + ver = rs.getInt("level"); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + err = true; + } finally { + if (c != null) { releaseConnection(c, err); } + } + return ver; + } + + private void doUpdate(Connection c, String sql) throws SQLException { + Statement stmt = c.createStatement(); + stmt.executeUpdate(sql); + stmt.close(); + } + + private void doLoadMaps() { + Connection c = null; + boolean err = false; + + mapKey.clear(); + // Read the maps table - cache results + try { + c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * from " + tableMaps + ";"); + while (rs.next()) { + int key = rs.getInt("ID"); + String worldID = rs.getString("WorldID"); + String mapID = rs.getString("MapID"); + String variant = rs.getString("Variant"); + long serverid = rs.getLong("ServerID"); + if (serverid == serverID) { // One of ours + mapKey.put(worldID + ":" + mapID + ":" + variant, key); + } + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Error loading map table - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + c = null; + } + } + + private Integer getMapKey(DynmapWorld w, MapType mt, ImageVariant var) { + String id = w.getName() + ":" + mt.getPrefix() + ":" + var.toString(); + synchronized(mapKey) { + Integer k = mapKey.get(id); + if (k == null) { // No hit: new value so we need to add it to table + Connection c = null; + boolean err = false; + try { + c = getConnection(); + // Insert row + PreparedStatement stmt = c.prepareStatement("INSERT INTO " + tableMaps + " (WorldID,MapID,Variant,ServerID) VALUES (?, ?, ?, ?);"); + stmt.setString(1, w.getName()); + stmt.setString(2, mt.getPrefix()); + stmt.setString(3, var.toString()); + stmt.setLong(4, serverID); + stmt.executeUpdate(); + stmt.close(); + // Query key assigned + stmt = c.prepareStatement("SELECT ID FROM " + tableMaps + " WHERE WorldID = ? AND MapID = ? AND Variant = ? AND ServerID = ?;"); + stmt.setString(1, w.getName()); + stmt.setString(2, mt.getPrefix()); + stmt.setString(3, var.toString()); + stmt.setLong(4, serverID); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + k = rs.getInt("ID"); + mapKey.put(id, k); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Error updating Maps table - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + } + + return k; + } + } + + private boolean initializeTables() { + Connection c = null; + boolean err = false; + int version = getSchemaVersion(); // Get the existing schema version for the DB (if any) + // If new, add our tables + if (version == 0) { + try { + c = getConnection(); + doUpdate(c, "CREATE TABLE " + tableMaps + " (ID SERIAL PRIMARY KEY, WorldID VARCHAR(64) NOT NULL, MapID VARCHAR(64) NOT NULL, Variant VARCHAR(16) NOT NULL, ServerID BIGINT NOT NULL DEFAULT 0)"); + doUpdate(c, "CREATE TABLE " + tableTiles + " (MapID INT NOT NULL, x INT NOT NULL, y INT NOT NULL, zoom INT NOT NULL, HashCode BIGINT NOT NULL, LastUpdate BIGINT NOT NULL, Format INT NOT NULL, Image BYTEA, PRIMARY KEY(MapID, x, y, zoom))"); + doUpdate(c, "CREATE TABLE " + tableFaces + " (PlayerName VARCHAR(64) NOT NULL, TypeID INT NOT NULL, Image BYTEA, PRIMARY KEY(PlayerName, TypeID))"); + doUpdate(c, "CREATE TABLE " + tableMarkerIcons + " (IconName VARCHAR(128) PRIMARY KEY NOT NULL, Image BYTEA)"); + doUpdate(c, "CREATE TABLE " + tableMarkerFiles + " (FileName VARCHAR(128) PRIMARY KEY NOT NULL, Content TEXT)"); + doUpdate(c, "CREATE TABLE " + tableStandaloneFiles + " (FileName VARCHAR(128) NOT NULL, ServerID BIGINT NOT NULL DEFAULT 0, Content TEXT, PRIMARY KEY (FileName, ServerID))"); + doUpdate(c, "CREATE TABLE " + tableSchemaVersion + " (level INT PRIMARY KEY NOT NULL)"); + doUpdate(c, "INSERT INTO " + tableSchemaVersion + " (level) VALUES (3)"); + } catch (SQLException x) { + Log.severe("Error creating tables - " + x.getMessage()); + err = true; + return false; + } finally { + releaseConnection(c, err); + c = null; + } + } + else if (version == 1) { + try { + c = getConnection(); + doUpdate(c, "CREATE TABLE " + tableStandaloneFiles + " (FileName VARCHAR(128) NOT NULL, ServerID BIGINT NOT NULL DEFAULT 0, Content TEXT, PRIMARY KEY (FileName, ServerID))"); + doUpdate(c, "ALTER TABLE " + tableMaps + " ADD COLUMN ServerID BIGINT NOT NULL DEFAULT 0 AFTER Variant"); + doUpdate(c, "UPDATE " + tableSchemaVersion + " SET level=3 WHERE level = 1;"); + } catch (SQLException x) { + Log.severe("Error creating tables - " + x.getMessage()); + err = true; + return false; + } finally { + releaseConnection(c, err); + c = null; + } + } + else if (version == 2) { + try { + c = getConnection(); + doUpdate(c, "DELETE FROM " + tableStandaloneFiles + ";"); + doUpdate(c, "ALTER TABLE " + tableStandaloneFiles + " DROP COLUMN Content;"); + doUpdate(c, "ALTER TABLE " + tableStandaloneFiles + " ADD COLUMN Content TEXT;"); + doUpdate(c, "UPDATE " + tableSchemaVersion + " SET level=3 WHERE level = 2;"); + } catch (SQLException x) { + Log.severe("Error creating tables - " + x.getMessage()); + err = true; + return false; + } finally { + releaseConnection(c, err); + c = null; + } + } + // Load maps table - cache results + doLoadMaps(); + + return true; + } + + private Connection getConnection() throws SQLException { + Connection c = null; + synchronized (cpool) { + while (c == null) { + for (int i = 0; i < cpool.length; i++) { // See if available connection + if (cpool[i] != null) { // Found one + c = cpool[i]; + cpool[i] = null; + break; + } + } + if (c == null) { + if (cpoolCount < POOLSIZE) { // Still more we can have + c = DriverManager.getConnection(connectionString, userid, password); + configureConnection(c); + cpoolCount++; + } + else { + try { + cpool.wait(); + } catch (InterruptedException e) { + throw new SQLException("Interruped"); + } + } + } + } + } + return c; + } + private static Connection configureConnection(Connection conn) throws SQLException { + return conn; + } + + private void releaseConnection(Connection c, boolean err) { + if (c == null) return; + synchronized (cpool) { + if (!err) { // Find slot to keep it in pool + for (int i = 0; i < POOLSIZE; i++) { + if (cpool[i] == null) { + cpool[i] = c; + c = null; // Mark it recovered (no close needed + cpool.notifyAll(); + break; + } + } + } + if (c != null) { // If broken, just toss it + try { c.close(); } catch (SQLException x) {} + cpoolCount--; // And reduce count + cpool.notifyAll(); + } + } + } + + @Override + public MapStorageTile getTile(DynmapWorld world, MapType map, int x, int y, + int zoom, ImageVariant var) { + return new StorageTile(world, map, x, y, zoom, var); + } + + @Override + public MapStorageTile getTile(DynmapWorld world, String uri) { + String[] suri = uri.split("/"); + if (suri.length < 2) return null; + String mname = suri[0]; // Map URI - might include variant + MapType mt = null; + ImageVariant imgvar = null; + // Find matching map type and image variant + for (int mti = 0; (mt == null) && (mti < world.maps.size()); mti++) { + MapType type = world.maps.get(mti); + ImageVariant[] var = type.getVariants(); + for (int ivi = 0; (imgvar == null) && (ivi < var.length); ivi++) { + if (mname.equals(type.getPrefix() + var[ivi].variantSuffix)) { + mt = type; + imgvar = var[ivi]; + } + } + } + if (mt == null) { // Not found? + return null; + } + // Now, take the last section and parse out coordinates and zoom + String fname = suri[suri.length-1]; + String[] coord = fname.split("[_\\.]"); + if (coord.length < 3) { // 3 or 4 + return null; + } + int zoom = 0; + int x, y; + try { + if (coord[0].charAt(0) == 'z') { + zoom = coord[0].length(); + x = Integer.parseInt(coord[1]); + y = Integer.parseInt(coord[2]); + } + else { + x = Integer.parseInt(coord[0]); + y = Integer.parseInt(coord[1]); + } + return getTile(world, mt, x, y, zoom, imgvar); + } catch (NumberFormatException nfx) { + return null; + } + } + + @Override + public void enumMapTiles(DynmapWorld world, MapType map, + MapStorageTileEnumCB cb) { + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processEnumMapTiles(world, mt, var, cb); + } + } + } + + private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb) { + Connection c = null; + boolean err = false; + Integer mapkey = getMapKey(world, map, var); + if (mapkey == null) return; + try { + c = getConnection(); + // Query tiles for given mapkey + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT x,y,zoom,Format FROM " + tableTiles + " WHERE MapID=" + mapkey + ";"); + while (rs.next()) { + StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); + cb.tileFound(st, MapType.ImageEncoding.fromOrd(rs.getInt("Format"))); + st.cleanup(); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Tile enum error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + } + + @Override + public void purgeMapTiles(DynmapWorld world, MapType map) { + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processPurgeMapTiles(world, mt, var); + } + } + } + + private void processPurgeMapTiles(DynmapWorld world, MapType map, ImageVariant var) { + Connection c = null; + boolean err = false; + Integer mapkey = getMapKey(world, map, var); + if (mapkey == null) return; + try { + c = getConnection(); + // Query tiles for given mapkey + Statement stmt = c.createStatement(); + stmt.executeUpdate("DELETE FROM " + tableTiles + " WHERE MapID=" + mapkey + ";"); + stmt.close(); + } catch (SQLException x) { + Log.severe("Tile purge error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + } + + @Override + public boolean setPlayerFaceImage(String playername, FaceType facetype, + BufferOutputStream encImage) { + Connection c = null; + boolean err = false; + boolean exists = hasPlayerFaceImage(playername, facetype); + // If delete, and doesn't exist, quit + if ((encImage == null) && (!exists)) return false; + + try { + c = getConnection(); + PreparedStatement stmt; + if (encImage == null) { // If delete + stmt = c.prepareStatement("DELETE FROM " + tableFaces + " WHERE PlayerName=? AND TypeIDx=?;"); + stmt.setString(1, playername); + stmt.setInt(2, facetype.typeID); + } + else if (exists) { + stmt = c.prepareStatement("UPDATE " + tableFaces + " SET Image=? WHERE PlayerName=? AND TypeID=?;"); + stmt.setBinaryStream(1, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + stmt.setString(2, playername); + stmt.setInt(3, facetype.typeID); + } + else { + stmt = c.prepareStatement("INSERT INTO " + tableFaces + " (PlayerName,TypeID,Image) VALUES (?,?,?);"); + stmt.setString(1, playername); + stmt.setInt(2, facetype.typeID); + stmt.setBinaryStream(3, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + } + stmt.executeUpdate(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Face write error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return !err; + } + + @Override + public BufferInputStream getPlayerFaceImage(String playername, + FaceType facetype) { + Connection c = null; + boolean err = false; + BufferInputStream image = null; + try { + c = getConnection(); + PreparedStatement stmt = c.prepareStatement("SELECT Image FROM " + tableFaces + " WHERE PlayerName=? AND TypeID=?;"); + stmt.setString(1, playername); + stmt.setInt(2, facetype.typeID); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + byte[] img = rs.getBytes("Image"); + image = new BufferInputStream(img); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Face read error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return image; + } + + @Override + public boolean hasPlayerFaceImage(String playername, FaceType facetype) { + Connection c = null; + boolean err = false; + boolean exists = false; + try { + c = getConnection(); + PreparedStatement stmt = c.prepareStatement("SELECT TypeID FROM " + tableFaces + " WHERE PlayerName=? AND TypeID=?;"); + stmt.setString(1, playername); + stmt.setInt(2, facetype.typeID); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + exists = true; + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Face exists error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return exists; + } + + @Override + public boolean setMarkerImage(String markerid, BufferOutputStream encImage) { + Connection c = null; + boolean err = false; + PreparedStatement stmt = null; + ResultSet rs = null; + + try { + c = getConnection(); + boolean exists = false; + stmt = c.prepareStatement("SELECT IconName FROM " + tableMarkerIcons + " WHERE IconName=?;"); + stmt.setString(1, markerid); + rs = stmt.executeQuery(); + if (rs.next()) { + exists = true; + } + rs.close(); + rs = null; + stmt.close(); + stmt = null; + if (encImage == null) { // If delete + // If delete, and doesn't exist, quit + if (!exists) return false; + stmt = c.prepareStatement("DELETE FROM " + tableMarkerIcons + " WHERE IconName=?;"); + stmt.setString(1, markerid); + stmt.executeUpdate(); + } + else if (exists) { + stmt = c.prepareStatement("UPDATE " + tableMarkerIcons + " SET Image=? WHERE IconName=?;"); + stmt.setBinaryStream(1, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + stmt.setString(2, markerid); + } + else { + stmt = c.prepareStatement("INSERT INTO " + tableMarkerIcons + " (IconName,Image) VALUES (?,?);"); + stmt.setString(1, markerid); + stmt.setBinaryStream(2, new BufferInputStream(encImage.buf, encImage.len), encImage.len); + } + stmt.executeUpdate(); + } catch (SQLException x) { + Log.severe("Marker write error - " + x.getMessage()); + err = true; + } finally { + if (rs != null) { try { rs.close(); } catch (SQLException sx) {} } + if (stmt != null) { try { stmt.close(); } catch (SQLException sx) {} } + releaseConnection(c, err); + } + return !err; + } + + @Override + public BufferInputStream getMarkerImage(String markerid) { + Connection c = null; + boolean err = false; + BufferInputStream image = null; + try { + c = getConnection(); + PreparedStatement stmt = c.prepareStatement("SELECT Image FROM " + tableMarkerIcons + " WHERE IconName=?;"); + stmt.setString(1, markerid); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + byte[] img = rs.getBytes("Image"); + image = new BufferInputStream(img); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Marker read error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return image; + } + + @Override + public boolean setMarkerFile(String world, String content) { + Connection c = null; + boolean err = false; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + c = getConnection(); + boolean exists = false; + stmt = c.prepareStatement("SELECT FileName FROM " + tableMarkerFiles + " WHERE FileName=?;"); + stmt.setString(1, world); + rs = stmt.executeQuery(); + if (rs.next()) { + exists = true; + } + rs.close(); + rs = null; + stmt.close(); + stmt = null; + if (content == null) { // If delete + // If delete, and doesn't exist, quit + if (!exists) return false; + stmt = c.prepareStatement("DELETE FROM " + tableMarkerFiles + " WHERE FileName=?;"); + stmt.setString(1, world); + stmt.executeUpdate(); + } + else if (exists) { + stmt = c.prepareStatement("UPDATE " + tableMarkerFiles + " SET Content=? WHERE FileName=?;"); + stmt.setBytes(1, content.getBytes(UTF8)); + stmt.setString(2, world); + } + else { + stmt = c.prepareStatement("INSERT INTO " + tableMarkerFiles + " (FileName,Content) VALUES (?,?);"); + stmt.setString(1, world); + stmt.setBytes(2, content.getBytes(UTF8)); + } + stmt.executeUpdate(); + } catch (SQLException x) { + Log.severe("Marker file write error - " + x.getMessage()); + err = true; + } finally { + if (rs != null) { try { rs.close(); } catch (SQLException sx) {} } + if (stmt != null) { try { stmt.close(); } catch (SQLException sx) {} } + releaseConnection(c, err); + } + return !err; + } + + @Override + public String getMarkerFile(String world) { + Connection c = null; + boolean err = false; + String content = null; + try { + c = getConnection(); + PreparedStatement stmt = c.prepareStatement("SELECT Content FROM " + tableMarkerFiles + " WHERE FileName=?;"); + stmt.setString(1, world); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + byte[] img = rs.getBytes("Content"); + content = new String(img, UTF8); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Marker file read error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return content; + } + + @Override + public String getMarkersURI(boolean login_enabled) { + return "standalone/PostgreSQL_markers.php?marker="; + } + + @Override + public String getTilesURI(boolean login_enabled) { + return "standalone/PostgreSQL_tiles.php?tile="; + } + + @Override + public String getConfigurationJSONURI(boolean login_enabled) { + return "standalone/PostgreSQL_configuration.php"; // ?serverid={serverid}"; + } + + @Override + public String getUpdateJSONURI(boolean login_enabled) { + return "standalone/PostgreSQL_update.php?world={world}&ts={timestamp}"; // &serverid={serverid}"; + } + + @Override + public String getSendMessageURI() { + return "standalone/PostgreSQL_sendmessage.php"; + } + + @Override + public BufferInputStream getStandaloneFile(String fileid) { + Connection c = null; + boolean err = false; + BufferInputStream content = null; + try { + c = getConnection(); + PreparedStatement stmt = c.prepareStatement("SELECT Content FROM " + tableStandaloneFiles + " WHERE FileName=? AND ServerID=?;"); + stmt.setString(1, fileid); + stmt.setLong(2, serverID); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + byte[] img = rs.getBytes("Content"); + content = new BufferInputStream(img); + } + rs.close(); + stmt.close(); + } catch (SQLException x) { + Log.severe("Standalone file read error - " + x.getMessage()); + err = true; + } finally { + releaseConnection(c, err); + } + return content; + } + + @Override + public boolean setStandaloneFile(String fileid, BufferOutputStream content) { + Connection c = null; + boolean err = false; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + c = getConnection(); + boolean exists = false; + stmt = c.prepareStatement("SELECT FileName FROM " + tableStandaloneFiles + " WHERE FileName=? AND ServerID=?;"); + stmt.setString(1, fileid); + stmt.setLong(2, serverID); + rs = stmt.executeQuery(); + if (rs.next()) { + exists = true; + } + rs.close(); + rs = null; + stmt.close(); + stmt = null; + if (content == null) { // If delete + // If delete, and doesn't exist, quit + if (!exists) return true; + stmt = c.prepareStatement("DELETE FROM " + tableStandaloneFiles + " WHERE FileName=? AND ServerID=?;"); + stmt.setString(1, fileid); + stmt.setLong(2, serverID); + stmt.executeUpdate(); + } + else if (exists) { + stmt = c.prepareStatement("UPDATE " + tableStandaloneFiles + " SET Content=? WHERE FileName=? AND ServerID=?;"); + stmt.setBinaryStream(1, new BufferInputStream(content.buf, content.len), content.len); + stmt.setString(2, fileid); + stmt.setLong(3, serverID); + } + else { + stmt = c.prepareStatement("INSERT INTO " + tableStandaloneFiles + " (FileName,ServerID,Content) VALUES (?,?,?);"); + stmt.setString(1, fileid); + stmt.setLong(2, serverID); + stmt.setBinaryStream(3, new BufferInputStream(content.buf, content.len), content.len); + } + stmt.executeUpdate(); + } catch (SQLException x) { + Log.severe("Standalone file write error - " + x.getMessage()); + err = true; + } finally { + if (rs != null) { try { rs.close(); } catch (SQLException sx) {} } + if (stmt != null) { try { stmt.close(); } catch (SQLException sx) {} } + releaseConnection(c, err); + } + return !err; + } + @Override + public boolean wrapStandaloneJSON(boolean login_enabled) { + return false; + } + @Override + public boolean wrapStandalonePHP() { + return false; + } + @Override + public String getStandaloneLoginURI() { + return "standalone/PostgreSQL_login.php"; + } + @Override + public String getStandaloneRegisterURI() { + return "standalone/PostgreSQL_register.php"; + } + @Override + public void setLoginEnabled(DynmapCore core) { + writeConfigPHP(core); + } +} From 0d336ea98856ca0de6d878f789645d5af2051cbd Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 29 Sep 2019 15:29:43 -0500 Subject: [PATCH 05/26] Update Forge 1.13.2 verison --- forge-1.13.2/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-1.13.2/build.gradle b/forge-1.13.2/build.gradle index 026cac9d..18531050 100644 --- a/forge-1.13.2/build.gradle +++ b/forge-1.13.2/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'eclipse' dependencies { compile project(":DynmapCore") compile project(":DynmapCoreAPI") - minecraft 'net.minecraftforge:forge:1.13.2-25.0.191' + minecraft 'net.minecraftforge:forge:1.13.2-25.0.219' } sourceCompatibility = 1.8 @@ -33,7 +33,7 @@ repositories { } minecraft { - mappings channel: 'snapshot', version: '20190415-1.13.2' + mappings channel: 'snapshot', version: '20190530-1.13.2' runs { server { workingDirectory project.file('run').canonicalPath From ab64f92fcb60aae0162edc7959c0f28754a5fc69 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 29 Sep 2019 16:44:39 -0500 Subject: [PATCH 06/26] Handle legacy block IDs in RPs --- .../main/java/org/dynmap/renderer/DynmapBlockState.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java index 618ec8f1..f9648417 100644 --- a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java +++ b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java @@ -180,6 +180,15 @@ public class DynmapBlockState { DynmapBlockState blk = blocksByName.get(name); if ((blk == null) && (name.indexOf(':') == -1)) { blk = blocksByName.get("minecraft:" + name); + if (blk == null) { // If still null, see if legacy ID number + try { + int v = Integer.parseInt(name); + if (v >= 0) { + blk = blocksByLegacyID.get(v); + } + } catch (NumberFormatException nfx) { + } + } } return (blk != null) ? blk : AIR; } From 398e2d4f5f008fa1d473555d5f8f752bc9b9bdef Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 29 Sep 2019 16:53:14 -0500 Subject: [PATCH 07/26] Bump to beta-6 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 12c66a9a..a1e9a0e3 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ allprojects { apply plugin: 'java' group = 'us.dynmap' - version = '3.0-beta-5' + version = '3.0-beta-6' } From 1be9f663c751f9b5084e8134dca37f319d7a3d35 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 29 Sep 2019 17:51:58 -0500 Subject: [PATCH 08/26] Handle meta=0 as undefined in CTM --- DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java b/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java index 9d9a704b..21ed3a3c 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java @@ -343,8 +343,8 @@ public class CTMTexturePack { bs = DynmapBlockState.getBaseStateByName(token); addbase = true; } - if (bs.isAir()) { - Log.info("Unknown block ID in CTM: " + token); + if (bs == DynmapBlockState.AIR) { + Log.info("Unknown block ID in CTM: " + token); } else if (addbase) { addBaseBlockStateToIDSet(list, bs); From c9ec8c032cd70dce9ca07da9d420b9474b6ebbb4 Mon Sep 17 00:00:00 2001 From: MiniDigger Date: Mon, 4 Nov 2019 17:28:10 +0100 Subject: [PATCH 09/26] Add new repo for bStats --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a1e9a0e3..92bbd828 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ allprojects { maven { url "http://repo.mikeprimm.com" } maven { url "http://repo.maven.apache.org/maven2" } maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } - maven { url "http://repo.bstats.org/content/repositories/releases/" } + maven { url "https://repo.codemc.org/repository/maven-public/" } } apply plugin: 'java' From 95f727d61c5b77ce85b54bed7f1f9b746d296ece Mon Sep 17 00:00:00 2001 From: MiniDigger Date: Mon, 4 Nov 2019 17:29:28 +0100 Subject: [PATCH 10/26] Update bStats to 1.5 --- spigot/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spigot/build.gradle b/spigot/build.gradle index 121ff590..9f295c27 100644 --- a/spigot/build.gradle +++ b/spigot/build.gradle @@ -11,7 +11,7 @@ dependencies { compile group: 'de.bananaco', name: 'bPermissions', version:'2.9.1' compile group: 'com.platymuus.bukkit.permissions', name: 'PermissionsBukkit', version:'1.6' compile group: 'org.anjocaido', name: 'EssentialsGroupManager', version:'2.10.1' - compile group: 'org.bstats', name: 'bstats-bukkit', version:'1.1' + compile group: 'org.bstats', name: 'bstats-bukkit', version:'1.5' compile group: 'com.googlecode.json-simple', name: 'json-simple', version:'1.1.1' compile group: 'com.google.code.gson', name: 'gson', version:'2.8.2' compile project(':bukkit-helper') From e9518fd048594bb5b26f98ccf89959d4d4569a42 Mon Sep 17 00:00:00 2001 From: Paul Vogel Date: Tue, 5 Nov 2019 13:53:10 +0100 Subject: [PATCH 11/26] Fix typo for "LuckPerms" in plugin.yml softdepend --- spigot/src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 2e722eb7..a1ef6a25 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: org.dynmap.bukkit.DynmapPlugin version: "${version}-${buildnumber}" authors: [mikeprimm] website: "https://forums.dynmap.us" -softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, GroupManager, LuckPerm ] +softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, GroupManager, LuckPerms ] commands: dynmap: description: Controls Dynmap. From 4ec3964988de0f65af6604e49a956199ba729e5d Mon Sep 17 00:00:00 2001 From: linuxdaemon Date: Tue, 12 Nov 2019 00:57:51 -0600 Subject: [PATCH 12/26] Fix MariaDB Driver package name The package name for the MariaDB JDBC drivers available from https://mariadb.com/kb/en/library/mariadb-connector-j/ is `org.mariadb` not `com.mariadb`. Fixes #2653 --- .../main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java index 78618bb4..2bfa8049 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java @@ -289,7 +289,7 @@ public class MariaDBMapStorage extends MapStorage { connectionString = "jdbc:mariadb://" + hostname + ":" + port + "/" + database + "?allowReconnect=true"; Log.info("Opening MariaDB database " + hostname + ":" + port + "/" + database + " as map store"); try { - Class.forName("com.mariadb.jdbc.Driver"); + Class.forName("org.mariadb.jdbc.Driver"); // Initialize/update tables, if needed if(!initializeTables()) { return false; From 016441fdab60f651f30e84c4d519d3ce299b99b0 Mon Sep 17 00:00:00 2001 From: Arvind Kumar Date: Tue, 12 Nov 2019 23:51:10 +0530 Subject: [PATCH 13/26] Fixed typo in config file comment --- forge-1.10.2/src/main/resources/configuration.txt | 2 +- forge-1.11.2/src/main/resources/configuration.txt | 2 +- forge-1.12.2/src/main/resources/configuration.txt | 2 +- forge-1.13.2/src/main/resources/configuration.txt | 2 +- forge-1.8.9/src/main/resources/configuration.txt | 2 +- forge-1.9.4/src/main/resources/configuration.txt | 2 +- spigot/src/main/resources/configuration.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/forge-1.10.2/src/main/resources/configuration.txt b/forge-1.10.2/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.10.2/src/main/resources/configuration.txt +++ b/forge-1.10.2/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/forge-1.11.2/src/main/resources/configuration.txt b/forge-1.11.2/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.11.2/src/main/resources/configuration.txt +++ b/forge-1.11.2/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/forge-1.12.2/src/main/resources/configuration.txt b/forge-1.12.2/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.12.2/src/main/resources/configuration.txt +++ b/forge-1.12.2/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/forge-1.13.2/src/main/resources/configuration.txt b/forge-1.13.2/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.13.2/src/main/resources/configuration.txt +++ b/forge-1.13.2/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/forge-1.8.9/src/main/resources/configuration.txt b/forge-1.8.9/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.8.9/src/main/resources/configuration.txt +++ b/forge-1.8.9/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/forge-1.9.4/src/main/resources/configuration.txt b/forge-1.9.4/src/main/resources/configuration.txt index 45ee61fe..d67e9baa 100644 --- a/forge-1.9.4/src/main/resources/configuration.txt +++ b/forge-1.9.4/src/main/resources/configuration.txt @@ -140,7 +140,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false diff --git a/spigot/src/main/resources/configuration.txt b/spigot/src/main/resources/configuration.txt index 9e398f45..10aee57e 100644 --- a/spigot/src/main/resources/configuration.txt +++ b/spigot/src/main/resources/configuration.txt @@ -141,7 +141,7 @@ components: messagettl: 5 # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages #scrollback: 100 - # Optiona; set maximum number of lines visible for chatbox + # Optional: set maximum number of lines visible for chatbox #visiblelines: 10 # Optional: send push button sendbutton: false From 59578e68243316f867b42aa3c0e62ce5c291ca44 Mon Sep 17 00:00:00 2001 From: Lennart Becker Date: Fri, 29 Nov 2019 12:33:03 +0100 Subject: [PATCH 14/26] Standalone: Load changes immediately after page load --- DynmapCore/src/main/resources/extracted/web/js/map.js | 1 + 1 file changed, 1 insertion(+) diff --git a/DynmapCore/src/main/resources/extracted/web/js/map.js b/DynmapCore/src/main/resources/extracted/web/js/map.js index 9922d02d..9d8ca201 100644 --- a/DynmapCore/src/main/resources/extracted/web/js/map.js +++ b/DynmapCore/src/main/resources/extracted/web/js/map.js @@ -375,6 +375,7 @@ DynMap.prototype = { componentstoload--; if (componentstoload == 0) { // Actually start updating once all components are loaded. + me.update(); setTimeout(function() { me.update(); }, me.options.updaterate); } }); From 9e7174dcdea52746503c0f2c907359975c6e9feb Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Sun, 1 Dec 2019 03:38:05 +0100 Subject: [PATCH 15/26] Update LuckPerms and bStats --- build.gradle | 2 +- spigot/build.gradle | 4 +- .../java/org/dynmap/bukkit/DynmapPlugin.java | 54 +++++++++---------- .../permissions/LuckPermsPermissions.java | 44 ++++++++------- 4 files changed, 54 insertions(+), 50 deletions(-) diff --git a/build.gradle b/build.gradle index a1e9a0e3..92bbd828 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ allprojects { maven { url "http://repo.mikeprimm.com" } maven { url "http://repo.maven.apache.org/maven2" } maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } - maven { url "http://repo.bstats.org/content/repositories/releases/" } + maven { url "https://repo.codemc.org/repository/maven-public/" } } apply plugin: 'java' diff --git a/spigot/build.gradle b/spigot/build.gradle index 121ff590..bc4b7994 100644 --- a/spigot/build.gradle +++ b/spigot/build.gradle @@ -4,14 +4,14 @@ description = 'dynmap' dependencies { compile group: 'org.bukkit', name: 'bukkit', version:'1.7.10-R0.1-SNAPSHOT' compile 'com.nijikokun.bukkit:Permissions:3.1.6' - compile 'me.lucko.luckperms:luckperms-api:4.3' + compile 'net.luckperms:api:5.0' compile project(":dynmap-api") compile project(path: ":DynmapCore", configuration: "shadow") compile group: 'ru.tehkode', name: 'PermissionsEx', version:'1.19.1' compile group: 'de.bananaco', name: 'bPermissions', version:'2.9.1' compile group: 'com.platymuus.bukkit.permissions', name: 'PermissionsBukkit', version:'1.6' compile group: 'org.anjocaido', name: 'EssentialsGroupManager', version:'2.10.1' - compile group: 'org.bstats', name: 'bstats-bukkit', version:'1.1' + compile group: 'org.bstats', name: 'bstats-bukkit', version:'1.5' compile group: 'com.googlecode.json-simple', name: 'json-simple', version:'1.1.1' compile group: 'com.google.code.gson', name: 'gson', version:'2.8.2' compile project(':bukkit-helper') diff --git a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java index be7e73e1..d36e6484 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java +++ b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java @@ -16,7 +16,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import org.bstats.Metrics; +import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Chunk; @@ -1585,35 +1585,31 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { private void initMetrics() { metrics = new Metrics(this); - metrics.addCustomChart(new Metrics.MultiLineChart("features_used") { - @Override - public HashMap getValues(HashMap hashMap) { - hashMap.put("internal_web_server", core.configuration.getBoolean("disable-webserver", false) ? 0 : 1); - hashMap.put("login_security", core.configuration.getBoolean("login-enabled", false) ? 1 : 0); - hashMap.put("player_info_protected", core.player_info_protected ? 1 : 0); - for (String mod : modsused) - hashMap.put(mod + "_blocks", 1); - return hashMap; - } - }); + metrics.addCustomChart(new Metrics.MultiLineChart("features_used", () -> { + Map hashMap = new HashMap<>(); + hashMap.put("internal_web_server", core.configuration.getBoolean("disable-webserver", false) ? 0 : 1); + hashMap.put("login_security", core.configuration.getBoolean("login-enabled", false) ? 1 : 0); + hashMap.put("player_info_protected", core.player_info_protected ? 1 : 0); + for (String mod : modsused) + hashMap.put(mod + "_blocks", 1); + return hashMap; + })); - metrics.addCustomChart(new Metrics.MultiLineChart("map_data") { - @Override - public HashMap getValues(HashMap hashMap) { - hashMap.put("worlds", core.mapManager != null ? core.mapManager.getWorlds().size() : 0); - int maps = 0, hdmaps = 0; - if (core.mapManager != null) - for (DynmapWorld w : core.mapManager.getWorlds()) { - for (MapType mt : w.maps) - if (mt instanceof HDMap) - ++hdmaps; - maps += w.maps.size(); - } - hashMap.put("maps", maps); - hashMap.put("hd_maps", hdmaps); - return hashMap; - } - }); + metrics.addCustomChart(new Metrics.MultiLineChart("map_data", () -> { + Map hashMap = new HashMap<>(); + hashMap.put("worlds", core.mapManager != null ? core.mapManager.getWorlds().size() : 0); + int maps = 0, hdmaps = 0; + if (core.mapManager != null) + for (DynmapWorld w : core.mapManager.getWorlds()) { + for (MapType mt : w.maps) + if (mt instanceof HDMap) + ++hdmaps; + maps += w.maps.size(); + } + hashMap.put("maps", maps); + hashMap.put("hd_maps", hdmaps); + return hashMap; + })); } @Override public void processSignChange(int blkid, String world, int x, int y, int z, diff --git a/spigot/src/main/java/org/dynmap/bukkit/permissions/LuckPermsPermissions.java b/spigot/src/main/java/org/dynmap/bukkit/permissions/LuckPermsPermissions.java index 7ab582b2..f0848a7e 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/permissions/LuckPermsPermissions.java +++ b/spigot/src/main/java/org/dynmap/bukkit/permissions/LuckPermsPermissions.java @@ -6,9 +6,12 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import me.lucko.luckperms.api.LuckPermsApi; -import me.lucko.luckperms.api.User; -import me.lucko.luckperms.api.caching.PermissionData; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.user.User; +import net.luckperms.api.model.user.UserManager; +import net.luckperms.api.cacheddata.CachedPermissionData; +import net.luckperms.api.cacheddata.CachedDataManager; +import net.luckperms.api.query.QueryOptions; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -18,19 +21,19 @@ import org.dynmap.Log; public class LuckPermsPermissions implements PermissionProvider { String name; - LuckPermsApi luckPerms; + LuckPerms luckPerms; public static LuckPermsPermissions create(Server server, String name) { if (!server.getPluginManager().isPluginEnabled("LuckPerms")) return null; - LuckPermsApi luckPerms = server.getServicesManager().load(LuckPermsApi.class); + LuckPerms luckPerms = server.getServicesManager().load(LuckPerms.class); if (luckPerms == null) return null; - Log.info("Using LuckPerms " + luckPerms.getPlatformInfo().getVersion() + " for access control"); + Log.info("Using LuckPerms " + luckPerms.getPluginMetadata().getVersion() + " for access control"); return new LuckPermsPermissions(name, luckPerms); } - public LuckPermsPermissions(String name, LuckPermsApi luckPerms) { + public LuckPermsPermissions(String name, LuckPerms luckPerms) { this.name = name; this.luckPerms = luckPerms; } @@ -43,10 +46,10 @@ public class LuckPermsPermissions implements PermissionProvider { @Override public Set hasOfflinePermissions(String player, Set perms) { Set result = new HashSet<>(); - PermissionData user = getUser(player); + CachedPermissionData user = getUser(player); if (user != null) { for (String p : perms) { - if (user.getPermissionValue(name + "." + p).asBoolean()) + if (user.checkPermission(name + "." + p).asBoolean()) result.add(p); } } @@ -55,34 +58,39 @@ public class LuckPermsPermissions implements PermissionProvider { @Override public boolean hasOfflinePermission(String player, String perm) { - PermissionData user = getUser(player); + CachedPermissionData user = getUser(player); if (user == null) return false; - return user.getPermissionValue(name + "." + perm).asBoolean(); + return user.checkPermission(name + "." + perm).asBoolean(); } - private PermissionData getUser(String username) { + private CachedPermissionData getUser(String username) { OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(username); UUID uuid; if (offlinePlayer != null && offlinePlayer.getUniqueId() != null) uuid = offlinePlayer.getUniqueId(); else - uuid = joinFuture(luckPerms.getStorage().getUUID(username)); + uuid = joinFuture(luckPerms.getUserManager().lookupUniqueId(username)); if (uuid == null) return null; - User user = luckPerms.getUser(uuid); + User user = luckPerms.getUserManager().getUser(uuid); if (user == null) { - joinFuture(luckPerms.getStorage().loadUser(uuid)); - user = luckPerms.getUser(uuid); + joinFuture(luckPerms.getUserManager().loadUser(uuid)); + user = luckPerms.getUserManager().getUser(uuid); } if (user == null) return null; - return user.getCachedData().getPermissionData(luckPerms.getContextManager().getStaticContexts()); + CachedDataManager data = user.getCachedData(); + return luckPerms + .getContextManager() + .getQueryOptions(user) + .map(queryOptions -> data.getPermissionData(queryOptions)) + .orElse(null); } private static T joinFuture(Future future) { @@ -92,4 +100,4 @@ public class LuckPermsPermissions implements PermissionProvider { throw new RuntimeException(e); } } -} \ No newline at end of file +} From dd78b11144753f869c1a3dabed373c6364a783df Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Mon, 2 Dec 2019 19:35:18 +0100 Subject: [PATCH 16/26] Bump to beta-7 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 92bbd828..ef54d8f5 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ allprojects { apply plugin: 'java' group = 'us.dynmap' - version = '3.0-beta-6' + version = '3.0-beta-7' } From 24bb87d6355fe59bf1ceb42392fa0947fbae42e9 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 8 Dec 2019 00:59:19 -0600 Subject: [PATCH 17/26] Fix incorrect transparency handling on copyblock: texture handling --- .../java/org/dynmap/hdmap/HDBlockStateTextureMap.java | 10 ++++++---- .../src/main/java/org/dynmap/hdmap/TexturePack.java | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockStateTextureMap.java b/DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockStateTextureMap.java index 2b6d98b6..9617de69 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockStateTextureMap.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockStateTextureMap.java @@ -97,7 +97,7 @@ public class HDBlockStateTextureMap { if ((this.blockset != null) && (this.blockset.equals("core") == false)) { HDBlockModels.resetIfNotBlockSet(bs, this.blockset); } - copyToStateIndex(bs, this); + copyToStateIndex(bs, this, null); } } else { // Else, loop over all state IDs for given block @@ -110,7 +110,7 @@ public class HDBlockStateTextureMap { if ((this.blockset != null) && (this.blockset.equals("core") == false)) { HDBlockModels.resetIfNotBlockSet(bs, this.blockset); } - copyToStateIndex(bs, this); + copyToStateIndex(bs, this, null); } } } @@ -144,9 +144,11 @@ public class HDBlockStateTextureMap { return m; } // Copy given block state to given state index - public static void copyToStateIndex(DynmapBlockState blk, HDBlockStateTextureMap map) { + public static void copyToStateIndex(DynmapBlockState blk, HDBlockStateTextureMap map, TexturePack.BlockTransparency trans) { resize(blk.globalStateIndex); - TexturePack.BlockTransparency trans = map.trans; + if (trans == null) { + trans = map.trans; + } // Force waterloogged blocks to use SEMITRANSPARENT (same as water) if ((trans == TexturePack.BlockTransparency.TRANSPARENT) && blk.isWaterlogged()) { trans = TexturePack.BlockTransparency.SEMITRANSPARENT; diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/TexturePack.java b/DynmapCore/src/main/java/org/dynmap/hdmap/TexturePack.java index bb9759e2..b37d4fc1 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/TexturePack.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/TexturePack.java @@ -2222,13 +2222,13 @@ public class TexturePack { if (stateids == null) { for (int sid = 0; sid < dblk.getStateCount(); sid++) { DynmapBlockState dblk2 = dblk.getState(sid); - HDBlockStateTextureMap.copyToStateIndex(dblk2, map); + HDBlockStateTextureMap.copyToStateIndex(dblk2, map, trans); } } else { for (int stateid = stateids.nextSetBit(0); stateid >= 0; stateid = stateids.nextSetBit(stateid+1)) { DynmapBlockState dblk2 = dblk.getState(stateid); - HDBlockStateTextureMap.copyToStateIndex(dblk2, map); + HDBlockStateTextureMap.copyToStateIndex(dblk2, map, trans); } } } From f910b1135dfe0e66ce9efa4b73565d0fc550e5e7 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 8 Dec 2019 01:01:21 -0600 Subject: [PATCH 18/26] Fix texture order handling for mod support API handling for stairs --- .../renderer/CopyStairBlockRenderer.java | 223 ++++++++++++++++++ .../modsupport/impl/StairBlockModelImpl.java | 2 +- 2 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CopyStairBlockRenderer.java diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CopyStairBlockRenderer.java b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CopyStairBlockRenderer.java new file mode 100644 index 00000000..e3d33ccc --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/renderer/CopyStairBlockRenderer.java @@ -0,0 +1,223 @@ +package org.dynmap.hdmap.renderer; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.Map; + +import org.dynmap.renderer.CustomRenderer; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.renderer.MapDataContext; +import org.dynmap.renderer.RenderPatch; +import org.dynmap.renderer.RenderPatchFactory; + +public class CopyStairBlockRenderer extends CustomRenderer { + private static final int TEXTURE_X_PLUS = 0; + private static final int TEXTURE_Y_PLUS = 1; + private static final int TEXTURE_Z_PLUS = 2; + private static final int TEXTURE_X_MINUS = 3; + private static final int TEXTURE_Y_MINUS = 4; + private static final int TEXTURE_Z_MINUS = 5; + + private static BitSet stair_ids = new BitSet(); + + // Array of meshes for normal steps - index = (data value & 7) + private RenderPatch[][] stepmeshes = new RenderPatch[8][]; + // Array of meshes for 3/4 steps - index = (data value & 7), with extra one clockwise from normal step + private RenderPatch[][] step_3_4_meshes = new RenderPatch[8][]; + // Array of meshes for 1/4 steps - index = (data value & 7), with clockwise quarter clopped from normal step + private RenderPatch[][] step_1_4_meshes = new RenderPatch[8][]; + + private void setID(String bname) { + DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(bname); + if (bbs.isNotAir()) { + for (int i = 0; i < bbs.getStateCount(); i++) { + stair_ids.set(bbs.getState(i).globalStateIndex); + } + } + } + @Override + public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, BitSet blockdatamask, Map custparm) { + if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm)) + return false; + setID(blkname); /* Mark block as a stair */ + /* Build step meshes */ + for(int i = 0; i < 8; i++) { + stepmeshes[i] = buildStepMeshes(rpf, i); + step_1_4_meshes[i] = buildCornerStepMeshes(rpf, i); + step_3_4_meshes[i] = buildIntCornerStepMeshes(rpf, i); + } + return true; + } + + @Override + public int getMaximumTextureCount() { + return 6; + } + + @Override + public String[] getTileEntityFieldsNeeded() { + return null; + } + + private static final int[] patchlist = { TEXTURE_Y_PLUS, TEXTURE_Y_MINUS, TEXTURE_Z_PLUS, TEXTURE_Z_MINUS, TEXTURE_X_PLUS, TEXTURE_X_MINUS }; + + private void addBox(RenderPatchFactory rpf, List list, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { + addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patchlist); + } + + private RenderPatch[] buildStepMeshes(RenderPatchFactory rpf, int dat) { + ArrayList list = new ArrayList(); + /* If inverted, add half top */ + if((dat & 0x4) != 0) { + addBox(rpf, list, 0, 1, 0.5, 1, 0, 1); + } + else { // Else, add half bottom + addBox(rpf, list, 0, 1, 0.0, 0.5, 0, 1); + } + switch(dat & 0x3) { + case 0: + addBox(rpf, list, 0.5, 1, 0, 1, 0, 1); + break; + case 1: + addBox(rpf, list, 0, 0.5, 0, 1, 0, 1); + break; + case 2: + addBox(rpf, list, 0, 1, 0, 1, 0.5, 1); + break; + case 3: + addBox(rpf, list, 0, 1, 0, 1, 0, 0.5); + break; + } + return list.toArray(new RenderPatch[list.size()]); + } + + private RenderPatch[] buildCornerStepMeshes(RenderPatchFactory rpf, int dat) { + ArrayList list = new ArrayList(); + /* If inverted, add half top */ + if((dat & 0x4) != 0) { + addBox(rpf, list, 0, 1, 0.5, 1, 0, 1); + } + else { // Else, add half bottom + addBox(rpf, list, 0, 1, 0.0, 0.5, 0, 1); + } + switch(dat & 0x3) { + case 0: + addBox(rpf, list, 0.5, 1, 0, 1, 0, 0.5); + break; + case 1: + addBox(rpf, list, 0, 0.5, 0, 1, 0, 0.5); + break; + case 2: + addBox(rpf, list, 0, 0.5, 0, 1, 0.5, 1); + break; + case 3: + addBox(rpf, list, 0.5, 1, 0, 1, 0.5, 1); + break; + } + return list.toArray(new RenderPatch[list.size()]); + } + + private RenderPatch[] buildIntCornerStepMeshes(RenderPatchFactory rpf, int dat) { + ArrayList list = new ArrayList(); + /* If inverted, add half top */ + if((dat & 0x4) != 0) { + addBox(rpf, list, 0, 1, 0.5, 1, 0, 1); + } + else { // Else, add half bottom + addBox(rpf, list, 0, 1, 0.0, 0.5, 0, 1); + } + switch(dat & 0x3) { + case 0: + addBox(rpf, list, 0.5, 1, 0, 1, 0, 1); + addBox(rpf, list, 0, 0.5, 0, 1, 0, 0.5); + break; + case 1: + addBox(rpf, list, 0.5, 1, 0, 1, 0, 1); + addBox(rpf, list, 0, 0.5, 0, 1, 0.5, 1); + break; + case 2: + addBox(rpf, list, 0, 0.5, 0, 1, 0, 1); + addBox(rpf, list, 0.5, 1, 0, 1, 0, 0.5); + break; + case 3: + addBox(rpf, list, 0, 0.5, 0, 1, 0, 1); + addBox(rpf, list, 0.5, 1, 0, 1, 0.5, 1); + break; + } + return list.toArray(new RenderPatch[list.size()]); + } + + // Steps + // 0 = up to east + // 1 = up to west + // 2 = up to south + // 3 = up to north + // Corners + // 0 = NE + // 1 = NW + // 2 = SW + // 3 = SE + // Interior Corners + // 0 = open to SW + // 1 = open to NW + // 2 = open to SE + // 3 = open to NE + private static final int off_x[] = { 1, -1, 0, 0, 1, -1, 0, 0 }; + private static final int off_z[] = { 0, 0, 1, -1, 0, 0, 1, -1 }; + private static final int match1[] = { 2, 3, 0, 1, 6, 7, 4, 5 }; + private static final int corner1[] = { 3, 1, 3, 1, 7, 5, 7, 5 }; + private static final int icorner1[] = { 1, 2, 1, 2, 5, 6, 5, 6 }; + private static final int match2[] = { 3, 2, 1, 0, 7, 6, 5, 4 }; + private static final int corner2[] = { 0, 2, 2, 0, 4, 6, 6, 4 }; + private static final int icorner2[] = { 0, 3, 3, 0, 4, 7, 7, 4 }; + + @Override + public RenderPatch[] getRenderPatchList(MapDataContext ctx) { + return getBaseRenderPatchList(ctx); + } + + private RenderPatch[] getBaseRenderPatchList(MapDataContext ctx) { + int data = ctx.getBlockType().stateIndex & 0x07; /* Get block data */ + /* Check block behind stair */ + DynmapBlockState corner = ctx.getBlockTypeAt(off_x[data], 0, off_z[data]); + if (stair_ids.get(corner.globalStateIndex)) { /* If it is a stair */ + int cornerdat = corner.stateIndex & 0x07; + if(cornerdat == match1[data]) { /* If right orientation */ + /* Make sure we don't have matching stair to side */ + DynmapBlockState side = ctx.getBlockTypeAt(-off_x[cornerdat], 0, -off_z[cornerdat]); + if((!stair_ids.get(side.globalStateIndex)) || ((side.stateIndex & 0x07) != data)) { + return step_1_4_meshes[corner1[data]]; + } + } + else if(cornerdat == match2[data]) { /* If other orientation */ + /* Make sure we don't have matching stair to side */ + DynmapBlockState side = ctx.getBlockTypeAt(-off_x[cornerdat], 0, -off_z[cornerdat]); + if((!stair_ids.get(side.globalStateIndex)) || ((side.stateIndex & 0x07) != data)) { + return step_1_4_meshes[corner2[data]]; + } + } + } + /* Check block in front of stair */ + corner = ctx.getBlockTypeAt(-off_x[data], 0, -off_z[data]); + if(stair_ids.get(corner.globalStateIndex)) { /* If it is a stair */ + int cornerdat = corner.stateIndex & 0x07; + if(cornerdat == match1[data]) { /* If right orientation */ + /* Make sure we don't have matching stair to side */ + DynmapBlockState side = ctx.getBlockTypeAt(off_x[cornerdat], 0, off_z[cornerdat]); + if((!stair_ids.get(side.globalStateIndex)) || ((side.stateIndex & 0x07) != data)) { + return step_3_4_meshes[icorner1[data]]; + } + } + else if(cornerdat == match2[data]) { /* If other orientation */ + /* Make sure we don't have matching stair to side */ + DynmapBlockState side = ctx.getBlockTypeAt(off_x[cornerdat], 0, off_z[cornerdat]); + if((!stair_ids.get(side.globalStateIndex)) || ((side.stateIndex & 0x07) != data)) { + return step_3_4_meshes[icorner2[data]]; + } + } + } + + return stepmeshes[data]; + } +} diff --git a/DynmapCore/src/main/java/org/dynmap/modsupport/impl/StairBlockModelImpl.java b/DynmapCore/src/main/java/org/dynmap/modsupport/impl/StairBlockModelImpl.java index 697d485e..e4881595 100644 --- a/DynmapCore/src/main/java/org/dynmap/modsupport/impl/StairBlockModelImpl.java +++ b/DynmapCore/src/main/java/org/dynmap/modsupport/impl/StairBlockModelImpl.java @@ -15,7 +15,7 @@ public class StairBlockModelImpl extends BlockModelImpl implements StairBlockMod public String getLine() { String ids = this.getIDsAndMeta(); if (ids == null) return null; - return String.format("customblock:%s,class=org.dynmap.hdmap.renderer.StairBlockRenderer", ids); + return String.format("customblock:%s,class=org.dynmap.hdmap.renderer.CopyStairBlockRenderer", ids); } } From a1351ed09435d612cda4bd873a84afc57cec608c Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 8 Dec 2019 01:55:39 -0600 Subject: [PATCH 19/26] Force lower case on tiles and matchTiles references in CTM --- DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java b/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java index 21ed3a3c..5e0b69b6 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java @@ -486,7 +486,7 @@ public class CTMTexturePack { this.matchTiles = null; } else { - String[] tok = tokenize(v, " "); + String[] tok = tokenize(v.toLowerCase(), " "); for (int i = 0; i < tok.length; i++) { String t = tok[i]; if (t.endsWith(".png")) { /* Strip off PNG */ @@ -505,7 +505,7 @@ public class CTMTexturePack { return null; } else { - v = v.trim(); + v = v.trim().toLowerCase(); if (v.length() == 0) { return null; } From ebd883b1c2b9b6f39800bbd781d734fe1ecfe1b3 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Thu, 12 Dec 2019 21:15:11 +0100 Subject: [PATCH 20/26] Added support for 1.15 --- bukkit-helper-115/.gitignore | 1 + bukkit-helper-115/bin/.gitignore | 1 + .../bin/BukkitVersionHelperSpigot115.java | 193 ++++++++ bukkit-helper-115/build.gradle | 10 + .../v115/BukkitVersionHelperSpigot115.java | 205 ++++++++ .../bukkit/helper/v115/MapChunkCache115.java | 458 ++++++++++++++++++ settings.gradle | 2 + spigot/build.gradle | 4 + .../main/java/org/dynmap/bukkit/Helper.java | 4 + 9 files changed, 878 insertions(+) create mode 100644 bukkit-helper-115/.gitignore create mode 100644 bukkit-helper-115/bin/.gitignore create mode 100644 bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java create mode 100644 bukkit-helper-115/build.gradle create mode 100644 bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java create mode 100644 bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java diff --git a/bukkit-helper-115/.gitignore b/bukkit-helper-115/.gitignore new file mode 100644 index 00000000..84c048a7 --- /dev/null +++ b/bukkit-helper-115/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/bukkit-helper-115/bin/.gitignore b/bukkit-helper-115/bin/.gitignore new file mode 100644 index 00000000..ddf9c656 --- /dev/null +++ b/bukkit-helper-115/bin/.gitignore @@ -0,0 +1 @@ +/main/ diff --git a/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java new file mode 100644 index 00000000..59722674 --- /dev/null +++ b/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java @@ -0,0 +1,193 @@ +package org.dynmap.bukkit.helper.v115; + +import java.lang.reflect.Field; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.dynmap.DynmapChunk; +import org.dynmap.Log; +import org.dynmap.bukkit.helper.BukkitVersionHelperCB; +import org.dynmap.bukkit.helper.BukkitWorld; +import org.dynmap.bukkit.helper.v115.MapChunkCache115; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.Polygon; + +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockFluids; +import net.minecraft.server.v1_15_R1.BlockLogAbstract; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.Material; + +/** + * Helper for isolation of bukkit version specific issues + */ +public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { + + /** CraftChunkSnapshot */ + protected Class datapalettearray; + private Field blockid_field; + + @Override + protected boolean isBlockIdNeeded() { + return false; + } + + @Override + protected boolean isBiomeBaseListNeeded() { + return false; + } + + public BukkitVersionHelperSpigot115() { + datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); + blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); + } + + @Override + public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { + try { + return (Object[]) blockid_field.get(css); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + return null; + } + @Override + public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { + w.unloadChunk(cx, cz, false); + } + + /** + * Get block short name list + */ + @Override + public String[] getBlockNames() { + int cnt = Block.REGISTRY_ID.a(); + String[] names = new String[cnt]; + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); + } + return names; + } + + /** + * Get list of defined biomebase objects + */ + @Override + public Object[] getBiomeBaseList() { + if (biomelist == null) { + biomelist = new Object[1024]; + for (int i = 0; i < 1024; i++) { + biomelist[i] = IRegistry.BIOME.fromId(i); + } + } + return biomelist; + } + + /** Get ID from biomebase */ + @Override + public int getBiomeBaseID(Object bb) { + return IRegistry.BIOME.a((BiomeBase)bb); + } + + public static IdentityHashMap dataToState; + + /** + * Initialize block states (org.dynmap.blockstate.DynmapBlockState) + */ + @Override + public void initializeBlockStates() { + dataToState = new IdentityHashMap(); + HashMap lastBlockState = new HashMap(); + + int cnt = Block.REGISTRY_ID.a(); + // Loop through block data states + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one + int idx = 0; + if (lastbs != null) { // Yes + idx = lastbs.getStateCount(); // Get number of states so far, since this is next + } + // Build state name + String sb = ""; + String fname = bd.toString(); + int off1 = fname.indexOf('['); + if (off1 >= 0) { + int off2 = fname.indexOf(']'); + sb = fname.substring(off1+1, off2); + } + Material mat = bd.getMaterial(); + DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); + if ((!bd.getFluid().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty + bs.setWaterlogged(); + } + if (mat == Material.AIR) { + bs.setAir(); + } + if (mat == Material.LEAVES) { + bs.setLeaves(); + } + if (bd.getBlock() instanceof BlockLogAbstract) { + bs.setLog(); + } + if (mat.isSolid()) { + bs.setSolid(); + } + dataToState.put(bd, bs); + lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); + Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); + } + } + /** + * Create chunk cache for given chunks of given world + * @param dw - world + * @param chunks - chunk list + * @return cache + */ + @Override + public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { + MapChunkCache115 c = new MapChunkCache115(); + c.setChunks(dw, chunks); + return c; + } + + /** + * Get biome base water multiplier + */ + @Override + public int getBiomeBaseWaterMult(Object bb) { + return ((BiomeBase)bb).p(); + } + + @Override + public Polygon getWorldBorder(World world) { + Polygon p = null; + WorldBorder wb = world.getWorldBorder(); + if (wb != null) { + Location c = wb.getCenter(); + double size = wb.getSize(); + if ((size > 1) && (size < 1E7)) { + size = size / 2; + p = new Polygon(); + p.addVertex(c.getX()-size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()+size); + p.addVertex(c.getX()-size, c.getZ()+size); + } + } + return p; + } + +} diff --git a/bukkit-helper-115/build.gradle b/bukkit-helper-115/build.gradle new file mode 100644 index 00000000..598021f9 --- /dev/null +++ b/bukkit-helper-115/build.gradle @@ -0,0 +1,10 @@ + +description = 'bukkit-helper-1.15' + +dependencies { + compile project(':bukkit-helper') + compile project(':dynmap-api') + compile project(path: ':DynmapCore', configuration: 'shadow') + compile group: 'org.bukkit', name: 'bukkit', version:'1.15-R0.1-SNAPSHOT' + compile group: 'org.bukkit', name: 'craftbukkit', version:'1.15-R0.1-SNAPSHOT' +} diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java new file mode 100644 index 00000000..b8772c3c --- /dev/null +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java @@ -0,0 +1,205 @@ +package org.dynmap.bukkit.helper.v115; + +import java.lang.reflect.Field; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.dynmap.DynmapChunk; +import org.dynmap.Log; +import org.dynmap.bukkit.helper.BukkitVersionHelperCB; +import org.dynmap.bukkit.helper.BukkitWorld; +import org.dynmap.bukkit.helper.v115.MapChunkCache115; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.Polygon; + +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockFluids; +import net.minecraft.server.v1_15_R1.BlockLogAbstract; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.Material; + +/** + * Helper for isolation of bukkit version specific issues + */ +public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { + + /** CraftChunkSnapshot */ + protected Class datapalettearray; + private Field blockid_field; + + @Override + protected boolean isBlockIdNeeded() { + return false; + } + + @Override + protected boolean isBiomeBaseListNeeded() { + return false; + } + + public BukkitVersionHelperSpigot115() { + datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); + blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); + } + + @Override + public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { + try { + return (Object[]) blockid_field.get(css); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + return null; + } + @Override + public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { + w.unloadChunk(cx, cz, false); + } + + /** + * Get block short name list + */ + @Override + public String[] getBlockNames() { + int cnt = Block.REGISTRY_ID.a(); + String[] names = new String[cnt]; + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); + } + return names; + } + + /** + * Get list of defined biomebase objects + */ + @Override + public Object[] getBiomeBaseList() { + if (biomelist == null) { + biomelist = new Object[1024]; + for (int i = 0; i < 1024; i++) { + biomelist[i] = IRegistry.BIOME.fromId(i); + } + } + return biomelist; + } + + /** Get ID from biomebase */ + @Override + public int getBiomeBaseID(Object bb) { + return IRegistry.BIOME.a((BiomeBase)bb); + } + + public static IdentityHashMap dataToState; + + /** + * Initialize block states (org.dynmap.blockstate.DynmapBlockState) + */ + @Override + public void initializeBlockStates() { + dataToState = new IdentityHashMap(); + HashMap lastBlockState = new HashMap(); + + int cnt = Block.REGISTRY_ID.a(); + // Loop through block data states + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one + int idx = 0; + if (lastbs != null) { // Yes + idx = lastbs.getStateCount(); // Get number of states so far, since this is next + } + // Build state name + String sb = ""; + String fname = bd.toString(); + int off1 = fname.indexOf('['); + if (off1 >= 0) { + int off2 = fname.indexOf(']'); + sb = fname.substring(off1+1, off2); + } + Material mat = bd.getMaterial(); + DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); + if ((!bd.getFluid().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty + bs.setWaterlogged(); + } + if (mat == Material.AIR) { + bs.setAir(); + } + if (mat == Material.LEAVES) { + bs.setLeaves(); + } + if (bd.getBlock() instanceof BlockLogAbstract) { + bs.setLog(); + } + if (mat.isSolid()) { + bs.setSolid(); + } + dataToState.put(bd, bs); + lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); + Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); + } + } + /** + * Create chunk cache for given chunks of given world + * @param dw - world + * @param chunks - chunk list + * @return cache + */ + @Override + public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { + MapChunkCache115 c = new MapChunkCache115(); + c.setChunks(dw, chunks); + return c; + } + + /** + * Get biome base water multiplier + */ + @Override + public int getBiomeBaseWaterMult(Object bb) { + return ((BiomeBase)bb).p(); + } + + /** Get temperature from biomebase */ + @Override + public float getBiomeBaseTemperature(Object bb) { + return ((BiomeBase)bb).getTemperature(); + } + + /** Get humidity from biomebase */ + @Override + public float getBiomeBaseHumidity(Object bb) { + return ((BiomeBase)bb).getHumidity(); + } + + @Override + public Polygon getWorldBorder(World world) { + Polygon p = null; + WorldBorder wb = world.getWorldBorder(); + if (wb != null) { + Location c = wb.getCenter(); + double size = wb.getSize(); + if ((size > 1) && (size < 1E7)) { + size = size / 2; + p = new Polygon(); + p.addVertex(c.getX()-size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()+size); + p.addVertex(c.getX()-size, c.getZ()+size); + } + } + return p; + } + +} diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java new file mode 100644 index 00000000..0ae32101 --- /dev/null +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java @@ -0,0 +1,458 @@ +package org.dynmap.bukkit.helper.v115; + +import org.bukkit.Bukkit; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.libs.jline.internal.Log; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; + +import java.io.IOException; +import java.util.Arrays; + +import org.bukkit.ChunkSnapshot; +import org.bukkit.World; +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapCore; +import org.dynmap.bukkit.helper.AbstractMapChunkCache; +import org.dynmap.bukkit.helper.BukkitVersionHelper; +import org.dynmap.bukkit.helper.SnapshotCache; +import org.dynmap.bukkit.helper.SnapshotCache.SnapshotRec; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.DynIntHashMap; +import org.dynmap.utils.VisibilityLimit; + +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_15_R1.ChunkRegionLoader; +import net.minecraft.server.v1_15_R1.DataBits; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.NBTTagList; + +/** + * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread + */ +public class MapChunkCache115 extends AbstractMapChunkCache { + + public static class NBTSnapshot implements Snapshot { + private static interface Section { + public DynmapBlockState getBlockType(int x, int y, int z); + public int getBlockSkyLight(int x, int y, int z); + public int getBlockEmittedLight(int x, int y, int z); + public boolean isEmpty(); + } + private final int x, z; + private final Section[] section; + private final int[] hmap; // Height map + private final int[] biome; + private final Object[] biomebase; + private final long captureFulltime; + private final int sectionCnt; + private final long inhabitedTicks; + + private static final int BLOCKS_PER_SECTION = 16 * 16 * 16; + private static final int COLUMNS_PER_CHUNK = 16 * 16 * 4; + private static final byte[] emptyData = new byte[BLOCKS_PER_SECTION / 2]; + private static final byte[] fullData = new byte[BLOCKS_PER_SECTION / 2]; + + static + { + Arrays.fill(fullData, (byte)0xFF); + } + + private static class EmptySection implements Section { + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return DynmapBlockState.AIR; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + return 15; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) { + return 0; + } + @Override + public boolean isEmpty() { + return true; + } + } + + private static final EmptySection empty_section = new EmptySection(); + + private static class StdSection implements Section { + DynmapBlockState[] states; + byte[] skylight; + byte[] emitlight; + + public StdSection() { + states = new DynmapBlockState[BLOCKS_PER_SECTION]; + Arrays.fill(states, DynmapBlockState.AIR); + skylight = emptyData; + emitlight = emptyData; + } + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return states[((y & 0xF) << 8) | (z << 4) | x]; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) + { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public boolean isEmpty() { + return false; + } + } + /** + * Construct empty chunk snapshot + * + * @param x + * @param z + */ + public NBTSnapshot(int worldheight, int x, int z, long captime, long inhabitedTime) + { + this.x = x; + this.z = z; + this.captureFulltime = captime; + this.biome = new int[COLUMNS_PER_CHUNK]; + this.biomebase = new Object[COLUMNS_PER_CHUNK]; + this.sectionCnt = worldheight / 16; + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + + /* Create empty height map */ + this.hmap = new int[16 * 16]; + + this.inhabitedTicks = inhabitedTime; + } + + public NBTSnapshot(NBTTagCompound nbt, int worldheight) { + this.x = nbt.getInt("xPos"); + this.z = nbt.getInt("zPos"); + this.captureFulltime = 0; + this.hmap = nbt.getIntArray("HeightMap"); + this.sectionCnt = worldheight / 16; + if (nbt.hasKey("InhabitedTime")) { + this.inhabitedTicks = nbt.getLong("InhabitedTime"); + } + else { + this.inhabitedTicks = 0; + } + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + /* Get sections */ + NBTTagList sect = nbt.getList("Sections", 10); + for (int i = 0; i < sect.size(); i++) { + NBTTagCompound sec = sect.getCompound(i); + int secnum = sec.getByte("Y"); + if (secnum >= this.sectionCnt) { + //Log.info("Section " + (int) secnum + " above world height " + worldheight); + continue; + } + if (secnum < 0) + continue; + //System.out.println("section(" + secnum + ")=" + sec.asString()); + // Create normal section to initialize + StdSection cursect = new StdSection(); + this.section[secnum] = cursect; + DynmapBlockState[] states = cursect.states; + DynmapBlockState[] palette = null; + // If we've got palette and block states list, process non-empty section + if (sec.hasKeyOfType("Palette", 9) && sec.hasKeyOfType("BlockStates", 12)) { + NBTTagList plist = sec.getList("Palette", 10); + long[] statelist = sec.getLongArray("BlockStates"); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + NBTTagCompound tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.hasKey("Properties")) { + StringBuilder statestr = new StringBuilder(); + NBTTagCompound prop = tc.getCompound("Properties"); + for (String pid : prop.getKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).asString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + int bitsperblock = (statelist.length * 64) / 4096; + DataBits db = new DataBits(bitsperblock, 4096, statelist); + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + states[j] = DynmapBlockState.getStateByGlobalIndex(db.a(j)); + } + } + else { + for (int j = 0; j < 4096; j++) { + int v = db.a(j); + states[j] = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + } + } + } + cursect.emitlight = sec.getByteArray("BlockLight"); + if (sec.hasKey("SkyLight")) { + cursect.skylight = sec.getByteArray("SkyLight"); + } + } + /* Get biome data */ + this.biome = new int[COLUMNS_PER_CHUNK]; + this.biomebase = new Object[COLUMNS_PER_CHUNK]; + Object[] bbl = BukkitVersionHelper.helper.getBiomeBaseList(); + if (nbt.hasKey("Biomes")) { + int[] bb = nbt.getIntArray("Biomes"); + if (bb != null) { + for (int i = 0; i < bb.length; i++) { + int bv = bb[i]; + if (bv < 0) bv = 0; + this.biome[i] = bv; + this.biomebase[i] = bbl[bv]; + } + } + } + } + + public int getX() + { + return x; + } + + public int getZ() + { + return z; + } + + public DynmapBlockState getBlockType(int x, int y, int z) + { + return section[y >> 4].getBlockType(x, y, z); + } + + public int getBlockSkyLight(int x, int y, int z) + { + return section[y >> 4].getBlockSkyLight(x, y, z); + } + + public int getBlockEmittedLight(int x, int y, int z) + { + return section[y >> 4].getBlockEmittedLight(x, y, z); + } + + public int getHighestBlockYAt(int x, int z) + { + return hmap[z << 4 | x]; + } + + public final long getCaptureFullTime() + { + return captureFulltime; + } + + public boolean isSectionEmpty(int sy) + { + return section[sy].isEmpty(); + } + + public long getInhabitedTicks() { + return inhabitedTicks; + } + + @Override + public Biome getBiome(int x, int z) { + return AbstractMapChunkCache.getBiomeByID(biome[z << 4 | x]); + } + + @Override + public Object[] getBiomeBaseFromSnapshot() { + return this.biomebase; + } + } + + private NBTTagCompound fetchLoadedChunkNBT(World w, int x, int z) { + CraftWorld cw = (CraftWorld) w; + NBTTagCompound nbt = null; + if (cw.isChunkLoaded(x, z)) { + Chunk c = cw.getHandle().getChunkAt(x, z); + if ((c != null) && c.loaded) { + nbt = ChunkRegionLoader.saveChunk(cw.getHandle(), c); + } + } + if (nbt != null) { + nbt = nbt.getCompound("Level"); + if (nbt != null) { + String stat = nbt.getString("Status"); + if ((stat == null) || (stat.equals("full") == false)) { + nbt = null; + } + } + } + return nbt; + } + + private NBTTagCompound loadChunkNBT(World w, int x, int z) { + CraftWorld cw = (CraftWorld) w; + NBTTagCompound nbt = null; + ChunkCoordIntPair cc = new ChunkCoordIntPair(x, z); + try { + nbt = cw.getHandle().getChunkProvider().playerChunkMap.read(cc); + } catch (IOException iox) { + } + if (nbt != null) { + nbt = nbt.getCompound("Level"); + if (nbt != null) { + String stat = nbt.getString("Status"); + if ((stat == null) || (stat.equals("full") == false)) { + nbt = null; + if ((stat == null) || stat.equals("") && DynmapCore.migrateChunks()) { + Chunk c = cw.getHandle().getChunkAt(x, z); + if (c != null) { + nbt = fetchLoadedChunkNBT(w, x, z); + cw.getHandle().unloadChunk(c); + } + } + } + } + } + return nbt; + } + + @Override + public Snapshot wrapChunkSnapshot(ChunkSnapshot css) { + // TODO Auto-generated method stub + return null; + } + + // Load chunk snapshots + @Override + public int loadChunks(int max_to_load) { + if(dw.isLoaded() == false) + return 0; + int cnt = 0; + if(iterator == null) + iterator = chunks.listIterator(); + + DynmapCore.setIgnoreChunkLoads(true); + // Load the required chunks. + while((cnt < max_to_load) && iterator.hasNext()) { + long startTime = System.nanoTime(); + DynmapChunk chunk = iterator.next(); + boolean vis = true; + if(visible_limits != null) { + vis = false; + for(VisibilityLimit limit : visible_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = true; + break; + } + } + } + if(vis && (hidden_limits != null)) { + for(VisibilityLimit limit : hidden_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = false; + break; + } + } + } + /* Check if cached chunk snapshot found */ + Snapshot ss = null; + long inhabited_ticks = 0; + DynIntHashMap tileData = null; + int idx = (chunk.x-x_min) + (chunk.z - z_min)*x_dim; + SnapshotRec ssr = SnapshotCache.sscache.getSnapshot(dw.getName(), chunk.x, chunk.z, blockdata, biome, biomeraw, highesty); + if(ssr != null) { + inhabited_ticks = ssr.inhabitedTicks; + if(!vis) { + if(hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) + ss = STONE; + else if(hidestyle == HiddenChunkStyle.FILL_OCEAN) + ss = OCEAN; + else + ss = EMPTY; + } + else { + ss = ssr.ss; + } + snaparray[idx] = ss; + snaptile[idx] = ssr.tileData; + inhabitedTicks[idx] = inhabited_ticks; + + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + continue; + } + // Fetch NTB for chunk if loaded + NBTTagCompound nbt = fetchLoadedChunkNBT(w, chunk.x, chunk.z); + boolean did_load = false; + if (nbt == null) { + // Load NTB for chunk, if it exists + nbt = loadChunkNBT(w, chunk.x, chunk.z); + did_load = true; + } + if (nbt != null) { + NBTSnapshot nss = new NBTSnapshot(nbt, w.getMaxHeight()); + ss = nss; + inhabited_ticks = nss.getInhabitedTicks(); + if(!vis) { + if(hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) + ss = STONE; + else if(hidestyle == HiddenChunkStyle.FILL_OCEAN) + ss = OCEAN; + else + ss = EMPTY; + } + } + else { + ss = EMPTY; + } + ssr = new SnapshotRec(); + ssr.ss = ss; + ssr.inhabitedTicks = inhabited_ticks; + ssr.tileData = tileData; + SnapshotCache.sscache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr, blockdata, biome, biomeraw, highesty); + snaparray[idx] = ss; + snaptile[idx] = ssr.tileData; + inhabitedTicks[idx] = inhabited_ticks; + if (nbt == null) + endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); + else if (did_load) + endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); + else + endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); + cnt++; + } + DynmapCore.setIgnoreChunkLoads(false); + + if(iterator.hasNext() == false) { /* If we're done */ + isempty = true; + /* Fill missing chunks with empty dummy chunk */ + for(int i = 0; i < snaparray.length; i++) { + if(snaparray[i] == null) + snaparray[i] = EMPTY; + else if(snaparray[i] != EMPTY) + isempty = false; + } + } + + return cnt; + } +} diff --git a/settings.gradle b/settings.gradle index c65e3579..e4124566 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include ':bukkit-helper-113-1' include ':bukkit-helper-113-2' include ':bukkit-helper-114' include ':bukkit-helper-114-1' +include ':bukkit-helper-115' include ':bukkit-helper' include ':dynmap-api' include ':DynmapCore' @@ -22,6 +23,7 @@ project(':bukkit-helper-113-1').projectDir = "$rootDir/bukkit-helper-113-1" as F project(':bukkit-helper-113-2').projectDir = "$rootDir/bukkit-helper-113-2" as File project(':bukkit-helper-114').projectDir = "$rootDir/bukkit-helper-114" as File project(':bukkit-helper-114-1').projectDir = "$rootDir/bukkit-helper-114-1" as File +project(':bukkit-helper-115').projectDir = "$rootDir/bukkit-helper-115" as File project(':bukkit-helper').projectDir = "$rootDir/bukkit-helper" as File project(':dynmap-api').projectDir = "$rootDir/dynmap-api" as File project(':DynmapCore').projectDir = "$rootDir/DynmapCore" as File diff --git a/spigot/build.gradle b/spigot/build.gradle index 121ff590..3c7a2ec5 100644 --- a/spigot/build.gradle +++ b/spigot/build.gradle @@ -30,6 +30,9 @@ dependencies { implementation(project(':bukkit-helper-114-1')) { transitive = false } + implementation(project(':bukkit-helper-115')) { + transitive = false + } } processResources { @@ -59,6 +62,7 @@ shadowJar { include(dependency(':bukkit-helper-113-2')) include(dependency(':bukkit-helper-114')) include(dependency(':bukkit-helper-114-1')) + include(dependency(':bukkit-helper-115')) } relocate('org.bstats', 'org.dynmap.bstats') destinationDir = file '../target' diff --git a/spigot/src/main/java/org/dynmap/bukkit/Helper.java b/spigot/src/main/java/org/dynmap/bukkit/Helper.java index c785ba54..fd0b28ca 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/Helper.java +++ b/spigot/src/main/java/org/dynmap/bukkit/Helper.java @@ -10,6 +10,7 @@ import org.dynmap.bukkit.helper.v113_1.BukkitVersionHelperSpigot113_1; import org.dynmap.bukkit.helper.v113_2.BukkitVersionHelperSpigot113_2; import org.dynmap.bukkit.helper.v114.BukkitVersionHelperSpigot114; import org.dynmap.bukkit.helper.v114_1.BukkitVersionHelperSpigot114_1; +import org.dynmap.bukkit.helper.v115.BukkitVersionHelperSpigot115; public class Helper { @@ -34,6 +35,9 @@ public class Helper { else if(Bukkit.getServer().getClass().getName().contains("GlowServer")) { Log.info("Loading Glowstone support"); BukkitVersionHelper.helper = new BukkitVersionHelperGlowstone(); + } + else if (v.contains("(MC: 1.15)")) { + BukkitVersionHelper.helper = new BukkitVersionHelperSpigot115(); } else if (v.contains("(MC: 1.14.1)") || v.contains("(MC: 1.14.2)") || v.contains("(MC: 1.14.3)") || v.contains("(MC: 1.14.4)")) { From 73bab0a37feb20c4f8f7debd6d154b00c9332364 Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 12 Dec 2019 20:09:16 +0100 Subject: [PATCH 21/26] experimental spigot 1.15 support --- bukkit-helper-115/.gitignore | 1 + bukkit-helper-115/bin/.gitignore | 1 + .../bin/BukkitVersionHelperSpigot115.java | 205 ++++++++ bukkit-helper-115/build.gradle | 10 + .../v115/BukkitVersionHelperSpigot115.java | 205 ++++++++ .../bukkit/helper/v115/MapChunkCache115.java | 465 ++++++++++++++++++ .../helper/BukkitVersionHelperGeneric.java | 15 +- settings.gradle | 2 + spigot/build.gradle | 4 + .../main/java/org/dynmap/bukkit/Helper.java | 4 + 10 files changed, 910 insertions(+), 2 deletions(-) create mode 100644 bukkit-helper-115/.gitignore create mode 100644 bukkit-helper-115/bin/.gitignore create mode 100644 bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java create mode 100644 bukkit-helper-115/build.gradle create mode 100644 bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java create mode 100644 bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java diff --git a/bukkit-helper-115/.gitignore b/bukkit-helper-115/.gitignore new file mode 100644 index 00000000..84c048a7 --- /dev/null +++ b/bukkit-helper-115/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/bukkit-helper-115/bin/.gitignore b/bukkit-helper-115/bin/.gitignore new file mode 100644 index 00000000..ddf9c656 --- /dev/null +++ b/bukkit-helper-115/bin/.gitignore @@ -0,0 +1 @@ +/main/ diff --git a/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java new file mode 100644 index 00000000..dd1bde9f --- /dev/null +++ b/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java @@ -0,0 +1,205 @@ +package org.dynmap.bukkit.helper.v115; + +import java.lang.reflect.Field; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.dynmap.DynmapChunk; +import org.dynmap.Log; +import org.dynmap.bukkit.helper.BukkitVersionHelperCB; +import org.dynmap.bukkit.helper.BukkitWorld; +import org.dynmap.bukkit.helper.v115.MapChunkCache115; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.Polygon; + +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockFluids; +import net.minecraft.server.v1_15_R1.BlockLogAbstract; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.Material; + +/** + * Helper for isolation of bukkit version specific issues + */ +public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { + + /** CraftChunkSnapshot */ + protected Class datapalettearray; + private Field blockid_field; + + @Override + protected boolean isBlockIdNeeded() { + return false; + } + + @Override + protected boolean isBiomeBaseListNeeded() { + return false; + } + + public BukkitVersionHelperSpigot115() { + datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); + blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); + } + + @Override + public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { + try { + return (Object[]) blockid_field.get(css); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + return null; + } + @Override + public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { + w.unloadChunk(cx, cz, false); + } + + /** + * Get block short name list + */ + @Override + public String[] getBlockNames() { + int cnt = Block.REGISTRY_ID.a(); + String[] names = new String[cnt]; + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); + } + return names; + } + + /** + * Get list of defined biomebase objects + */ + @Override + public Object[] getBiomeBaseList() { + if (biomelist == null) { + biomelist = new BiomeBase[1024]; + for (int i = 0; i < 1024; i++) { + biomelist[i] = IRegistry.BIOME.fromId(i); + } + } + return biomelist; + } + + /** Get ID from biomebase */ + @Override + public int getBiomeBaseID(Object bb) { + return IRegistry.BIOME.a((BiomeBase)bb); + } + + public static IdentityHashMap dataToState; + + /** + * Initialize block states (org.dynmap.blockstate.DynmapBlockState) + */ + @Override + public void initializeBlockStates() { + dataToState = new IdentityHashMap(); + HashMap lastBlockState = new HashMap(); + + int cnt = Block.REGISTRY_ID.a(); + // Loop through block data states + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one + int idx = 0; + if (lastbs != null) { // Yes + idx = lastbs.getStateCount(); // Get number of states so far, since this is next + } + // Build state name + String sb = ""; + String fname = bd.toString(); + int off1 = fname.indexOf('['); + if (off1 >= 0) { + int off2 = fname.indexOf(']'); + sb = fname.substring(off1+1, off2); + } + Material mat = bd.getMaterial(); + DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); + if ((!bd.getFluid().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty + bs.setWaterlogged(); + } + if (mat == Material.AIR) { + bs.setAir(); + } + if (mat == Material.LEAVES) { + bs.setLeaves(); + } + if (bd.getBlock() instanceof BlockLogAbstract) { + bs.setLog(); + } + if (mat.isSolid()) { + bs.setSolid(); + } + dataToState.put(bd, bs); + lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); + Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); + } + } + /** + * Create chunk cache for given chunks of given world + * @param dw - world + * @param chunks - chunk list + * @return cache + */ + @Override + public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { + MapChunkCache115 c = new MapChunkCache115(); + c.setChunks(dw, chunks); + return c; + } + + /** + * Get biome base water multiplier + */ + @Override + public int getBiomeBaseWaterMult(Object bb) { + return ((BiomeBase)bb).o(); + } + + /** Get temperature from biomebase */ + @Override + public float getBiomeBaseTemperature(Object bb) { + return ((BiomeBase)bb).getTemperature(); + } + + /** Get humidity from biomebase */ + @Override + public float getBiomeBaseHumidity(Object bb) { + return ((BiomeBase)bb).getHumidity(); + } + + @Override + public Polygon getWorldBorder(World world) { + Polygon p = null; + WorldBorder wb = world.getWorldBorder(); + if (wb != null) { + Location c = wb.getCenter(); + double size = wb.getSize(); + if ((size > 1) && (size < 1E7)) { + size = size / 2; + p = new Polygon(); + p.addVertex(c.getX()-size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()+size); + p.addVertex(c.getX()-size, c.getZ()+size); + } + } + return p; + } + +} diff --git a/bukkit-helper-115/build.gradle b/bukkit-helper-115/build.gradle new file mode 100644 index 00000000..598021f9 --- /dev/null +++ b/bukkit-helper-115/build.gradle @@ -0,0 +1,10 @@ + +description = 'bukkit-helper-1.15' + +dependencies { + compile project(':bukkit-helper') + compile project(':dynmap-api') + compile project(path: ':DynmapCore', configuration: 'shadow') + compile group: 'org.bukkit', name: 'bukkit', version:'1.15-R0.1-SNAPSHOT' + compile group: 'org.bukkit', name: 'craftbukkit', version:'1.15-R0.1-SNAPSHOT' +} diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java new file mode 100644 index 00000000..dd1bde9f --- /dev/null +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java @@ -0,0 +1,205 @@ +package org.dynmap.bukkit.helper.v115; + +import java.lang.reflect.Field; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.dynmap.DynmapChunk; +import org.dynmap.Log; +import org.dynmap.bukkit.helper.BukkitVersionHelperCB; +import org.dynmap.bukkit.helper.BukkitWorld; +import org.dynmap.bukkit.helper.v115.MapChunkCache115; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.Polygon; + +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockFluids; +import net.minecraft.server.v1_15_R1.BlockLogAbstract; +import net.minecraft.server.v1_15_R1.IBlockData; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.Material; + +/** + * Helper for isolation of bukkit version specific issues + */ +public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { + + /** CraftChunkSnapshot */ + protected Class datapalettearray; + private Field blockid_field; + + @Override + protected boolean isBlockIdNeeded() { + return false; + } + + @Override + protected boolean isBiomeBaseListNeeded() { + return false; + } + + public BukkitVersionHelperSpigot115() { + datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); + blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); + } + + @Override + public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { + try { + return (Object[]) blockid_field.get(css); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + return null; + } + @Override + public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { + w.unloadChunk(cx, cz, false); + } + + /** + * Get block short name list + */ + @Override + public String[] getBlockNames() { + int cnt = Block.REGISTRY_ID.a(); + String[] names = new String[cnt]; + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); + } + return names; + } + + /** + * Get list of defined biomebase objects + */ + @Override + public Object[] getBiomeBaseList() { + if (biomelist == null) { + biomelist = new BiomeBase[1024]; + for (int i = 0; i < 1024; i++) { + biomelist[i] = IRegistry.BIOME.fromId(i); + } + } + return biomelist; + } + + /** Get ID from biomebase */ + @Override + public int getBiomeBaseID(Object bb) { + return IRegistry.BIOME.a((BiomeBase)bb); + } + + public static IdentityHashMap dataToState; + + /** + * Initialize block states (org.dynmap.blockstate.DynmapBlockState) + */ + @Override + public void initializeBlockStates() { + dataToState = new IdentityHashMap(); + HashMap lastBlockState = new HashMap(); + + int cnt = Block.REGISTRY_ID.a(); + // Loop through block data states + for (int i = 0; i < cnt; i++) { + IBlockData bd = Block.getByCombinedId(i); + String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); + DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one + int idx = 0; + if (lastbs != null) { // Yes + idx = lastbs.getStateCount(); // Get number of states so far, since this is next + } + // Build state name + String sb = ""; + String fname = bd.toString(); + int off1 = fname.indexOf('['); + if (off1 >= 0) { + int off2 = fname.indexOf(']'); + sb = fname.substring(off1+1, off2); + } + Material mat = bd.getMaterial(); + DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); + if ((!bd.getFluid().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty + bs.setWaterlogged(); + } + if (mat == Material.AIR) { + bs.setAir(); + } + if (mat == Material.LEAVES) { + bs.setLeaves(); + } + if (bd.getBlock() instanceof BlockLogAbstract) { + bs.setLog(); + } + if (mat.isSolid()) { + bs.setSolid(); + } + dataToState.put(bd, bs); + lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); + Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); + } + } + /** + * Create chunk cache for given chunks of given world + * @param dw - world + * @param chunks - chunk list + * @return cache + */ + @Override + public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { + MapChunkCache115 c = new MapChunkCache115(); + c.setChunks(dw, chunks); + return c; + } + + /** + * Get biome base water multiplier + */ + @Override + public int getBiomeBaseWaterMult(Object bb) { + return ((BiomeBase)bb).o(); + } + + /** Get temperature from biomebase */ + @Override + public float getBiomeBaseTemperature(Object bb) { + return ((BiomeBase)bb).getTemperature(); + } + + /** Get humidity from biomebase */ + @Override + public float getBiomeBaseHumidity(Object bb) { + return ((BiomeBase)bb).getHumidity(); + } + + @Override + public Polygon getWorldBorder(World world) { + Polygon p = null; + WorldBorder wb = world.getWorldBorder(); + if (wb != null) { + Location c = wb.getCenter(); + double size = wb.getSize(); + if ((size > 1) && (size < 1E7)) { + size = size / 2; + p = new Polygon(); + p.addVertex(c.getX()-size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()-size); + p.addVertex(c.getX()+size, c.getZ()+size); + p.addVertex(c.getX()-size, c.getZ()+size); + } + } + return p; + } + +} diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java new file mode 100644 index 00000000..e14aecce --- /dev/null +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java @@ -0,0 +1,465 @@ +package org.dynmap.bukkit.helper.v115; + +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; + +import java.io.IOException; +import java.util.Arrays; + +import org.bukkit.ChunkSnapshot; +import org.bukkit.World; +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapCore; +import org.dynmap.bukkit.helper.AbstractMapChunkCache; +import org.dynmap.bukkit.helper.BukkitVersionHelper; +import org.dynmap.bukkit.helper.SnapshotCache; +import org.dynmap.bukkit.helper.SnapshotCache.SnapshotRec; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.DynIntHashMap; +import org.dynmap.utils.VisibilityLimit; + +import net.minecraft.server.v1_15_R1.BiomeBase; +import net.minecraft.server.v1_15_R1.BiomeStorage; +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_15_R1.ChunkRegionLoader; +import net.minecraft.server.v1_15_R1.DataBits; +import net.minecraft.server.v1_15_R1.IRegistry; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.NBTTagList; + +/** + * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread + */ +public class MapChunkCache115 extends AbstractMapChunkCache { + + public static class NBTSnapshot implements Snapshot { + private static interface Section { + public DynmapBlockState getBlockType(int x, int y, int z); + public int getBlockSkyLight(int x, int y, int z); + public int getBlockEmittedLight(int x, int y, int z); + public boolean isEmpty(); + } + private final int x, z; + private final Section[] section; + private final int[] hmap; // Height map + private final BiomeStorage biomestorage; + private final BiomeBase[] biomebase; + private final long captureFulltime; + private final int sectionCnt; + private final long inhabitedTicks; + + private static final int BLOCKS_PER_SECTION = 16 * 16 * 16; + private static final byte[] emptyData = new byte[BLOCKS_PER_SECTION / 2]; + private static final byte[] fullData = new byte[BLOCKS_PER_SECTION / 2]; + + static + { + Arrays.fill(fullData, (byte)0xFF); + } + + private static class EmptySection implements Section { + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return DynmapBlockState.AIR; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + return 15; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) { + return 0; + } + @Override + public boolean isEmpty() { + return true; + } + } + + private static final EmptySection empty_section = new EmptySection(); + + private static class StdSection implements Section { + DynmapBlockState[] states; + byte[] skylight; + byte[] emitlight; + + public StdSection() { + states = new DynmapBlockState[BLOCKS_PER_SECTION]; + Arrays.fill(states, DynmapBlockState.AIR); + skylight = emptyData; + emitlight = emptyData; + } + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return states[((y & 0xF) << 8) | (z << 4) | x]; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) + { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public boolean isEmpty() { + return false; + } + } + /** + * Construct empty chunk snapshot + * + * @param x + * @param z + */ + public NBTSnapshot(int worldheight, int x, int z, long captime, long inhabitedTime) + { + this.x = x; + this.z = z; + this.captureFulltime = captime; + this.biomebase = new BiomeBase[BiomeStorage.a]; + this.biomestorage = new BiomeStorage(this.biomebase); + this.sectionCnt = worldheight / 16; + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + + /* Create empty height map */ + this.hmap = new int[16 * 16]; + + this.inhabitedTicks = inhabitedTime; + } + + public NBTSnapshot(World w, NBTTagCompound nbt, int worldheight) { + this.x = nbt.getInt("xPos"); + this.z = nbt.getInt("zPos"); + ChunkCoordIntPair cc = new ChunkCoordIntPair(this.x, this.z); + this.captureFulltime = 0; + this.hmap = nbt.getIntArray("HeightMap"); + this.sectionCnt = worldheight / 16; + if (nbt.hasKey("InhabitedTime")) { + this.inhabitedTicks = nbt.getLong("InhabitedTime"); + } + else { + this.inhabitedTicks = 0; + } + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + /* Get sections */ + NBTTagList sect = nbt.getList("Sections", 10); + for (int i = 0; i < sect.size(); i++) { + NBTTagCompound sec = sect.getCompound(i); + int secnum = sec.getByte("Y"); + if (secnum >= this.sectionCnt) { + //Log.info("Section " + (int) secnum + " above world height " + worldheight); + continue; + } + if (secnum < 0) + continue; + //System.out.println("section(" + secnum + ")=" + sec.asString()); + // Create normal section to initialize + StdSection cursect = new StdSection(); + this.section[secnum] = cursect; + DynmapBlockState[] states = cursect.states; + DynmapBlockState[] palette = null; + // If we've got palette and block states list, process non-empty section + if (sec.hasKeyOfType("Palette", 9) && sec.hasKeyOfType("BlockStates", 12)) { + NBTTagList plist = sec.getList("Palette", 10); + long[] statelist = sec.getLongArray("BlockStates"); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + NBTTagCompound tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.hasKey("Properties")) { + StringBuilder statestr = new StringBuilder(); + NBTTagCompound prop = tc.getCompound("Properties"); + for (String pid : prop.getKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).asString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + int bitsperblock = (statelist.length * 64) / 4096; + DataBits db = new DataBits(bitsperblock, 4096, statelist); + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + states[j] = DynmapBlockState.getStateByGlobalIndex(db.a(j)); + } + } + else { + for (int j = 0; j < 4096; j++) { + int v = db.a(j); + states[j] = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + } + } + } + cursect.emitlight = sec.getByteArray("BlockLight"); + if (sec.hasKey("SkyLight")) { + cursect.skylight = sec.getByteArray("SkyLight"); + } + } + /* Get biome data */ + BiomeStorage biomestorage = null; + this.biomebase = new BiomeBase[BiomeStorage.a]; + BiomeBase[] bbl = (BiomeBase[])BukkitVersionHelper.helper.getBiomeBaseList(); + if (nbt.hasKey("Biomes")) { + int[] bb = nbt.getIntArray("Biomes"); + if (bb != null) { + for (int i = 0; i < bb.length; i++) { + int bv = bb[i]; + if (bv < 0) bv = 0; + this.biomebase[i] = bbl[bv]; + } + biomestorage = new BiomeStorage(cc, ((CraftWorld)w).getHandle().getChunkProvider().getChunkGenerator().getWorldChunkManager(), bb); + } + } + if(biomestorage == null) { + biomestorage = new BiomeStorage(this.biomebase); + } + this.biomestorage = biomestorage; + } + + public int getX() + { + return x; + } + + public int getZ() + { + return z; + } + + public DynmapBlockState getBlockType(int x, int y, int z) + { + return section[y >> 4].getBlockType(x, y, z); + } + + public int getBlockSkyLight(int x, int y, int z) + { + return section[y >> 4].getBlockSkyLight(x, y, z); + } + + public int getBlockEmittedLight(int x, int y, int z) + { + return section[y >> 4].getBlockEmittedLight(x, y, z); + } + + public int getHighestBlockYAt(int x, int z) + { + return hmap[z << 4 | x]; + } + + public final long getCaptureFullTime() + { + return captureFulltime; + } + + public boolean isSectionEmpty(int sy) + { + return section[sy].isEmpty(); + } + + public long getInhabitedTicks() { + return inhabitedTicks; + } + + @Override + public Biome getBiome(int x, int z) { + BiomeBase bb = this.biomestorage.getBiome(x, 64, z); + int biomeId = IRegistry.BIOME.a(bb); + return AbstractMapChunkCache.getBiomeByID(biomeId); + } + + @Override + public Object[] getBiomeBaseFromSnapshot() { + return this.biomebase; + } + } + + private NBTTagCompound fetchLoadedChunkNBT(World w, int x, int z) { + CraftWorld cw = (CraftWorld) w; + NBTTagCompound nbt = null; + if (cw.isChunkLoaded(x, z)) { + Chunk c = cw.getHandle().getChunkAt(x, z); + if ((c != null) && c.loaded) { + nbt = ChunkRegionLoader.saveChunk(cw.getHandle(), c); + } + } + if (nbt != null) { + nbt = nbt.getCompound("Level"); + if (nbt != null) { + String stat = nbt.getString("Status"); + if ((stat == null) || (stat.equals("full") == false)) { + nbt = null; + } + } + } + return nbt; + } + + private NBTTagCompound loadChunkNBT(World w, int x, int z) { + CraftWorld cw = (CraftWorld) w; + NBTTagCompound nbt = null; + ChunkCoordIntPair cc = new ChunkCoordIntPair(x, z); + try { + nbt = cw.getHandle().getChunkProvider().playerChunkMap.read(cc); + } catch (IOException iox) { + } + if (nbt != null) { + nbt = nbt.getCompound("Level"); + if (nbt != null) { + String stat = nbt.getString("Status"); + if ((stat == null) || (stat.equals("full") == false)) { + nbt = null; + if ((stat == null) || stat.equals("") && DynmapCore.migrateChunks()) { + Chunk c = cw.getHandle().getChunkAt(x, z); + if (c != null) { + nbt = fetchLoadedChunkNBT(w, x, z); + cw.getHandle().unloadChunk(c); + } + } + } + } + } + return nbt; + } + + @Override + public Snapshot wrapChunkSnapshot(ChunkSnapshot css) { + // TODO Auto-generated method stub + return null; + } + + // Load chunk snapshots + @Override + public int loadChunks(int max_to_load) { + if(dw.isLoaded() == false) + return 0; + int cnt = 0; + if(iterator == null) + iterator = chunks.listIterator(); + + DynmapCore.setIgnoreChunkLoads(true); + // Load the required chunks. + while((cnt < max_to_load) && iterator.hasNext()) { + long startTime = System.nanoTime(); + DynmapChunk chunk = iterator.next(); + boolean vis = true; + if(visible_limits != null) { + vis = false; + for(VisibilityLimit limit : visible_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = true; + break; + } + } + } + if(vis && (hidden_limits != null)) { + for(VisibilityLimit limit : hidden_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = false; + break; + } + } + } + /* Check if cached chunk snapshot found */ + Snapshot ss = null; + long inhabited_ticks = 0; + DynIntHashMap tileData = null; + int idx = (chunk.x-x_min) + (chunk.z - z_min)*x_dim; + SnapshotRec ssr = SnapshotCache.sscache.getSnapshot(dw.getName(), chunk.x, chunk.z, blockdata, biome, biomeraw, highesty); + if(ssr != null) { + inhabited_ticks = ssr.inhabitedTicks; + if(!vis) { + if(hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) + ss = STONE; + else if(hidestyle == HiddenChunkStyle.FILL_OCEAN) + ss = OCEAN; + else + ss = EMPTY; + } + else { + ss = ssr.ss; + } + snaparray[idx] = ss; + snaptile[idx] = ssr.tileData; + inhabitedTicks[idx] = inhabited_ticks; + + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + continue; + } + // Fetch NTB for chunk if loaded + NBTTagCompound nbt = fetchLoadedChunkNBT(w, chunk.x, chunk.z); + boolean did_load = false; + if (nbt == null) { + // Load NTB for chunk, if it exists + nbt = loadChunkNBT(w, chunk.x, chunk.z); + did_load = true; + } + if (nbt != null) { + NBTSnapshot nss = new NBTSnapshot(w, nbt, w.getMaxHeight()); + ss = nss; + inhabited_ticks = nss.getInhabitedTicks(); + if(!vis) { + if(hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) + ss = STONE; + else if(hidestyle == HiddenChunkStyle.FILL_OCEAN) + ss = OCEAN; + else + ss = EMPTY; + } + } + else { + ss = EMPTY; + } + ssr = new SnapshotRec(); + ssr.ss = ss; + ssr.inhabitedTicks = inhabited_ticks; + ssr.tileData = tileData; + SnapshotCache.sscache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr, blockdata, biome, biomeraw, highesty); + snaparray[idx] = ss; + snaptile[idx] = ssr.tileData; + inhabitedTicks[idx] = inhabited_ticks; + if (nbt == null) + endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); + else if (did_load) + endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); + else + endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); + cnt++; + } + DynmapCore.setIgnoreChunkLoads(false); + + if(iterator.hasNext() == false) { /* If we're done */ + isempty = true; + /* Fill missing chunks with empty dummy chunk */ + for(int i = 0; i < snaparray.length; i++) { + if(snaparray[i] == null) + snaparray[i] = EMPTY; + else if(snaparray[i] != EMPTY) + isempty = false; + } + } + + return cnt; + } +} diff --git a/bukkit-helper/src/main/java/org/dynmap/bukkit/helper/BukkitVersionHelperGeneric.java b/bukkit-helper/src/main/java/org/dynmap/bukkit/helper/BukkitVersionHelperGeneric.java index b973db38..5f0da4cf 100644 --- a/bukkit-helper/src/main/java/org/dynmap/bukkit/helper/BukkitVersionHelperGeneric.java +++ b/bukkit-helper/src/main/java/org/dynmap/bukkit/helper/BukkitVersionHelperGeneric.java @@ -47,6 +47,8 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper { private Method cw_gethandle; /** BiomeBase related helpers */ + protected Class biomestorage; + protected Field biomestoragebase; protected Class biomebase; protected Class biomebasearray; protected Field biomebaselist; @@ -131,7 +133,12 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper { /* CraftChunkSnapshot */ craftchunksnapshot = getOBCClass("org.bukkit.craftbukkit.CraftChunkSnapshot"); biomebasearray = getNMSClass("[Lnet.minecraft.server.BiomeBase;"); - ccss_biome = getPrivateField(craftchunksnapshot, new String[] { "biome" }, biomebasearray); + ccss_biome = getPrivateFieldNoFail(craftchunksnapshot, new String[] { "biome" }, biomebasearray); + if(ccss_biome == null) { + biomestorage = getNMSClass("net.minecraft.server.BiomeStorage"); + biomestoragebase = getPrivateField(biomestorage, new String[] { "f" }, biomebasearray); + ccss_biome = getPrivateField(craftchunksnapshot, new String[] { "biome" }, biomestorage); + } /* CraftChunk */ craftchunk = getOBCClass("org.bukkit.craftbukkit.CraftChunk"); cc_gethandle = getMethod(craftchunk, new String[] { "getHandle" }, new Class[0]); @@ -321,7 +328,11 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper { * Get list of defined biomebase objects */ public Object[] getBiomeBaseList() { - return (Object[]) getFieldValue(biomebase, biomebaselist, new Object[0]); + Object baselist = getFieldValue(biomebase, biomebaselist, new Object[0]); + if(biomestoragebase != null) + baselist = getFieldValue(baselist, biomestoragebase, new Object[0]); + + return (Object[])baselist; } /** Get temperature from biomebase */ public float getBiomeBaseTemperature(Object bb) { diff --git a/settings.gradle b/settings.gradle index c65e3579..e4124566 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include ':bukkit-helper-113-1' include ':bukkit-helper-113-2' include ':bukkit-helper-114' include ':bukkit-helper-114-1' +include ':bukkit-helper-115' include ':bukkit-helper' include ':dynmap-api' include ':DynmapCore' @@ -22,6 +23,7 @@ project(':bukkit-helper-113-1').projectDir = "$rootDir/bukkit-helper-113-1" as F project(':bukkit-helper-113-2').projectDir = "$rootDir/bukkit-helper-113-2" as File project(':bukkit-helper-114').projectDir = "$rootDir/bukkit-helper-114" as File project(':bukkit-helper-114-1').projectDir = "$rootDir/bukkit-helper-114-1" as File +project(':bukkit-helper-115').projectDir = "$rootDir/bukkit-helper-115" as File project(':bukkit-helper').projectDir = "$rootDir/bukkit-helper" as File project(':dynmap-api').projectDir = "$rootDir/dynmap-api" as File project(':DynmapCore').projectDir = "$rootDir/DynmapCore" as File diff --git a/spigot/build.gradle b/spigot/build.gradle index 121ff590..57747297 100644 --- a/spigot/build.gradle +++ b/spigot/build.gradle @@ -30,6 +30,9 @@ dependencies { implementation(project(':bukkit-helper-114-1')) { transitive = false } + implementation(project(':bukkit-helper-115')) { + transitive = false + } } processResources { @@ -59,6 +62,7 @@ shadowJar { include(dependency(':bukkit-helper-113-2')) include(dependency(':bukkit-helper-114')) include(dependency(':bukkit-helper-114-1')) + include(dependency(':bukkit-helper-115')) } relocate('org.bstats', 'org.dynmap.bstats') destinationDir = file '../target' diff --git a/spigot/src/main/java/org/dynmap/bukkit/Helper.java b/spigot/src/main/java/org/dynmap/bukkit/Helper.java index c785ba54..a3573f5a 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/Helper.java +++ b/spigot/src/main/java/org/dynmap/bukkit/Helper.java @@ -10,6 +10,7 @@ import org.dynmap.bukkit.helper.v113_1.BukkitVersionHelperSpigot113_1; import org.dynmap.bukkit.helper.v113_2.BukkitVersionHelperSpigot113_2; import org.dynmap.bukkit.helper.v114.BukkitVersionHelperSpigot114; import org.dynmap.bukkit.helper.v114_1.BukkitVersionHelperSpigot114_1; +import org.dynmap.bukkit.helper.v115.BukkitVersionHelperSpigot115; public class Helper { @@ -35,6 +36,9 @@ public class Helper { Log.info("Loading Glowstone support"); BukkitVersionHelper.helper = new BukkitVersionHelperGlowstone(); } + else if (v.contains("(MC: 1.15)")) { + BukkitVersionHelper.helper = new BukkitVersionHelperSpigot115(); + } else if (v.contains("(MC: 1.14.1)") || v.contains("(MC: 1.14.2)") || v.contains("(MC: 1.14.3)") || v.contains("(MC: 1.14.4)")) { BukkitVersionHelper.helper = new BukkitVersionHelperSpigot114_1(); From 168850f61e816522c5115ebe28c38df6d164ce56 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 14 Dec 2019 06:31:15 +0100 Subject: [PATCH 22/26] fixed biome data handling in MapChunkCache115.java --- .../bukkit/helper/v115/MapChunkCache115.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java index e14aecce..d79ca4ff 100644 --- a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/MapChunkCache115.java @@ -223,17 +223,25 @@ public class MapChunkCache115 extends AbstractMapChunkCache { BiomeBase[] bbl = (BiomeBase[])BukkitVersionHelper.helper.getBiomeBaseList(); if (nbt.hasKey("Biomes")) { int[] bb = nbt.getIntArray("Biomes"); - if (bb != null) { + if (bb != null && bb.length <= 256) { for (int i = 0; i < bb.length; i++) { int bv = bb[i]; if (bv < 0) bv = 0; this.biomebase[i] = bbl[bv]; } + } + else if (bb != null && bb.length > 256) { biomestorage = new BiomeStorage(cc, ((CraftWorld)w).getHandle().getChunkProvider().getChunkGenerator().getWorldChunkManager(), bb); - } + for(int iz = 0; iz < 16; iz++) { + for(int ix = 0; ix < 16; ix++) { + this.biomebase[iz << 4 | ix] = biomestorage.getBiome(ix >> 2, 0, iz >> 2); + } + } + } + } if(biomestorage == null) { - biomestorage = new BiomeStorage(this.biomebase); + biomestorage = new BiomeStorage(cc, ((CraftWorld)w).getHandle().getChunkProvider().getChunkGenerator().getWorldChunkManager()); } this.biomestorage = biomestorage; } From 5fe21ff8c8c1248b8d31d5f387354b984a4406e9 Mon Sep 17 00:00:00 2001 From: baconwaifu Date: Sat, 14 Dec 2019 15:25:56 -0500 Subject: [PATCH 23/26] Change marker column to BYTEA to make markers work again. --- .../org/dynmap/storage/postgresql/PostgreSQLMapStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java index 25b083dc..0d4b0818 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/postgresql/PostgreSQLMapStorage.java @@ -452,7 +452,7 @@ public class PostgreSQLMapStorage extends MapStorage { doUpdate(c, "CREATE TABLE " + tableTiles + " (MapID INT NOT NULL, x INT NOT NULL, y INT NOT NULL, zoom INT NOT NULL, HashCode BIGINT NOT NULL, LastUpdate BIGINT NOT NULL, Format INT NOT NULL, Image BYTEA, PRIMARY KEY(MapID, x, y, zoom))"); doUpdate(c, "CREATE TABLE " + tableFaces + " (PlayerName VARCHAR(64) NOT NULL, TypeID INT NOT NULL, Image BYTEA, PRIMARY KEY(PlayerName, TypeID))"); doUpdate(c, "CREATE TABLE " + tableMarkerIcons + " (IconName VARCHAR(128) PRIMARY KEY NOT NULL, Image BYTEA)"); - doUpdate(c, "CREATE TABLE " + tableMarkerFiles + " (FileName VARCHAR(128) PRIMARY KEY NOT NULL, Content TEXT)"); + doUpdate(c, "CREATE TABLE " + tableMarkerFiles + " (FileName VARCHAR(128) PRIMARY KEY NOT NULL, Content BYTEA)"); doUpdate(c, "CREATE TABLE " + tableStandaloneFiles + " (FileName VARCHAR(128) NOT NULL, ServerID BIGINT NOT NULL DEFAULT 0, Content TEXT, PRIMARY KEY (FileName, ServerID))"); doUpdate(c, "CREATE TABLE " + tableSchemaVersion + " (level INT PRIMARY KEY NOT NULL)"); doUpdate(c, "INSERT INTO " + tableSchemaVersion + " (level) VALUES (3)"); From 95cc8c687ae40300bb863827316cefb19b0de257 Mon Sep 17 00:00:00 2001 From: Ralph Drake Date: Sat, 14 Dec 2019 18:17:02 -0500 Subject: [PATCH 24/26] Update gradle.properties --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 4687f10d..4e52490b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx2G +org.gradle.parallel=true From 29f3774c7ca78b40636712a03d9b54a756a69f91 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 14 Dec 2019 23:12:44 -0600 Subject: [PATCH 25/26] Get 1.13.2 forge building again --- forge-1.13.2/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-1.13.2/build.gradle b/forge-1.13.2/build.gradle index 18531050..0e05e2cc 100644 --- a/forge-1.13.2/build.gradle +++ b/forge-1.13.2/build.gradle @@ -33,7 +33,7 @@ repositories { } minecraft { - mappings channel: 'snapshot', version: '20190530-1.13.2' + mappings channel: 'snapshot', version: '20190215-1.13.1' runs { server { workingDirectory project.file('run').canonicalPath From fae7fded403438c8f178e1cb1c17dd44bde2eb52 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 Dec 2019 00:26:17 -0600 Subject: [PATCH 26/26] Patch up 1.15 water shading --- bukkit-helper-114-1/bin/.gitignore | 1 - .../bin/BukkitVersionHelperSpigot114_1.java | 193 ------------------ bukkit-helper-114/bin/.gitignore | 1 - bukkit-helper-115/bin/.gitignore | 1 - .../bin/BukkitVersionHelperSpigot115.java | 193 ------------------ .../v115/BukkitVersionHelperSpigot115.java | 2 +- 6 files changed, 1 insertion(+), 390 deletions(-) delete mode 100644 bukkit-helper-114-1/bin/.gitignore delete mode 100644 bukkit-helper-114-1/bin/BukkitVersionHelperSpigot114_1.java delete mode 100644 bukkit-helper-114/bin/.gitignore delete mode 100644 bukkit-helper-115/bin/.gitignore delete mode 100644 bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java diff --git a/bukkit-helper-114-1/bin/.gitignore b/bukkit-helper-114-1/bin/.gitignore deleted file mode 100644 index ddf9c656..00000000 --- a/bukkit-helper-114-1/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/main/ diff --git a/bukkit-helper-114-1/bin/BukkitVersionHelperSpigot114_1.java b/bukkit-helper-114-1/bin/BukkitVersionHelperSpigot114_1.java deleted file mode 100644 index 4f6d2c59..00000000 --- a/bukkit-helper-114-1/bin/BukkitVersionHelperSpigot114_1.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.dynmap.bukkit.helper.v114_1; - -import java.lang.reflect.Field; - -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import org.bukkit.Chunk; -import org.bukkit.ChunkSnapshot; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.WorldBorder; -import org.dynmap.DynmapChunk; -import org.dynmap.Log; -import org.dynmap.bukkit.helper.BukkitVersionHelperCB; -import org.dynmap.bukkit.helper.BukkitWorld; -import org.dynmap.bukkit.helper.v114_1.MapChunkCache114_1; -import org.dynmap.renderer.DynmapBlockState; -import org.dynmap.utils.MapChunkCache; -import org.dynmap.utils.Polygon; - -import net.minecraft.server.v1_14_R1.BiomeBase; -import net.minecraft.server.v1_14_R1.Block; -import net.minecraft.server.v1_14_R1.BlockFluids; -import net.minecraft.server.v1_14_R1.BlockLogAbstract; -import net.minecraft.server.v1_14_R1.IBlockData; -import net.minecraft.server.v1_14_R1.IRegistry; -import net.minecraft.server.v1_14_R1.Material; - -/** - * Helper for isolation of bukkit version specific issues - */ -public class BukkitVersionHelperSpigot114_1 extends BukkitVersionHelperCB { - - /** CraftChunkSnapshot */ - protected Class datapalettearray; - private Field blockid_field; - - @Override - protected boolean isBlockIdNeeded() { - return false; - } - - @Override - protected boolean isBiomeBaseListNeeded() { - return false; - } - - public BukkitVersionHelperSpigot114_1() { - datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); - blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); - } - - @Override - public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { - try { - return (Object[]) blockid_field.get(css); - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } - return null; - } - @Override - public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { - w.unloadChunk(cx, cz, false); - } - - /** - * Get block short name list - */ - @Override - public String[] getBlockNames() { - int cnt = Block.REGISTRY_ID.a(); - String[] names = new String[cnt]; - for (int i = 0; i < cnt; i++) { - IBlockData bd = Block.getByCombinedId(i); - names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); - Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); - } - return names; - } - - /** - * Get list of defined biomebase objects - */ - @Override - public Object[] getBiomeBaseList() { - if (biomelist == null) { - biomelist = new Object[1024]; - for (int i = 0; i < 1024; i++) { - biomelist[i] = IRegistry.BIOME.fromId(i); - } - } - return biomelist; - } - - /** Get ID from biomebase */ - @Override - public int getBiomeBaseID(Object bb) { - return IRegistry.BIOME.a((BiomeBase)bb); - } - - public static IdentityHashMap dataToState; - - /** - * Initialize block states (org.dynmap.blockstate.DynmapBlockState) - */ - @Override - public void initializeBlockStates() { - dataToState = new IdentityHashMap(); - HashMap lastBlockState = new HashMap(); - - int cnt = Block.REGISTRY_ID.a(); - // Loop through block data states - for (int i = 0; i < cnt; i++) { - IBlockData bd = Block.getByCombinedId(i); - String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); - DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one - int idx = 0; - if (lastbs != null) { // Yes - idx = lastbs.getStateCount(); // Get number of states so far, since this is next - } - // Build state name - String sb = ""; - String fname = bd.toString(); - int off1 = fname.indexOf('['); - if (off1 >= 0) { - int off2 = fname.indexOf(']'); - sb = fname.substring(off1+1, off2); - } - Material mat = bd.getMaterial(); - DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); - if ((!bd.p().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty - bs.setWaterlogged(); - } - if (mat == Material.AIR) { - bs.setAir(); - } - if (mat == Material.LEAVES) { - bs.setLeaves(); - } - if (bd.getBlock() instanceof BlockLogAbstract) { - bs.setLog(); - } - if (mat.isSolid()) { - bs.setSolid(); - } - dataToState.put(bd, bs); - lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); - Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); - } - } - /** - * Create chunk cache for given chunks of given world - * @param dw - world - * @param chunks - chunk list - * @return cache - */ - @Override - public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { - MapChunkCache114_1 c = new MapChunkCache114_1(); - c.setChunks(dw, chunks); - return c; - } - - /** - * Get biome base water multiplier - */ - @Override - public int getBiomeBaseWaterMult(Object bb) { - return ((BiomeBase)bb).n(); - } - - @Override - public Polygon getWorldBorder(World world) { - Polygon p = null; - WorldBorder wb = world.getWorldBorder(); - if (wb != null) { - Location c = wb.getCenter(); - double size = wb.getSize(); - if ((size > 1) && (size < 1E7)) { - size = size / 2; - p = new Polygon(); - p.addVertex(c.getX()-size, c.getZ()-size); - p.addVertex(c.getX()+size, c.getZ()-size); - p.addVertex(c.getX()+size, c.getZ()+size); - p.addVertex(c.getX()-size, c.getZ()+size); - } - } - return p; - } - -} diff --git a/bukkit-helper-114/bin/.gitignore b/bukkit-helper-114/bin/.gitignore deleted file mode 100644 index ddf9c656..00000000 --- a/bukkit-helper-114/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/main/ diff --git a/bukkit-helper-115/bin/.gitignore b/bukkit-helper-115/bin/.gitignore deleted file mode 100644 index ddf9c656..00000000 --- a/bukkit-helper-115/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/main/ diff --git a/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java deleted file mode 100644 index 59722674..00000000 --- a/bukkit-helper-115/bin/BukkitVersionHelperSpigot115.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.dynmap.bukkit.helper.v115; - -import java.lang.reflect.Field; - -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import org.bukkit.Chunk; -import org.bukkit.ChunkSnapshot; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.WorldBorder; -import org.dynmap.DynmapChunk; -import org.dynmap.Log; -import org.dynmap.bukkit.helper.BukkitVersionHelperCB; -import org.dynmap.bukkit.helper.BukkitWorld; -import org.dynmap.bukkit.helper.v115.MapChunkCache115; -import org.dynmap.renderer.DynmapBlockState; -import org.dynmap.utils.MapChunkCache; -import org.dynmap.utils.Polygon; - -import net.minecraft.server.v1_15_R1.BiomeBase; -import net.minecraft.server.v1_15_R1.Block; -import net.minecraft.server.v1_15_R1.BlockFluids; -import net.minecraft.server.v1_15_R1.BlockLogAbstract; -import net.minecraft.server.v1_15_R1.IBlockData; -import net.minecraft.server.v1_15_R1.IRegistry; -import net.minecraft.server.v1_15_R1.Material; - -/** - * Helper for isolation of bukkit version specific issues - */ -public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { - - /** CraftChunkSnapshot */ - protected Class datapalettearray; - private Field blockid_field; - - @Override - protected boolean isBlockIdNeeded() { - return false; - } - - @Override - protected boolean isBiomeBaseListNeeded() { - return false; - } - - public BukkitVersionHelperSpigot115() { - datapalettearray = getNMSClass("[Lnet.minecraft.server.DataPaletteBlock;"); - blockid_field = getPrivateField(craftchunksnapshot, new String[] { "blockids" }, datapalettearray); - } - - @Override - public Object[] getBlockIDFieldFromSnapshot(ChunkSnapshot css) { - try { - return (Object[]) blockid_field.get(css); - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } - return null; - } - @Override - public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) { - w.unloadChunk(cx, cz, false); - } - - /** - * Get block short name list - */ - @Override - public String[] getBlockNames() { - int cnt = Block.REGISTRY_ID.a(); - String[] names = new String[cnt]; - for (int i = 0; i < cnt; i++) { - IBlockData bd = Block.getByCombinedId(i); - names[i] = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); - Log.info(i + ": blk=" + names[i] + ", bd=" + bd.toString()); - } - return names; - } - - /** - * Get list of defined biomebase objects - */ - @Override - public Object[] getBiomeBaseList() { - if (biomelist == null) { - biomelist = new Object[1024]; - for (int i = 0; i < 1024; i++) { - biomelist[i] = IRegistry.BIOME.fromId(i); - } - } - return biomelist; - } - - /** Get ID from biomebase */ - @Override - public int getBiomeBaseID(Object bb) { - return IRegistry.BIOME.a((BiomeBase)bb); - } - - public static IdentityHashMap dataToState; - - /** - * Initialize block states (org.dynmap.blockstate.DynmapBlockState) - */ - @Override - public void initializeBlockStates() { - dataToState = new IdentityHashMap(); - HashMap lastBlockState = new HashMap(); - - int cnt = Block.REGISTRY_ID.a(); - // Loop through block data states - for (int i = 0; i < cnt; i++) { - IBlockData bd = Block.getByCombinedId(i); - String bname = IRegistry.BLOCK.getKey(bd.getBlock()).toString(); - DynmapBlockState lastbs = lastBlockState.get(bname); // See if we have seen this one - int idx = 0; - if (lastbs != null) { // Yes - idx = lastbs.getStateCount(); // Get number of states so far, since this is next - } - // Build state name - String sb = ""; - String fname = bd.toString(); - int off1 = fname.indexOf('['); - if (off1 >= 0) { - int off2 = fname.indexOf(']'); - sb = fname.substring(off1+1, off2); - } - Material mat = bd.getMaterial(); - DynmapBlockState bs = new DynmapBlockState(lastbs, idx, bname, sb, mat.toString()); - if ((!bd.getFluid().isEmpty()) && ((bd.getBlock() instanceof BlockFluids) == false)) { // Test if fluid type for block is not empty - bs.setWaterlogged(); - } - if (mat == Material.AIR) { - bs.setAir(); - } - if (mat == Material.LEAVES) { - bs.setLeaves(); - } - if (bd.getBlock() instanceof BlockLogAbstract) { - bs.setLog(); - } - if (mat.isSolid()) { - bs.setSolid(); - } - dataToState.put(bd, bs); - lastBlockState.put(bname, (lastbs == null) ? bs : lastbs); - Log.verboseinfo(i + ": blk=" + bname + ", idx=" + idx + ", state=" + sb + ", waterlogged=" + bs.isWaterlogged()); - } - } - /** - * Create chunk cache for given chunks of given world - * @param dw - world - * @param chunks - chunk list - * @return cache - */ - @Override - public MapChunkCache getChunkCache(BukkitWorld dw, List chunks) { - MapChunkCache115 c = new MapChunkCache115(); - c.setChunks(dw, chunks); - return c; - } - - /** - * Get biome base water multiplier - */ - @Override - public int getBiomeBaseWaterMult(Object bb) { - return ((BiomeBase)bb).p(); - } - - @Override - public Polygon getWorldBorder(World world) { - Polygon p = null; - WorldBorder wb = world.getWorldBorder(); - if (wb != null) { - Location c = wb.getCenter(); - double size = wb.getSize(); - if ((size > 1) && (size < 1E7)) { - size = size / 2; - p = new Polygon(); - p.addVertex(c.getX()-size, c.getZ()-size); - p.addVertex(c.getX()+size, c.getZ()-size); - p.addVertex(c.getX()+size, c.getZ()+size); - p.addVertex(c.getX()-size, c.getZ()+size); - } - } - return p; - } - -} diff --git a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java index b8772c3c..73650f42 100644 --- a/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java +++ b/bukkit-helper-115/src/main/java/org/dynmap/bukkit/helper/v115/BukkitVersionHelperSpigot115.java @@ -168,7 +168,7 @@ public class BukkitVersionHelperSpigot115 extends BukkitVersionHelperCB { */ @Override public int getBiomeBaseWaterMult(Object bb) { - return ((BiomeBase)bb).p(); + return ((BiomeBase)bb).o(); } /** Get temperature from biomebase */