Compare commits

...

8 Commits

Author SHA1 Message Date
Mike Primm 9e29cb8e44 Turn back on loading of unpopulated chunks - flag in CB doesn't appear
to be reliable
2012-08-09 17:52:10 -05:00
Mike Primm fca05e399a Set to 0.70.1 2012-08-08 23:47:07 -05:00
Mike Primm a6490743ca Bump to 0.80 2012-08-07 23:51:33 -05:00
Mike Primm 7da1051d5f Clean up spout-textures.txt when spout is disabled (avoid block id conflicts) 2012-08-07 23:39:14 -05:00
Mike Primm ddf14b7c3b Add spout controls (enable/disable, use-existing-textures), load other spout mods first 2012-08-07 23:20:00 -05:00
Mike Primm 38a4d869f6 Add support for AsyncPlayerChatEvent - make silly nagging STFU - still support 1.2.5 2012-08-04 23:02:24 -05:00
Mike Primm 905802e558 Streamline chunk load/unload, ignore unpopulated chunks, handle broken Spout mods better 2012-08-02 02:07:17 -05:00
Mike Primm 30db9c04b6 Bump to 0.70 2012-07-30 22:26:22 -05:00
6 changed files with 206 additions and 121 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.dynmap</groupId>
<artifactId>dynmap</artifactId>
<version>0.60</version>
<version>0.70.1</version>
<name>dynmap</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -41,6 +41,7 @@ import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerBedLeaveEvent;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
@@ -51,6 +52,8 @@ import org.bukkit.event.world.SpawnChangeEvent;
import org.bukkit.event.world.StructureGrowEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.material.MaterialData;
import org.bukkit.material.Tree;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
@@ -93,6 +96,30 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
public SpoutPluginBlocks spb;
public PluginManager pm;
private class BukkitEnableCoreCallback extends DynmapCore.EnableCoreCallbacks {
@Override
public void configurationLoaded() {
/* Check for Spout */
if(detectSpout()) {
if(core.configuration.getBoolean("spout/enabled", true)) {
has_spout = true;
Log.info("Detected Spout");
spb = new SpoutPluginBlocks();
spb.processSpoutBlocks(DynmapPlugin.this, core);
}
else {
Log.info("Detected Spout - Support Disabled");
}
}
if(!has_spout) { /* If not, clean up old spout texture, if needed */
File st = new File(core.getDataFolder(), "renderdata/spout-texture.txt");
if(st.exists())
st.delete();
}
}
}
private static class BlockToCheck {
Location loc;
int typeid;
@@ -197,16 +224,36 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
}, DynmapPlugin.this);
break;
case PLAYER_CHAT:
pm.registerEvents(new Listener() {
@EventHandler(priority=EventPriority.MONITOR)
public void onPlayerChat(PlayerChatEvent evt) {
if(evt.isCancelled()) return;
DynmapPlayer p = null;
if(evt.getPlayer() != null)
p = new BukkitPlayer(evt.getPlayer());
core.listenerManager.processChatEvent(EventType.PLAYER_CHAT, p, evt.getMessage());
}
}, DynmapPlugin.this);
try {
Class.forName("org.bukkit.event.player.AsyncPlayerChatEvent");
pm.registerEvents(new Listener() {
@EventHandler(priority=EventPriority.MONITOR)
public void onPlayerChat(AsyncPlayerChatEvent evt) {
if(evt.isCancelled()) return;
final Player p = evt.getPlayer();
final String msg = evt.getMessage();
getServer().getScheduler().scheduleSyncDelayedTask(DynmapPlugin.this, new Runnable() {
public void run() {
DynmapPlayer dp = null;
if(p != null)
dp = new BukkitPlayer(p);
core.listenerManager.processChatEvent(EventType.PLAYER_CHAT, dp, msg);
}
});
}
}, DynmapPlugin.this);
} catch (ClassNotFoundException cnfx) {
pm.registerEvents(new Listener() {
@EventHandler(priority=EventPriority.MONITOR)
public void onPlayerChat(PlayerChatEvent evt) {
if(evt.isCancelled()) return;
DynmapPlayer p = null;
if(evt.getPlayer() != null)
p = new BukkitPlayer(evt.getPlayer());
core.listenerManager.processChatEvent(EventType.PLAYER_CHAT, p, evt.getMessage());
}
}, DynmapPlugin.this);
}
break;
case BLOCK_BREAK:
pm.registerEvents(new Listener() {
@@ -545,14 +592,6 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
if(dataDirectory.exists() == false)
dataDirectory.mkdirs();
/* Check for Spout */
if(detectSpout()) {
has_spout = true;
Log.info("Detected Spout");
spb = new SpoutPluginBlocks();
spb.processSpoutBlocks(dataDirectory);
}
/* Get MC version */
String bukkitver = getServer().getVersion();
String mcver = "1.0.0";
@@ -573,7 +612,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
core.setServer(new BukkitServer());
/* Enable core */
if(!core.enableCore()) {
if(!core.enableCore(new BukkitEnableCoreCallback())) {
this.setEnabled(false);
return;
}
@@ -1,5 +1,6 @@
package org.dynmap.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -19,7 +20,7 @@ import org.dynmap.Log;
import org.dynmap.common.BiomeMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.dynmap.utils.MapIterator.BlockStep;
import org.dynmap.utils.BlockStep;
import org.getspout.spoutapi.block.SpoutChunk;
/**
@@ -27,16 +28,14 @@ import org.getspout.spoutapi.block.SpoutChunk;
*/
public class NewMapChunkCache implements MapChunkCache {
private static boolean init = false;
private static Method poppreservedchunk = null;
private static Method gethandle = null;
private static Method removeentities = null;
private static Method getworldhandle = null;
private static Field doneflag = null;
private static boolean use_spout = false;
private static boolean use_sections = false;
private World w;
private DynmapWorld dw;
private Object craftworld;
private int nsect;
private List<DynmapChunk> chunks;
private ListIterator<DynmapChunk> iterator;
@@ -664,15 +663,6 @@ public class NewMapChunkCache implements MapChunkCache {
@SuppressWarnings({ "rawtypes", "unchecked" })
public NewMapChunkCache() {
if(!init) {
/* Get CraftWorld.popPreservedChunk(x,z) - reduces memory bloat from map traversals (optional) */
try {
Class c = Class.forName("org.bukkit.craftbukkit.CraftWorld");
poppreservedchunk = c.getDeclaredMethod("popPreservedChunk", new Class[] { int.class, int.class });
/* getHandle() */
getworldhandle = c.getDeclaredMethod("getHandle", new Class[0]);
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
}
/* Get CraftChunk.getChunkSnapshot(boolean,boolean,boolean) and CraftChunk.getHandle() */
try {
Class c = Class.forName("org.bukkit.craftbukkit.CraftChunk");
@@ -684,8 +674,10 @@ public class NewMapChunkCache implements MapChunkCache {
try {
Class c = Class.forName("net.minecraft.server.Chunk");
removeentities = c.getDeclaredMethod("removeEntities", new Class[0]);
doneflag = c.getField("done");
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
} catch (NoSuchFieldException nsfx) {
}
/* Check for ChunkSnapshot.isSectionEmpty(int) method */
try {
@@ -703,12 +695,6 @@ public class NewMapChunkCache implements MapChunkCache {
this.dw = dw;
this.w = dw.getWorld();
nsect = dw.worldheight >> 4;
if((getworldhandle != null) && (craftworld == null)) {
try {
craftworld = getworldhandle.invoke(w); /* World.getHandle() */
} catch (Exception x) {
}
}
this.chunks = chunks;
/* Compute range */
if(chunks.size() == 0) {
@@ -809,6 +795,27 @@ public class NewMapChunkCache implements MapChunkCache {
didgenerate = didload = w.loadChunk(chunk.x, chunk.z, true);
/* If it did load, make cache of it */
if(didload) {
Chunk c = w.getChunkAt(chunk.x, chunk.z); /* Get the chunk */
/* Try to get n.m.s.Chunk handle */
Object nmschunk = null;
if(gethandle != null) {
try {
nmschunk = gethandle.invoke(c);
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
/* Test if chunk isn't populated */
boolean populated = true;
//TODO: figure out why this doesn't appear to be reliable in Bukkit
//if((nmschunk != null) && (doneflag != null)) {
// try {
// populated = doneflag.getBoolean(nmschunk);
// } catch (IllegalArgumentException e) {
// } catch (IllegalAccessException e) {
// }
//}
if(!vis) {
if(hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN)
ss = STONE;
@@ -817,8 +824,10 @@ public class NewMapChunkCache implements MapChunkCache {
else
ss = EMPTY;
}
else if(!populated) { /* If not populated, treat as empty */
ss = EMPTY;
}
else {
Chunk c = w.getChunkAt(chunk.x, chunk.z);
if(blockdata || highesty) {
ss = c.getChunkSnapshot(highesty, biome, biomeraw);
if(use_spout) {
@@ -832,46 +841,38 @@ public class NewMapChunkCache implements MapChunkCache {
}
}
snaparray[(chunk.x-x_min) + (chunk.z - z_min)*x_dim] = ss;
}
if ((!wasLoaded) && didload) {
chunks_read++;
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */
if(!(didgenerate && do_save)) {
boolean did_remove = false;
Chunk cc = w.getChunkAt(chunk.x, chunk.z);
if((gethandle != null) && (removeentities != null)) {
try {
Object chk = gethandle.invoke(cc);
if(chk != null) {
removeentities.invoke(chk);
did_remove = true;
/* If wasn't loaded before, we need to do unload */
if (!wasLoaded) {
chunks_read++;
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */
if(!(didgenerate && do_save)) {
boolean did_remove = false;
if(removeentities != null) {
try {
if(nmschunk != null) {
removeentities.invoke(nmschunk);
did_remove = true;
}
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
if(!did_remove) {
if(c != null) {
for(Entity e: c.getEntities())
e.remove();
}
} catch (InvocationTargetException itx) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
if(!did_remove) {
if(cc != null) {
for(Entity e: cc.getEntities())
e.remove();
}
}
}
/* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen
* because isChunkInUse defined "in use" as being within 256 blocks of a player,
* while the actual in-use chunk area for a player where the chunks are managed
* by the MC base server is 21x21 (or about a 160 block radius).
* Also, if we did generate it, need to save it */
w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false);
/* And pop preserved chunk - this is a bad leak in Bukkit for map traversals like us */
try {
if(poppreservedchunk != null)
poppreservedchunk.invoke(w, chunk.x, chunk.z);
} catch (Exception x) {
Log.severe("Cannot pop preserved chunk - " + x.toString());
/* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen
* because isChunkInUse defined "in use" as being within 256 blocks of a player,
* while the actual in-use chunk area for a player where the chunks are managed
* by the MC base server is 21x21 (or about a 160 block radius).
* Also, if we did generate it, need to save it */
w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false);
}
}
cnt++;
@@ -14,6 +14,8 @@ import java.util.List;
import javax.imageio.ImageIO;
import org.bukkit.Material;
import org.bukkit.plugin.Plugin;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.getspout.spoutapi.block.design.BlockDesign;
import org.getspout.spoutapi.block.design.GenericBlockDesign;
@@ -53,13 +55,42 @@ public class SpoutPluginBlocks {
sb.append("block:id=" + blk.getCustomId() + ",allfaces=12049,transparency=TRANSPARENT\n");
}
private static String fixIDString(String id) {
StringBuilder sb = new StringBuilder();
int len = id.length();
sb.setLength(len);
for(int i = 0; i < len; i++) {
char c = id.charAt(i);
if(Character.isJavaIdentifierStart(c)) {
sb.setCharAt(i, c);
}
else {
sb.setCharAt(i, '_');
}
}
return sb.toString();
}
/* Process spout blocks - return true if something changed */
public boolean processSpoutBlocks(File datadir) {
public boolean processSpoutBlocks(DynmapPlugin plugin, DynmapCore core) {
/* First, see if any spout plugins that need to be enabled */
for(Plugin p : plugin.getServer().getPluginManager().getPlugins()) {
List<String> dep = p.getDescription().getDepend();
if((dep != null) && (dep.contains("Spout"))) {
Log.info("Found Spout plugin: " + p.getName());
if(p.isEnabled() == false) {
plugin.getPluginLoader().enablePlugin(p);
}
}
}
File datadir = core.getDataFolder();
if(textYPosField == null) {
if(initSpoutAccess() == false)
return false;
}
HashMap<String, String> texturelist = new HashMap<String, String>();
boolean use_existing_texture = core.configuration.getBoolean("spout/use-existing-textures", true);
int cnt = 0;
File f = new File(datadir, "texturepacks/standard/spout");
@@ -72,8 +103,7 @@ public class SpoutPluginBlocks {
/* Loop through blocks - try to freshen files, if needed */
for(CustomBlock b : cb) {
BlockDesign bd = b.getBlockDesign();
String blkid = bd.getTexturePlugin() + "." + b.getName();
blkid = blkid.replace(' ', '_');
String blkid = bd.getTexturePlugin() + "." + fixIDString(b.getName());
/* If not GenericCubiodBlockDesign, we don't handle it */
if((bd instanceof GenericCuboidBlockDesign) == false) {
Log.info("Block " + blkid + " not suppored - only cubiod blocks");
@@ -110,53 +140,59 @@ public class SpoutPluginBlocks {
String txtid = texturelist.get(txname); /* Get texture */
if(txtid == null) { /* Not found yet */
File imgfile = new File(f, blkid + ".png");
BufferedImage img = null;
boolean urlloaded = false;
try {
URL url = new URL(txname);
img = ImageIO.read(url); /* Load skin for player */
urlloaded = true;
} catch (IOException iox) {
if(txname.startsWith("http") == false) { /* Not URL - try file */
File tf = new File(txname);
if(tf.exists() == false) {
/* Horrible hack - try to find temp file (some SpoutMaterials versions) */
try {
File tmpf = File.createTempFile("dynmap", "test");
tf = new File(tmpf.getParent(), txname);
tmpf.delete();
} catch (IOException iox2) {}
/* If not reusing loaded textures OR not previously loaded */
if((!use_existing_texture) || (!imgfile.exists())) {
BufferedImage img = null;
boolean urlloaded = false;
try {
URL url = new URL(txname);
img = ImageIO.read(url); /* Load skin for player */
urlloaded = true;
} catch (IOException iox) {
if(txname.startsWith("http") == false) { /* Not URL - try file */
File tf = new File(txname);
if(tf.exists() == false) {
/* Horrible hack - try to find temp file (some SpoutMaterials versions) */
try {
File tmpf = File.createTempFile("dynmap", "test");
tf = new File(tmpf.getParent(), txname);
tmpf.delete();
} catch (IOException iox2) {}
}
if(tf.exists()) {
try {
img = ImageIO.read(tf);
urlloaded = true;
} catch (IOException iox3) {
}
}
}
if(tf.exists()) {
try {
img = ImageIO.read(tf);
urlloaded = true;
} catch (IOException iox3) {
if(img == null) {
Log.severe("Error loading texture for custom block '" + blkid + "' (" + b.getCustomId() + ") from " + txname + "(" + iox.getMessage() + ")");
if(imgfile.exists()) {
try {
img = ImageIO.read(imgfile); /* Load existing */
Log.info("Loaded cached texture file for " + blkid);
} catch (IOException iox2) {
Log.severe("Error loading cached texture file for " + blkid + " - " + iox2.getMessage());
}
}
}
}
if(img == null) {
Log.severe("Error loading texture for custom block '" + blkid + "' (" + b.getCustomId() + ") from " + txname + "(" + iox.getMessage() + ")");
if(imgfile.exists()) {
try {
img = ImageIO.read(imgfile); /* Load existing */
Log.info("Loaded cached texture file for " + blkid);
} catch (IOException iox2) {
Log.severe("Error loading cached texture file for " + blkid + " - " + iox2.getMessage());
}
if(img != null) {
try {
if(urlloaded)
ImageIO.write(img, "png", imgfile);
} catch (IOException iox) {
Log.severe("Error writing " + blkid + ".png");
} finally {
img.flush();
}
}
}
if(img != null) {
try {
if(urlloaded)
ImageIO.write(img, "png", imgfile);
} catch (IOException iox) {
Log.severe("Error writing " + blkid + ".png");
} finally {
img.flush();
}
if(imgfile.exists()) { /* If exists now, log it */
String tfid = "txtid" + texturelist.size();
sb.append("texturefile:id=" + tfid + ",filename=spout/" + blkid + ".png,xcount=" + w/sz + ",ycount=" + h/sz + "\n");
texturelist.put(txname, tfid);
+9
View File
@@ -377,6 +377,15 @@ url:
# markers base URL
#markers: "tiles/"
# Spout support controls
spout:
# If false, ignore spout even if detected
enabled: true
# If true, previously loaded textures will be assumed to still be valid (faster startup, but
# can result in stale textures if originals are updated - delete files in texturepacks/standard/spoout
# to clean cached textures and force reload on next startup)
use-existing-textures: true
# Set to true to enable verbose startup messages - can help with debugging map configuration problems
# Set to false for a much quieter startup log
verbose: false
+1 -1
View File
@@ -2,7 +2,7 @@ name: dynmap
main: org.dynmap.bukkit.DynmapPlugin
version: "${project.version}-${BUILD_NUMBER}"
authors: [FrozenCow, mikeprimm]
softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, SpoutMaterials ]
softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit ]
commands:
dynmap:
description: Controls Dynmap.