Server-side multiworld support with several improvements overal.

This commit is contained in:
FrozenCow 2011-02-12 23:45:33 +01:00
parent 2fd91ef94b
commit 5b0171c459
20 changed files with 398 additions and 296 deletions

View file

@ -3,84 +3,48 @@ package org.dynmap;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.config.ConfigurationNode;
import org.dynmap.debug.Debugger;
import org.dynmap.debug.Debug;
public class MapManager extends Thread {
public class MapManager {
protected static final Logger log = Logger.getLogger("Minecraft");
private World world;
private Debugger debugger;
private MapType[] maps;
public StaleQueue staleQueue;
private MapType[] mapTypes;
public AsynchronousQueue<MapTile> tileQueue;
public UpdateQueue updateQueue;
public PlayerList playerList;
/* lock for our data structures */
public static final Object lock = new Object();
/* whether the worker thread should be running now */
private boolean running = false;
/* path to image tile directory */
public File tileDirectory;
/* web files location */
public File webDirectory;
/* bind web server to ip-address */
public String bindaddress = "0.0.0.0";
/* port to run web server on */
public int serverport = 8123;
/* time to pause between rendering tiles (ms) */
public int renderWait = 500;
public boolean loadChunks = true;
public void debug(String msg) {
debugger.debug(msg);
}
private static File combinePaths(File parent, String path) {
return combinePaths(parent, new File(path));
}
private static File combinePaths(File parent, File path) {
if (path.isAbsolute())
return path;
return new File(parent, path.getPath());
}
public MapManager(World world, Debugger debugger, ConfigurationNode configuration) {
this.world = world;
this.debugger = debugger;
this.staleQueue = new StaleQueue();
public MapManager(ConfigurationNode configuration) {
this.updateQueue = new UpdateQueue();
tileDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("tilespath", "web/tiles"));
webDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("webpath", "web"));
renderWait = (int) (configuration.getDouble("renderinterval", 0.5) * 1000);
loadChunks = configuration.getBoolean("loadchunks", true);
if (!tileDirectory.isDirectory())
tileDirectory.mkdirs();
maps = loadMapTypes(configuration);
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
@Override
public void handle(MapTile t) {
render(t);
}
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
mapTypes = loadMapTypes(configuration);
tileQueue.start();
}
void renderFullWorld(Location l) {
debugger.debug("Full render starting...");
for (MapType map : maps) {
World world = l.getWorld();
log.info("Full render starting...");
for (MapType map : mapTypes) {
int requiredChunkCount = 200;
HashSet<MapTile> found = new HashSet<MapTile>();
HashSet<MapTile> rendered = new HashSet<MapTile>();
@ -114,10 +78,10 @@ public class MapManager extends Thread {
loadedChunks.add(chunk);
}
if (map.render(tile)) {
if (render(tile)) {
found.remove(tile);
rendered.add(tile);
updateQueue.pushUpdate(new Client.Tile(tile.getName()));
updateQueue.pushUpdate(new Client.Tile(tile.getFilename()));
for (MapTile adjTile : map.getAdjecentTiles(tile)) {
if (!found.contains(adjTile) && !rendered.contains(adjTile)) {
found.add(adjTile);
@ -135,10 +99,17 @@ public class MapManager extends Thread {
world.unloadChunk(c.x, c.z, false, true);
}
}
debugger.debug("Full render finished.");
log.info("Full render finished.");
}
private MapType[] loadMapTypes(ConfigurationNode configuration) {
Event.Listener<MapTile> invalitateListener = new Event.Listener<MapTile>() {
@Override
public void triggered(MapTile t) {
invalidateTile(t);
}
};
List<?> configuredMaps = (List<?>) configuration.getProperty("maps");
ArrayList<MapType> mapTypes = new ArrayList<MapType>();
for (Object configuredMapObj : configuredMaps) {
@ -148,83 +119,24 @@ public class MapManager extends Thread {
String typeName = (String) configuredMap.get("class");
log.info("Loading map '" + typeName.toString() + "'...");
Class<?> mapTypeClass = Class.forName(typeName);
Constructor<?> constructor = mapTypeClass.getConstructor(MapManager.class, World.class, Debugger.class, Map.class);
MapType mapType = (MapType) constructor.newInstance(this, world, debugger, configuredMap);
Constructor<?> constructor = mapTypeClass.getConstructor(Map.class);
MapType mapType = (MapType) constructor.newInstance(configuredMap);
mapType.onTileInvalidated.addListener(invalitateListener);
mapTypes.add(mapType);
} catch (Exception e) {
debugger.error("Error loading map", e);
log.log(Level.SEVERE, "Error loading maptype", e);
e.printStackTrace();
}
}
MapType[] result = new MapType[mapTypes.size()];
mapTypes.toArray(result);
return result;
}
/* initialize and start map manager */
public void startManager() {
synchronized (lock) {
running = true;
this.start();
try {
this.setPriority(MIN_PRIORITY);
log.info("Set minimum priority for worker thread");
} catch (SecurityException e) {
log.info("Failed to set minimum priority for worker thread!");
}
}
}
/* stop map manager */
public void stopManager() {
synchronized (lock) {
if (!running)
return;
log.info("Stopping map renderer...");
running = false;
try {
this.join();
} catch (InterruptedException e) {
log.info("Waiting for map renderer to stop is interrupted");
}
}
}
/* the worker/renderer thread */
public void run() {
try {
log.info("Map renderer has started.");
while (running) {
MapTile t = staleQueue.popStaleTile();
if (t != null) {
debugger.debug("Rendering tile " + t + "...");
boolean isNonEmptyTile = t.getMap().render(t);
updateQueue.pushUpdate(new Client.Tile(t.getName()));
try {
Thread.sleep(renderWait);
} catch (InterruptedException e) {
}
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
log.info("Map renderer has stopped.");
} catch (Exception ex) {
debugger.error("Exception on rendering-thread: " + ex.toString());
ex.printStackTrace();
}
}
public void touch(int x, int y, int z) {
for (int i = 0; i < maps.length; i++) {
MapTile[] tiles = maps[i].getTiles(new Location(world, x, y, z));
public void touch(Location l) {
Debug.debug("Touched " + l.toString());
for (int i = 0; i < mapTypes.length; i++) {
MapTile[] tiles = mapTypes[i].getTiles(l);
for (int j = 0; j < tiles.length; j++) {
invalidateTile(tiles[j]);
}
@ -232,7 +144,32 @@ public class MapManager extends Thread {
}
public void invalidateTile(MapTile tile) {
debugger.debug("Invalidating tile " + tile.getName());
staleQueue.pushStaleTile(tile);
Debug.debug("Invalidating tile " + tile.getFilename());
tileQueue.push(tile);
}
public void startRendering() {
tileQueue.start();
}
public void stopRendering() {
tileQueue.stop();
}
public boolean render(MapTile tile) {
return tile.getMap().render(tile, getTileFile(tile));
}
private HashMap<World, File> worldTileDirectories = new HashMap<World, File>();
private File getTileFile(MapTile tile) {
World world = tile.getWorld();
File worldTileDirectory = worldTileDirectories.get(world);
if (worldTileDirectory == null) {
worldTileDirectory = new File(DynmapPlugin.tilesDirectory, tile.getWorld().getName());
worldTileDirectory.mkdirs();
worldTileDirectories.put(world, worldTileDirectory);
}
return new File(worldTileDirectory, tile.getFilename());
}
}