mirror of
https://github.com/encounter/dynmap.git
synced 2026-03-30 11:08:39 -07:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3cd322d5f | |||
| 3c4a88a874 | |||
| db3ab5a437 | |||
| 530eb0ae9e | |||
| 02091acd45 | |||
| 9c32d7168e |
@@ -1,73 +1,76 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.dynmap</groupId>
|
||||
<artifactId>dynmap</artifactId>
|
||||
<version>0.29.1</version>
|
||||
<name>dynmap</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<BUILD_NUMBER>dev</BUILD_NUMBER>
|
||||
</properties>
|
||||
<url>http://github.com/webbukkit/dynmap/</url>
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/webbukkit/dynmap/issues</url>
|
||||
</issueManagement>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>*.yml</include>
|
||||
<include>*.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>*.yml</exclude>
|
||||
<exclude>*.txt</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.dynmap:dynmap-api:jar:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.dynmap</groupId>
|
||||
<artifactId>dynmap</artifactId>
|
||||
<version>0.30</version>
|
||||
<name>dynmap</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<BUILD_NUMBER>dev</BUILD_NUMBER>
|
||||
</properties>
|
||||
<url>http://github.com/webbukkit/dynmap/</url>
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/webbukkit/dynmap/issues</url>
|
||||
</issueManagement>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>*.yml</include>
|
||||
<include>*.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>*.yml</exclude>
|
||||
<exclude>*.txt</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.dynmap:dynmap-api:jar:*</include>
|
||||
<include>org.eclipse.jetty:jetty-*:jar:*</include>
|
||||
<include>javax.servlet:javax.servlet-api:jar:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/package.xml</descriptor>
|
||||
</descriptors>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/package.xml</descriptor>
|
||||
</descriptors>
|
||||
<!-- Hack for bug in maven-assembly: http://jira.codehaus.org/browse/MASSEMBLY-449 -->
|
||||
<archiverConfig>
|
||||
<fileMode>420</fileMode> <!-- 420(dec) = 644(oct) -->
|
||||
@@ -76,36 +79,52 @@
|
||||
</archiverConfig>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>build</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.nijikokun.bukkit</groupId>
|
||||
<artifactId>Permissions</artifactId>
|
||||
<version>[2.5.4,)</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>[1.0.0-R1-SNAPSHOT,)</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dynmap</groupId>
|
||||
<artifactId>dynmap-api</artifactId>
|
||||
<version>[0.25,)</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.nijikokun.bukkit</groupId>
|
||||
<artifactId>Permissions</artifactId>
|
||||
<version>[2.5.4,)</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>[1.0.0-R1-SNAPSHOT,)</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dynmap</groupId>
|
||||
<artifactId>dynmap-api</artifactId>
|
||||
<version>[0.25,)</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>8.0.1.v20110908</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>8.0.1.v20110908</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -4,12 +4,12 @@ package org.dynmap;
|
||||
* Generic block location
|
||||
*/
|
||||
public class DynmapLocation {
|
||||
public int x, y, z;
|
||||
public double x, y, z;
|
||||
public String world;
|
||||
|
||||
public DynmapLocation() {}
|
||||
|
||||
public DynmapLocation(String w, int x, int y, int z) {
|
||||
public DynmapLocation(String w, double x, double y, double z) {
|
||||
world = w;
|
||||
this.x = x; this.y = y; this.z = z;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
package org.dynmap;
|
||||
|
||||
import org.dynmap.Event.Listener;
|
||||
import org.dynmap.web.handlers.ClientUpdateHandler;
|
||||
import org.dynmap.web.handlers.SendMessageHandler;
|
||||
import org.dynmap.servlet.ClientUpdateServlet;
|
||||
import org.dynmap.servlet.SendMessageServlet;
|
||||
import org.json.simple.JSONObject;
|
||||
import static org.dynmap.JSONUtils.*;
|
||||
|
||||
@@ -10,12 +9,11 @@ public class InternalClientUpdateComponent extends ClientUpdateComponent {
|
||||
|
||||
public InternalClientUpdateComponent(final DynmapPlugin plugin, final ConfigurationNode configuration) {
|
||||
super(plugin, configuration);
|
||||
final boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
|
||||
final boolean hidewebchatip = configuration.getBoolean("hidewebchatip", false);
|
||||
final boolean trust_client_name = configuration.getBoolean("trustclientname", false);
|
||||
final boolean useplayerloginip = configuration.getBoolean("use-player-login-ip", true);
|
||||
final boolean checkuserban = configuration.getBoolean("block-banned-player-chat", true);
|
||||
final boolean requireplayerloginip = configuration.getBoolean("require-player-login-ip", false);
|
||||
plugin.addServlet("/up/world/*", new ClientUpdateServlet(plugin));
|
||||
|
||||
final Boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
|
||||
final Boolean hidewebchatip = configuration.getBoolean("hidewebchatip", false);
|
||||
final Boolean trust_client_name = configuration.getBoolean("trustclientname", false);
|
||||
final float webchatInterval = configuration.getFloat("webchat-interval", 1);
|
||||
final String spammessage = plugin.configuration.getString("spammessage", "You may only chat once every %interval% seconds.");
|
||||
|
||||
@@ -26,38 +24,31 @@ public class InternalClientUpdateComponent extends ClientUpdateComponent {
|
||||
s(t, "webchat-interval", webchatInterval);
|
||||
}
|
||||
});
|
||||
|
||||
plugin.webServer.handlers.put("/up/", new ClientUpdateHandler(plugin));
|
||||
|
||||
|
||||
if (allowwebchat) {
|
||||
SendMessageHandler messageHandler = new SendMessageHandler() {{
|
||||
SendMessageServlet messageHandler = new SendMessageServlet() {{
|
||||
maximumMessageInterval = (int)(webchatInterval * 1000);
|
||||
spamMessage = "\""+spammessage+"\"";
|
||||
hideip = hidewebchatip;
|
||||
this.plug_in = plugin;
|
||||
this.trustclientname = trust_client_name;
|
||||
this.use_player_login_ip = useplayerloginip;
|
||||
this.require_player_login_ip = requireplayerloginip;
|
||||
this.check_user_ban = checkuserban;
|
||||
onMessageReceived.addListener(new Listener<SendMessageHandler.Message>() {
|
||||
onMessageReceived.addListener(new Event.Listener<Message> () {
|
||||
@Override
|
||||
public void triggered(Message t) {
|
||||
webChat(t.name, t.message);
|
||||
}
|
||||
});
|
||||
}};
|
||||
|
||||
plugin.webServer.handlers.put("/up/sendmessage", messageHandler);
|
||||
plugin.addServlet("/up/sendmessage", messageHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void webChat(String name, String message) {
|
||||
if(plugin.mapManager == null)
|
||||
return;
|
||||
// TODO: Change null to something meaningful.
|
||||
plugin.mapManager.pushUpdate(new Client.ChatMessage("web", null, name, message, null));
|
||||
Log.info(unescapeString(plugin.configuration.getString("webprefix", "\u00A72[WEB] ")) + name + ": " + unescapeString(plugin.configuration.getString("websuffix", "\u00A7f")) + message);
|
||||
ChatEvent event = new ChatEvent("web", name, message);
|
||||
ChatEvent event = new ChatEvent("web", name, message);
|
||||
plugin.events.trigger("webchat", event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
@@ -86,6 +87,34 @@ public class MapManager {
|
||||
private DynmapScheduledThreadPoolExecutor render_pool;
|
||||
private static final int POOL_SIZE = 3;
|
||||
|
||||
/* Touch event queues */
|
||||
private static class TouchEvent {
|
||||
int x, y, z;
|
||||
String world;
|
||||
String reason;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (x << 16) ^ (y << 24) ^ z;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
TouchEvent te = (TouchEvent)o;
|
||||
if((x != te.x) || (y != te.y) || (z != te.z) || (world.equals(te.world) == false))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private static class TouchVolumeEvent {
|
||||
int xmin, ymin, zmin;
|
||||
int xmax, ymax, zmax;
|
||||
String world;
|
||||
String reason;
|
||||
}
|
||||
private ConcurrentHashMap<TouchEvent, Object> touch_events = new ConcurrentHashMap<TouchEvent, Object>();
|
||||
private LinkedList<TouchVolumeEvent> touch_volume_events = new LinkedList<TouchVolumeEvent>();
|
||||
private Object touch_lock = new Object();
|
||||
|
||||
private HashMap<String, MapStats> mapstats = new HashMap<String, MapStats>();
|
||||
|
||||
private static class MapStats {
|
||||
@@ -94,9 +123,10 @@ public class MapManager {
|
||||
int updatedcnt;
|
||||
int transparentcnt;
|
||||
}
|
||||
|
||||
/* synchronized using 'lock' */
|
||||
private HashMap<String, TriggerStats> trigstats = new HashMap<String, TriggerStats>();
|
||||
|
||||
|
||||
private static class TriggerStats {
|
||||
long callsmade;
|
||||
long callswithtiles;
|
||||
@@ -235,10 +265,10 @@ public class MapManager {
|
||||
rendertype = RENDERTYPE_FULLRENDER;
|
||||
}
|
||||
else {
|
||||
cxmin = (l.x - radius)>>4;
|
||||
czmin = (l.z - radius)>>4;
|
||||
cxmax = (l.x + radius+15)>>4;
|
||||
czmax = (l.z + radius+15)>>4;
|
||||
cxmin = ((int)l.x - radius)>>4;
|
||||
czmin = ((int)l.z - radius)>>4;
|
||||
cxmax = ((int)l.x + radius + 15)>>4;
|
||||
czmax = ((int)l.z + radius + 15)>>4;
|
||||
rendertype = RENDERTYPE_RADIUSRENDER;
|
||||
}
|
||||
this.mapname = mapname;
|
||||
@@ -453,7 +483,7 @@ public class MapManager {
|
||||
renderedmaps.addAll(map.getMapsSharingRender(world));
|
||||
|
||||
/* Now, prime the render queue */
|
||||
for (MapTile mt : map.getTiles(world, loc.x, loc.y, loc.z)) {
|
||||
for (MapTile mt : map.getTiles(world, (int)loc.x, (int)loc.y, (int)loc.z)) {
|
||||
if (!found.getFlag(mt.tileOrdinalX(), mt.tileOrdinalY())) {
|
||||
found.setFlag(mt.tileOrdinalX(), mt.tileOrdinalY(), true);
|
||||
renderQueue.add(mt);
|
||||
@@ -462,7 +492,7 @@ public class MapManager {
|
||||
if(!updaterender) { /* Only add other seed points for fullrender */
|
||||
/* Add spawn location too (helps with some worlds where 0,64,0 may not be generated */
|
||||
DynmapLocation sloc = world.getSpawnLocation();
|
||||
for (MapTile mt : map.getTiles(world, sloc.x, sloc.y, sloc.z)) {
|
||||
for (MapTile mt : map.getTiles(world, (int)sloc.x, (int)sloc.y, (int)sloc.z)) {
|
||||
if (!found.getFlag(mt.tileOrdinalX(), mt.tileOrdinalY())) {
|
||||
found.setFlag(mt.tileOrdinalX(), mt.tileOrdinalY(), true);
|
||||
renderQueue.add(mt);
|
||||
@@ -470,7 +500,7 @@ public class MapManager {
|
||||
}
|
||||
if(world.seedloc != null) {
|
||||
for(DynmapLocation seed : world.seedloc) {
|
||||
for (MapTile mt : map.getTiles(world, seed.x, seed.y, seed.z)) {
|
||||
for (MapTile mt : map.getTiles(world, (int)seed.x, (int)seed.y, (int)seed.z)) {
|
||||
if (!found.getFlag(mt.tileOrdinalX(),mt.tileOrdinalY())) {
|
||||
found.setFlag(mt.tileOrdinalX(),mt.tileOrdinalY(), true);
|
||||
renderQueue.add(mt);
|
||||
@@ -688,6 +718,13 @@ public class MapManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class DoTouchProcessing implements Runnable {
|
||||
public void run() {
|
||||
processTouchEvents();
|
||||
scheduleDelayedJob(this, 1000); /* Once per second */
|
||||
}
|
||||
}
|
||||
|
||||
public MapManager(DynmapPlugin plugin, ConfigurationNode configuration) {
|
||||
plug_in = plugin;
|
||||
mapman = this;
|
||||
@@ -1010,62 +1047,29 @@ public class MapManager {
|
||||
}
|
||||
}
|
||||
|
||||
public int touch(DynmapLocation l, String reason) {
|
||||
return touch(l.world, l.x, l.y, l.z, reason);
|
||||
}
|
||||
|
||||
public int touch(String wname, int x, int y, int z, String reason) {
|
||||
DynmapWorld world = getWorld(wname);
|
||||
if (world == null)
|
||||
return 0;
|
||||
int invalidates = 0;
|
||||
for (int i = 0; i < world.maps.size(); i++) {
|
||||
MapTile[] tiles = world.maps.get(i).getTiles(world, x, y, z);
|
||||
for (int j = 0; j < tiles.length; j++) {
|
||||
if(invalidateTile(tiles[j]))
|
||||
invalidates++;
|
||||
}
|
||||
}
|
||||
if(reason != null) {
|
||||
TriggerStats ts = trigstats.get(reason);
|
||||
if(ts == null) {
|
||||
ts = new TriggerStats();
|
||||
trigstats.put(reason, ts);
|
||||
}
|
||||
ts.callsmade++;
|
||||
if(invalidates > 0) {
|
||||
ts.callswithtiles++;
|
||||
ts.tilesqueued += invalidates;
|
||||
}
|
||||
}
|
||||
return invalidates;
|
||||
public void touch(String wname, int x, int y, int z, String reason) {
|
||||
TouchEvent evt = new TouchEvent();
|
||||
evt.world = wname;
|
||||
evt.x = x;
|
||||
evt.y = y;
|
||||
evt.z = z;
|
||||
evt.reason = reason;
|
||||
touch_events.putIfAbsent(evt, reason);
|
||||
}
|
||||
|
||||
public int touchVolume(String wname, int minx, int miny, int minz, int maxx, int maxy, int maxz, String reason) {
|
||||
DynmapWorld world = getWorld(wname);
|
||||
if (world == null)
|
||||
return 0;
|
||||
int invalidates = 0;
|
||||
for (int i = 0; i < world.maps.size(); i++) {
|
||||
MapTile[] tiles = world.maps.get(i).getTiles(world, minx, miny, minz, maxx, maxy, maxz);
|
||||
for (int j = 0; j < tiles.length; j++) {
|
||||
if(invalidateTile(tiles[j]))
|
||||
invalidates++;
|
||||
}
|
||||
public void touchVolume(String wname, int minx, int miny, int minz, int maxx, int maxy, int maxz, String reason) {
|
||||
TouchVolumeEvent evt = new TouchVolumeEvent();
|
||||
evt.world = wname;
|
||||
evt.xmin = minx;
|
||||
evt.xmax = maxx;
|
||||
evt.ymin = miny;
|
||||
evt.ymax = maxy;
|
||||
evt.zmin = minz;
|
||||
evt.zmax = maxz;
|
||||
evt.reason = reason;
|
||||
synchronized(touch_lock) {
|
||||
touch_volume_events.add(evt);
|
||||
}
|
||||
if(reason != null) {
|
||||
TriggerStats ts = trigstats.get(reason);
|
||||
if(ts == null) {
|
||||
ts = new TriggerStats();
|
||||
trigstats.put(reason, ts);
|
||||
}
|
||||
ts.callsmade++;
|
||||
if(invalidates > 0) {
|
||||
ts.callswithtiles++;
|
||||
ts.tilesqueued += invalidates;
|
||||
}
|
||||
}
|
||||
return invalidates;
|
||||
}
|
||||
|
||||
public boolean invalidateTile(MapTile tile) {
|
||||
@@ -1089,6 +1093,7 @@ public class MapManager {
|
||||
tileQueue.start();
|
||||
scheduleDelayedJob(new DoZoomOutProcessing(), 60000);
|
||||
scheduleDelayedJob(new CheckWorldTimes(), 5000);
|
||||
scheduleDelayedJob(new DoTouchProcessing(), 1000);
|
||||
/* Resume pending jobs */
|
||||
for(FullWorldRenderState job : active_renders.values()) {
|
||||
scheduleDelayedJob(job, 5000);
|
||||
@@ -1401,4 +1406,99 @@ public class MapManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process touch events
|
||||
*/
|
||||
private void processTouchEvents() {
|
||||
ArrayList<TouchEvent> te = null;
|
||||
ArrayList<TouchVolumeEvent> tve = null;
|
||||
|
||||
if(touch_events.isEmpty() == false) {
|
||||
te = new ArrayList<TouchEvent>(touch_events.keySet());
|
||||
for(int i = 0; i < te.size(); i++) {
|
||||
touch_events.remove(te.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(touch_lock) {
|
||||
if(touch_volume_events.isEmpty() == false) {
|
||||
tve = new ArrayList<TouchVolumeEvent>(touch_volume_events);
|
||||
touch_volume_events.clear();
|
||||
}
|
||||
}
|
||||
DynmapWorld world = null;
|
||||
String wname = "";
|
||||
|
||||
/* If any touch events, process them */
|
||||
if(te != null) {
|
||||
for(TouchEvent evt : te) {
|
||||
int invalidates = 0;
|
||||
/* If different world, look it up */
|
||||
if(evt.world.equals(wname) == false) {
|
||||
wname = evt.world;
|
||||
world = getWorld(wname);
|
||||
}
|
||||
if(world == null) continue;
|
||||
for (int i = 0; i < world.maps.size(); i++) {
|
||||
MapTile[] tiles = world.maps.get(i).getTiles(world, evt.x, evt.y, evt.z);
|
||||
for (int j = 0; j < tiles.length; j++) {
|
||||
if(invalidateTile(tiles[j]))
|
||||
invalidates++;
|
||||
}
|
||||
}
|
||||
if(evt.reason != null) {
|
||||
synchronized(lock) {
|
||||
TriggerStats ts = trigstats.get(evt.reason);
|
||||
if(ts == null) {
|
||||
ts = new TriggerStats();
|
||||
trigstats.put(evt.reason, ts);
|
||||
}
|
||||
ts.callsmade++;
|
||||
if(invalidates > 0) {
|
||||
ts.callswithtiles++;
|
||||
ts.tilesqueued += invalidates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
te.clear(); /* Clean up set */
|
||||
}
|
||||
|
||||
/* If any volume touches */
|
||||
if(tve != null) {
|
||||
for(TouchVolumeEvent evt : tve) {
|
||||
/* If different world, look it up */
|
||||
if(evt.world.equals(wname) == false) {
|
||||
wname = evt.world;
|
||||
world = getWorld(wname);
|
||||
}
|
||||
if(world == null) continue;
|
||||
int invalidates = 0;
|
||||
for (int i = 0; i < world.maps.size(); i++) {
|
||||
MapTile[] tiles = world.maps.get(i).getTiles(world, evt.xmin, evt.ymin, evt.zmin, evt.xmax, evt.ymax, evt.zmax);
|
||||
for (int j = 0; j < tiles.length; j++) {
|
||||
if(invalidateTile(tiles[j]))
|
||||
invalidates++;
|
||||
}
|
||||
}
|
||||
if(evt.reason != null) {
|
||||
synchronized(lock) {
|
||||
TriggerStats ts = trigstats.get(evt.reason);
|
||||
if(ts == null) {
|
||||
ts = new TriggerStats();
|
||||
trigstats.put(evt.reason, ts);
|
||||
}
|
||||
ts.callsmade++;
|
||||
if(invalidates > 0) {
|
||||
ts.callswithtiles++;
|
||||
ts.tilesqueued += invalidates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Clean up */
|
||||
tve.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.dynmap.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.Event;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class ClientConfigurationServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 9106801553080522469L;
|
||||
private DynmapPlugin plugin;
|
||||
private byte[] cachedConfiguration = null;
|
||||
public ClientConfigurationServlet(DynmapPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.events.addListener("worldactivated", new Event.Listener<DynmapWorld>() {
|
||||
@Override
|
||||
public void triggered(DynmapWorld t) {
|
||||
cachedConfiguration = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
|
||||
byte[] outputBytes = cachedConfiguration;
|
||||
if (outputBytes == null) {
|
||||
JSONObject json = new JSONObject();
|
||||
plugin.events.<JSONObject>trigger("buildclientconfiguration", json);
|
||||
|
||||
String s = json.toJSONString();
|
||||
|
||||
outputBytes = s.getBytes("UTF-8");
|
||||
}
|
||||
if (outputBytes != null) {
|
||||
cachedConfiguration = outputBytes;
|
||||
}
|
||||
String dateStr = new Date().toString();
|
||||
res.addHeader("Date", dateStr);
|
||||
res.setContentType("text/plain; charset=utf-8");
|
||||
res.addHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
|
||||
res.addHeader("Last-modified", dateStr);
|
||||
res.setContentLength(outputBytes.length);
|
||||
res.getOutputStream().write(outputBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.dynmap.servlet;
|
||||
|
||||
import static org.dynmap.JSONUtils.s;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.dynmap.ClientUpdateEvent;
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.web.HttpField;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class ClientUpdateServlet extends HttpServlet {
|
||||
private DynmapPlugin plugin;
|
||||
|
||||
public ClientUpdateServlet(DynmapPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
Pattern updatePathPattern = Pattern.compile("/([^/]+)/([0-9]*)");
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String path = req.getPathInfo();
|
||||
Matcher match = updatePathPattern.matcher(path);
|
||||
|
||||
if (!match.matches()) {
|
||||
resp.sendError(404, "World not found");
|
||||
return;
|
||||
}
|
||||
|
||||
String worldName = match.group(1);
|
||||
String timeKey = match.group(2);
|
||||
|
||||
DynmapWorld dynmapWorld = null;
|
||||
if(plugin.mapManager != null) {
|
||||
dynmapWorld = plugin.mapManager.getWorld(worldName);
|
||||
}
|
||||
if (dynmapWorld == null || dynmapWorld.world == null) {
|
||||
resp.sendError(404, "World not found");
|
||||
return;
|
||||
}
|
||||
long current = System.currentTimeMillis();
|
||||
long since = 0;
|
||||
|
||||
try {
|
||||
since = Long.parseLong(timeKey);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
|
||||
JSONObject u = new JSONObject();
|
||||
s(u, "timestamp", current);
|
||||
plugin.events.trigger("buildclientupdate", new ClientUpdateEvent(since, dynmapWorld, u));
|
||||
|
||||
byte[] bytes = u.toJSONString().getBytes("UTF-8");
|
||||
|
||||
String dateStr = new Date().toString();
|
||||
resp.addHeader(HttpField.Date, dateStr);
|
||||
resp.addHeader(HttpField.ContentType, "text/plain; charset=utf-8");
|
||||
resp.addHeader(HttpField.Expires, "Thu, 01 Dec 1994 16:00:00 GMT");
|
||||
resp.addHeader(HttpField.LastModified, dateStr);
|
||||
resp.addHeader(HttpField.ContentLength, Integer.toString(bytes.length));
|
||||
|
||||
resp.getOutputStream().write(bytes);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
package org.dynmap.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.json.simple.JSONAware;
|
||||
import org.json.simple.JSONStreamAware;
|
||||
|
||||
public class JSONServlet {
|
||||
public static void respond(HttpServletResponse response, JSONStreamAware json) throws IOException {
|
||||
response.setContentType("application/json");
|
||||
PrintWriter writer = response.getWriter();
|
||||
json.writeJSONString(writer);
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package org.dynmap.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.dynmap.Log;
|
||||
|
||||
public class MainServlet extends HttpServlet {
|
||||
public static class Header {
|
||||
public String name;
|
||||
public String value;
|
||||
public Header(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Registration {
|
||||
public String pattern;
|
||||
public HttpServlet servlet;
|
||||
|
||||
public Registration(String pattern, HttpServlet servlet) {
|
||||
this.pattern = pattern;
|
||||
this.servlet = servlet;
|
||||
}
|
||||
}
|
||||
|
||||
List<Registration> registrations = new LinkedList<Registration>();
|
||||
public List<Header> customHeaders = new LinkedList<Header>();
|
||||
|
||||
public void addServlet(String pattern, HttpServlet servlet) {
|
||||
registrations.add(new Registration(pattern, servlet));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
HashMap<String, Object> properties = new HashMap<String, Object>();
|
||||
String path = req.getPathInfo();
|
||||
|
||||
for(Header header : customHeaders) {
|
||||
resp.setHeader(header.name, header.value);
|
||||
}
|
||||
|
||||
Registration bestMatch = null;
|
||||
String bestMatchPart = null;
|
||||
HashMap<String, Object> bestProperties = null;
|
||||
|
||||
for (Registration r : registrations) {
|
||||
String matchingPart = match(r.pattern, path, properties);
|
||||
if (matchingPart != null) {
|
||||
if (bestMatchPart == null || bestMatchPart.length() < matchingPart.length()) {
|
||||
bestMatch = r;
|
||||
bestMatchPart = matchingPart;
|
||||
bestProperties = properties;
|
||||
properties = new HashMap<String, Object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMatch == null) {
|
||||
resp.sendError(404);
|
||||
} else {
|
||||
String leftOverPath = path.substring(bestMatchPart.length());
|
||||
HttpServletRequest newreq = new RequestWrapper(req, leftOverPath);
|
||||
for(String key : bestProperties.keySet()) {
|
||||
newreq.setAttribute(key, bestProperties.get(key));
|
||||
}
|
||||
bestMatch.servlet.service(newreq, resp);
|
||||
}
|
||||
}
|
||||
|
||||
public String match(String pattern, String path, Map<String, Object> properties) {
|
||||
int patternStart = 0;
|
||||
int pathStart = 0;
|
||||
while (patternStart < pattern.length()) {
|
||||
if (pattern.charAt(patternStart) == '{') {
|
||||
// Found a variable.
|
||||
int endOfVariable = pattern.indexOf('}', patternStart+1);
|
||||
String variableName = pattern.substring(patternStart+1, endOfVariable);
|
||||
|
||||
int endOfSection = indexOfAny(path, new char[] { '/', '?' }, pathStart);
|
||||
if (endOfSection < 0) {
|
||||
endOfSection = path.length();
|
||||
}
|
||||
String variableValue = path.substring(pathStart, endOfSection);
|
||||
|
||||
// Store variable.
|
||||
properties.put(variableName, variableValue);
|
||||
|
||||
patternStart = endOfVariable+1;
|
||||
pathStart = endOfSection;
|
||||
} else {
|
||||
int endOfLiteral = pattern.indexOf('{', patternStart);
|
||||
if (endOfLiteral < 0) {
|
||||
endOfLiteral = pattern.length();
|
||||
}
|
||||
String literal = pattern.substring(patternStart, endOfLiteral);
|
||||
int endOfPathLiteral = pathStart + literal.length();
|
||||
if (endOfPathLiteral > path.length()) {
|
||||
return null;
|
||||
}
|
||||
String matchingLiteral = path.substring(pathStart, endOfPathLiteral);
|
||||
if (!literal.equals(matchingLiteral)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
patternStart = endOfLiteral;
|
||||
pathStart = endOfPathLiteral;
|
||||
}
|
||||
}
|
||||
// Return the part of the url that matches the pattern. (if the pattern does not contain any variables, this will be equal to the pattern)
|
||||
return path.substring(0, pathStart);
|
||||
}
|
||||
|
||||
private int indexOfAny(String s, char[] cs, int startIndex) {
|
||||
for(int i = startIndex; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
for(int j = 0; j < cs.length; j++) {
|
||||
if (c == cs[j]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class RequestWrapper extends HttpServletRequestWrapper {
|
||||
String pathInfo;
|
||||
public RequestWrapper(HttpServletRequest request, String pathInfo) {
|
||||
super(request);
|
||||
this.pathInfo = pathInfo;
|
||||
}
|
||||
@Override
|
||||
public String getPathInfo() {
|
||||
return pathInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
-64
@@ -1,5 +1,19 @@
|
||||
package org.dynmap.web.handlers;
|
||||
package org.dynmap.servlet;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.Event;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
@@ -7,96 +21,81 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.Event;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.web.HttpField;
|
||||
import org.dynmap.web.HttpHandler;
|
||||
import org.dynmap.web.HttpMethod;
|
||||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
|
||||
public class SendMessageHandler implements HttpHandler {
|
||||
public class SendMessageServlet extends HttpServlet {
|
||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||
|
||||
private static final JSONParser parser = new JSONParser();
|
||||
public Event<Message> onMessageReceived = new Event<SendMessageHandler.Message>();
|
||||
public Event<Message> onMessageReceived = new Event<Message>();
|
||||
private Charset cs_utf8 = Charset.forName("UTF-8");
|
||||
public int maximumMessageInterval = 1000;
|
||||
public boolean hideip = false;
|
||||
public boolean trustclientname = false;
|
||||
public boolean use_player_login_ip = false;
|
||||
public boolean require_player_login_ip = false;
|
||||
public boolean check_user_ban = false;
|
||||
public DynmapPlugin plug_in;
|
||||
|
||||
public String spamMessage = "\"You may only chat once every %interval% seconds.\"";
|
||||
private HashMap<String, WebUser> disallowedUsers = new HashMap<String, WebUser>();
|
||||
private LinkedList<WebUser> disallowedUserQueue = new LinkedList<WebUser>();
|
||||
private Object disallowedUsersLock = new Object();
|
||||
private HashMap<String,String> useralias = new HashMap<String,String>();
|
||||
private int aliasindex = 1;
|
||||
|
||||
public boolean use_player_login_ip = false;
|
||||
public boolean require_player_login_ip = false;
|
||||
public boolean check_user_ban = false;
|
||||
public DynmapPlugin plug_in;
|
||||
|
||||
|
||||
@Override
|
||||
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||
if (!request.method.equals(HttpMethod.Post)) {
|
||||
response.status = HttpStatus.MethodNotAllowed;
|
||||
response.fields.put(HttpField.Accept, HttpMethod.Post);
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
InputStreamReader reader = new InputStreamReader(request.getInputStream(), cs_utf8);
|
||||
|
||||
JSONObject o = null;
|
||||
try {
|
||||
o = (JSONObject)parser.parse(reader);
|
||||
} catch (ParseException e) {
|
||||
response.sendError(HttpStatus.BadRequest.getCode());
|
||||
return;
|
||||
}
|
||||
|
||||
InputStreamReader reader = new InputStreamReader(request.body, cs_utf8);
|
||||
|
||||
JSONObject o = (JSONObject)parser.parse(reader);
|
||||
final Message message = new Message();
|
||||
|
||||
|
||||
message.name = "";
|
||||
if(trustclientname) {
|
||||
message.name = String.valueOf(o.get("name"));
|
||||
}
|
||||
boolean isip = true;
|
||||
if((message.name == null) || message.name.equals("")) {
|
||||
/* If proxied client address, get original */
|
||||
if(request.fields.containsKey("X-Forwarded-For"))
|
||||
message.name = request.fields.get("X-Forwarded-For");
|
||||
/* If from loopback, we're probably getting from proxy - need to trust client */
|
||||
else if(request.rmtaddr.getAddress().isLoopbackAddress())
|
||||
message.name = String.valueOf(o.get("name"));
|
||||
else
|
||||
message.name = request.rmtaddr.getAddress().getHostAddress();
|
||||
/* If proxied client address, get original */
|
||||
if(request.getHeader("X-Forwarded-For") != null)
|
||||
message.name = request.getHeader("X-Forwarded-For");
|
||||
/* If from loopback, we're probably getting from proxy - need to trust client */
|
||||
else if(request.getRemoteAddr() == "127.0.0.1")
|
||||
message.name = String.valueOf(o.get("name"));
|
||||
else
|
||||
message.name = request.getRemoteAddr();
|
||||
}
|
||||
if(use_player_login_ip) {
|
||||
if (use_player_login_ip) {
|
||||
List<String> ids = plug_in.getIDsForIP(message.name);
|
||||
if(ids != null) {
|
||||
if (ids != null) {
|
||||
String id = ids.get(0);
|
||||
if(check_user_ban) {
|
||||
if (check_user_ban) {
|
||||
OfflinePlayer p = plug_in.getServer().getOfflinePlayer(id);
|
||||
if((p != null) && p.isBanned()) {
|
||||
if ((p != null) && p.isBanned()) {
|
||||
Log.info("Ignore message from '" + message.name + "' - banned player (" + id + ")");
|
||||
response.fields.put("Content-Length", "0");
|
||||
response.status = HttpStatus.Forbidden;
|
||||
response.getBody();
|
||||
response.sendError(HttpStatus.Forbidden.getCode());
|
||||
return;
|
||||
}
|
||||
}
|
||||
message.name = ids.get(0);
|
||||
isip = false;
|
||||
}
|
||||
else if(require_player_login_ip) {
|
||||
} else if (require_player_login_ip) {
|
||||
Log.info("Ignore message from '" + message.name + "' - no matching player login recorded");
|
||||
response.fields.put("Content-Length", "0");
|
||||
response.status = HttpStatus.Forbidden;
|
||||
response.getBody();
|
||||
response.sendError(HttpStatus.Forbidden.getCode());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(hideip && isip) { /* If hiding IP, find or assign alias */
|
||||
synchronized(disallowedUsersLock) {
|
||||
if (hideip && isip) { /* If hiding IP, find or assign alias */
|
||||
synchronized (disallowedUsersLock) {
|
||||
String n = useralias.get(message.name);
|
||||
if(n == null) { /* Make ID */
|
||||
if (n == null) { /* Make ID */
|
||||
n = String.format("web-%03d", aliasindex);
|
||||
aliasindex++;
|
||||
useralias.put(message.name, n);
|
||||
@@ -108,7 +107,7 @@ public class SendMessageHandler implements HttpHandler {
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
|
||||
synchronized(disallowedUsersLock) {
|
||||
synchronized (disallowedUsersLock) {
|
||||
// Allow users that user that are now allowed to send messages.
|
||||
while (!disallowedUserQueue.isEmpty()) {
|
||||
WebUser wu = disallowedUserQueue.getFirst();
|
||||
@@ -122,25 +121,21 @@ public class SendMessageHandler implements HttpHandler {
|
||||
|
||||
WebUser user = disallowedUsers.get(message.name);
|
||||
if (user == null) {
|
||||
user = new WebUser() {{
|
||||
name = message.name;
|
||||
nextMessageTime = now+maximumMessageInterval;
|
||||
}};
|
||||
user = new WebUser() {
|
||||
{
|
||||
name = message.name;
|
||||
nextMessageTime = now + maximumMessageInterval;
|
||||
}
|
||||
};
|
||||
disallowedUsers.put(user.name, user);
|
||||
disallowedUserQueue.add(user);
|
||||
} else {
|
||||
response.fields.put("Content-Length", "0");
|
||||
response.status = HttpStatus.Forbidden;
|
||||
response.getBody();
|
||||
response.sendError(HttpStatus.Forbidden.getCode());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onMessageReceived.trigger(message);
|
||||
|
||||
response.fields.put(HttpField.ContentLength, "0");
|
||||
response.status = HttpStatus.OK;
|
||||
response.getBody();
|
||||
}
|
||||
|
||||
public static class Message {
|
||||
@@ -8,7 +8,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.dynmap.DynmapLocation;
|
||||
|
||||
public class SnapshotCache {
|
||||
private CacheHashMap snapcache;
|
||||
@@ -49,24 +48,9 @@ public class SnapshotCache {
|
||||
snapcache = new CacheHashMap(max_size);
|
||||
refqueue = new ReferenceQueue<ChunkSnapshot>();
|
||||
}
|
||||
private String getKey(DynmapLocation loc) {
|
||||
return loc.world + ":" + (loc.x>>4) + ":" + (loc.z>>4);
|
||||
}
|
||||
private String getKey(String w, int cx, int cz) {
|
||||
return w + ":" + cx + ":" + cz;
|
||||
}
|
||||
/**
|
||||
* Invalidate cached snapshot, if in cache
|
||||
*/
|
||||
public void invalidateSnapshot(DynmapLocation loc) {
|
||||
String key = getKey(loc);
|
||||
CacheRec rec = snapcache.remove(key);
|
||||
if(rec != null) {
|
||||
snapcache.reverselookup.remove(rec.ref);
|
||||
rec.ref.clear();
|
||||
}
|
||||
processRefQueue();
|
||||
}
|
||||
/**
|
||||
* Invalidate cached snapshot, if in cache
|
||||
*/
|
||||
@@ -77,7 +61,7 @@ public class SnapshotCache {
|
||||
snapcache.reverselookup.remove(rec.ref);
|
||||
rec.ref.clear();
|
||||
}
|
||||
processRefQueue();
|
||||
//processRefQueue();
|
||||
}
|
||||
/**
|
||||
* Invalidate cached snapshot, if in cache
|
||||
@@ -93,7 +77,7 @@ public class SnapshotCache {
|
||||
}
|
||||
}
|
||||
}
|
||||
processRefQueue();
|
||||
//processRefQueue();
|
||||
}
|
||||
/**
|
||||
* Look for chunk snapshot in cache
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.dynmap.web;
|
||||
|
||||
|
||||
public interface HttpHandler {
|
||||
void handle(String path, HttpRequest request, HttpResponse response) throws Exception;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.dynmap.web;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class HttpRequest {
|
||||
public String method;
|
||||
public String path;
|
||||
public String version;
|
||||
public Map<String, String> fields = new HashMap<String, String>();
|
||||
public InputStream body;
|
||||
public InetSocketAddress rmtaddr;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.dynmap.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpResponse {
|
||||
private HttpServerConnection connection;
|
||||
public String version = "1.1";
|
||||
public HttpStatus status = null;
|
||||
public Map<String, String> fields = new HashMap<String, String>();
|
||||
|
||||
private OutputStream body;
|
||||
public OutputStream getBody() throws IOException {
|
||||
if (body != null) {
|
||||
connection.writeResponseHeader(this);
|
||||
OutputStream b = body;
|
||||
body = null;
|
||||
return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpResponse(HttpServerConnection connection, OutputStream body) {
|
||||
this.connection = connection;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
package org.dynmap.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.dynmap.Log;
|
||||
|
||||
public class HttpServer extends Thread {
|
||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||
|
||||
private ServerSocket sock = null;
|
||||
private Thread listeningThread;
|
||||
|
||||
private InetAddress bindAddress;
|
||||
private int port;
|
||||
private boolean check_banned_ips;
|
||||
private int max_sessions;
|
||||
|
||||
public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder());
|
||||
|
||||
private Object lock = new Object();
|
||||
private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>();
|
||||
private HashSet<HttpServerConnection> keepalive_connections = new HashSet<HttpServerConnection>();
|
||||
private Plugin plugin;
|
||||
private static Map<String, String> headers = new HashMap<String,String>();
|
||||
|
||||
public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips, int max_sessions, Plugin plg) {
|
||||
this.bindAddress = bindAddress;
|
||||
this.port = port;
|
||||
this.check_banned_ips = check_banned_ips;
|
||||
this.max_sessions = max_sessions;
|
||||
this.plugin = plg;
|
||||
}
|
||||
|
||||
public InetAddress getAddress() {
|
||||
return bindAddress;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void startServer() throws IOException {
|
||||
sock = new ServerSocket(port, 50, bindAddress); /* 5 too low - more than a couple users during render will get connect errors on some tile loads */
|
||||
listeningThread = this;
|
||||
start();
|
||||
Log.info("Dynmap WebServer started on " + bindAddress + ":" + port);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket s = sock;
|
||||
while (listeningThread == Thread.currentThread()) {
|
||||
try {
|
||||
Socket socket = s.accept();
|
||||
if(checkForBannedIp(socket.getRemoteSocketAddress())) {
|
||||
try { socket.close(); } catch (IOException iox) {}
|
||||
socket = null;
|
||||
}
|
||||
|
||||
HttpServerConnection requestThread = new HttpServerConnection(socket, this);
|
||||
synchronized(lock) {
|
||||
active_connections.add(requestThread);
|
||||
requestThread.start();
|
||||
/* If we're at limit, wait here until we're free to accept another */
|
||||
while((listeningThread == Thread.currentThread()) &&
|
||||
(active_connections.size() >= max_sessions)) {
|
||||
lock.wait(500);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */
|
||||
Log.info("map WebServer.run() stops with IOException");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.info("Webserver shut down.");
|
||||
} catch (Exception ex) {
|
||||
Log.severe("Exception on WebServer-thread", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
Log.info("Shutting down webserver...");
|
||||
listeningThread = null;
|
||||
try {
|
||||
if (sock != null) {
|
||||
sock.close();
|
||||
sock = null;
|
||||
}
|
||||
/* And kill off the active connections */
|
||||
HashSet<HttpServerConnection> sc;
|
||||
synchronized(lock) {
|
||||
sc = new HashSet<HttpServerConnection>(active_connections);
|
||||
}
|
||||
for(HttpServerConnection c : sc) {
|
||||
c.shutdownConnection();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.warning("Exception while closing socket for webserver shutdown", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canKeepAlive(HttpServerConnection c) {
|
||||
synchronized(lock) {
|
||||
/* If less than half of our limit are keep-alive, approve */
|
||||
if(keepalive_connections.size() < (max_sessions/2)) {
|
||||
keepalive_connections.add(c);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void connectionEnded(HttpServerConnection c) {
|
||||
synchronized(lock) {
|
||||
active_connections.remove(c);
|
||||
keepalive_connections.remove(c);
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<String> banned_ips = new HashSet<String>();
|
||||
private HashSet<String> banned_ips_notified = new HashSet<String>();
|
||||
private long last_loaded = 0;
|
||||
private long lastmod = 0;
|
||||
private static final long BANNED_RELOAD_INTERVAL = 15000; /* Every 15 seconds */
|
||||
|
||||
private void loadBannedIPs() {
|
||||
banned_ips.clear();
|
||||
banned_ips_notified.clear();
|
||||
banned_ips.addAll(plugin.getServer().getIPBans());
|
||||
}
|
||||
|
||||
/* Return true if address is banned */
|
||||
public boolean checkForBannedIp(SocketAddress socketAddress) {
|
||||
if(!check_banned_ips)
|
||||
return false;
|
||||
|
||||
long t = System.currentTimeMillis();
|
||||
if((t < last_loaded) || ((t-last_loaded) > BANNED_RELOAD_INTERVAL)) {
|
||||
loadBannedIPs();
|
||||
last_loaded = t;
|
||||
}
|
||||
/* Follow same technique as MC uses - toString the SocketAddress and clip out string between "/" and ":" */
|
||||
String ip = socketAddress.toString();
|
||||
ip = ip.substring(ip.indexOf("/") + 1);
|
||||
ip = ip.substring(0, ip.indexOf(":"));
|
||||
if(banned_ips.contains(ip)) {
|
||||
if(banned_ips_notified.contains(ip) == false) {
|
||||
Log.info("Rejected connection by banned IP address - " + socketAddress.toString());
|
||||
banned_ips_notified.add(ip);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* Return true if address is banned */
|
||||
public boolean checkForBannedIp(String ipaddr) {
|
||||
if(!check_banned_ips)
|
||||
return false;
|
||||
|
||||
long t = System.currentTimeMillis();
|
||||
if((t < last_loaded) || ((t-last_loaded) > BANNED_RELOAD_INTERVAL)) {
|
||||
loadBannedIPs();
|
||||
last_loaded = t;
|
||||
}
|
||||
if(banned_ips.contains(ipaddr)) {
|
||||
if(banned_ips_notified.contains(ipaddr) == false) {
|
||||
Log.info("Rejected connection by banned IP address - " + ipaddr);
|
||||
banned_ips_notified.add(ipaddr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Map<String,String> getCustomHeaders() {
|
||||
return headers;
|
||||
}
|
||||
public static void setCustomHeaders(Map<String,String> hdrs) {
|
||||
headers = hdrs;
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
package org.dynmap.web;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.Socket;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.debug.Debug;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class HttpServerConnection extends Thread {
|
||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||
|
||||
private static Pattern requestHeaderLine = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+HTTP/(.+)$");
|
||||
private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$");
|
||||
|
||||
private Socket socket;
|
||||
private HttpServer server;
|
||||
private boolean do_shutdown;
|
||||
private boolean can_keepalive;
|
||||
|
||||
private PrintStream printOut;
|
||||
private StringWriter sw = new StringWriter();
|
||||
private Matcher requestHeaderLineMatcher;
|
||||
private Matcher requestHeaderFieldMatcher;
|
||||
|
||||
public HttpServerConnection(Socket socket, HttpServer server) {
|
||||
this.socket = socket;
|
||||
this.server = server;
|
||||
do_shutdown = false;
|
||||
can_keepalive = false;
|
||||
}
|
||||
|
||||
private final static void readLine(InputStream in, StringWriter sw) throws IOException {
|
||||
int readc;
|
||||
while((readc = in.read()) > 0) {
|
||||
char c = (char)readc;
|
||||
if (c == '\n')
|
||||
break;
|
||||
else if (c != '\r')
|
||||
sw.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
private final String readLine(InputStream in) throws IOException {
|
||||
readLine(in, sw);
|
||||
String r = sw.toString();
|
||||
sw.getBuffer().setLength(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
private final boolean readRequestHeader(InputStream in, HttpRequest request) throws IOException {
|
||||
String statusLine = readLine(in);
|
||||
|
||||
if (statusLine == null)
|
||||
return false;
|
||||
|
||||
if (requestHeaderLineMatcher == null) {
|
||||
requestHeaderLineMatcher = requestHeaderLine.matcher(statusLine);
|
||||
} else {
|
||||
requestHeaderLineMatcher.reset(statusLine);
|
||||
}
|
||||
|
||||
Matcher m = requestHeaderLineMatcher;
|
||||
if (!m.matches())
|
||||
return false;
|
||||
request.method = m.group(1);
|
||||
request.path = m.group(2);
|
||||
request.version = m.group(3);
|
||||
|
||||
String line;
|
||||
while (!(line = readLine(in)).equals("")) {
|
||||
if (requestHeaderFieldMatcher == null) {
|
||||
requestHeaderFieldMatcher = requestHeaderField.matcher(line);
|
||||
} else {
|
||||
requestHeaderFieldMatcher.reset(line);
|
||||
}
|
||||
|
||||
m = requestHeaderFieldMatcher;
|
||||
// Warning: unknown lines are ignored.
|
||||
if (m.matches()) {
|
||||
String fieldName = m.group(1);
|
||||
String fieldValue = m.group(2);
|
||||
// TODO: Does not support duplicate field-names.
|
||||
request.fields.put(fieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final void writeResponseHeader(PrintStream out, HttpResponse response) throws IOException {
|
||||
out.append("HTTP/");
|
||||
out.append(response.version);
|
||||
out.append(" ");
|
||||
out.append(String.valueOf(response.status.getCode()));
|
||||
out.append(" ");
|
||||
out.append(response.status.getText());
|
||||
out.append("\r\n");
|
||||
for (Entry<String, String> field : response.fields.entrySet()) {
|
||||
out.append(field.getKey());
|
||||
out.append(": ");
|
||||
out.append(field.getValue());
|
||||
out.append("\r\n");
|
||||
}
|
||||
for(Entry<String, String> custom : HttpServer.getCustomHeaders().entrySet()) {
|
||||
out.append(custom.getKey());
|
||||
out.append(": ");
|
||||
out.append(custom.getValue());
|
||||
out.append("\r\n");
|
||||
}
|
||||
out.append("\r\n");
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public final void writeResponseHeader(HttpResponse response) throws IOException {
|
||||
writeResponseHeader(printOut, response);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (socket == null)
|
||||
return;
|
||||
socket.setSoTimeout(5000);
|
||||
socket.setTcpNoDelay(true);
|
||||
InetSocketAddress rmtaddr = (InetSocketAddress)socket.getRemoteSocketAddress(); /* Get remote address */
|
||||
InputStream in = socket.getInputStream();
|
||||
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream(), 40960);
|
||||
|
||||
printOut = new PrintStream(out, false);
|
||||
while (true) {
|
||||
/* Check for start of each request - kicks out persistent connections */
|
||||
if(server.checkForBannedIp(rmtaddr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.rmtaddr = rmtaddr;
|
||||
if (!readRequestHeader(in, request)) {
|
||||
return;
|
||||
}
|
||||
String fwd_for = request.fields.get("X-Forwarded-For");
|
||||
if(fwd_for != null) {
|
||||
String[] ff = fwd_for.split(",");
|
||||
for(int i = 0; i < ff.length; i++) {
|
||||
if(server.checkForBannedIp(ff[i]))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long bound = -1;
|
||||
BoundInputStream boundBody = null;
|
||||
{
|
||||
String contentLengthStr = request.fields.get(HttpField.ContentLength);
|
||||
if (contentLengthStr != null) {
|
||||
try {
|
||||
bound = Long.parseLong(contentLengthStr);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
if (bound >= 0) {
|
||||
request.body = boundBody = new BoundInputStream(in, bound);
|
||||
} else {
|
||||
request.body = in;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean iskeepalive = false;
|
||||
String keepalive = request.fields.get(HttpField.Connection);
|
||||
if((keepalive != null) && (keepalive.toLowerCase().indexOf("keep-alive") >= 0)) {
|
||||
/* See if we're clear to do keepalive */
|
||||
if(!iskeepalive)
|
||||
iskeepalive = server.canKeepAlive(this);
|
||||
}
|
||||
|
||||
// TODO: Optimize HttpHandler-finding by using a real path-aware tree.
|
||||
HttpHandler handler = null;
|
||||
String relativePath = null;
|
||||
for (Entry<String, HttpHandler> entry : server.handlers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
boolean directoryHandler = key.endsWith("/");
|
||||
if (directoryHandler && request.path.startsWith(entry.getKey()) || !directoryHandler && request.path.equals(entry.getKey())) {
|
||||
relativePath = request.path.substring(entry.getKey().length());
|
||||
relativePath = URLDecoder.decode(relativePath,"utf-8");
|
||||
handler = entry.getValue();
|
||||
break;
|
||||
}
|
||||
/* Wildcard handler for non-directory matches */
|
||||
else if(key.endsWith("*") && request.path.startsWith(key.substring(0, key.length()-1))) { relativePath = request.path.substring(entry.getKey().length());
|
||||
relativePath = request.path.substring(entry.getKey().length()-1);
|
||||
relativePath = URLDecoder.decode(relativePath,"utf-8");
|
||||
handler = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpResponse response = new HttpResponse(this, out);
|
||||
|
||||
if(iskeepalive) {
|
||||
response.fields.put(HttpField.Connection, "keep-alive");
|
||||
response.fields.put("Keep-Alive", "timeout=5");
|
||||
}
|
||||
else {
|
||||
response.fields.put(HttpField.Connection, "close");
|
||||
}
|
||||
try {
|
||||
handler.handle(relativePath, request, response);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Log.severe("HttpHandler '" + handler + "' has thown an exception", e);
|
||||
out.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (bound > 0 && boundBody.skip(bound) < bound) {
|
||||
Debug.debug("Incoming stream was only read partially by handler '" + handler + "'.");
|
||||
//socket.close();
|
||||
//return;
|
||||
}
|
||||
|
||||
boolean isKeepalive = iskeepalive && !"close".equals(request.fields.get(HttpField.Connection)) && !"close".equals(response.fields.get(HttpField.Connection));
|
||||
String contentLength = response.fields.get("Content-Length");
|
||||
if (isKeepalive && contentLength == null) {
|
||||
// A handler has been a bad boy, but we're here to fix it.
|
||||
response.fields.put("Content-Length", "0");
|
||||
OutputStream responseBody = response.getBody();
|
||||
|
||||
// The HttpHandler has already send the headers and written to the body without setting the Content-Length.
|
||||
if (responseBody == null) {
|
||||
Debug.debug("Response was given without Content-Length by '" + handler + "' for path '" + request.path + "'.");
|
||||
out.flush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out.flush();
|
||||
|
||||
if (!isKeepalive) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
} catch (Exception e) {
|
||||
if(!do_shutdown) {
|
||||
Log.severe("Exception while handling request: ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
server.connectionEnded(this);
|
||||
}
|
||||
}
|
||||
public void shutdownConnection() {
|
||||
try {
|
||||
do_shutdown = true;
|
||||
if(socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
join(); /* Wait for thread to die */
|
||||
} catch (IOException iox) {
|
||||
} catch (InterruptedException ix) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.dynmap.web.handlers;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.util.Date;
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.Event;
|
||||
import org.dynmap.web.HttpHandler;
|
||||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class ClientConfigurationHandler implements HttpHandler {
|
||||
private DynmapPlugin plugin;
|
||||
private byte[] cachedConfiguration = null;
|
||||
public ClientConfigurationHandler(DynmapPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.events.addListener("worldactivated", new Event.Listener<DynmapWorld>() {
|
||||
@Override
|
||||
public void triggered(DynmapWorld t) {
|
||||
cachedConfiguration = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||
if (cachedConfiguration == null) {
|
||||
JSONObject configurationObject = new JSONObject();
|
||||
plugin.events.<JSONObject>trigger("buildclientconfiguration", configurationObject);
|
||||
|
||||
String s = configurationObject.toJSONString();
|
||||
|
||||
cachedConfiguration = s.getBytes("UTF-8");
|
||||
}
|
||||
String dateStr = new Date().toString();
|
||||
|
||||
response.fields.put("Date", dateStr);
|
||||
response.fields.put("Content-Type", "text/plain; charset=utf-8");
|
||||
response.fields.put("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
|
||||
response.fields.put("Last-modified", dateStr);
|
||||
response.fields.put("Content-Length", Integer.toString(cachedConfiguration.length));
|
||||
response.status = HttpStatus.OK;
|
||||
|
||||
BufferedOutputStream out = null;
|
||||
out = new BufferedOutputStream(response.getBody());
|
||||
out.write(cachedConfiguration);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.dynmap.web.handlers;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.dynmap.ClientUpdateEvent;
|
||||
import org.dynmap.DynmapPlugin;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.web.HttpField;
|
||||
import org.dynmap.web.HttpHandler;
|
||||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
import org.json.simple.JSONObject;
|
||||
import static org.dynmap.JSONUtils.*;
|
||||
|
||||
public class ClientUpdateHandler implements HttpHandler {
|
||||
private DynmapPlugin plugin;
|
||||
|
||||
public ClientUpdateHandler(DynmapPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
Pattern updatePathPattern = Pattern.compile("world/([^/]+)/([0-9]*)");
|
||||
private static final HttpStatus WorldNotFound = new HttpStatus(HttpStatus.NotFound.getCode(), "World Not Found");
|
||||
@Override
|
||||
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||
|
||||
Matcher match = updatePathPattern.matcher(path);
|
||||
|
||||
if (!match.matches()) {
|
||||
response.status = HttpStatus.Forbidden;
|
||||
return;
|
||||
}
|
||||
|
||||
String worldName = match.group(1);
|
||||
String timeKey = match.group(2);
|
||||
|
||||
DynmapWorld dynmapWorld = null;
|
||||
if(plugin.mapManager != null) {
|
||||
dynmapWorld = plugin.mapManager.getWorld(worldName);
|
||||
}
|
||||
if (dynmapWorld == null || dynmapWorld.world == null) {
|
||||
response.status = WorldNotFound;
|
||||
return;
|
||||
}
|
||||
long current = System.currentTimeMillis();
|
||||
long since = 0;
|
||||
|
||||
if (path.length() > 0) {
|
||||
try {
|
||||
since = Long.parseLong(timeKey);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject u = new JSONObject();
|
||||
s(u, "timestamp", current);
|
||||
plugin.events.trigger("buildclientupdate", new ClientUpdateEvent(since, dynmapWorld, u));
|
||||
|
||||
byte[] bytes = u.toJSONString().getBytes("UTF-8");
|
||||
|
||||
String dateStr = new Date().toString();
|
||||
response.fields.put(HttpField.Date, dateStr);
|
||||
response.fields.put(HttpField.ContentType, "text/plain; charset=utf-8");
|
||||
response.fields.put(HttpField.Expires, "Thu, 01 Dec 1994 16:00:00 GMT");
|
||||
response.fields.put(HttpField.LastModified, dateStr);
|
||||
response.fields.put(HttpField.ContentLength, Integer.toString(bytes.length));
|
||||
response.status = HttpStatus.OK;
|
||||
|
||||
BufferedOutputStream out = null;
|
||||
out = new BufferedOutputStream(response.getBody());
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.dynmap.web.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.dynmap.web.HttpField;
|
||||
import org.dynmap.web.HttpHandler;
|
||||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
|
||||
public abstract class FileHandler implements HttpHandler {
|
||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||
|
||||
private LinkedList<byte[]> bufferpool = new LinkedList<byte[]>();
|
||||
private Object lock = new Object();
|
||||
private static final int MAX_FREE_IN_POOL = 2;
|
||||
|
||||
private static Map<String, String> mimes = new HashMap<String, String>();
|
||||
static {
|
||||
mimes.put(".html", "text/html");
|
||||
mimes.put(".htm", "text/html");
|
||||
mimes.put(".js", "text/javascript");
|
||||
mimes.put(".png", "image/png");
|
||||
mimes.put(".jpg", "image/jpeg");
|
||||
mimes.put(".css", "text/css");
|
||||
mimes.put(".txt", "text/plain");
|
||||
}
|
||||
|
||||
public static final String getMimeTypeFromExtension(String extension) {
|
||||
String m = mimes.get(extension);
|
||||
if (m != null)
|
||||
return m;
|
||||
return "application/octet-steam";
|
||||
}
|
||||
|
||||
protected abstract InputStream getFileInput(String path, HttpRequest request, HttpResponse response);
|
||||
|
||||
protected void closeFileInput(String path, InputStream in) throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
protected String getExtension(String path) {
|
||||
int dotindex = path.lastIndexOf('.');
|
||||
if (dotindex > 0)
|
||||
return path.substring(dotindex);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final String formatPath(String path) {
|
||||
int qmark = path.indexOf('?');
|
||||
if (qmark >= 0)
|
||||
path = path.substring(0, qmark);
|
||||
|
||||
if (path.startsWith("/") || path.startsWith("."))
|
||||
return null;
|
||||
if (path.length() == 0)
|
||||
path = getDefaultFilename(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
protected String getDefaultFilename(String path) {
|
||||
return path + "index.html";
|
||||
}
|
||||
|
||||
private byte[] allocateReadBuffer() {
|
||||
byte[] buf;
|
||||
synchronized(lock) {
|
||||
buf = bufferpool.poll();
|
||||
}
|
||||
if(buf == null) {
|
||||
buf = new byte[40960];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void freeReadBuffer(byte[] buf) {
|
||||
synchronized(lock) {
|
||||
if(bufferpool.size() < MAX_FREE_IN_POOL)
|
||||
bufferpool.push(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||
InputStream fileInput = null;
|
||||
try {
|
||||
path = formatPath(path);
|
||||
fileInput = getFileInput(path, request, response);
|
||||
if (fileInput == null) {
|
||||
response.status = HttpStatus.NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
String extension = getExtension(path);
|
||||
String mimeType = getMimeTypeFromExtension(extension);
|
||||
|
||||
response.fields.put(HttpField.ContentType, mimeType);
|
||||
response.status = HttpStatus.OK;
|
||||
OutputStream out = response.getBody();
|
||||
byte[] readBuffer = allocateReadBuffer();
|
||||
try {
|
||||
int readBytes;
|
||||
while ((readBytes = fileInput.read(readBuffer)) > 0) {
|
||||
out.write(readBuffer, 0, readBytes);
|
||||
}
|
||||
} finally {
|
||||
freeReadBuffer(readBuffer);
|
||||
}
|
||||
} finally {
|
||||
if (fileInput != null) {
|
||||
try { closeFileInput(path, fileInput); fileInput = null; } catch (IOException ex) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user