Add dynmap-core to common build

This commit is contained in:
Mike Primm 2018-08-12 00:27:15 -05:00
parent f66e63fe45
commit 805a7aa799
4388 changed files with 108713 additions and 3 deletions

View File

@ -4,8 +4,8 @@ description = 'dynmap'
dependencies {
compile group: 'org.bukkit', name: 'bukkit', version:'1.7.10-R0.1-SNAPSHOT'
compile 'com.nijikokun.bukkit:Permissions:3.1.6'
compile project(":dynmap-api")
compile "us.dynmap:DynmapCore:${project.version}"
compile project(path: ":dynmap-api", configuration: "shadow")
compile project(path: ":dynmap-core", configuration: "shadow")
compile group: 'ru.tehkode', name: 'PermissionsEx', version:'1.19.1'
compile group: 'de.bananaco', name: 'bPermissions', version:'2.9.1'
compile group: 'com.platymuus.bukkit.permissions', name: 'PermissionsBukkit', version:'1.6'
@ -39,7 +39,7 @@ shadowJar {
dependencies {
include(dependency('org.bstats::'))
include(dependency(':dynmap-api'))
include(dependency('us.dynmap:DynmapCore:'))
include(dependency(":dynmap-core"))
include(dependency(':bukkit-helper'))
include(dependency(':bukkit-helper-113'))
}

1
dynmap-core/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

52
dynmap-core/build.gradle Normal file
View File

@ -0,0 +1,52 @@
description = "DynmapCore"
dependencies {
compile "us.dynmap:DynmapCoreAPI:${project.version}"
compile 'org.eclipse.jetty:jetty-server:8.1.21.v20160908'
compile 'org.eclipse.jetty:jetty-servlet:8.1.21.v20160908'
compile 'com.googlecode.json-simple:json-simple:1.1.1'
compile 'org.yaml:snakeyaml:1.9'
compile 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20180219.1'
}
processResources {
// replace stuff in mcmod.info, nothing else
from('src/main/resources') {
include 'core.yml'
include 'lightings.txt'
include 'perspectives.txt'
include 'extracted/web/version.js'
include 'extracted/web/index.html'
include 'extracted/web/login.html'
// replace version and mcversion
expand(
buildnumber: project.parent.ext.globals.buildNumber,
version: project.version
)
}
}
jar {
classifier = 'unshaded'
}
shadowJar {
dependencies {
include(dependency('com.googlecode.json-simple:json-simple:'))
include(dependency('org.yaml:snakeyaml:'))
include(dependency('com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:'))
include(dependency('org.eclipse.jetty::'))
include(dependency('org.eclipse.jetty.orbit:javax.servlet:'))
}
relocate('org.json.simple', 'org.dynmap.json.simple')
relocate('org.yaml.snakeyaml', 'org.dynmap.snakeyaml')
relocate('org.eclipse.jetty', 'org.dynmap.jetty')
relocate('org.owasp.html', 'org.dynmap.org.owasp.html')
relocate('javax.servlet', 'org.dynmap.javax.servlet' )
destinationDir = file '../target'
classifier = ''
}
artifacts {
archives shadowJar
}

1
dynmap-core/src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

View File

@ -0,0 +1,161 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
public class AsynchronousQueue<T> {
private Object lock = new Object();
private Thread thread;
private LinkedBlockingQueue<T> queue = new LinkedBlockingQueue<T>();
private Set<T> set = new HashSet<T>();
private Handler<T> handler;
private int dequeueTime;
private int accelDequeueTime;
public int accelDequeueThresh;
private int pendingcnt;
private int pendinglimit;
private boolean normalprio;
public AsynchronousQueue(Handler<T> handler, int dequeueTime, int accelDequeueThresh, int accelDequeueTime, int pendinglimit, boolean normalprio) {
this.handler = handler;
this.dequeueTime = dequeueTime;
this.accelDequeueTime = accelDequeueTime;
this.accelDequeueThresh = accelDequeueThresh;
if(pendinglimit < 1) pendinglimit = 1;
this.pendinglimit = pendinglimit;
this.normalprio = normalprio;
}
public boolean push(T t) {
synchronized (lock) {
if (!set.add(t)) {
return false;
}
}
queue.offer(t);
return true;
}
private T pop() {
try {
T t = queue.take();
synchronized (lock) {
set.remove(t);
}
return t;
} catch (InterruptedException ix) {
return null;
}
}
public boolean remove(T t) {
synchronized (lock) {
if (set.remove(t)) {
queue.remove(t);
return true;
}
}
return false;
}
public int size() {
return set.size();
}
public List<T> popAll() {
List<T> s;
synchronized(lock) {
s = new ArrayList<T>(queue);
queue.clear();
set.clear();
}
return s;
}
public void start() {
synchronized (lock) {
thread = new Thread(new Runnable() {
@Override
public void run() {
running();
}
});
thread.start();
try {
if(!normalprio)
thread.setPriority(Thread.MIN_PRIORITY);
} catch (SecurityException e) {
Log.info("Failed to set minimum priority for worker thread!");
}
}
}
public void stop() {
synchronized (lock) {
if (thread == null)
return;
Thread oldThread = thread;
thread = null;
Log.info("Stopping map renderer...");
oldThread.interrupt();
try {
oldThread.join(1000);
} catch (InterruptedException e) {
Log.info("Waiting for map renderer to stop is interrupted");
}
}
}
private void running() {
try {
while (Thread.currentThread() == thread) {
synchronized(lock) {
while(pendingcnt >= pendinglimit) {
try {
lock.wait(accelDequeueTime);
} catch (InterruptedException ix) {
if(Thread.currentThread() != thread)
return;
throw ix;
}
}
}
T t = pop();
if (t != null) {
synchronized(lock) {
pendingcnt++;
}
handler.handle(t);
}
if(set.size() >= accelDequeueThresh)
sleep(accelDequeueTime);
else
sleep(dequeueTime);
}
} catch (Exception ex) {
Log.severe("Exception on rendering-thread", ex);
}
}
private boolean sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
return false;
}
return true;
}
public void done(T t) {
synchronized (lock) {
if(pendingcnt > 0) pendingcnt--;
lock.notifyAll();
}
}
}

View File

@ -0,0 +1,12 @@
package org.dynmap;
public class ChatEvent {
public String source;
public String name;
public String message;
public ChatEvent(String source, String name, String message) {
this.source = source;
this.name = name;
this.message = message;
}
}

View File

@ -0,0 +1,275 @@
package org.dynmap;
import java.io.IOException;
import java.io.Writer;
import java.util.Random;
import org.json.simple.JSONAware;
import org.json.simple.JSONStreamAware;
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
import org.dynmap.common.DynmapChatColor;
public class Client {
public static class Update implements JSONAware, JSONStreamAware {
public long timestamp = System.currentTimeMillis();
@Override
public String toJSONString() {
return org.dynmap.web.Json.stringifyJson(this);
}
@Override
public void writeJSONString(Writer w) throws IOException {
w.write(toJSONString());
}
}
public static class ChatMessage extends Update {
public String type = "chat";
public String source;
public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones)
public String message;
public String account;
public String channel;
public ChatMessage(String source, String channel, String playerName, String message, String playeraccount) {
this.source = source;
if (ClientUpdateComponent.hideNames)
this.playerName = "";
else if (ClientUpdateComponent.usePlayerColors)
this.playerName = Client.encodeColorInHTML(playerName);
else
this.playerName = Client.stripColor(playerName);
this.message = DynmapChatColor.stripColor(message);
this.account = playeraccount;
this.channel = channel;
}
@Override
public boolean equals(Object o) {
if(o instanceof ChatMessage) {
ChatMessage m = (ChatMessage)o;
return m.source.equals(source) && m.playerName.equals(playerName) && m.message.equals(message);
}
return false;
}
@Override
public int hashCode() {
return source.hashCode() ^ playerName.hashCode() ^ message.hashCode();
}
}
public static class PlayerJoinMessage extends Update {
public String type = "playerjoin";
public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones)
public String account;
public PlayerJoinMessage(String playerName, String playeraccount) {
if (ClientUpdateComponent.hideNames)
this.playerName = "";
else if (ClientUpdateComponent.usePlayerColors)
this.playerName = Client.encodeColorInHTML(playerName);
else
this.playerName = Client.stripColor(playerName);
this.account = playeraccount;
}
@Override
public boolean equals(Object o) {
if(o instanceof PlayerJoinMessage) {
PlayerJoinMessage m = (PlayerJoinMessage)o;
return m.playerName.equals(playerName);
}
return false;
}
@Override
public int hashCode() {
return account.hashCode();
}
}
public static class PlayerQuitMessage extends Update {
public String type = "playerquit";
public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones)
public String account;
public PlayerQuitMessage(String playerName, String playeraccount) {
if (ClientUpdateComponent.hideNames)
this.playerName = "";
else if (ClientUpdateComponent.usePlayerColors)
this.playerName = Client.encodeColorInHTML(playerName);
else
this.playerName = Client.stripColor(playerName);
this.account = playeraccount;
}
@Override
public boolean equals(Object o) {
if(o instanceof PlayerQuitMessage) {
PlayerQuitMessage m = (PlayerQuitMessage)o;
return m.playerName.equals(playerName);
}
return false;
}
@Override
public int hashCode() {
return account.hashCode();
}
}
public static class Tile extends Update {
public String type = "tile";
public String name;
public Tile(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if(o instanceof Tile) {
Tile m = (Tile)o;
return m.name.equals(name);
}
return false;
}
@Override
public int hashCode() {
return name.hashCode();
}
}
public static class DayNight extends Update {
public String type = "daynight";
public boolean isday;
public DayNight(boolean isday) {
this.isday = isday;
}
@Override
public boolean equals(Object o) {
if(o instanceof DayNight) {
return true;
}
return false;
}
@Override
public int hashCode() {
return 12345;
}
}
public static class ComponentMessage extends Update {
public String type = "component";
/* Each subclass must provide 'ctype' string for component 'type' */
}
// Strip color - assume we're returning safe html text
public static String stripColor(String s) {
s = DynmapChatColor.stripColor(s); /* Strip standard color encoding */
/* Handle Essentials nickname encoding too */
int idx = 0;
while((idx = s.indexOf('&', idx)) >= 0) {
char c = s.charAt(idx+1); /* Get next character */
if(c == '&') { /* Another ampersand */
s = s.substring(0, idx) + s.substring(idx+1);
}
else {
s = s.substring(0, idx) + s.substring(idx+2);
}
idx++;
}
// Apply sanitize policy before returning
return sanitizeHTML(s);
}
private static String[][] codes = {
{ "0", "<span style=\'color:#000000\'>" },
{ "1", "<span style=\'color:#0000AA\'>" },
{ "2", "<span style=\'color:#00AA00\'>" },
{ "3", "<span style=\'color:#00AAAA\'>" },
{ "4", "<span style=\'color:#AA0000\'>" },
{ "5", "<span style=\'color:#AA00AA\'>" },
{ "6", "<span style=\'color:#FFAA00\'>" },
{ "7", "<span style=\'color:#AAAAAA\'>" },
{ "8", "<span style=\'color:#555555\'>" },
{ "9", "<span style=\'color:#5555FF\'>" },
{ "a", "<span style=\'color:#55FF55\'>" },
{ "b", "<span style=\'color:#55FFFF\'>" },
{ "c", "<span style=\'color:#FF5555\'>" },
{ "d", "<span style=\'color:#FF55FF\'>" },
{ "e", "<span style=\'color:#FFFF55\'>" },
{ "f", "<span style=\'color:#FFFFFF\'>" },
{ "l", "<span style=\'font-weight:bold\'>" },
{ "m", "<span style=\'text-decoration:line-through\'>" },
{ "n", "<span style=\'text-decoration:underline\'>" },
{ "o", "<span style=\'font-style:italic\'>" },
{ "r", "<span style=\'font-style:normal,text-decoration:none,font-weight:normal\'>" }
};
private static Random rnd = new Random();
private static String rndchars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Replace color codes with corresponding <span - assume we're returning safe HTML text
public static String encodeColorInHTML(String s) {
StringBuilder sb = new StringBuilder();
int cnt = s.length();
int spancnt = 0;
boolean magic = false;
for (int i = 0; i < cnt; i++) {
char c = s.charAt(i);
if (c == '\u00A7') { // Escape?
i++; // Move past it
c = s.charAt(i);
if (c == 'k') { // Magic text?
magic = true;
}
else if (c == 'r') { // reset
magic = false;
}
for (int j = 0; j < codes.length; j++) {
if (codes[j][0].charAt(0) == c) { // Matching code?
sb.append(codes[j][1]); // Substitute
spancnt++;
break;
}
}
}
else if (c == '&') { // Essentials color code?
i++; // Move past it
c = s.charAt(i);
if (c == '&') { // Amp?
sb.append(c);
}
else {
if (c == 'k') { // Magic text?
magic = true;
}
else if (c == 'r') { // reset
magic = false;
}
for (int j = 0; j < codes.length; j++) {
if (codes[j][0].charAt(0) == c) { // Matching code?
sb.append(codes[j][1]); // Substitute
spancnt++;
break;
}
}
}
}
else if (magic) {
sb.append(rndchars.charAt(rnd.nextInt(rndchars.length())));
}
else {
sb.append(c);
}
}
for (int i = 0; i < spancnt; i++) {
sb.append("</span>");
}
return sanitizeHTML(sb.toString());
}
private static PolicyFactory sanitizer = null;
public static String sanitizeHTML(String html) {
PolicyFactory s = sanitizer;
if (s == null) {
// Generous but safe html formatting allowances
s = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(Sanitizers.IMAGES).and(Sanitizers.LINKS).and(Sanitizers.STYLES);
sanitizer = s;
}
return sanitizer.sanitize(html);
}
}

View File

@ -0,0 +1,67 @@
package org.dynmap;
import static org.dynmap.JSONUtils.a;
import static org.dynmap.JSONUtils.s;
import java.util.List;
import java.util.Map;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class ClientComponent extends Component {
private boolean disabled;
public ClientComponent(final DynmapCore plugin, final ConfigurationNode configuration) {
super(plugin, configuration);
plugin.events.addListener("buildclientconfiguration", new Event.Listener<JSONObject>() {
@Override
public void triggered(JSONObject root) {
if(!disabled)
buildClientConfiguration(root);
}
});
}
protected void disableComponent() {
disabled = true;
}
protected void buildClientConfiguration(JSONObject root) {
JSONObject o = createClientConfiguration();
a(root, "components", o);
}
protected JSONObject createClientConfiguration() {
JSONObject o = convertMap(configuration);
o.remove("class");
return o;
}
protected static final JSONObject convertMap(Map<String, ?> m) {
JSONObject o = new JSONObject();
for(Map.Entry<String, ?> entry : m.entrySet()) {
s(o, entry.getKey(), convert(entry.getValue()));
}
return o;
}
@SuppressWarnings("unchecked")
protected static final JSONArray convertList(List<?> l) {
JSONArray o = new JSONArray();
for(Object entry : l) {
o.add(convert(entry));
}
return o;
}
@SuppressWarnings("unchecked")
protected static final Object convert(Object o) {
if (o instanceof Map<?, ?>) {
return convertMap((Map<String, ?>)o);
} else if (o instanceof List<?>) {
return convertList((List<?>)o);
}
return o;
}
}

View File

@ -0,0 +1,76 @@
package org.dynmap;
import static org.dynmap.JSONUtils.a;
import static org.dynmap.JSONUtils.s;
import org.dynmap.Event.Listener;
import org.json.simple.JSONObject;
public class ClientConfigurationComponent extends Component {
public ClientConfigurationComponent(final DynmapCore core, ConfigurationNode configuration) {
super(core, configuration);
core.events.<JSONObject>addListener("buildclientconfiguration", new Listener<JSONObject>() {
@Override
public void triggered(JSONObject t) {
ConfigurationNode c = core.configuration;
s(t, "confighash", core.getConfigHashcode());
s(t, "updaterate", c.getFloat("updaterate", 1.0f));
s(t, "showplayerfacesinmenu", c.getBoolean("showplayerfacesinmenu", true));
s(t, "joinmessage", c.getString("joinmessage", "%playername% joined"));
s(t, "quitmessage", c.getString("quitmessage", "%playername% quit"));
s(t, "spammessage", c.getString("spammessage", "You may only chat once every %interval% seconds."));
s(t, "webprefix", unescapeString(c.getString("webprefix", "[WEB] ")));
s(t, "defaultzoom", c.getInteger("defaultzoom", 0));
s(t, "sidebaropened", c.getString("sidebaropened", "false"));
s(t, "dynmapversion", core.getDynmapPluginVersion());
s(t, "coreversion", core.getDynmapCoreVersion());
s(t, "cyrillic", c.getBoolean("cyrillic-support", false));
s(t, "showlayercontrol", c.getString("showlayercontrol", "true"));
s(t, "grayplayerswhenhidden", c.getBoolean("grayplayerswhenhidden", true));
s(t, "login-enabled", core.isLoginSupportEnabled());
String sn = core.getServer().getServerName();
if(sn.equals("Unknown Server"))
sn = "Minecraft Dynamic Map";
s(t, "title", c.getString("webpage-title", sn));
s(t, "msg-maptypes", c.getString("msg/maptypes", "Map Types"));
s(t, "msg-players", c.getString("msg/players", "Players"));
s(t, "msg-chatrequireslogin", c.getString("msg/chatrequireslogin", "Chat Requires Login"));
s(t, "msg-chatnotallowed", c.getString("msg/chatnotallowed", "You are not permitted to send chat messages"));
s(t, "msg-hiddennamejoin", c.getString("msg/hiddennamejoin", "Player joined"));
s(t, "msg-hiddennamequit", c.getString("msg/hiddennamequit", "Player quit"));
s(t, "maxcount", core.getMaxPlayers());
DynmapWorld defaultWorld = null;
String defmap = null;
a(t, "worlds", null);
for(DynmapWorld world : core.mapManager.getWorlds()) {
if (world.maps.size() == 0) continue;
if (defaultWorld == null) defaultWorld = world;
JSONObject wo = new JSONObject();
s(wo, "name", world.getName());
s(wo, "title", world.getTitle());
s(wo, "protected", world.isProtected());
DynmapLocation center = world.getCenterLocation();
s(wo, "center/x", center.x);
s(wo, "center/y", center.y);
s(wo, "center/z", center.z);
s(wo, "extrazoomout", world.getExtraZoomOutLevels());
s(wo, "sealevel", world.sealevel);
s(wo, "worldheight", world.worldheight);
a(t, "worlds", wo);
for(MapType mt : world.maps) {
mt.buildClientConfiguration(wo, world);
if(defmap == null) defmap = mt.getName();
}
}
s(t, "defaultworld", c.getString("defaultworld", defaultWorld == null ? "world" : defaultWorld.getName()));
s(t, "defaultmap", c.getString("defaultmap", defmap == null ? "surface" : defmap));
if(c.getString("followmap", null) != null)
s(t, "followmap", c.getString("followmap"));
if(c.getInteger("followzoom",-1) >= 0)
s(t, "followzoom", c.getInteger("followzoom", 0));
}
});
}
}

View File

@ -0,0 +1,176 @@
package org.dynmap;
import static org.dynmap.JSONUtils.a;
import static org.dynmap.JSONUtils.s;
import java.util.List;
import org.dynmap.common.DynmapPlayer;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class ClientUpdateComponent extends Component {
private int hideifshadow;
private int hideifunder;
private boolean hideifsneaking;
private boolean hideifinvisiblepotion;
private boolean is_protected;
public static boolean usePlayerColors;
public static boolean hideNames;
public ClientUpdateComponent(final DynmapCore core, ConfigurationNode configuration) {
super(core, configuration);
hideNames = configuration.getBoolean("hidenames", false);
hideifshadow = configuration.getInteger("hideifshadow", 15);
hideifunder = configuration.getInteger("hideifundercover", 15);
hideifsneaking = configuration.getBoolean("hideifsneaking", false);
hideifinvisiblepotion = configuration.getBoolean("hide-if-invisiblity-potion", true);
is_protected = configuration.getBoolean("protected-player-info", false);
usePlayerColors = configuration.getBoolean("use-name-colors", false);
if(is_protected)
core.player_info_protected = true;
core.events.addListener("buildclientupdate", new Event.Listener<ClientUpdateEvent>() {
@Override
public void triggered(ClientUpdateEvent e) {
buildClientUpdate(e);
}
});
}
protected void buildClientUpdate(ClientUpdateEvent e) {
DynmapWorld world = e.world;
JSONObject u = e.update;
long since = e.timestamp;
String worldName = world.getName();
boolean see_all = true;
if(is_protected && (!e.include_all_users)) {
if(e.user != null)
see_all = core.getServer().checkPlayerPermission(e.user, "playermarkers.seeall");
else
see_all = false;
}
if((e.include_all_users) && is_protected) { /* If JSON request AND protected, leave mark for script */
s(u, "protected", true);
}
s(u, "confighash", core.getConfigHashcode());
s(u, "servertime", world.getTime() % 24000);
s(u, "hasStorm", world.hasStorm());
s(u, "isThundering", world.isThundering());
s(u, "players", new JSONArray());
List<DynmapPlayer> players = core.playerList.getVisiblePlayers();
for(DynmapPlayer p : players) {
boolean hide = false;
DynmapLocation pl = p.getLocation();
DynmapWorld pw = core.getWorld(pl.world);
if(pw == null) {
hide = true;
}
JSONObject jp = new JSONObject();
s(jp, "type", "player");
if (hideNames)
s(jp, "name", "");
else if (usePlayerColors)
s(jp, "name", Client.encodeColorInHTML(p.getDisplayName()));
else
s(jp, "name", Client.stripColor(p.getDisplayName()));
s(jp, "account", p.getName());
if((!hide) && (hideifshadow < 15)) {
if(pw.getLightLevel((int)pl.x, (int)pl.y, (int)pl.z) <= hideifshadow) {
hide = true;
}
}
if((!hide) && (hideifunder < 15)) {
if(pw.canGetSkyLightLevel()) { /* If we can get real sky level */
if(pw.getSkyLightLevel((int)pl.x, (int)pl.y, (int)pl.z) <= hideifunder) {
hide = true;
}
}
else if(pw.isNether() == false) { /* Not nether */
if(pw.getHighestBlockYAt((int)pl.x, (int)pl.z) > pl.y) {
hide = true;
}
}
}
if((!hide) && hideifsneaking && p.isSneaking()) {
hide = true;
}
if((!hide) && is_protected && (!see_all)) {
if(e.user != null) {
hide = !core.testIfPlayerVisibleToPlayer(e.user, p.getName());
}
else {
hide = true;
}
}
if((!hide) && hideifinvisiblepotion && p.isInvisible()) {
hide = true;
}
/* Don't leak player location for world not visible on maps, or if sendposition disbaled */
DynmapWorld pworld = MapManager.mapman.worldsLookup.get(pl.world);
/* Fix typo on 'sendpositon' to 'sendposition', keep bad one in case someone used it */
if(configuration.getBoolean("sendposition", true) && configuration.getBoolean("sendpositon", true) &&
(pworld != null) && pworld.sendposition && (!hide)) {
s(jp, "world", pl.world);
s(jp, "x", pl.x);
s(jp, "y", pl.y);
s(jp, "z", pl.z);
}
else {
s(jp, "world", "-some-other-bogus-world-");
s(jp, "x", 0.0);
s(jp, "y", 64.0);
s(jp, "z", 0.0);
}
/* Only send health if enabled AND we're on visible world */
if (configuration.getBoolean("sendhealth", false) && (pworld != null) && pworld.sendhealth && (!hide)) {
s(jp, "health", p.getHealth());
s(jp, "armor", p.getArmorPoints());
}
else {
s(jp, "health", 0);
s(jp, "armor", 0);
}
s(jp, "sort", p.getSortWeight());
a(u, "players", jp);
}
List<DynmapPlayer> hidden = core.playerList.getHiddenPlayers();
if(configuration.getBoolean("includehiddenplayers", false)) {
for(DynmapPlayer p : hidden) {
JSONObject jp = new JSONObject();
s(jp, "type", "player");
if (hideNames)
s(jp, "name", "");
else if (usePlayerColors)
s(jp, "name", Client.encodeColorInHTML(p.getDisplayName()));
else
s(jp, "name", Client.stripColor(p.getDisplayName()));
s(jp, "account", p.getName());
s(jp, "world", "-hidden-player-");
s(jp, "x", 0.0);
s(jp, "y", 64.0);
s(jp, "z", 0.0);
s(jp, "health", 0);
s(jp, "armor", 0);
s(jp, "sort", p.getSortWeight());
a(u, "players", jp);
}
s(u, "currentcount", core.getCurrentPlayers());
}
else {
s(u, "currentcount", core.getCurrentPlayers() - hidden.size());
}
s(u, "updates", new JSONArray());
for(Object update : core.mapManager.getWorldUpdates(worldName, since)) {
a(u, "updates", (Client.Update)update);
}
}
}

View File

@ -0,0 +1,17 @@
package org.dynmap;
import org.json.simple.JSONObject;
public class ClientUpdateEvent {
public long timestamp;
public DynmapWorld world;
public JSONObject update;
public String user;
public boolean include_all_users;
public ClientUpdateEvent(long timestamp, DynmapWorld world, JSONObject update) {
this.timestamp = timestamp;
this.world = world;
this.update = update;
}
}

View File

@ -0,0 +1,89 @@
package org.dynmap;
/**
* Simple replacement for java.awt.Color for dynmap - it's not an invariant, so we don't make millions
* of them during rendering
*/
public class Color {
/* ARGB value */
private int val;
public static final int TRANSPARENT = 0;
public Color(int red, int green, int blue, int alpha) {
setRGBA(red, green, blue, alpha);
}
public Color(int red, int green, int blue) {
setRGBA(red, green, blue, 0xFF);
}
public Color() {
setTransparent();
}
public final int getRed() {
return (val >> 16) & 0xFF;
}
public final int getGreen() {
return (val >> 8) & 0xFF;
}
public final int getBlue() {
return val & 0xFF;
}
public final int getAlpha() {
return ((val >> 24) & 0xFF);
}
public final boolean isTransparent() {
return ((val & 0xFF000000) == TRANSPARENT);
}
public final void setTransparent() {
val = TRANSPARENT;
}
public final void setColor(Color c) {
val = c.val;
}
public final void setRGBA(int red, int green, int blue, int alpha) {
val = ((alpha & 0xFF) << 24) | ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF);
}
public final int getARGB() {
return val;
}
public final void setARGB(int c) {
val = c;
}
public final int getComponent(int idx) {
return 0xFF & (val >> ((3-idx)*8));
}
public final void setAlpha(int v) {
val = (val & 0x00FFFFFF) | (v << 24);
}
/**
* Scale each color component, based on the corresponding component
* @param c - color to blend
*/
public final void blendColor(Color c) {
blendColor(c.val);
}
/**
* Scale each color component, based on the corresponding component
* @param argb - ARGB to blend
*/
public final void blendColor(int argb) {
int nval = (((((val >> 24) & 0xFF) * ((argb >> 24) & 0xFF)) / 255) << 24);
nval = nval | (((((val >> 16) & 0xFF) * ((argb >> 16) & 0xFF)) / 255) << 16);
nval = nval | (((((val >> 8) & 0xFF) * ((argb >> 8) & 0xFF)) / 255) << 8);
nval = nval | (((val & 0xFF) * (argb & 0xFF)) / 255);
val = nval;
}
/**
* Scale each color component, based on the corresponding component
* @param argb0 - first color
* @param argb1 second color
* @return blended color
*/
public static final int blendColor(int argb0, int argb1) {
int nval = (((((argb0 >> 24) & 0xFF) * ((argb1 >> 24) & 0xFF)) / 255) << 24);
nval = nval | (((((argb0 >> 16) & 0xFF) * ((argb1 >> 16) & 0xFF)) / 255) << 16);
nval = nval | (((((argb0 >> 8) & 0xFF) * ((argb1 >> 8) & 0xFF)) / 255) << 8);
nval = nval | (((argb0 & 0xFF) * (argb1 & 0xFF)) / 255);
return nval;
}
}

View File

@ -0,0 +1,286 @@
package org.dynmap;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Scanner;
import org.dynmap.common.BiomeMap;
import org.dynmap.debug.Debug;
public class ColorScheme {
private static final HashMap<String, ColorScheme> cache = new HashMap<String, ColorScheme>();
public String name;
/* Switch to arrays - faster than map */
public Color[][] colors; /* [blk-type][step] */
public Color[][][] datacolors; /* [bkt-type][blk-dat][step] */
public final Color[][] biomecolors; /* [Biome.ordinal][step] */
public final Color[][] raincolors; /* [rain * 63][step] */
public final Color[][] tempcolors; /* [temp * 63][step] */
public ColorScheme(String name, Color[][] colors, Color[][][] datacolors, Color[][] biomecolors, Color[][] raincolors, Color[][] tempcolors) {
this.name = name;
this.colors = colors;
this.datacolors = datacolors;
this.biomecolors = biomecolors;
this.raincolors = raincolors;
this.tempcolors = tempcolors;
//TODO: see if we can fix this for IDs vs names...
// for(int i = 0; i < colors.length; i++) {
// int id = MapManager.mapman.getBlockAlias(i);
// if(id != i) {
// this.colors[i] = this.colors[id];
// this.datacolors[i] = this.datacolors[id];
// }
// }
}
private static File getColorSchemeDirectory(DynmapCore core) {
return new File(core.getDataFolder(), "colorschemes");
}
public static ColorScheme getScheme(DynmapCore core, String name) {
if (name == null)
name = "default";
ColorScheme scheme = cache.get(name);
if (scheme == null) {
scheme = loadScheme(core, name);
cache.put(name, scheme);
}
return scheme;
}
public static ColorScheme loadScheme(DynmapCore core, String name) {
File colorSchemeFile = new File(getColorSchemeDirectory(core), name + ".txt");
Color[][] colors = new Color[4096][];
Color[][][] datacolors = new Color[4096][][];
Color[][] biomecolors = new Color[BiomeMap.values().length][];
Color[][] raincolors = new Color[64][];
Color[][] tempcolors = new Color[64][];
/* Default the biome color */
for(int i = 0; i < biomecolors.length; i++) {
Color[] c = new Color[5];
int red = 0x80 | (0x40 * ((i >> 0) & 1)) | (0x20 * ((i >> 3) & 1)) | (0x10 * ((i >> 6) & 1));
int green = 0x80 | (0x40 * ((i >> 1) & 1)) | (0x20 * ((i >> 4) & 1)) | (0x10 * ((i >> 7) & 1));
int blue = 0x80 | (0x40 * ((i >> 2) & 1)) | (0x20 * ((i >> 5) & 1));
c[0] = new Color(red, green, blue);
c[3] = new Color(red*4/5, green*4/5, blue*4/5);
c[1] = new Color(red/2, green/2, blue/2);
c[2] = new Color(red*2/5, green*2/5, blue*2/5);
c[4] = new Color((c[1].getRed()+c[3].getRed())/2, (c[1].getGreen()+c[3].getGreen())/2, (c[1].getBlue()+c[3].getBlue())/2, (c[1].getAlpha()+c[3].getAlpha())/2);
biomecolors[i] = c;
}
InputStream stream;
try {
Debug.debug("Loading colors from '" + colorSchemeFile + "'...");
stream = new FileInputStream(colorSchemeFile);
Scanner scanner = new Scanner(stream);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith("#") || line.equals("")) {
continue;
}
/* Make parser less pedantic - tabs or spaces should be fine */
String[] split = line.split("[\t ]");
int cnt = 0;
for(String s: split) { if(s.length() > 0) cnt++; }
String[] nsplit = new String[cnt];
cnt = 0;
for(String s: split) { if(s.length() > 0) { nsplit[cnt] = s; cnt++; } }
split = nsplit;
if (split.length < 17) {
continue;
}
Integer id;
Integer dat = null;
boolean isbiome = false;
boolean istemp = false;
boolean israin = false;
int idx = split[0].indexOf(':');
if(idx > 0) { /* ID:data - data color */
id = new Integer(split[0].substring(0, idx));
dat = new Integer(split[0].substring(idx+1));
}
else if(split[0].charAt(0) == '[') { /* Biome color data */
String bio = split[0].substring(1);
idx = bio.indexOf(']');
if(idx >= 0) bio = bio.substring(0, idx);
isbiome = true;
id = -1;
BiomeMap[] bm = BiomeMap.values();
for(int i = 0; i < bm.length; i++) {
if(bm[i].toString().equalsIgnoreCase(bio)) {
id = i;
break;
}
else if(bio.equalsIgnoreCase("BIOME_" + i)) {
id = i;
break;
}
}
if(id < 0) { /* Not biome - check for rain or temp */
if(bio.startsWith("RAINFALL-")) {
try {
double v = Double.parseDouble(bio.substring(9));
if((v >= 0) && (v <= 1.00)) {
id = (int)(v * 63.0);
israin = true;
}
} catch (NumberFormatException nfx) {
}
}
else if(bio.startsWith("TEMPERATURE-")) {
try {
double v = Double.parseDouble(bio.substring(12));
if((v >= 0) && (v <= 1.00)) {
id = (int)(v * 63.0);
istemp = true;
}
} catch (NumberFormatException nfx) {
}
}
}
}
else {
id = new Integer(split[0]);
}
if((!isbiome) && (id >= colors.length)) {
Color[][] newcolors = new Color[id+1][];
System.arraycopy(colors, 0, newcolors, 0, colors.length);
colors = newcolors;
Color[][][] newdatacolors = new Color[id+1][][];
System.arraycopy(datacolors, 0, newdatacolors, 0, datacolors.length);
datacolors = newdatacolors;
}
Color[] c = new Color[5];
/* store colors by raycast sequence number */
c[0] = new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), Integer.parseInt(split[4]));
c[3] = new Color(Integer.parseInt(split[5]), Integer.parseInt(split[6]), Integer.parseInt(split[7]), Integer.parseInt(split[8]));
c[1] = new Color(Integer.parseInt(split[9]), Integer.parseInt(split[10]), Integer.parseInt(split[11]), Integer.parseInt(split[12]));
c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), Integer.parseInt(split[15]), Integer.parseInt(split[16]));
/* Blended color - for 'smooth' option on flat map */
c[4] = new Color((c[1].getRed()+c[3].getRed())/2, (c[1].getGreen()+c[3].getGreen())/2, (c[1].getBlue()+c[3].getBlue())/2, (c[1].getAlpha()+c[3].getAlpha())/2);
if(isbiome) {
if(istemp) {
tempcolors[id] = c;
}
else if(israin) {
raincolors[id] = c;
}
else if((id >= 0) && (id < biomecolors.length))
biomecolors[id] = c;
}
else if(dat != null) {
Color[][] dcolor = datacolors[id]; /* Existing list? */
if(dcolor == null) {
dcolor = new Color[16][]; /* Make 16 index long list */
datacolors[id] = dcolor;
}
if((dat >= 0) && (dat < 16)) { /* Add color to list */
dcolor[dat] = c;
}
if(dat == 0) { /* Index zero is base color too */
colors[id] = c;
}
}
else {
colors[id] = c;
}
}
scanner.close();
/* Last, push base color into any open slots in data colors list */
for(int k = 0; k < datacolors.length; k++) {
Color[][] dc = datacolors[k]; /* see if data colors too */
if(dc != null) {
Color[] c = colors[k];
for(int i = 0; i < 16; i++) {
if(dc[i] == null)
dc[i] = c;
}
}
}
/* And interpolate any missing rain and temperature colors */
interpolateColorTable(tempcolors);
interpolateColorTable(raincolors);
} catch (RuntimeException e) {
Log.severe("Could not load colors '" + name + "' ('" + colorSchemeFile + "').", e);
return null;
} catch (FileNotFoundException e) {
Log.severe("Could not load colors '" + name + "' ('" + colorSchemeFile + "'): File not found.", e);
}
return new ColorScheme(name, colors, datacolors, biomecolors, raincolors, tempcolors);
}
public static void interpolateColorTable(Color[][] c) {
int idx = -1;
for(int k = 0; k < c.length; k++) {
if(c[k] == null) { /* Missing? */
if((idx >= 0) && (k == (c.length-1))) { /* We're last - so fill forward from last color */
for(int kk = idx+1; kk <= k; kk++) {
c[kk] = c[idx];
}
}
/* Skip - will backfill when we find next color */
}
else if(idx == -1) { /* No previous color, just backfill this color */
for(int kk = 0; kk < k; kk++) {
c[kk] = c[k];
}
idx = k; /* This is now last defined color */
}
else { /* Else, interpolate between last idx and this one */
int cnt = c[k].length;
for(int kk = idx+1; kk < k; kk++) {
double interp = (double)(kk-idx)/(double)(k-idx);
Color[] cc = new Color[cnt];
for(int jj = 0; jj < cnt; jj++) {
cc[jj] = new Color(
(int)((1.0-interp)*c[idx][jj].getRed() + interp*c[k][jj].getRed()),
(int)((1.0-interp)*c[idx][jj].getGreen() + interp*c[k][jj].getGreen()),
(int)((1.0-interp)*c[idx][jj].getBlue() + interp*c[k][jj].getBlue()),
(int)((1.0-interp)*c[idx][jj].getAlpha() + interp*c[k][jj].getAlpha()));
}
c[kk] = cc;
}
idx = k;
}
}
}
public Color[] getRainColor(double rain) {
int idx = (int)(rain * 63.0);
if((idx >= 0) && (idx < raincolors.length))
return raincolors[idx];
else
return null;
}
public Color[] getTempColor(double temp) {
int idx = (int)(temp * 63.0);
if((idx >= 0) && (idx < tempcolors.length))
return tempcolors[idx];
else
return null;
}
public void resizeColorArray(int idx) {
if(idx >= colors.length){
Color[][] newcolors = new Color[idx+1][];
System.arraycopy(colors, 0, newcolors, 0, colors.length);
colors = newcolors;
Color[][][] newdatacolors = new Color[idx+1][][];
System.arraycopy(datacolors, 0, newdatacolors, 0, datacolors.length);
datacolors = newdatacolors;
}
}
public static void reset() {
cache.clear();
}
}

View File

@ -0,0 +1,21 @@
package org.dynmap;
public abstract class Component {
protected DynmapCore core;
protected ConfigurationNode configuration;
public Component(DynmapCore core, ConfigurationNode configuration) {
this.core = core;
this.configuration = configuration;
}
public void dispose() {
}
/* Substitute proper values for escape sequences */
public static String unescapeString(String v) {
/* Replace color code &color; */
v = v.replace("&color;", "\u00A7");
return v;
}
}

View File

@ -0,0 +1,47 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ComponentManager {
public Set<Component> components = new HashSet<Component>();
public Map<String, List<Component>> componentLookup = new HashMap<String, List<Component>>();
public void add(Component c) {
if (components.add(c)) {
String key = c.getClass().toString();
List<Component> clist = componentLookup.get(key);
if (clist == null) {
clist = new ArrayList<Component>();
componentLookup.put(key, clist);
}
clist.add(c);
}
}
public void remove(Component c) {
if (components.remove(c)) {
String key = c.getClass().toString();
List<Component> clist = componentLookup.get(key);
if (clist != null) {
clist.remove(c);
}
}
}
public void clear() {
componentLookup.clear();
components.clear();
}
public Iterable<Component> getComponents(Class<Component> c) {
List<Component> list = componentLookup.get(c.toString());
if (list == null)
return new ArrayList<Component>();
return list;
}
}

View File

@ -0,0 +1,447 @@
package org.dynmap;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.CollectionNode;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.reader.UnicodeReader;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.representer.Representer;
public class ConfigurationNode implements Map<String, Object> {
public Map<String, Object> entries;
private File f;
private Yaml yaml;
public ConfigurationNode() {
entries = new LinkedHashMap<String, Object>();
}
private void initparse() {
if(yaml == null) {
DumperOptions options = new DumperOptions();
options.setIndent(4);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setPrettyFlow(true);
yaml = new Yaml(new SafeConstructor(), new EmptyNullRepresenter(), options);
}
}
public ConfigurationNode(File f) {
this.f = f;
entries = new LinkedHashMap<String, Object>();
}
public ConfigurationNode(Map<String, Object> map) {
if (map == null) {
throw new IllegalArgumentException();
}
entries = map;
}
public ConfigurationNode(InputStream in) {
load(in);
}
@SuppressWarnings("unchecked")
public boolean load(InputStream in) {
initparse();
Object o = yaml.load(new UnicodeReader(in));
if((o != null) && (o instanceof Map))
entries = (Map<String, Object>)o;
return (entries != null);
}
@SuppressWarnings("unchecked")
public boolean load() {
initparse();
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
Object o = yaml.load(new UnicodeReader(fis));
if((o != null) && (o instanceof Map))
entries = (Map<String, Object>)o;
fis.close();
}
catch (YAMLException e) {
Log.severe("Error parsing " + f.getPath() + ". Use http://yamllint.com to debug the YAML syntax." );
throw e;
} catch(IOException iox) {
Log.severe("Error reading " + f.getPath());
return false;
} finally {
if(fis != null) {
try { fis.close(); } catch (IOException x) {}
}
}
return (entries != null);
}
public boolean save() {
return save(f);
}
public boolean save(File file) {
initparse();
FileOutputStream stream = null;
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
try {
stream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(stream, "UTF-8");
yaml.dump(entries, writer);
return true;
} catch (IOException e) {
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {}
}
return false;
}
@SuppressWarnings("unchecked")
public Object getObject(String path) {
if (path.isEmpty())
return entries;
int separator = path.indexOf('/');
if (separator < 0)
return get(path);
String localKey = path.substring(0, separator);
Object subvalue = get(localKey);
if (subvalue == null)
return null;
if (!(subvalue instanceof Map<?, ?>))
return null;
Map<String, Object> submap;
try {
submap = (Map<String, Object>)subvalue;
} catch (ClassCastException e) {
return null;
}
String subpath = path.substring(separator + 1);
return new ConfigurationNode(submap).getObject(subpath);
}
public Object getObject(String path, Object def) {
Object o = getObject(path);
if (o == null)
return def;
return o;
}
@SuppressWarnings("unchecked")
public <T> T getGeneric(String path, T def) {
Object o = getObject(path, def);
try {
return (T)o;
} catch(ClassCastException e) {
return def;
}
}
public int getInteger(String path, int def) {
return Integer.parseInt(getObject(path, def).toString());
}
public double getLong(String path, long def) {
return Long.parseLong(getObject(path, def).toString());
}
public float getFloat(String path, float def) {
return Float.parseFloat(getObject(path, def).toString());
}
public double getDouble(String path, double def) {
return Double.parseDouble(getObject(path, def).toString());
}
public boolean getBoolean(String path, boolean def) {
return Boolean.parseBoolean(getObject(path, def).toString());
}
public String getString(String path) {
return getString(path, null);
}
public List<String> getStrings(String path, List<String> def) {
Object o = getObject(path);
if (!(o instanceof List<?>)) {
return def;
}
ArrayList<String> strings = new ArrayList<String>();
for(Object i : (List<?>)o) {
strings.add(i.toString());
}
return strings;
}
public String getString(String path, String def) {
Object o = getObject(path, def);
if (o == null)
return null;
return o.toString();
}
@SuppressWarnings("unchecked")
public <T> List<T> getList(String path) {
try {
List<T> list = (List<T>)getObject(path, null);
return list;
} catch (ClassCastException e) {
try {
T o = (T)getObject(path, null);
if (o == null) {
return new ArrayList<T>();
}
ArrayList<T> al = new ArrayList<T>();
al.add(o);
return al;
} catch (ClassCastException e2) {
return new ArrayList<T>();
}
}
}
public List<Map<String,Object>> getMapList(String path) {
return getList(path);
}
public ConfigurationNode getNode(String path) {
Map<String, Object> v = null;
v = getGeneric(path, v);
if (v == null)
return null;
return new ConfigurationNode(v);
}
@SuppressWarnings("unchecked")
public List<ConfigurationNode> getNodes(String path) {
List<Object> o = getList(path);
if(o == null)
return new ArrayList<ConfigurationNode>();
ArrayList<ConfigurationNode> nodes = new ArrayList<ConfigurationNode>();
for(Object i : (List<?>)o) {
if (i instanceof Map<?, ?>) {
Map<String, Object> map;
try {
map = (Map<String, Object>)i;
} catch(ClassCastException e) {
continue;
}
nodes.add(new ConfigurationNode(map));
}
}
return nodes;
}
public void extend(Map<String, Object> other) {
if (other != null)
extendMap(this, other);
}
private final static Object copyValue(Object v) {
if(v instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> mv = (Map<String, Object>)v;
LinkedHashMap<String, Object> newv = new LinkedHashMap<String,Object>();
for(Map.Entry<String, Object> me : mv.entrySet()) {
newv.put(me.getKey(), copyValue(me.getValue()));
}
return newv;
}
else if(v instanceof List) {
@SuppressWarnings("unchecked")
List<Object> lv = (List<Object>)v;
ArrayList<Object> newv = new ArrayList<Object>();
for(int i = 0; i < lv.size(); i++) {
newv.add(copyValue(lv.get(i)));
}
return newv;
}
else {
return v;
}
}
private final static void extendMap(Map<String, Object> left, Map<String, Object> right) {
ConfigurationNode original = new ConfigurationNode(left);
for(Map.Entry<String, Object> entry : right.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
original.put(key, copyValue(value));
}
}
public <T> T createInstance(Class<?>[] constructorParameters, Object[] constructorArguments) {
String typeName = getString("class");
try {
Class<?> mapTypeClass = Class.forName(typeName);
Class<?>[] constructorParameterWithConfiguration = new Class<?>[constructorParameters.length+1];
for(int i = 0; i < constructorParameters.length; i++) { constructorParameterWithConfiguration[i] = constructorParameters[i]; }
constructorParameterWithConfiguration[constructorParameterWithConfiguration.length-1] = ConfigurationNode.class;
Object[] constructorArgumentsWithConfiguration = new Object[constructorArguments.length+1];
for(int i = 0; i < constructorArguments.length; i++) { constructorArgumentsWithConfiguration[i] = constructorArguments[i]; }
constructorArgumentsWithConfiguration[constructorArgumentsWithConfiguration.length-1] = this;
Constructor<?> constructor = mapTypeClass.getConstructor(constructorParameterWithConfiguration);
@SuppressWarnings("unchecked")
T t = (T)constructor.newInstance(constructorArgumentsWithConfiguration);
return t;
} catch (Exception e) {
// TODO: Remove reference to MapManager.
Log.severe("Error loading maptype", e);
e.printStackTrace();
}
return null;
}
public <T> List<T> createInstances(String path, Class<?>[] constructorParameters, Object[] constructorArguments) {
List<ConfigurationNode> nodes = getNodes(path);
List<T> instances = new ArrayList<T>();
for(ConfigurationNode node : nodes) {
T instance = node.<T>createInstance(constructorParameters, constructorArguments);
if (instance != null) {
instances.add(instance);
}
}
return instances;
}
@Override
public int size() {
return entries.size();
}
@Override
public boolean isEmpty() {
return entries.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return entries.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return entries.containsValue(value);
}
@Override
public Object get(Object key) {
return entries.get(key);
}
@Override
public Object put(String key, Object value) {
return entries.put(key, value);
}
@Override
public Object remove(Object key) {
return entries.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
entries.putAll(m);
}
@Override
public void clear() {
entries.clear();
}
@Override
public Set<String> keySet() {
return entries.keySet();
}
@Override
public Collection<Object> values() {
return entries.values();
}
@Override
public Set<java.util.Map.Entry<String, Object>> entrySet() {
return entries.entrySet();
}
private class EmptyNullRepresenter extends Representer {
public EmptyNullRepresenter() {
super();
this.nullRepresenter = new EmptyRepresentNull();
}
protected class EmptyRepresentNull implements Represent {
public Node representData(Object data) {
return representScalar(Tag.NULL, ""); // Changed "null" to "" so as to avoid writing nulls
}
}
// Code borrowed from snakeyaml (http://code.google.com/p/snakeyaml/source/browse/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java)
@Override
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
NodeTuple tuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
Node valueNode = tuple.getValueNode();
if (valueNode instanceof CollectionNode) {
// Removed null check
if (Tag.SEQ.equals(valueNode.getTag())) {
SequenceNode seq = (SequenceNode) valueNode;
if (seq.getValue().isEmpty()) {
return null; // skip empty lists
}
}
if (Tag.MAP.equals(valueNode.getTag())) {
MappingNode seq = (MappingNode) valueNode;
if (seq.getValue().isEmpty()) {
return null; // skip empty maps
}
}
}
return tuple;
}
// End of borrowed code
}
}

View File

@ -0,0 +1,22 @@
package org.dynmap;
public class DynmapChunk {
public int x, z;
public DynmapChunk(int x, int z) {
this.x = x;
this.z = z;
}
@Override
public boolean equals(Object o) {
if(o instanceof DynmapChunk) {
DynmapChunk dc = (DynmapChunk)o;
return (dc.x == this.x) && (dc.z == this.z);
}
return false;
}
@Override
public int hashCode() {
return x ^ (z << 5);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
package org.dynmap;
/**
* Generic block location
*/
public class DynmapLocation {
public double x, y, z;
public String world;
public DynmapLocation() {}
public DynmapLocation(String w, double x, double y, double z) {
world = w;
this.x = x; this.y = y; this.z = z;
}
}

View File

@ -0,0 +1,635 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.hdmap.HDLighting;
import org.dynmap.hdmap.HDMap;
import org.dynmap.hdmap.HDPerspective;
import org.dynmap.hdmap.HDShader;
/**
* Handler for world and map edit commands (via /dmap)
*/
public class DynmapMapCommands {
private boolean checkIfActive(DynmapCore core, DynmapCommandSender sender) {
if((!core.getPauseFullRadiusRenders()) || (!core.getPauseUpdateRenders())) {
sender.sendMessage("Cannot edit map data while rendering active - run '/dynmap pause all' to pause rendering");
return true;
}
return false;
}
public boolean processCommand(DynmapCommandSender sender, String cmd, String commandLabel, String[] args, DynmapCore core) {
/* Re-parse args - handle doublequotes */
args = DynmapCore.parseArgs(args, sender);
if(args.length < 1)
return false;
cmd = args[0];
boolean rslt = false;
if(cmd.equalsIgnoreCase("worldlist")) {
rslt = handleWorldList(sender, args, core);
}
else if(cmd.equalsIgnoreCase("perspectivelist")) {
rslt = handlePerspectiveList(sender, args, core);
}
else if(cmd.equalsIgnoreCase("shaderlist")) {
rslt = handleShaderList(sender, args, core);
}
else if(cmd.equalsIgnoreCase("lightinglist")) {
rslt = handleLightingList(sender, args, core);
}
else if(cmd.equalsIgnoreCase("maplist")) {
rslt = handleMapList(sender, args, core);
}
else if(cmd.equalsIgnoreCase("blocklist")) {
rslt = handleBlockList(sender, args, core);
}
/* Other commands are edits - must be paused to run these */
else if(checkIfActive(core, sender)) {
rslt = true;
}
else {
if(cmd.equalsIgnoreCase("worldset")) {
rslt = handleWorldSet(sender, args, core);
}
else if(cmd.equalsIgnoreCase("mapdelete")) {
rslt = handleMapDelete(sender, args, core);
}
else if(cmd.equalsIgnoreCase("worldreset")) {
rslt = handleWorldReset(sender, args, core);
}
else if(cmd.equalsIgnoreCase("mapset")) {
rslt = handleMapSet(sender, args, core, false);
}
else if(cmd.equalsIgnoreCase("mapadd")) {
rslt = handleMapSet(sender, args, core, true);
}
if(rslt)
sender.sendMessage("If you are done editing map data, run '/dynmap pause none' to resume rendering");
}
return rslt;
}
private boolean handleWorldList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.worldlist"))
return true;
Set<String> wnames = null;
if(args.length > 1) {
wnames = new HashSet<String>();
for(int i = 1; i < args.length; i++)
wnames.add(DynmapWorld.normalizeWorldName(args[i]));
}
/* Get active worlds */
for(DynmapWorld w : core.getMapManager().getWorlds()) {
if((wnames != null) && (wnames.contains(w.getName()) == false)) {
continue;
}
StringBuilder sb = new StringBuilder();
sb.append("world ").append(w.getName()).append(": loaded=").append(w.isLoaded()).append(", enabled=").append(w.isEnabled());
sb.append(", title=").append(w.getTitle());
DynmapLocation loc = w.getCenterLocation();
if(loc != null) {
sb.append(", center=").append(loc.x).append("/").append(loc.y).append("/").append(loc.z);
}
sb.append(", extrazoomout=").append(w.getExtraZoomOutLevels()).append(", sendhealth=").append(w.sendhealth);
sb.append(", sendposition=").append(w.sendposition);
sb.append(", protected=").append(w.is_protected);
if(w.tileupdatedelay > 0) {
sb.append(", tileupdatedelay=").append(w.tileupdatedelay);
}
sender.sendMessage(sb.toString());
}
/* Get disabled worlds */
for(String wn : core.getMapManager().getDisabledWorlds()) {
if((wnames != null) && (wnames.contains(wn) == false)) {
continue;
}
sender.sendMessage("world " + wn + ": isenabled=false");
}
return true;
}
private boolean handleWorldSet(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.worldset"))
return true;
if(args.length < 3) {
sender.sendMessage("World name and setting:newvalue required");
return true;
}
String wname = args[1]; /* Get world name */
/* Test if render active - quit if so */
if(checkIfActive(core, sender)) {
return true;
}
DynmapWorld w = core.getWorld(wname); /* Try to get world */
boolean did_update = false;
for(int i = 2; i < args.length; i++) {
String[] tok = args[i].split(":"); /* Split at colon */
if(tok.length != 2) {
sender.sendMessage("Syntax error: " + args[i]);
return false;
}
if(tok[0].equalsIgnoreCase("enabled")) {
did_update |= core.setWorldEnable(wname, !tok[1].equalsIgnoreCase("false"));
}
else if(tok[0].equalsIgnoreCase("title")) {
if(w == null) {
sender.sendMessage("Cannot set extrazoomout on disabled or undefined world");
return true;
}
w.setTitle(tok[1]);
core.updateWorldConfig(w);
did_update = true;
}
else if(tok[0].equalsIgnoreCase("sendposition")) {
if(w == null) {
sender.sendMessage("Cannot set sendposition on disabled or undefined world");
return true;
}
w.sendposition = tok[1].equals("true");
core.updateWorldConfig(w);
did_update = true;
}
else if(tok[0].equalsIgnoreCase("sendhealth")) {
if(w == null) {
sender.sendMessage("Cannot set sendhealth on disabled or undefined world");
return true;
}
w.sendhealth = tok[1].equals("true");
core.updateWorldConfig(w);
did_update = true;
}
else if(tok[0].equalsIgnoreCase("protected")) {
if(w == null) {
sender.sendMessage("Cannot set protected on disabled or undefined world");
return true;
}
w.is_protected = tok[1].equals("true");
core.updateWorldConfig(w);
did_update = true;
}
else if(tok[0].equalsIgnoreCase("extrazoomout")) { /* Extrazoomout setting */
if(w == null) {
sender.sendMessage("Cannot set extrazoomout on disabled or undefined world");
return true;
}
int exo = -1;
try {
exo = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {}
if((exo < 0) || (exo > 32)) {
sender.sendMessage("Invalid value for extrazoomout: " + tok[1]);
return true;
}
did_update |= core.setWorldZoomOut(wname, exo);
}
else if(tok[0].equalsIgnoreCase("tileupdatedelay")) { /* tileupdatedelay setting */
if(w == null) {
sender.sendMessage("Cannot set tileupdatedelay on disabled or undefined world");
return true;
}
int tud = -1;
try {
tud = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {}
did_update |= core.setWorldTileUpdateDelay(wname, tud);
}
else if(tok[0].equalsIgnoreCase("center")) { /* Center */
if(w == null) {
sender.sendMessage("Cannot set center on disabled or undefined world");
return true;
}
boolean good = false;
DynmapLocation loc = null;
try {
String[] toks = tok[1].split("/");
if(toks.length == 3) {
double x = 0, y = 0, z = 0;
x = Double.valueOf(toks[0]);
y = Double.valueOf(toks[1]);
z = Double.valueOf(toks[2]);
loc = new DynmapLocation(wname, x, y, z);
good = true;
}
else if(tok[1].equalsIgnoreCase("default")) {
good = true;
}
else if(tok[1].equalsIgnoreCase("here")) {
if(sender instanceof DynmapPlayer) {
loc = ((DynmapPlayer)sender).getLocation();
good = true;
}
else {
sender.sendMessage("Setting center to 'here' requires player");
return true;
}
}
} catch (NumberFormatException nfx) {}
if(!good) {
sender.sendMessage("Center value must be formatted x/y/z or be set to 'default' or 'here'");
return true;
}
did_update |= core.setWorldCenter(wname, loc);
}
else if(tok[0].equalsIgnoreCase("order")) {
if(w == null) {
sender.sendMessage("Cannot set order on disabled or undefined world");
return true;
}
int order = -1;
try {
order = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {}
if(order < 1) {
sender.sendMessage("Order value must be number from 1 to number of worlds");
return true;
}
did_update |= core.setWorldOrder(wname, order-1);
}
}
/* If world updatd, refresh it */
if(did_update) {
sender.sendMessage("Refreshing configuration for world " + wname);
core.refreshWorld(wname);
}
return true;
}
private boolean handleMapList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.maplist"))
return true;
if(args.length < 2) {
sender.sendMessage("World name is required");
return true;
}
String wname = args[1]; /* Get world name */
DynmapWorld w = core.getWorld(wname); /* Try to get world */
if(w == null) {
sender.sendMessage("Only loaded world can be listed");
return true;
}
List<MapType> maps = w.maps;
for(MapType mt : maps) {
if(mt instanceof HDMap) {
HDMap hdmt = (HDMap)mt;
StringBuilder sb = new StringBuilder();
sb.append("map ").append(mt.getName()).append(": prefix=").append(hdmt.getPrefix()).append(", title=").append(hdmt.getTitle());
sb.append(", perspective=").append(hdmt.getPerspective().getName()).append(", shader=").append(hdmt.getShader().getName());
sb.append(", lighting=").append(hdmt.getLighting().getName()).append(", mapzoomin=").append(hdmt.getMapZoomIn()).append(", mapzoomout=").append(hdmt.getMapZoomOutLevels());
sb.append(", img-format=").append(hdmt.getImageFormatSetting()).append(", icon=").append(hdmt.getIcon());
sb.append(", append-to-world=").append(hdmt.getAppendToWorld()).append(", boostzoom=").append(hdmt.getBoostZoom());
sb.append(", protected=").append(hdmt.isProtected());
if(hdmt.tileupdatedelay > 0) {
sb.append(", tileupdatedelay=").append(hdmt.tileupdatedelay);
}
sender.sendMessage(sb.toString());
}
}
return true;
}
private boolean handleMapDelete(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.mapdelete"))
return true;
if(args.length < 2) {
sender.sendMessage("World:map name required");
return true;
}
for(int i = 1; i < args.length; i++) {
String world_map_name = args[i];
String[] tok = world_map_name.split(":");
if(tok.length != 2) {
sender.sendMessage("Invalid world:map name: " + world_map_name);
return true;
}
String wname = tok[0];
String mname = tok[1];
DynmapWorld w = core.getWorld(wname); /* Try to get world */
if(w == null) {
sender.sendMessage("Cannot delete maps from disabled or unloaded world: " + wname);
return true;
}
List<MapType> maps = new ArrayList<MapType>(w.maps);
boolean done = false;
for(int idx = 0; (!done) && (idx < maps.size()); idx++) {
MapType mt = maps.get(idx);
if(mt.getName().equals(mname)) {
w.maps.remove(mt);
done = true;
}
}
/* If done, save updated config for world */
if(done) {
if(core.updateWorldConfig(w)) {
sender.sendMessage("Refreshing configuration for world " + wname);
core.refreshWorld(wname);
}
}
}
return true;
}
private boolean handleWorldReset(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.worldreset"))
return true;
if(args.length < 2) {
sender.sendMessage("World name required");
return true;
}
String wname = args[1]; /* Get world name */
DynmapWorld w = core.getWorld(wname); /* Try to get world */
/* If not loaded, cannot reset */
if(w == null) {
sender.sendMessage("Cannot reset world that is not loaded or enabled");
return true;
}
ConfigurationNode cn = null;
if(args.length > 2) {
cn = core.getTemplateConfigurationNode(args[2]);
}
else { /* Else get default */
cn = core.getDefaultTemplateConfigurationNode(w);
}
if(cn == null) {
sender.sendMessage("Cannot load template");
return true;
}
ConfigurationNode cfg = w.saveConfiguration(); /* Get configuration */
cfg.extend(cn); /* And apply template */
/* And set world config */
if(core.replaceWorldConfig(wname, cfg)) {
sender.sendMessage("Reset configuration for world " + wname);
core.refreshWorld(wname);
}
return true;
}
private boolean handleMapSet(DynmapCommandSender sender, String[] args, DynmapCore core, boolean isnew) {
if(!core.checkPlayerPermission(sender, isnew?"dmap.mapadd":"dmap.mapset"))
return true;
if(args.length < 2) {
sender.sendMessage("World:map name required");
return true;
}
String world_map_name = args[1];
String[] tok = world_map_name.split(":");
if(tok.length != 2) {
sender.sendMessage("Invalid world:map name: " + world_map_name);
return true;
}
String wname = tok[0];
String mname = tok[1];
DynmapWorld w = core.getWorld(wname); /* Try to get world */
if(w == null) {
sender.sendMessage("Cannot update maps from disabled or unloaded world: " + wname);
return true;
}
HDMap mt = null;
/* Find the map */
for(MapType map : w.maps) {
if(map instanceof HDMap) {
if(map.getName().equals(mname)) {
mt = (HDMap)map;
break;
}
}
}
/* If new, make default map instance */
if(isnew) {
if(mt != null) {
sender.sendMessage("Map " + mname + " already exists on world " + wname);
return true;
}
ConfigurationNode cn = new ConfigurationNode();
cn.put("name", mname);
mt = new HDMap(core, cn);
if(mt.getName() != null) {
w.maps.add(mt); /* Add to end, by default */
}
else {
sender.sendMessage("Map " + mname + " not valid");
return true;
}
}
else {
if(mt == null) {
sender.sendMessage("Map " + mname + " not found on world " + wname);
return true;
}
}
boolean did_update = isnew;
for(int i = 2; i < args.length; i++) {
tok = args[i].split(":", 2); /* Split at colon */
if(tok.length < 2) {
String[] newtok = new String[2];
newtok[0] = tok[0];
newtok[1] = "";
tok = newtok;
}
if(tok[0].equalsIgnoreCase("prefix")) {
/* Check to make sure prefix is unique */
for(MapType map : w.maps){
if(map == mt) continue;
if(map instanceof HDMap) {
if(((HDMap)map).getPrefix().equals(tok[1])) {
sender.sendMessage("Prefix " + tok[1] + " already in use");
return true;
}
}
}
did_update |= mt.setPrefix(tok[1]);
}
else if(tok[0].equalsIgnoreCase("title")) {
did_update |= mt.setTitle(tok[1]);
}
else if(tok[0].equalsIgnoreCase("icon")) {
did_update |= mt.setIcon(tok[1]);
}
else if(tok[0].equalsIgnoreCase("mapzoomin")) {
int mzi = -1;
try {
mzi = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {
}
if((mzi < 0) || (mzi > 32)) {
sender.sendMessage("Invalid mapzoomin value: " + tok[1]);
return true;
}
did_update |= mt.setMapZoomIn(mzi);
}
else if(tok[0].equalsIgnoreCase("mapzoomout")) {
int mzi = -1;
try {
mzi = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {
}
if((mzi < 0) || (mzi > 32)) {
sender.sendMessage("Invalid mapzoomout value: " + tok[1]);
return true;
}
did_update |= mt.setMapZoomOut(mzi);
}
else if(tok[0].equalsIgnoreCase("boostzoom")) {
int mzi = -1;
try {
mzi = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {
}
if((mzi < 0) || (mzi > 3)) {
sender.sendMessage("Invalid boostzoom value: " + tok[1]);
return true;
}
did_update |= mt.setBoostZoom(mzi);
}
else if(tok[0].equalsIgnoreCase("tileupdatedelay")) {
int tud = -1;
try {
tud = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {
}
did_update |= mt.setTileUpdateDelay(tud);
}
else if(tok[0].equalsIgnoreCase("perspective")) {
if(MapManager.mapman != null) {
HDPerspective p = MapManager.mapman.hdmapman.perspectives.get(tok[1]);
if(p == null) {
sender.sendMessage("Perspective not found: " + tok[1]);
return true;
}
did_update |= mt.setPerspective(p);
}
}
else if(tok[0].equalsIgnoreCase("shader")) {
if(MapManager.mapman != null) {
HDShader s = MapManager.mapman.hdmapman.shaders.get(tok[1]);
if(s == null) {
sender.sendMessage("Shader not found: " + tok[1]);
return true;
}
did_update |= mt.setShader(s);
}
}
else if(tok[0].equalsIgnoreCase("lighting")) {
if(MapManager.mapman != null) {
HDLighting l = MapManager.mapman.hdmapman.lightings.get(tok[1]);
if(l == null) {
sender.sendMessage("Lighting not found: " + tok[1]);
return true;
}
did_update |= mt.setLighting(l);
}
}
else if(tok[0].equalsIgnoreCase("img-format")) {
if((!tok[1].equals("default")) && (MapType.ImageFormat.fromID(tok[1]) == null)) {
sender.sendMessage("Image format not found: " + tok[1]);
return true;
}
did_update |= mt.setImageFormatSetting(tok[1]);
}
else if(tok[0].equalsIgnoreCase("order")) {
int idx = -1;
try {
idx = Integer.valueOf(tok[1]);
} catch (NumberFormatException nfx) {
}
if(idx < 1) {
sender.sendMessage("Invalid order position: " + tok[1]);
return true;
}
idx--;
/* Remove and insert at position */
w.maps.remove(mt);
if(idx < w.maps.size())
w.maps.add(idx, mt);
else
w.maps.add(mt);
did_update = true;
}
else if(tok[0].equalsIgnoreCase("append-to-world")) {
did_update |= mt.setAppendToWorld(tok[1]);
}
else if(tok[0].equalsIgnoreCase("protected")) {
did_update |= mt.setProtected(Boolean.parseBoolean(tok[1]));
}
}
if(did_update) {
if(core.updateWorldConfig(w)) {
sender.sendMessage("Refreshing configuration for world " + wname);
core.refreshWorld(wname);
}
}
return true;
}
private boolean handlePerspectiveList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.perspectivelist"))
return true;
if(MapManager.mapman != null) {
StringBuilder sb = new StringBuilder();
for(HDPerspective p : MapManager.mapman.hdmapman.perspectives.values()) {
sb.append(p.getName()).append(' ');
}
sender.sendMessage(sb.toString());
}
return true;
}
private boolean handleShaderList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.shaderlist"))
return true;
if(MapManager.mapman != null) {
StringBuilder sb = new StringBuilder();
for(HDShader p : MapManager.mapman.hdmapman.shaders.values()) {
sb.append(p.getName()).append(' ');
}
sender.sendMessage(sb.toString());
}
return true;
}
private boolean handleLightingList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.lightinglist"))
return true;
if(MapManager.mapman != null) {
StringBuilder sb = new StringBuilder();
for(HDLighting p : MapManager.mapman.hdmapman.lightings.values()) {
sb.append(p.getName()).append(' ');
}
sender.sendMessage(sb.toString());
}
return true;
}
private boolean handleBlockList(DynmapCommandSender sender, String[] args, DynmapCore core) {
if(!core.checkPlayerPermission(sender, "dmap.blklist"))
return true;
Map<String, Integer> map = core.getServer().getBlockUniqueIDMap();
TreeSet<String> keys = new TreeSet<String>(map.keySet());
for (String k : keys) {
sender.sendMessage(k + ": " + map.get(k));
}
return true;
}
}

View File

@ -0,0 +1,575 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.dynmap.MapType.ImageEncoding;
import org.dynmap.hdmap.TexturePack;
import org.dynmap.storage.MapStorage;
import org.dynmap.storage.MapStorageTile;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.ImageIOManager;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.RectangleVisibilityLimit;
import org.dynmap.utils.RoundVisibilityLimit;
import org.dynmap.utils.TileFlags;
import org.dynmap.utils.VisibilityLimit;
import org.dynmap.utils.Polygon;
import java.awt.image.BufferedImage;
import java.io.IOException;
public abstract class DynmapWorld {
public List<MapType> maps = new ArrayList<MapType>();
public List<MapTypeState> mapstate = new ArrayList<MapTypeState>();
public UpdateQueue updates = new UpdateQueue();
public DynmapLocation center;
public List<DynmapLocation> seedloc; /* All seed location - both direct and based on visibility limits */
private List<DynmapLocation> seedloccfg; /* Configured full render seeds only */
public List<VisibilityLimit> visibility_limits;
public List<VisibilityLimit> hidden_limits;
public MapChunkCache.HiddenChunkStyle hiddenchunkstyle;
public int servertime;
public boolean sendposition;
public boolean sendhealth;
private int extrazoomoutlevels; /* Number of additional zoom out levels to generate */
private boolean cancelled;
private final String wname;
private final int hashcode;
private final String raw_wname;
private String title;
public int tileupdatedelay;
private boolean is_enabled;
boolean is_protected; /* If true, user needs 'dynmap.world.<worldid>' privilege to see world */
protected int[] brightnessTable = new int[16]; // 0-256 scaled brightness table
private MapStorage storage; // Storage handler for this world's maps
/* World height data */
public final int worldheight;
public final int heightshift;
public final int heightmask;
public int sealevel;
protected DynmapWorld(String wname, int worldheight, int sealevel) {
this.raw_wname = wname;
this.wname = normalizeWorldName(wname);
this.hashcode = this.wname.hashCode();
this.title = wname;
this.worldheight = worldheight;
this.sealevel = sealevel;
int shift;
for(shift = 0; ((1 << shift) < worldheight); shift++) {}
heightshift = shift;
heightmask = (1 << shift) - 1;
/* Generate default brightness table for surface world */
for (int i = 0; i <= 15; ++i) {
float f1 = 1.0F - (float)i / 15.0F;
setBrightnessTableEntry(i, ((1.0F - f1) / (f1 * 3.0F + 1.0F)));
}
}
protected void setBrightnessTableEntry(int level, float value) {
if ((level < 0) || (level > 15)) return;
this.brightnessTable[level] = (int)(256.0 * value);
if (this.brightnessTable[level] > 256) this.brightnessTable[level] = 256;
if (this.brightnessTable[level] < 0) this.brightnessTable[level] = 0;
}
/**
* Get world's brightness table
* @return table
*/
public int[] getBrightnessTable() {
return brightnessTable;
}
public void setExtraZoomOutLevels(int lvl) {
extrazoomoutlevels = lvl;
}
public int getExtraZoomOutLevels() { return extrazoomoutlevels; }
public void enqueueZoomOutUpdate(MapStorageTile tile) {
MapTypeState mts = getMapState(tile.map);
if (mts != null) {
mts.setZoomOutInv(tile.x, tile.y, tile.zoom);
}
}
public void freshenZoomOutFiles() {
MapTypeState.ZoomOutCoord c = new MapTypeState.ZoomOutCoord();
for (MapTypeState mts : mapstate) {
if (cancelled) return;
MapType mt = mts.type;
MapType.ImageVariant var[] = mt.getVariants();
mts.startZoomOutIter(); // Start iterator
while (mts.nextZoomOutInv(c)) {
if(cancelled) return;
for (int varIdx = 0; varIdx < var.length; varIdx++) {
MapStorageTile tile = storage.getTile(this, mt, c.x, c.y, c.zoomlevel, var[varIdx]);
processZoomFile(mts, tile, varIdx == 0);
}
}
}
}
public void cancelZoomOutFreshen() {
cancelled = true;
}
public void activateZoomOutFreshen() {
cancelled = false;
}
private static final int[] stepseq = { 3, 1, 2, 0 };
private void processZoomFile(MapTypeState mts, MapStorageTile tile, boolean firstVariant) {
int step = 1 << tile.zoom;
MapStorageTile ztile = tile.getZoomOutTile();
int width = 128, height = 128;
BufferedImage zIm = null;
DynmapBufferedImage kzIm = null;
boolean blank = true;
int[] argb = new int[width*height];
int tx = ztile.x;
int ty = ztile.y;
ty = ty - step; /* Adjust for negative step */
/* create image buffer */
kzIm = DynmapBufferedImage.allocateBufferedImage(width, height);
zIm = kzIm.buf_img;
for(int i = 0; i < 4; i++) {
boolean doblit = true;
int tx1 = tx + step * (1 & stepseq[i]);
int ty1 = ty + step * (stepseq[i] >> 1);
MapStorageTile tile1 = storage.getTile(this, tile.map, tx1, ty1, tile.zoom, tile.var);
if (tile1 == null) continue;
tile1.getReadLock();
if (firstVariant) { // We're handling this one - but only clear on first variant (so that we don't miss updates later)
mts.clearZoomOutInv(tile1.x, tile1.y, tile1.zoom);
}
try {
MapStorageTile.TileRead tr = tile1.read();
if (tr != null) {
BufferedImage im = null;
try {
im = ImageIOManager.imageIODecode(tr.image);
} catch (IOException iox) {
// Broken file - zap it
tile1.delete();
}
if((im != null) && (im.getWidth() >= width) && (im.getHeight() >= height)) {
int iwidth = im.getWidth();
int iheight = im.getHeight();
if(iwidth > iheight) iwidth = iheight;
if ((iwidth == width) && (iheight == height)) {
im.getRGB(0, 0, width, height, argb, 0, width); /* Read data */
im.flush();
/* Do binlinear scale to 64x64 */
int off = 0;
for(int y = 0; y < height; y += 2) {
off = y*width;
for(int x = 0; x < width; x += 2, off += 2) {
int p0 = argb[off];
int p1 = argb[off+1];
int p2 = argb[off+width];
int p3 = argb[off+width+1];
int alpha = ((p0 >> 24) & 0xFF) + ((p1 >> 24) & 0xFF) + ((p2 >> 24) & 0xFF) + ((p3 >> 24) & 0xFF);
int red = ((p0 >> 16) & 0xFF) + ((p1 >> 16) & 0xFF) + ((p2 >> 16) & 0xFF) + ((p3 >> 16) & 0xFF);
int green = ((p0 >> 8) & 0xFF) + ((p1 >> 8) & 0xFF) + ((p2 >> 8) & 0xFF) + ((p3 >> 8) & 0xFF);
int blue = (p0 & 0xFF) + (p1 & 0xFF) + (p2 & 0xFF) + (p3 & 0xFF);
argb[off>>1] = (((alpha>>2)&0xFF)<<24) | (((red>>2)&0xFF)<<16) | (((green>>2)&0xFF)<<8) | ((blue>>2)&0xFF);
}
}
}
else {
int[] buf = new int[iwidth * iwidth];
im.getRGB(0, 0, iwidth, iwidth, buf, 0, iwidth);
im.flush();
TexturePack.scaleTerrainPNGSubImage(iwidth, width/2, buf, argb);
/* blit scaled rendered tile onto zoom-out tile */
zIm.setRGB(((i>>1) != 0)?0:width/2, (i & 1) * height/2, width/2, height/2, argb, 0, width/2);
doblit = false;
}
blank = false;
}
else {
if (tile1.map.getImageFormat().getEncoding() == ImageEncoding.JPG) {
Arrays.fill(argb, tile1.map.getBackgroundARGB(tile1.var));
}
else {
Arrays.fill(argb, 0);
}
tile1.delete(); // Delete unusable tile
}
}
else {
if (tile1.map.getImageFormat().getEncoding() == ImageEncoding.JPG) {
Arrays.fill(argb, tile1.map.getBackgroundARGB(tile1.var));
}
else {
Arrays.fill(argb, 0);
}
}
} finally {
tile1.releaseReadLock();
}
/* blit scaled rendered tile onto zoom-out tile */
if(doblit) {
zIm.setRGB(((i>>1) != 0)?0:width/2, (i & 1) * height/2, width/2, height/2, argb, 0, width);
}
}
ztile.getWriteLock();
try {
MapManager mm = MapManager.mapman;
if(mm == null)
return;
long crc = MapStorage.calculateImageHashCode(kzIm.argb_buf, 0, kzIm.argb_buf.length); /* Get hash of tile */
if(blank) {
if (ztile.exists()) {
ztile.delete();
MapManager.mapman.pushUpdate(this, new Client.Tile(ztile.getURI()));
enqueueZoomOutUpdate(ztile);
}
}
else /* if (!ztile.matchesHashCode(crc)) */ {
ztile.write(crc, zIm);
MapManager.mapman.pushUpdate(this, new Client.Tile(ztile.getURI()));
enqueueZoomOutUpdate(ztile);
}
} finally {
ztile.releaseWriteLock();
DynmapBufferedImage.freeBufferedImage(kzIm);
}
}
/* Get world name */
public String getName() {
return wname;
}
/* Test if world is nether */
public abstract boolean isNether();
/* Get world spawn location */
public abstract DynmapLocation getSpawnLocation();
public int hashCode() {
return this.hashcode;
}
/* Get world time */
public abstract long getTime();
/* World is storming */
public abstract boolean hasStorm();
/* World is thundering */
public abstract boolean isThundering();
/* World is loaded */
public abstract boolean isLoaded();
/* Set world unloaded */
public abstract void setWorldUnloaded();
/* Get light level of block */
public abstract int getLightLevel(int x, int y, int z);
/* Get highest Y coord of given location */
public abstract int getHighestBlockYAt(int x, int z);
/* Test if sky light level is requestable */
public abstract boolean canGetSkyLightLevel();
/* Return sky light level */
public abstract int getSkyLightLevel(int x, int y, int z);
/**
* Get world environment ID (lower case - normal, the_end, nether)
* @return environment ID
*/
public abstract String getEnvironment();
/**
* Get map chunk cache for world
* @param chunks - list of chunks to load
* @return cache
*/
public abstract MapChunkCache getChunkCache(List<DynmapChunk> chunks);
/**
* Get title for world
* @return title
*/
public String getTitle() {
return title;
}
/**
* Get center location
* @return center
*/
public DynmapLocation getCenterLocation() {
if(center != null)
return center;
else
return getSpawnLocation();
}
/* Load world configuration from configuration node */
public boolean loadConfiguration(DynmapCore core, ConfigurationNode worldconfig) {
is_enabled = worldconfig.getBoolean("enabled", false);
if (!is_enabled) {
return false;
}
title = worldconfig.getString("title", title);
ConfigurationNode ctr = worldconfig.getNode("center");
int mid_y = worldheight/2;
if(ctr != null)
center = new DynmapLocation(wname, ctr.getDouble("x", 0.0), ctr.getDouble("y", mid_y), ctr.getDouble("z", 0));
else
center = null;
List<ConfigurationNode> loclist = worldconfig.getNodes("fullrenderlocations");
seedloc = new ArrayList<DynmapLocation>();
seedloccfg = new ArrayList<DynmapLocation>();
servertime = (int)(getTime() % 24000);
sendposition = worldconfig.getBoolean("sendposition", true);
sendhealth = worldconfig.getBoolean("sendhealth", true);
is_protected = worldconfig.getBoolean("protected", false);
setExtraZoomOutLevels(worldconfig.getInteger("extrazoomout", 0));
setTileUpdateDelay(worldconfig.getInteger("tileupdatedelay", -1));
storage = core.getDefaultMapStorage();
if(loclist != null) {
for(ConfigurationNode loc : loclist) {
DynmapLocation lx = new DynmapLocation(wname, loc.getDouble("x", 0), loc.getDouble("y", mid_y), loc.getDouble("z", 0));
seedloc.add(lx); /* Add to both combined and configured seed list */
seedloccfg.add(lx);
}
}
/* Build maps */
maps.clear();
Log.verboseinfo("Loading maps of world '" + wname + "'...");
for(MapType map : worldconfig.<MapType>createInstances("maps", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(map.getName() != null) {
maps.add(map);
}
}
/* Rebuild map state list - match on indexes */
mapstate.clear();
for(MapType map : maps) {
MapTypeState ms = new MapTypeState(this, map);
ms.setInvalidatePeriod(map.getTileUpdateDelay(this));
mapstate.add(ms);
}
Log.info("Loaded " + maps.size() + " maps of world '" + wname + "'.");
/* Load visibility limits, if any are defined */
List<ConfigurationNode> vislimits = worldconfig.getNodes("visibilitylimits");
if(vislimits != null) {
visibility_limits = new ArrayList<VisibilityLimit>();
for(ConfigurationNode vis : vislimits) {
VisibilityLimit lim;
if (vis.containsKey("r")) { /* It is round visibility limit */
int x_center = vis.getInteger("x", 0);
int z_center = vis.getInteger("z", 0);
int radius = vis.getInteger("r", 0);
lim = new RoundVisibilityLimit(x_center, z_center, radius);
}
else { /* Rectangle visibility limit */
int x0 = vis.getInteger("x0", 0);
int x1 = vis.getInteger("x1", 0);
int z0 = vis.getInteger("z0", 0);
int z1 = vis.getInteger("z1", 0);
lim = new RectangleVisibilityLimit(x0, z0, x1, z1);
}
visibility_limits.add(lim);
/* Also, add a seed location for the middle of each visible area */
seedloc.add(new DynmapLocation(wname, lim.xCenter(), 64, lim.zCenter()));
}
}
/* Load hidden limits, if any are defined */
List<ConfigurationNode> hidelimits = worldconfig.getNodes("hiddenlimits");
if(hidelimits != null) {
hidden_limits = new ArrayList<VisibilityLimit>();
for(ConfigurationNode vis : hidelimits) {
VisibilityLimit lim;
if (vis.containsKey("r")) { /* It is round visibility limit */
int x_center = vis.getInteger("x", 0);
int z_center = vis.getInteger("z", 0);
int radius = vis.getInteger("r", 0);
lim = new RoundVisibilityLimit(x_center, z_center, radius);
}
else { /* Rectangle visibility limit */
int x0 = vis.getInteger("x0", 0);
int x1 = vis.getInteger("x1", 0);
int z0 = vis.getInteger("z0", 0);
int z1 = vis.getInteger("z1", 0);
lim = new RectangleVisibilityLimit(x0, z0, x1, z1);
}
hidden_limits.add(lim);
}
}
String hiddenchunkstyle = worldconfig.getString("hidestyle", "stone");
if(hiddenchunkstyle.equals("air"))
this.hiddenchunkstyle = MapChunkCache.HiddenChunkStyle.FILL_AIR;
else if(hiddenchunkstyle.equals("ocean"))
this.hiddenchunkstyle = MapChunkCache.HiddenChunkStyle.FILL_OCEAN;
else
this.hiddenchunkstyle = MapChunkCache.HiddenChunkStyle.FILL_STONE_PLAIN;
return true;
}
/*
* Make configuration node for saving world
*/
public ConfigurationNode saveConfiguration() {
ConfigurationNode node = new ConfigurationNode();
/* Add name and title */
node.put("name", wname);
node.put("title", getTitle());
node.put("enabled", is_enabled);
node.put("protected", is_protected);
if(tileupdatedelay > 0) {
node.put("tileupdatedelay", tileupdatedelay);
}
/* Add center */
if(center != null) {
ConfigurationNode c = new ConfigurationNode();
c.put("x", center.x);
c.put("y", center.y);
c.put("z", center.z);
node.put("center", c.entries);
}
/* Add seed locations, if any */
if(seedloccfg.size() > 0) {
ArrayList<Map<String,Object>> locs = new ArrayList<Map<String,Object>>();
for(int i = 0; i < seedloccfg.size(); i++) {
DynmapLocation dl = seedloccfg.get(i);
ConfigurationNode ll = new ConfigurationNode();
ll.put("x", dl.x);
ll.put("y", dl.y);
ll.put("z", dl.z);
locs.add(ll.entries);
}
node.put("fullrenderlocations", locs);
}
/* Add flags */
node.put("sendposition", sendposition);
node.put("sendhealth", sendhealth);
node.put("extrazoomout", extrazoomoutlevels);
/* Save visibility limits, if defined */
if(visibility_limits != null) {
ArrayList<Map<String,Object>> lims = new ArrayList<Map<String,Object>>();
for(int i = 0; i < visibility_limits.size(); i++) {
VisibilityLimit lim = visibility_limits.get(i);
LinkedHashMap<String, Object> lv = new LinkedHashMap<String,Object>();
if (lim instanceof RectangleVisibilityLimit) {
RectangleVisibilityLimit rect_lim = (RectangleVisibilityLimit) lim;
lv.put("x0", rect_lim.x_min);
lv.put("z0", rect_lim.z_min);
lv.put("x1", rect_lim.x_max);
lv.put("z1", rect_lim.z_max);
}
else {
RoundVisibilityLimit round_lim = (RoundVisibilityLimit) lim;
lv.put("x", round_lim.x_center);
lv.put("z", round_lim.z_center);
lv.put("r", round_lim.radius);
}
lims.add(lv);
}
node.put("visibilitylimits", lims);
}
/* Save hidden limits, if defined */
if(hidden_limits != null) {
ArrayList<Map<String,Object>> lims = new ArrayList<Map<String,Object>>();
for(int i = 0; i < hidden_limits.size(); i++) {
VisibilityLimit lim = visibility_limits.get(i);
LinkedHashMap<String, Object> lv = new LinkedHashMap<String,Object>();
if (lim instanceof RectangleVisibilityLimit) {
RectangleVisibilityLimit rect_lim = (RectangleVisibilityLimit) lim;
lv.put("x0", rect_lim.x_min);
lv.put("z0", rect_lim.z_min);
lv.put("x1", rect_lim.x_max);
lv.put("z1", rect_lim.z_max);
}
else {
RoundVisibilityLimit round_lim = (RoundVisibilityLimit) lim;
lv.put("x", round_lim.x_center);
lv.put("z", round_lim.z_center);
lv.put("r", round_lim.radius);
}
lims.add(lv);
}
node.put("hiddenlimits", lims);
}
/* Handle hide style */
String hide = "stone";
switch(hiddenchunkstyle) {
case FILL_AIR:
hide = "air";
break;
case FILL_OCEAN:
hide = "ocean";
break;
default:
break;
}
node.put("hidestyle", hide);
/* Handle map settings */
ArrayList<Map<String,Object>> mapinfo = new ArrayList<Map<String,Object>>();
for(MapType mt : maps) {
ConfigurationNode mnode = mt.saveConfiguration();
mapinfo.add(mnode);
}
node.put("maps", mapinfo);
return node;
}
public boolean isEnabled() {
return is_enabled;
}
public void setTitle(String title) {
this.title = title;
}
public static String normalizeWorldName(String n) {
return (n != null)?n.replace('/', '-').replace('[', '_').replace(']', '_'):null;
}
public String getRawName() {
return raw_wname;
}
public boolean isProtected() {
return is_protected;
}
public int getTileUpdateDelay() {
if(tileupdatedelay > 0)
return tileupdatedelay;
else
return MapManager.mapman.getDefTileUpdateDelay();
}
public void setTileUpdateDelay(int time_sec) {
tileupdatedelay = time_sec;
}
public static void doInitialScan(boolean doscan) {
}
// Return number of chunks found (-1 if not implemented)
public int getChunkMap(TileFlags map) {
return -1;
}
// Get map state for given map
public MapTypeState getMapState(MapType m) {
for (int i = 0; i < this.maps.size(); i++) {
MapType mt = this.maps.get(i);
if (mt == m) {
return this.mapstate.get(i);
}
}
return null;
}
public void purgeTree() {
storage.purgeMapTiles(this, null);
}
public void purgeMap(MapType mt) {
storage.purgeMapTiles(this, mt);
}
public MapStorage getMapStorage() {
return storage;
}
public Polygon getWorldBorder() {
return null;
}
}

View File

@ -0,0 +1,62 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Event<T> {
private List<Listener<T>> listeners = new LinkedList<Listener<T>>();
private Object lock = new Object();
public void addListener(Listener<T> l) {
synchronized(lock) {
listeners.add(l);
}
}
public void removeListener(Listener<T> l) {
synchronized(lock) {
listeners.remove(l);
}
}
/* Only use from main thread */
public void trigger(T t) {
ArrayList<Listener<T>> iterlist;
synchronized(lock) {
iterlist = new ArrayList<Listener<T>>(listeners);
}
for (Listener<T> l : iterlist) {
l.triggered(t);
}
}
/* Trigger on main thread */
public boolean triggerSync(DynmapCore core, final T t) {
Future<T> future = core.getServer().callSyncMethod(new Callable<T>() {
@Override
public T call() throws Exception {
trigger(t);
return t;
}
});
boolean success = false;
try {
if(future != null) {
future.get();
success = true;
}
} catch (ExecutionException ix) {
Log.severe("Exception in triggerSync", ix.getCause());
} catch (InterruptedException ix) {
}
return success;
}
public interface Listener<T> {
void triggered(T t);
}
}

View File

@ -0,0 +1,44 @@
package org.dynmap;
import java.util.HashMap;
import java.util.Map;
public class Events {
public Map<String, Event<?>> events = new HashMap<String, Event<?>>();
@SuppressWarnings("unchecked")
public <T> void addListener(String eventName, Event.Listener<T> listener) {
Event<?> genericEvent = events.get(eventName);
Event<T> event = null;
if (genericEvent != null) {
event = (Event<T>)genericEvent;
} else {
events.put(eventName, event = new Event<T>());
}
event.addListener(listener);
}
@SuppressWarnings("unchecked")
public <T> void removeListener(String eventName, Event.Listener<T> listener) {
Event<?> genericEvent = events.get(eventName);
Event<T> event = null;
if (genericEvent != null) {
event = (Event<T>)genericEvent;
event.removeListener(listener);
}
}
@SuppressWarnings("unchecked")
public <T> void trigger(String eventName, T argument) {
Event<?> genericEvent = events.get(eventName);
if (genericEvent == null)
return;
((Event<T>)genericEvent).trigger(argument);
}
@SuppressWarnings("unchecked")
public <T> void triggerSync(DynmapCore core, String eventName, T argument) {
Event<?> genericEvent = events.get(eventName);
if (genericEvent == null)
return;
((Event<T>)genericEvent).triggerSync(core, argument);
}
}

View File

@ -0,0 +1,5 @@
package org.dynmap;
public interface Handler<T> {
void handle(T t);
}

View File

@ -0,0 +1,150 @@
package org.dynmap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.dynmap.servlet.ClientUpdateServlet;
import org.dynmap.servlet.SendMessageServlet;
import org.json.simple.JSONObject;
import static org.dynmap.JSONUtils.*;
public class InternalClientUpdateComponent extends ClientUpdateComponent {
protected long jsonInterval;
protected long currentTimestamp = 0;
protected long lastTimestamp = 0;
protected long lastChatTimestamp = 0;
private long last_confighash;
private ConcurrentHashMap<String, JSONObject> updates = new ConcurrentHashMap<String, JSONObject>();
private JSONObject clientConfiguration = null;
private static InternalClientUpdateComponent singleton;
public InternalClientUpdateComponent(final DynmapCore dcore, final ConfigurationNode configuration) {
super(dcore, configuration);
dcore.addServlet("/up/world/*", new ClientUpdateServlet(dcore));
jsonInterval = (long)(configuration.getFloat("writeinterval", 1) * 1000);
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 = dcore.configuration.getString("spammessage", "You may only chat once every %interval% seconds.");
final Boolean use_player_ip = configuration.getBoolean("use-player-login-ip", true);
final Boolean req_player_ip = configuration.getBoolean("require-player-login-ip", false);
final Boolean block_banned_player_chat = configuration.getBoolean("block-banned-player-chat", false);
final Boolean req_login = configuration.getBoolean("webchat-requires-login", false);
final Boolean chat_perm = configuration.getBoolean("webchat-permissions", false);
final int length_limit = configuration.getInteger("chatlengthlimit", 256);
final List<String> trustedproxy = dcore.configuration.getStrings("trusted-proxies", null);
dcore.events.addListener("buildclientconfiguration", new Event.Listener<JSONObject>() {
@Override
public void triggered(JSONObject t) {
s(t, "allowwebchat", allowwebchat);
s(t, "webchat-interval", webchatInterval);
s(t, "webchat-requires-login", req_login);
s(t, "chatlengthlimit", length_limit);
}
});
if (allowwebchat) {
@SuppressWarnings("serial")
SendMessageServlet messageHandler = new SendMessageServlet() {{
maximumMessageInterval = (int)(webchatInterval * 1000);
spamMessage = "\""+spammessage+"\"";
hideip = hidewebchatip;
this.trustclientname = trust_client_name;
this.use_player_login_ip = use_player_ip;
this.require_player_login_ip = req_player_ip;
this.check_user_ban = block_banned_player_chat;
this.require_login = req_login;
this.chat_perms = chat_perm;
this.lengthlimit = length_limit;
this.core = dcore;
if(trustedproxy != null) {
for(String s : trustedproxy) {
this.proxyaddress.add(s.trim());
}
}
else {
this.proxyaddress.add("127.0.0.1");
this.proxyaddress.add("0:0:0:0:0:0:0:1");
}
onMessageReceived.addListener(new Event.Listener<Message> () {
@Override
public void triggered(Message t) {
core.webChat(t.name, t.message);
}
});
}};
dcore.addServlet("/up/sendmessage", messageHandler);
}
core.getServer().scheduleServerTask(new Runnable() {
@Override
public void run() {
currentTimestamp = System.currentTimeMillis();
if(last_confighash != core.getConfigHashcode()) {
writeConfiguration();
}
writeUpdates();
// if (allowwebchat) {
// handleWebChat();
// }
// if(core.isLoginSupportEnabled())
// handleRegister();
lastTimestamp = currentTimestamp;
core.getServer().scheduleServerTask(this, jsonInterval/50);
}}, jsonInterval/50);
core.events.addListener("initialized", new Event.Listener<Object>() {
@Override
public void triggered(Object t) {
writeConfiguration();
writeUpdates(); /* Make sure we stay in sync */
}
});
core.events.addListener("worldactivated", new Event.Listener<DynmapWorld>() {
@Override
public void triggered(DynmapWorld t) {
writeConfiguration();
writeUpdates(); /* Make sure we stay in sync */
}
});
/* Initialize */
writeConfiguration();
writeUpdates();
singleton = this;
}
@SuppressWarnings("unchecked")
protected void writeUpdates() {
if(core.mapManager == null) return;
//Handles Updates
for (DynmapWorld dynmapWorld : core.mapManager.getWorlds()) {
JSONObject update = new JSONObject();
update.put("timestamp", currentTimestamp);
ClientUpdateEvent clientUpdate = new ClientUpdateEvent(currentTimestamp - 30000, dynmapWorld, update);
clientUpdate.include_all_users = true;
core.events.trigger("buildclientupdate", clientUpdate);
updates.put(dynmapWorld.getName(), update);
}
}
protected void writeConfiguration() {
JSONObject clientConfiguration = new JSONObject();
core.events.trigger("buildclientconfiguration", clientConfiguration);
this.clientConfiguration = clientConfiguration;
last_confighash = core.getConfigHashcode();
}
public static JSONObject getWorldUpdate(String wname) {
if(singleton != null) {
return singleton.updates.get(wname);
}
return null;
}
public static JSONObject getClientConfig() {
if(singleton != null)
return singleton.clientConfiguration;
return null;
}
}

View File

@ -0,0 +1,72 @@
package org.dynmap;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class JSONUtils {
// Gets a value at the specified path.
public static Object g(JSONObject o, String path) {
int index = path.indexOf('/');
if (index == -1) {
return o.get(path);
} else {
String key = path.substring(0, index);
String subpath = path.substring(index+1);
Object oo = o.get(key);
JSONObject subobject;
if (oo == null) {
return null;
} else /*if (oo instanceof JSONObject)*/ {
subobject = (JSONObject)o;
}
return g(subobject, subpath);
}
}
// Sets a value on the specified path. If JSONObjects inside the path are missing, they'll be created.
@SuppressWarnings("unchecked")
public static void s(JSONObject o, String path, Object value) {
int index = path.indexOf('/');
if (index == -1) {
o.put(path, value);
} else {
String key = path.substring(0, index);
String subpath = path.substring(index+1);
Object oo = o.get(key);
JSONObject subobject;
if (oo == null) {
subobject = new JSONObject();
o.put(key, subobject);
} else /*if (oo instanceof JSONObject)*/ {
subobject = (JSONObject)oo;
}
s(subobject, subpath, value);
}
}
// Adds a value to the list at the specified path. If the list does not exist, it will be created.
@SuppressWarnings("unchecked")
public static void a(JSONObject o, String path, Object value) {
Object oo = g(o, path);
JSONArray array;
if (oo == null) {
array =new JSONArray();
s(o, path, array);
} else {
array = (JSONArray)oo;
}
if(value != null)
array.add(value);
}
// Simply creates a JSONArray.
@SuppressWarnings("unchecked")
public static JSONArray l(Object... items) {
JSONArray arr = new JSONArray();
for(Object item : items) {
arr.add(item);
}
return arr;
}
}

View File

@ -0,0 +1,523 @@
package org.dynmap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.dynmap.storage.MapStorage;
import org.dynmap.utils.BufferInputStream;
import org.dynmap.utils.BufferOutputStream;
import org.dynmap.web.Json;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import static org.dynmap.JSONUtils.*;
import java.nio.charset.Charset;
public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
protected long jsonInterval;
protected long currentTimestamp = 0;
protected long lastTimestamp = 0;
protected long lastChatTimestamp = 0;
protected JSONParser parser = new JSONParser();
private boolean hidewebchatip;
private boolean useplayerloginip;
private boolean requireplayerloginip;
private boolean trust_client_name;
private boolean checkuserban;
private boolean req_login;
private boolean chat_perms;
private int lengthlimit;
private HashMap<String,String> useralias = new HashMap<String,String>();
private int aliasindex = 1;
private long last_confighash;
private MessageDigest md;
private MapStorage storage;
private File baseStandaloneDir;
private static class FileToWrite {
String filename;
byte[] content;
boolean phpwrapper;
@Override
public boolean equals(Object o) {
if(o instanceof FileToWrite) {
return ((FileToWrite)o).filename.equals(this.filename);
}
return false;
}
}
private class FileProcessor implements Runnable {
public void run() {
while(true) {
FileToWrite f = null;
synchronized(lock) {
if(files_to_write.isEmpty() == false) {
f = files_to_write.removeFirst();
}
else {
pending = null;
return;
}
}
BufferOutputStream buf = null;
if (f.content != null) {
buf = new BufferOutputStream();
if(f.phpwrapper) {
buf.write("<?php /*\n".getBytes(cs_utf8));
}
buf.write(f.content);
if(f.phpwrapper) {
buf.write("\n*/ ?>\n".getBytes(cs_utf8));
}
}
if (!storage.setStandaloneFile(f.filename, buf)) {
Log.severe("Exception while writing JSON-file - " + f.filename);
}
}
}
}
private Object lock = new Object();
private FileProcessor pending;
private LinkedList<FileToWrite> files_to_write = new LinkedList<FileToWrite>();
private void enqueueFileWrite(String filename, byte[] content, boolean phpwrap) {
FileToWrite ftw = new FileToWrite();
ftw.filename = filename;
ftw.content = content;
ftw.phpwrapper = phpwrap;
synchronized(lock) {
boolean didadd = false;
if(pending == null) {
didadd = true;
pending = new FileProcessor();
}
files_to_write.remove(ftw);
files_to_write.add(ftw);
if(didadd) {
MapManager.scheduleDelayedJob(new FileProcessor(), 0);
}
}
}
private static Charset cs_utf8 = Charset.forName("UTF-8");
public JsonFileClientUpdateComponent(final DynmapCore core, final ConfigurationNode configuration) {
super(core, configuration);
final boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
jsonInterval = (long)(configuration.getFloat("writeinterval", 1) * 1000);
hidewebchatip = configuration.getBoolean("hidewebchatip", false);
useplayerloginip = configuration.getBoolean("use-player-login-ip", true);
requireplayerloginip = configuration.getBoolean("require-player-login-ip", false);
trust_client_name = configuration.getBoolean("trustclientname", false);
checkuserban = configuration.getBoolean("block-banned-player-chat", true);
req_login = configuration.getBoolean("webchat-requires-login", false);
chat_perms = configuration.getBoolean("webchat-permissions", false);
lengthlimit = configuration.getInteger("chatlengthlimit", 256);
storage = core.getDefaultMapStorage();
baseStandaloneDir = new File(core.configuration.getString("webpath", "web"), "standalone");
if (!baseStandaloneDir.isAbsolute()) {
baseStandaloneDir = new File(core.getDataFolder(), baseStandaloneDir.toString());
}
try {
md = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException nsax) {
Log.severe("Unable to get message digest SHA-1");
}
/* Generate our config.js file */
generateConfigJS(core);
core.getServer().scheduleServerTask(new Runnable() {
@Override
public void run() {
currentTimestamp = System.currentTimeMillis();
if(last_confighash != core.getConfigHashcode()) {
writeConfiguration();
}
writeUpdates();
if (allowwebchat) {
handleWebChat();
}
if(core.isLoginSupportEnabled())
handleRegister();
lastTimestamp = currentTimestamp;
core.getServer().scheduleServerTask(this, jsonInterval/50);
}}, jsonInterval/50);
core.events.addListener("buildclientconfiguration", new Event.Listener<JSONObject>() {
@Override
public void triggered(JSONObject t) {
s(t, "jsonfile", true);
s(t, "allowwebchat", allowwebchat);
s(t, "webchat-requires-login", req_login);
s(t, "loginrequired", core.isLoginRequired());
// For 'sendmessage.php'
s(t, "webchat-interval", configuration.getFloat("webchat-interval", 5.0f));
s(t, "chatlengthlimit", lengthlimit);
}
});
core.events.addListener("initialized", new Event.Listener<Object>() {
@Override
public void triggered(Object t) {
writeConfiguration();
writeUpdates(); /* Make sure we stay in sync */
writeLogins();
writeAccess();
}
});
core.events.addListener("server-started", new Event.Listener<Object>() {
@Override
public void triggered(Object t) {
writeConfiguration();
writeUpdates(); /* Make sure we stay in sync */
writeLogins();
writeAccess();
}
});
core.events.addListener("worldactivated", new Event.Listener<DynmapWorld>() {
@Override
public void triggered(DynmapWorld t) {
writeConfiguration();
writeUpdates(); /* Make sure we stay in sync */
writeAccess();
}
});
core.events.addListener("loginupdated", new Event.Listener<Object>() {
@Override
public void triggered(Object t) {
writeLogins();
writeAccess();
}
});
core.events.addListener("playersetupdated", new Event.Listener<Object>() {
@Override
public void triggered(Object t) {
writeAccess();
}
});
}
private void generateConfigJS(DynmapCore core) {
/* Test if login support is enabled */
boolean login_enabled = core.isLoginSupportEnabled();
// configuration: 'standalone/dynmap_config.json?_={timestamp}',
// update: 'standalone/dynmap_{world}.json?_={timestamp}',
// sendmessage: 'standalone/sendmessage.php',
// login: 'standalone/login.php',
// register: 'standalone/register.php',
// tiles : 'tiles/',
// markers : 'tiles/'
// configuration: 'standalone/configuration.php',
// update: 'standalone/update.php?world={world}&ts={timestamp}',
// sendmessage: 'standalone/sendmessage.php',
// login: 'standalone/login.php',
// register: 'standalone/register.php',
// tiles : 'standalone/tiles.php?tile=',
// markers : 'standalone/markers.php?marker='
MapStorage store = core.getDefaultMapStorage();
StringBuilder sb = new StringBuilder();
sb.append("var config = {\n");
sb.append(" url : {\n");
/* Get configuration URL */
sb.append(" configuration: '");
sb.append(core.configuration.getString("url/configuration", store.getConfigurationJSONURI(login_enabled)));
sb.append("',\n");
/* Get update URL */
sb.append(" update: '");
sb.append(core.configuration.getString("url/update", store.getUpdateJSONURI(login_enabled)));
sb.append("',\n");
/* Get sendmessage URL */
sb.append(" sendmessage: '");
sb.append(core.configuration.getString("url/sendmessage", store.getSendMessageURI()));
sb.append("',\n");
/* Get login URL */
sb.append(" login: '");
sb.append(core.configuration.getString("url/login", store.getStandaloneLoginURI()));
sb.append("',\n");
/* Get register URL */
sb.append(" register: '");
sb.append(core.configuration.getString("url/register", store.getStandaloneRegisterURI()));
sb.append("',\n");
/* Get tiles URL */
sb.append(" tiles: '");
sb.append(core.configuration.getString("url/tiles", store.getTilesURI(login_enabled)));
sb.append("',\n");
/* Get markers URL */
sb.append(" markers: '");
sb.append(core.configuration.getString("url/markers", store.getMarkersURI(login_enabled)));
sb.append("'\n }\n};\n");
byte[] outputBytes = sb.toString().getBytes(cs_utf8);
File f = new File(baseStandaloneDir, "config.js");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
fos.write(outputBytes);
} catch (IOException iox) {
Log.severe("Exception while writing " + f.getPath(), iox);
} finally {
if(fos != null) {
try {
fos.close();
} catch (IOException x) {}
fos = null;
}
}
}
protected void writeConfiguration() {
JSONObject clientConfiguration = new JSONObject();
core.events.trigger("buildclientconfiguration", clientConfiguration);
last_confighash = core.getConfigHashcode();
byte[] content = clientConfiguration.toJSONString().getBytes(cs_utf8);
String outputFile;
boolean dowrap = storage.wrapStandaloneJSON(core.isLoginSupportEnabled());
if(dowrap) {
outputFile = "dynmap_config.php";
}
else {
outputFile = "dynmap_config.json";
}
enqueueFileWrite(outputFile, content, dowrap);
}
@SuppressWarnings("unchecked")
protected void writeUpdates() {
if(core.mapManager == null) return;
//Handles Updates
ArrayList<DynmapWorld> wlist = new ArrayList<DynmapWorld>(core.mapManager.getWorlds()); // Grab copy of world list
for (int windx = 0; windx < wlist.size(); windx++) {
DynmapWorld dynmapWorld = wlist.get(windx);
JSONObject update = new JSONObject();
update.put("timestamp", currentTimestamp);
ClientUpdateEvent clientUpdate = new ClientUpdateEvent(currentTimestamp - 30000, dynmapWorld, update);
clientUpdate.include_all_users = true;
core.events.trigger("buildclientupdate", clientUpdate);
String outputFile;
boolean dowrap = storage.wrapStandaloneJSON(core.isLoginSupportEnabled());
if(dowrap) {
outputFile = "updates_" + dynmapWorld.getName() + ".php";
}
else {
outputFile = "dynmap_" + dynmapWorld.getName() + ".json";
}
byte[] content = Json.stringifyJson(update).getBytes(cs_utf8);
enqueueFileWrite(outputFile, content, dowrap);
}
}
private byte[] loginhash = new byte[16];
protected void writeLogins() {
String loginFile = "dynmap_login.php";
if(core.isLoginSupportEnabled()) {
String s = core.getLoginPHP(storage.wrapStandalonePHP());
if(s != null) {
byte[] bytes = s.getBytes(cs_utf8);
md.reset();
byte[] hash = md.digest(bytes);
if(Arrays.equals(hash, loginhash)) {
return;
}
enqueueFileWrite(loginFile, bytes, false);
loginhash = hash;
}
}
else {
enqueueFileWrite(loginFile, null, false);
}
}
private byte[] accesshash = new byte[16];
protected void writeAccess() {
String accessFile = "dynmap_access.php";
String s = core.getAccessPHP(storage.wrapStandalonePHP());
if(s != null) {
byte[] bytes = s.getBytes(cs_utf8);
md.reset();
byte[] hash = md.digest(bytes);
if(Arrays.equals(hash, accesshash)) {
return;
}
enqueueFileWrite(accessFile, bytes, false);
accesshash = hash;
}
}
protected void handleWebChat() {
BufferInputStream bis = storage.getStandaloneFile("dynmap_webchat.json");
if (bis != null && lastTimestamp != 0) {
JSONArray jsonMsgs = null;
Reader inputFileReader = null;
try {
inputFileReader = new InputStreamReader(bis, cs_utf8);
jsonMsgs = (JSONArray) parser.parse(inputFileReader);
} catch (IOException ex) {
Log.severe("Exception while reading JSON-file.", ex);
} catch (ParseException ex) {
Log.severe("Exception while parsing JSON-file.", ex);
} finally {
if(inputFileReader != null) {
try {
inputFileReader.close();
} catch (IOException iox) {
}
inputFileReader = null;
}
}
if (jsonMsgs != null) {
Iterator<?> iter = jsonMsgs.iterator();
boolean init_skip = (lastChatTimestamp == 0);
while (iter.hasNext()) {
boolean ok = true;
JSONObject o = (JSONObject) iter.next();
String ts = String.valueOf(o.get("timestamp"));
if(ts.equals("null")) ts = "0";
long cts;
try {
cts = Long.parseLong(ts);
} catch (NumberFormatException nfx) {
try {
cts = (long) Double.parseDouble(ts);
} catch (NumberFormatException nfx2) {
cts = 0;
}
}
if (cts > lastChatTimestamp) {
String name = String.valueOf(o.get("name"));
String ip = String.valueOf(o.get("ip"));
String uid = null;
Object usr = o.get("userid");
if(usr != null) {
uid = String.valueOf(usr);
}
boolean isip = true;
lastChatTimestamp = cts;
if(init_skip)
continue;
if(uid == null) {
if((!trust_client_name) || (name == null) || (name.equals(""))) {
if(ip != null)
name = ip;
}
if(useplayerloginip) { /* Try to match using IPs of player logins */
List<String> ids = core.getIDsForIP(name);
if(ids != null && !ids.isEmpty()) {
name = ids.get(0);
isip = false;
if(checkuserban) {
if(core.getServer().isPlayerBanned(name)) {
Log.info("Ignore message from '" + ip + "' - banned player (" + name + ")");
ok = false;
}
}
if(chat_perms && !core.getServer().checkPlayerPermission(name, "webchat")) {
Log.info("Rejected web chat from " + ip + ": not permitted (" + name + ")");
ok = false;
}
}
else if(requireplayerloginip) {
Log.info("Ignore message from '" + name + "' - no matching player login recorded");
ok = false;
}
}
if(hidewebchatip && isip) {
String n = useralias.get(name);
if(n == null) { /* Make ID */
n = String.format("web-%03d", aliasindex);
aliasindex++;
useralias.put(name, n);
}
name = n;
}
}
else {
if(core.getServer().isPlayerBanned(uid)) {
Log.info("Ignore message from '" + uid + "' - banned user");
ok = false;
}
if(chat_perms && !core.getServer().checkPlayerPermission(uid, "webchat")) {
Log.info("Rejected web chat from " + uid + ": not permitted");
ok = false;
}
name = uid;
}
if(ok) {
String message = String.valueOf(o.get("message"));
if((lengthlimit > 0) && (message.length() > lengthlimit))
message = message.substring(0, lengthlimit);
core.webChat(name, message);
}
}
}
}
}
}
protected void handleRegister() {
if(core.pendingRegisters() == false)
return;
BufferInputStream bis = storage.getStandaloneFile("dynmap_reg.php");
if (bis != null) {
BufferedReader br = null;
ArrayList<String> lines = new ArrayList<String>();
try {
br = new BufferedReader(new InputStreamReader(bis));
String line;
while ((line = br.readLine()) != null) {
if(line.startsWith("<?") || line.startsWith("*/")) {
continue;
}
lines.add(line);
}
} catch (IOException iox) {
Log.severe("Exception while reading dynmap_reg.php", iox);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException x) {
}
br = null;
}
}
for(int i = 0; i < lines.size(); i++) {
String[] vals = lines.get(i).split("=");
if(vals.length == 3) {
core.processCompletedRegister(vals[0].trim(), vals[1].trim(), vals[2].trim());
}
}
}
}
@Override
public void dispose() {
super.dispose();
}
}

View File

@ -0,0 +1,84 @@
package org.dynmap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dynmap.utils.DynmapLogger;
public class Log {
private static Logger log = Logger.getLogger("Dynmap");
private static String prefix = "";
private static DynmapLogger dlog = null;
public static boolean verbose = false;
public static void setLogger(Logger logger, String pre) {
log = logger;
if((pre != null) && (pre.length() > 0))
prefix = pre + " ";
else
prefix = "";
}
public static void setLogger(DynmapLogger logger) {
dlog = logger;
}
public static void setLoggerParent(Logger parent) {
log.setParent(parent);
}
public static void info(String msg) {
if (dlog != null) {
dlog.info(msg);
}
else {
log.log(Level.INFO, prefix + msg);
}
}
public static void verboseinfo(String msg) {
if(verbose) {
if (dlog != null) {
dlog.info(msg);
}
else {
log.log(Level.INFO, prefix + msg);
}
}
}
public static void severe(Throwable e) {
if (dlog != null) {
dlog.severe(e);
}
else {
log.log(Level.SEVERE, prefix + "Exception occured: ", e);
}
}
public static void severe(String msg) {
if (dlog != null) {
dlog.severe(msg);
}
else {
log.log(Level.SEVERE, prefix + msg);
}
}
public static void severe(String msg, Throwable e) {
if (dlog != null) {
dlog.severe(msg, e);
}
else {
log.log(Level.SEVERE, prefix + msg, e);
}
}
public static void warning(String msg) {
if (dlog != null) {
dlog.warning(msg);
}
else {
log.log(Level.WARNING, prefix + msg);
}
}
public static void warning(String msg, Throwable e) {
if (dlog != null) {
dlog.warning(msg, e);
}
else {
log.log(Level.WARNING, prefix + msg, e);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
package org.dynmap;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.dynmap.utils.MapChunkCache;
public abstract class MapTile {
protected DynmapWorld world;
public abstract boolean render(MapChunkCache cache, String mapname);
public abstract List<DynmapChunk> getRequiredChunks();
public abstract MapTile[] getAdjecentTiles();
public DynmapWorld getDynmapWorld() {
return world;
}
public MapTile(DynmapWorld world) {
this.world = world;
}
@Override
public abstract int hashCode();
@Override
public abstract boolean equals(Object obj);
public abstract boolean isBiomeDataNeeded();
public abstract boolean isHightestBlockYDataNeeded();
public abstract boolean isRawBiomeDataNeeded();
public abstract boolean isBlockTypeDataNeeded();
public abstract int tileOrdinalX();
public abstract int tileOrdinalY();
public ConfigurationNode saveTile() {
ConfigurationNode cn = new ConfigurationNode();
cn.put("class", this.getClass().getName());
cn.put("data", saveTileData());
return cn;
}
protected abstract String saveTileData();
public static MapTile restoreTile(DynmapWorld w, ConfigurationNode node) {
String cn = node.getString("class");
String dat = node.getString("data");
if((cn == null) || (dat == null)) return null;
try {
Class<?> cls = Class.forName(cn);
Constructor<?> con = cls.getConstructor(DynmapWorld.class, String.class);
return (MapTile)con.newInstance(w, dat);
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
} catch (InvocationTargetException itx) {
} catch (IllegalAccessException iax) {
} catch (InstantiationException ix) {
}
return null;
}
}

View File

@ -0,0 +1,206 @@
package org.dynmap;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.dynmap.utils.TileFlags;
import org.json.simple.JSONObject;
public abstract class MapType {
private boolean is_protected;
protected int tileupdatedelay;
public enum ImageVariant {
STANDARD(""), // Typical image
DAY("day"); // Day (no shadow) image
public final String variantSuffix;
public final String variantID;
ImageVariant(String varid) {
if (varid.length() > 0) {
variantSuffix = "_" + varid;
}
else {
variantSuffix = "";
}
variantID = varid;
}
}
public enum ImageEncoding {
PNG("png"), JPG("jpg");
public final String ext;
ImageEncoding(String ext) {
this.ext = ext;
}
public String getFileExt() { return ext; }
public static ImageEncoding fromOrd(int ix) {
ImageEncoding[] v = values();
if ((ix >= 0) && (ix < v.length))
return v[ix];
return null;
}
public static ImageEncoding fromExt(String x) {
ImageEncoding[] v = values();
for (int i = 0; i < v.length; i++) {
if (v[i].ext.equalsIgnoreCase(x)) {
return v[i];
}
}
return null;
}
}
public enum ImageFormat {
FORMAT_PNG("png", 0.0f, ImageEncoding.PNG),
FORMAT_JPG75("jpg-q75", 0.75f, ImageEncoding.JPG),
FORMAT_JPG80("jpg-q80", 0.80f, ImageEncoding.JPG),
FORMAT_JPG85("jpg-q85", 0.85f, ImageEncoding.JPG),
FORMAT_JPG("jpg", 0.85f, ImageEncoding.JPG),
FORMAT_JPG90("jpg-q90", 0.90f, ImageEncoding.JPG),
FORMAT_JPG95("jpg-q95", 0.95f, ImageEncoding.JPG),
FORMAT_JPG100("jpg-q100", 1.00f, ImageEncoding.JPG);
String id;
float qual;
ImageEncoding enc;
ImageFormat(String id, float quality, ImageEncoding enc) {
this.id = id;
this.qual = quality;
this.enc = enc;
}
public String getID() { return id; }
public String getFileExt() { return enc.getFileExt(); }
public float getQuality() { return qual; }
public ImageEncoding getEncoding() { return enc; }
public static ImageFormat fromID(String imgfmt) {
for(ImageFormat i_f : MapType.ImageFormat.values()) {
if(i_f.getID().equals(imgfmt)) {
return i_f;
}
}
return null;
}
};
public static class ZoomInfo {
public String prefix;
public int background_argb;
public ZoomInfo(String pre, int bg) { prefix = pre; background_argb = bg; }
}
public abstract void addMapTiles(List<MapTile> list, DynmapWorld w, int tx, int ty);
public abstract List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int x, int y, int z);
public abstract List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int minx, int miny, int minz, int maxx, int maxy, int maxz);
public abstract MapTile[] getAdjecentTiles(MapTile tile);
public abstract List<DynmapChunk> getRequiredChunks(MapTile tile);
public void buildClientConfiguration(JSONObject worldObject, DynmapWorld w) {
}
public List<MapTile> getTiles(DynmapWorld w, int x, int y, int z) {
List<TileFlags.TileCoord> coords = this.getTileCoords(w, x, y, z);
ArrayList<MapTile> tiles = new ArrayList<MapTile>();
for(TileFlags.TileCoord c : coords) {
this.addMapTiles(tiles, w, c.x, c.y);
}
return tiles;
}
public abstract String getName();
/* Get maps rendered concurrently with this map in this world */
public abstract List<MapType> getMapsSharingRender(DynmapWorld w);
/* Get names of maps rendered concurrently with this map type in this world */
public abstract List<String> getMapNamesSharingRender(DynmapWorld w);
/* Return number of zoom levels needed by this map (before extra levels from extrazoomout) */
public int getMapZoomOutLevels() { return 0; }
public ImageFormat getImageFormat() { return ImageFormat.FORMAT_PNG; }
public int getBackgroundARGBNight() { return 0; }
public int getBackgroundARGBDay() { return 0; }
public int getBackgroundARGB(ImageVariant var) {
if (var == ImageVariant.DAY)
return getBackgroundARGBDay();
else
return getBackgroundARGBNight();
}
public void purgeOldTiles(DynmapWorld world, TileFlags rendered) { }
public interface FileCallback {
public void fileFound(File f, File parent, boolean day);
}
protected void walkMapTree(File root, FileCallback cb, boolean day) {
LinkedList<File> dirs = new LinkedList<File>();
String ext = "." + getImageFormat().getFileExt();
dirs.add(root);
while(dirs.isEmpty() == false) {
File dir = dirs.pop();
String[] lst = dir.list();
if(lst == null) continue;
for(String fn : lst) {
if(fn.equals(".") || fn.equals(".."))
continue;
File f = new File(dir, fn);
if(f.isDirectory()) { /* If directory, add to list to process */
dirs.add(f);
}
else if(fn.endsWith(ext)) { /* Else, if matches suffix */
cb.fileFound(f, dir, day);
}
}
}
}
public ConfigurationNode saveConfiguration() {
ConfigurationNode cn = new ConfigurationNode();
cn.put("class", this.getClass().getName()); /* Add class */
cn.put("name", getName()); /* Get map name */
return cn;
}
public boolean isProtected() {
return is_protected;
}
public boolean setProtected(boolean p) {
if(is_protected != p) {
is_protected = p;
return true;
}
return false;
}
public abstract String getPrefix();
public int getTileUpdateDelay(DynmapWorld w) {
if(tileupdatedelay > 0)
return tileupdatedelay;
else
return w.getTileUpdateDelay();
}
public boolean setTileUpdateDelay(int delay) {
if(tileupdatedelay != delay) {
tileupdatedelay = delay;
return true;
}
return false;
}
private static final ImageVariant[] defVariant = { ImageVariant.STANDARD };
public ImageVariant[] getVariants() {
return defVariant;
}
}

View File

@ -0,0 +1,275 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.dynmap.utils.TileFlags;
public class MapTypeState {
public static final long DEF_INV_PERIOD = 30;
public static final long NANOS_PER_SECOND = 1000000000L;
public MapType type;
private Object invTileLock = new Object();
private TileFlags pendingInvTiles = new TileFlags();
private TileFlags pendingInvTilesAlt = new TileFlags();
private TileFlags invTiles = new TileFlags();
private TileFlags.Iterator invTilesIter = invTiles.getIterator();
private long nextInvTS;
private long invTSPeriod;
private ArrayList<TileFlags> zoomOutInvAccum = new ArrayList<TileFlags>();
private ArrayList<TileFlags> zoomOutInv = new ArrayList<TileFlags>();
private TileFlags.Iterator zoomOutInvIter = null;
private int zoomOutInvIterLevel = -1;
private final int zoomOutLevels;
public MapTypeState(DynmapWorld world, MapType mt) {
type = mt;
invTSPeriod = DEF_INV_PERIOD * NANOS_PER_SECOND;
nextInvTS = System.nanoTime() + invTSPeriod;
zoomOutLevels = world.getExtraZoomOutLevels() + mt.getMapZoomOutLevels();
for (int i = 0; i < zoomOutLevels; i++) {
zoomOutInv.add(null);
zoomOutInvAccum.add(null);
}
}
public void setInvalidatePeriod(long inv_per_in_secs) {
invTSPeriod = inv_per_in_secs * NANOS_PER_SECOND;
}
public boolean invalidateTile(int tx, int ty) {
boolean done;
synchronized(invTileLock) {
done = !pendingInvTiles.setFlag(tx, ty, true);
}
return done;
}
public int invalidateTiles(List<TileFlags.TileCoord> coords) {
int cnt = 0;
synchronized(invTileLock) {
for(TileFlags.TileCoord c : coords) {
if(!pendingInvTiles.setFlag(c.x, c.y, true)) {
cnt++;
}
}
}
return cnt;
}
public void tickMapTypeState(long now_nano) {
if(nextInvTS < now_nano) {
synchronized(invTileLock) {
TileFlags tmp = pendingInvTilesAlt;
pendingInvTilesAlt = pendingInvTiles;
pendingInvTiles = tmp;
invTiles.union(tmp);
tmp.clear();
nextInvTS = now_nano + invTSPeriod;
}
}
}
public boolean getNextInvalidTileCoord(TileFlags.TileCoord coord) {
boolean match;
synchronized(invTileLock) {
match = invTilesIter.next(coord);
}
return match;
}
public void validateTile(int tx, int ty) {
synchronized(invTileLock) {
invTiles.setFlag(tx, ty, false);
pendingInvTiles.setFlag(tx, ty, false);
pendingInvTilesAlt.setFlag(tx, ty, false);
}
}
public boolean isInvalidTile(int tx, int ty) {
synchronized(invTileLock) {
return invTiles.getFlag(tx, ty);
}
}
public List<String> save() {
synchronized(invTileLock) {
invTiles.union(pendingInvTiles);
invTiles.union(pendingInvTilesAlt);
pendingInvTiles.clear();
pendingInvTilesAlt.clear();
return invTiles.save();
}
}
public void restore(List<String> saved) {
synchronized(invTileLock) {
TileFlags tf = new TileFlags();
tf.load(saved);
invTiles.union(tf);
}
}
public List<List<String>> saveZoomOut() {
ArrayList<List<String>> rslt = new ArrayList<List<String>>();
synchronized(invTileLock) {
boolean empty = true;
for (TileFlags tf : zoomOutInv) {
List<String> val;
if (tf == null) {
val = Collections.emptyList();
}
else {
val = tf.save();
if (val == null) {
val = Collections.emptyList();
}
else {
empty = false;
}
}
rslt.add(val);
}
for (TileFlags tf : zoomOutInvAccum) {
List<String> val;
if (tf == null) {
val = Collections.emptyList();
}
else {
val = tf.save();
if (val == null) {
val = Collections.emptyList();
}
else {
empty = false;
}
}
rslt.add(val);
}
if (empty) {
rslt = null;
}
}
return rslt;
}
public void restoreZoomOut(List<List<String>> dat) {
synchronized(invTileLock) {
int cnt = dat.size();
int cntaccum = 0;
if (cnt > zoomOutInv.size()) {
if (cnt == (2*zoomOutInv.size())) {
cntaccum = cnt / 2;
}
cnt = zoomOutInv.size();
}
for (int i = 0; i < cnt; i++) {
List<String> lst = dat.get(i);
TileFlags tf = null;
if ((lst != null) && (lst.size() > 0)) {
tf = new TileFlags();
tf.load(lst);
}
zoomOutInv.set(i, tf);
}
for (int i = 0; i < cntaccum; i++) {
List<String> lst = dat.get(i + cnt);
TileFlags tf = null;
if ((lst != null) && (lst.size() > 0)) {
tf = new TileFlags();
tf.load(lst);
}
zoomOutInvAccum.set(i, tf);
}
}
}
public int getInvCount() {
synchronized(invTileLock) {
return invTiles.countFlags();
}
}
public void clear() {
synchronized(invTileLock) {
invTiles.clear();
}
}
// Set to zoom out accum
public void setZoomOutInv(int x, int y, int zoomlevel) {
if (zoomlevel >= zoomOutLevels) {
return;
}
synchronized(invTileLock) {
TileFlags tf = zoomOutInvAccum.get(zoomlevel);
if (tf == null) {
tf = new TileFlags();
zoomOutInvAccum.set(zoomlevel, tf);
}
if ((((x >> zoomlevel) << zoomlevel) != x) ||
(((y >> zoomlevel) << zoomlevel) != y)) {
Log.info("setZoomOutInv(" + x + "," + y + "," + zoomlevel + ")");
}
tf.setFlag(x >> zoomlevel, y >> zoomlevel, true);
}
}
// Clear flag in active zoom out flags
public boolean clearZoomOutInv(int x, int y, int zoomlevel) {
if (zoomlevel >= zoomOutLevels) {
return false;
}
synchronized(invTileLock) {
TileFlags tf = zoomOutInv.get(zoomlevel);
if (tf == null) {
return false;
}
return tf.setFlag(x >> zoomlevel, y >> zoomlevel, false);
}
}
public static class ZoomOutCoord extends TileFlags.TileCoord {
public int zoomlevel;
}
// Start zoom out iteration (stash and reset accumulator)
public void startZoomOutIter() {
synchronized(invTileLock) {
ArrayList<TileFlags> tmplist = zoomOutInv;
zoomOutInv = zoomOutInvAccum;
for (int i = 0; i < tmplist.size(); i++) {
tmplist.set(i, null);
}
zoomOutInvAccum = tmplist;
zoomOutInvIter = null;
zoomOutInvIterLevel = 0;
}
}
public boolean nextZoomOutInv(ZoomOutCoord coord) {
synchronized(invTileLock) {
// Try existing iterator
if (zoomOutInvIter != null) {
if (zoomOutInvIter.hasNext()) {
zoomOutInvIter.next(coord);
coord.zoomlevel = zoomOutInvIterLevel;
coord.x = coord.x << zoomOutInvIterLevel;
coord.y = coord.y << zoomOutInvIterLevel;
return true;
}
zoomOutInvIter = null;
}
for (; zoomOutInvIterLevel < zoomOutInv.size(); zoomOutInvIterLevel++) {
TileFlags tf = zoomOutInv.get(zoomOutInvIterLevel);
if (tf != null) {
zoomOutInvIter = tf.getIterator();
if (zoomOutInvIter.hasNext()) {
zoomOutInvIter.next(coord);
coord.zoomlevel = zoomOutInvIterLevel;
coord.x = coord.x << zoomOutInvIterLevel;
coord.y = coord.y << zoomOutInvIterLevel;
return true;
}
else {
zoomOutInvIter = null;
}
}
}
}
return false;
}
}

View File

@ -0,0 +1,306 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.dynmap.common.DynmapListenerManager.EventType;
import org.dynmap.common.DynmapListenerManager.WorldEventListener;
import org.dynmap.common.DynmapListenerManager.PlayerEventListener;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.markers.AreaMarker;
import org.dynmap.markers.Marker;
import org.dynmap.markers.MarkerAPI;
import org.dynmap.markers.MarkerIcon;
import org.dynmap.markers.MarkerSet;
import org.dynmap.markers.impl.MarkerSignManager;
import org.dynmap.utils.Polygon;
/**
* Markers component - ties in the component system, both on the server and client
*/
public class MarkersComponent extends ClientComponent {
private MarkerAPI api;
private MarkerSignManager signmgr;
private MarkerIcon spawnicon;
private String spawnlbl;
private MarkerSet offlineset;
private MarkerIcon offlineicon;
private MarkerSet spawnbedset;
private MarkerIcon spawnbedicon;
private String spawnbedformat;
private long maxofflineage;
private boolean showSpawn;
private boolean showBorder;
private HashMap<String, Long> offline_times = new HashMap<String, Long>();
private static final String OFFLINE_PLAYERS_SETID = "offline_players";
private static final String PLAYER_SPAWN_BED_SETID = "spawn_beds";
public MarkersComponent(final DynmapCore core, ConfigurationNode configuration) {
super(core, configuration);
api = core.getMarkerAPI();
/* If configuration has enabled sign support, prime it too */
if(configuration.getBoolean("enablesigns", false)) {
signmgr = MarkerSignManager.initializeSignManager(core, configuration.getString("default-sign-set", MarkerSet.DEFAULT));
}
showBorder = configuration.getBoolean("showworldborder", false);
showSpawn = configuration.getBoolean("showspawn", false);
/* If we're posting spawn point markers, initialize and add world listener */
if(showSpawn) {
String ico = configuration.getString("spawnicon", MarkerIcon.WORLD);
spawnlbl = configuration.getString("spawnlabel", "Spawn");
spawnicon = api.getMarkerIcon(ico); /* Load it */
if(spawnicon == null) {
spawnicon = api.getMarkerIcon(MarkerIcon.WORLD);
}
}
if (showSpawn || showBorder) {
/* Add listener for world loads */
WorldEventListener wel = new WorldEventListener() {
@Override
public void worldEvent(DynmapWorld w) {
DynmapLocation loc = w.getSpawnLocation(); /* Get location of spawn */
if(loc != null)
addUpdateWorld(w, loc);
}
};
core.listenerManager.addListener(EventType.WORLD_LOAD, wel);
/* Add listener for spawn changes */
core.listenerManager.addListener(EventType.WORLD_SPAWN_CHANGE, wel);
/* Initialize already loaded worlds */
for(DynmapWorld w : core.getMapManager().getWorlds()) {
DynmapLocation loc = w.getSpawnLocation();
if(loc != null)
addUpdateWorld(w, loc);
}
}
/* If showing offline players as markers */
if(configuration.getBoolean("showofflineplayers", false)) {
/* Make set, if needed */
offlineset = api.getMarkerSet(OFFLINE_PLAYERS_SETID);
if(offlineset == null) {
offlineset = api.createMarkerSet(OFFLINE_PLAYERS_SETID, configuration.getString("offlinelabel", "Offline"), null, true);
}
offlineset.setHideByDefault(configuration.getBoolean("offlinehidebydefault", true));
offlineset.setMinZoom(configuration.getInteger("offlineminzoom", 0));
maxofflineage = 60000L * configuration.getInteger("maxofflinetime", 30); /* 30 minutes */
/* Now, see if existing offline markers - check for last login on their users */
if(maxofflineage > 0) {
Set<Marker> prev_m = offlineset.getMarkers();
for(Marker m : prev_m) {
DynmapPlayer p = core.getServer().getOfflinePlayer(m.getMarkerID());
if(p != null) {
long ageout = p.getLastLoginTime() + maxofflineage;
if(ageout < System.currentTimeMillis()) {
m.deleteMarker();
}
else {
offline_times.put(p.getName(), ageout);
}
}
else {
m.deleteMarker();
}
}
}
offlineicon = api.getMarkerIcon(configuration.getString("offlineicon", "offlineuser"));
if(maxofflineage > 0) {
core.getServer().scheduleServerTask(new Runnable() {
public void run() {
long ts = System.currentTimeMillis();
ArrayList<String> deleted = new ArrayList<String>();
for(Map.Entry<String,Long> me : offline_times.entrySet()) {
if(ts > me.getValue()) {
deleted.add(me.getKey());
}
}
for(String id : deleted) {
Marker m = offlineset.findMarker(id);
if(m != null)
m.deleteMarker();
}
core.getServer().scheduleServerTask(this, 30 * 20);
}
}, 30 * 20); /* Check every 30 seconds */
}
/* Add listener for players coming and going */
core.listenerManager.addListener(EventType.PLAYER_JOIN, new PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
Marker m = offlineset.findMarker(p.getName());
if(m != null) {
m.deleteMarker();
offline_times.remove(p.getName());
}
}
});
core.listenerManager.addListener(EventType.PLAYER_QUIT, new PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
String pname = p.getName();
Marker m = offlineset.findMarker(pname);
if(m != null) {
m.deleteMarker();
offline_times.remove(p.getName());
}
if(core.playerList.isVisiblePlayer(pname)) {
DynmapLocation loc = p.getLocation();
m = offlineset.createMarker(p.getName(), core.getServer().stripChatColor(p.getDisplayName()), false,
loc.world, loc.x, loc.y, loc.z, offlineicon, true);
if(maxofflineage > 0)
offline_times.put(p.getName(), System.currentTimeMillis() + maxofflineage);
}
}
});
}
else {
/* Make set, if needed */
offlineset = api.getMarkerSet(OFFLINE_PLAYERS_SETID);
if(offlineset != null) {
offlineset.deleteMarkerSet();
}
}
/* If showing player spawn bed locations as markers */
if(configuration.getBoolean("showspawnbeds", false)) {
/* Make set, if needed */
spawnbedset = api.getMarkerSet(PLAYER_SPAWN_BED_SETID);
if(spawnbedset == null) {
spawnbedset = api.createMarkerSet(PLAYER_SPAWN_BED_SETID, configuration.getString("spawnbedlabel", "Spawn Beds"), null, true);
}
spawnbedset.setHideByDefault(configuration.getBoolean("spawnbedhidebydefault", true));
spawnbedset.setMinZoom(configuration.getInteger("spawnbedminzoom", 0));
spawnbedicon = api.getMarkerIcon(configuration.getString("spawnbedicon", "bed"));
spawnbedformat = configuration.getString("spawnbedformat", "%name%'s bed");
/* Add listener for players coming and going */
core.listenerManager.addListener(EventType.PLAYER_JOIN, new PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
updatePlayer(p);
}
});
core.listenerManager.addListener(EventType.PLAYER_QUIT, new PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
Marker m = spawnbedset.findMarker(p.getName()+"_bed");
if(m != null) {
m.deleteMarker();
}
}
});
core.listenerManager.addListener(EventType.PLAYER_BED_LEAVE, new PlayerEventListener() {
@Override
public void playerEvent(final DynmapPlayer p) {
core.getServer().scheduleServerTask(new Runnable() {
public void run() {
updatePlayer(p);
}
}, 0);
}
});
}
else {
/* Make set, if needed */
spawnbedset = api.getMarkerSet(PLAYER_SPAWN_BED_SETID);
if(spawnbedset != null) {
spawnbedset.deleteMarkerSet();
}
}
}
private void updatePlayer(DynmapPlayer p) {
DynmapLocation bl = p.getBedSpawnLocation();
Marker m = spawnbedset.findMarker(p.getName()+"_bed");
if(bl == null) { /* No bed location */
if(m != null) {
m.deleteMarker();
}
}
else {
if(m != null)
m.setLocation(bl.world, bl.x, bl.y, bl.z);
else
m = spawnbedset.createMarker(p.getName()+"_bed", spawnbedformat.replace("%name%", core.getServer().stripChatColor(p.getDisplayName())), false,
bl.world, bl.x, bl.y, bl.z,
spawnbedicon, true);
}
}
private void addUpdateWorld(DynmapWorld w, DynmapLocation loc) {
MarkerSet ms = api.getMarkerSet(MarkerSet.DEFAULT);
if(ms != null) {
String spawnid = "_spawn_" + w.getName();
Marker m = ms.findMarker(spawnid); /* See if defined */
if (showSpawn) {
if(m == null) { /* Not defined yet, add it */
ms.createMarker(spawnid, spawnlbl, w.getName(), loc.x, loc.y, loc.z,
spawnicon, false);
}
else {
m.setLocation(w.getName(), loc.x, loc.y, loc.z);
}
}
else {
if (m != null) {
m.deleteMarker();
}
}
String borderid = "_worldborder_" + w.getName();
AreaMarker am = ms.findAreaMarker(borderid);
Polygon p = null;
if (showBorder) {
p = w.getWorldBorder();
}
if ((p != null) && (p.size() > 1)) {
double[] x;
double[] z;
if (p.size() == 2) {
x = new double[4];
z = new double[4];
Polygon.Point2D p0 = p.getVertex(0);
Polygon.Point2D p1 = p.getVertex(1);
x[0] = p0.x; z[0] = p0.y;
x[1] = p0.x; z[1] = p1.y;
x[2] = p1.x; z[2] = p1.y;
x[3] = p1.x; z[3] = p0.y;
}
else {
int sz = p.size();
x = new double[sz];
z = new double[sz];
for (int i = 0; i < sz; i++) {
Polygon.Point2D pi = p.getVertex(i);
x[i] = pi.x; z[i] = pi.y;
}
}
if (am == null) {
am = ms.createAreaMarker(borderid, "Border", false, w.getName(), x, z, false);
}
else {
am.setCornerLocations(x, z);
}
am.setFillStyle(0.0, 0);
}
else {
if (am != null) {
am.deleteMarker();
}
}
}
}
@Override
public void dispose() {
if(signmgr != null) {
MarkerSignManager.terminateSignManager(this.core);
signmgr = null;
}
/* Don't unregister API - other plugins might be using it, and we want to keep non-persistent markers */
}
}

View File

@ -0,0 +1,226 @@
package org.dynmap;
import org.dynmap.MapType.ImageFormat;
import org.dynmap.common.DynmapListenerManager.EventType;
import org.dynmap.common.DynmapListenerManager.PlayerEventListener;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.debug.Debug;
import org.dynmap.storage.MapStorage;
import org.dynmap.utils.BufferOutputStream;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.ImageIOManager;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.UUID;
/**
* Listen for player logins, and process player faces by fetching skins *
*/
public class PlayerFaces {
private boolean fetchskins;
private boolean refreshskins;
private String skinurl;
public MapStorage storage;
public enum FaceType {
FACE_8X8("8x8", 0),
FACE_16X16("16x16", 1),
FACE_32X32("32x32", 2),
BODY_32X32("body", 3);
public final String id;
public final int typeID;
FaceType(String id, int typeid) {
this.id = id;
this.typeID = typeid;
}
public static FaceType byID(String i_d) {
for (FaceType ft : values()) {
if (ft.id.equals(i_d)) {
return ft;
}
}
return null;
}
public static FaceType byTypeID(int tid) {
for (FaceType ft : values()) {
if (ft.typeID == tid) {
return ft;
}
}
return null;
}
}
private class LoadPlayerImages implements Runnable {
public final String playername;
public final String playerskinurl;
public final UUID playeruuid;
public LoadPlayerImages(String playername, String playerskinurl, UUID playeruuid) {
this.playername = playername;
this.playerskinurl = playerskinurl;
this.playeruuid = playeruuid;
}
public void run() {
boolean has_8x8 = storage.hasPlayerFaceImage(playername, FaceType.FACE_8X8);
boolean has_16x16 = storage.hasPlayerFaceImage(playername, FaceType.FACE_16X16);
boolean has_32x32 = storage.hasPlayerFaceImage(playername, FaceType.FACE_32X32);
boolean has_body = storage.hasPlayerFaceImage(playername, FaceType.BODY_32X32);
boolean missing_any = !(has_8x8 && has_16x16 && has_32x32 && has_body);
BufferedImage img = null;
try {
if(fetchskins && (refreshskins || missing_any)) {
URL url = null;
if (skinurl.equals("") == false) {
url = new URL(skinurl.replace("%player%", URLEncoder.encode(playername, "UTF-8")));
}
else if (playerskinurl != null) {
url = new URL(playerskinurl);
}
if (url != null) {
img = ImageIO.read(url); /* Load skin for player */
}
}
} catch (IOException iox) {
Debug.debug("Error loading skin for '" + playername + "' - " + iox);
}
if(img == null) {
try {
InputStream in = getClass().getResourceAsStream("/char.png");
img = ImageIO.read(in); /* Load generic skin for player */
in.close();
} catch (IOException iox) {
Debug.debug("Error loading default skin for '" + playername + "' - " + iox);
}
}
if(img == null) { /* No image to process? Quit */
return;
}
if((img.getWidth() < 64) || (img.getHeight() < 32)) {
img.flush();
return;
}
int[] faceaccessory = new int[64]; /* 8x8 of face accessory */
/* Get buffered image for face at 8x8 */
DynmapBufferedImage face8x8 = DynmapBufferedImage.allocateBufferedImage(8, 8);
img.getRGB(8, 8, 8, 8, face8x8.argb_buf, 0, 8); /* Read face from image */
img.getRGB(40, 8, 8, 8, faceaccessory, 0, 8); /* Read face accessory from image */
/* Apply accessory to face: see if anything is transparent (if so, apply accessory */
boolean transp = false;
int v = faceaccessory[0];
for(int i = 0; i < 64; i++) {
if((faceaccessory[i] & 0xFF000000) == 0) {
transp = true;
break;
}
/* If any different values, render face too */
else if(faceaccessory[i] != v) {
transp = true;
break;
}
}
if(transp) {
for(int i = 0; i < 64; i++) {
if((faceaccessory[i] & 0xFF000000) != 0)
face8x8.argb_buf[i] = faceaccessory[i];
}
}
/* Write 8x8 file */
if(refreshskins || (!has_8x8)) {
BufferOutputStream bos = ImageIOManager.imageIOEncode(face8x8.buf_img, ImageFormat.FORMAT_PNG);
if (bos != null) {
storage.setPlayerFaceImage(playername, FaceType.FACE_8X8, bos);
}
}
/* Write 16x16 file */
if(refreshskins || (!has_16x16)) {
/* Make 16x16 version */
DynmapBufferedImage face16x16 = DynmapBufferedImage.allocateBufferedImage(16, 16);
for(int i = 0; i < 16; i++) {
for(int j = 0; j < 16; j++) {
face16x16.argb_buf[i*16+j] = face8x8.argb_buf[(i/2)*8 + (j/2)];
}
}
BufferOutputStream bos = ImageIOManager.imageIOEncode(face16x16.buf_img, ImageFormat.FORMAT_PNG);
if (bos != null) {
storage.setPlayerFaceImage(playername, FaceType.FACE_16X16, bos);
}
DynmapBufferedImage.freeBufferedImage(face16x16);
}
/* Write 32x32 file */
if(refreshskins || (!has_32x32)) {
/* Make 32x32 version */
DynmapBufferedImage face32x32 = DynmapBufferedImage.allocateBufferedImage(32, 32);
for(int i = 0; i < 32; i++) {
for(int j = 0; j < 32; j++) {
face32x32.argb_buf[i*32+j] = face8x8.argb_buf[(i/4)*8 + (j/4)];
}
}
BufferOutputStream bos = ImageIOManager.imageIOEncode(face32x32.buf_img, ImageFormat.FORMAT_PNG);
if (bos != null) {
storage.setPlayerFaceImage(playername, FaceType.FACE_32X32, bos);
}
DynmapBufferedImage.freeBufferedImage(face32x32);
}
/* Write body file */
if(refreshskins || (!has_body)) {
/* Make 32x32 version */
DynmapBufferedImage body32x32 = DynmapBufferedImage.allocateBufferedImage(32, 32);
/* Copy face at 12,0 to 20,8 (already handled accessory) */
for(int i = 0; i < 8; i++) {
for(int j = 0; j < 8; j++) {
body32x32.argb_buf[i*32+j+12] = face8x8.argb_buf[i*8 + j];
}
}
/* Copy body at 12,8 to 20,20 */
img.getRGB(20, 20, 8, 12, body32x32.argb_buf, 8*32+12, 32); /* Read body from image */
/* Copy legs at 12,20 to 16,32 and 16,20 to 20,32 */
img.getRGB(4, 20, 4, 12, body32x32.argb_buf, 20*32+12, 32); /* Read right leg from image */
img.getRGB(4, 20, 4, 12, body32x32.argb_buf, 20*32+16, 32); /* Read left leg from image */
/* Copy arms at 8,8 to 12,20 and 20,8 to 24,20 */
img.getRGB(44, 20, 4, 12, body32x32.argb_buf, 8*32+8, 32); /* Read right leg from image */
img.getRGB(44, 20, 4, 12, body32x32.argb_buf, 8*32+20, 32); /* Read left leg from image */
BufferOutputStream bos = ImageIOManager.imageIOEncode(body32x32.buf_img, ImageFormat.FORMAT_PNG);
if (bos != null) {
storage.setPlayerFaceImage(playername, FaceType.BODY_32X32, bos);
}
DynmapBufferedImage.freeBufferedImage(body32x32);
}
DynmapBufferedImage.freeBufferedImage(face8x8);
img.flush();
/* TODO: signal update for player icon to client */
}
}
public PlayerFaces(DynmapCore core) {
fetchskins = core.configuration.getBoolean("fetchskins", true); /* Control whether to fetch skins */
refreshskins = core.configuration.getBoolean("refreshskins", true); /* Control whether to update existing fetched skins or faces */
skinurl = core.configuration.getString("skin-url", "");
// These don't work anymore - Mojang retired them
if (skinurl.equals("http://s3.amazonaws.com/MinecraftSkins/%player%.png") ||
skinurl.equals("http://skins.minecraft.net/MinecraftSkins/%player%.png")) {
skinurl = "";
}
core.listenerManager.addListener(EventType.PLAYER_JOIN, new PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
Runnable job = new LoadPlayerImages(p.getName(), p.getSkinURL(), p.getUUID());
if(fetchskins)
MapManager.scheduleDelayedJob(job, 0);
else
job.run();
}
});
storage = core.getDefaultMapStorage();
}
}

View File

@ -0,0 +1,188 @@
package org.dynmap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.common.DynmapServerInterface;
public class PlayerList {
private DynmapServerInterface server;
private HashSet<String> hiddenPlayerNames = new HashSet<String>();
private File hiddenPlayersFile;
private ConfigurationNode configuration;
private DynmapPlayer[] online;
private HashMap<String, Set<String>> invisibility_asserts = new HashMap<String, Set<String>>();
private HashMap<String, Set<String>> visibility_asserts = new HashMap<String, Set<String>>();
public PlayerList(DynmapServerInterface server, File hiddenPlayersFile, ConfigurationNode configuration) {
this.server = server;
this.hiddenPlayersFile = hiddenPlayersFile;
this.configuration = configuration;
updateOnlinePlayers(null);
}
public void save() {
OutputStream stream;
try {
stream = new FileOutputStream(hiddenPlayersFile);
OutputStreamWriter writer = new OutputStreamWriter(stream);
for (String player : hiddenPlayerNames) {
writer.write(player);
writer.write("\n");
}
writer.close();
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void load() {
try {
Scanner scanner = new Scanner(hiddenPlayersFile);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
hiddenPlayerNames.add(line);
}
scanner.close();
} catch (FileNotFoundException e) {
return;
}
}
public void hide(String playerName) {
hiddenPlayerNames.add(playerName.toLowerCase());
save();
}
public void show(String playerName) {
hiddenPlayerNames.remove(playerName.toLowerCase());
save();
}
public void setVisible(String playerName, boolean visible) {
if (visible ^ configuration.getBoolean("display-whitelist", false))
show(playerName);
else
hide(playerName);
}
public void assertVisiblilty(String playerName, boolean visible, String plugin_id) {
playerName = playerName.toLowerCase();
if(visible) {
Set<String> ids = visibility_asserts.get(playerName);
if(ids == null) {
ids = new HashSet<String>();
visibility_asserts.put(playerName, ids);
}
ids.add(plugin_id);
}
else {
Set<String> ids = visibility_asserts.get(playerName);
if(ids != null) {
ids.remove(plugin_id);
if(ids.isEmpty()) {
visibility_asserts.remove(playerName);
}
}
}
}
public void assertInvisiblilty(String playerName, boolean invisible, String plugin_id) {
playerName = playerName.toLowerCase();
if(invisible) {
Set<String> ids = invisibility_asserts.get(playerName);
if(ids == null) {
ids = new HashSet<String>();
invisibility_asserts.put(playerName, ids);
}
ids.add(plugin_id);
}
else {
Set<String> ids = invisibility_asserts.get(playerName);
if(ids != null) {
ids.remove(plugin_id);
if(ids.isEmpty()) {
invisibility_asserts.remove(playerName);
}
}
}
}
public List<DynmapPlayer> getVisiblePlayers(String worldName) {
ArrayList<DynmapPlayer> visiblePlayers = new ArrayList<DynmapPlayer>();
DynmapPlayer[] onlinePlayers = online; /* Use copied list - we don't call from server thread */
boolean useWhitelist = configuration.getBoolean("display-whitelist", false);
for (int i = 0; i < onlinePlayers.length; i++) {
DynmapPlayer p = onlinePlayers[i];
if(p == null) continue;
if((worldName != null) && (p.getWorld().equals(worldName) == false)) continue;
String pname = p.getName().toLowerCase();
if (!(useWhitelist ^ hiddenPlayerNames.contains(pname))) {
if(!invisibility_asserts.containsKey(pname)) {
visiblePlayers.add(p);
}
}
else if(visibility_asserts.containsKey(pname)) {
visiblePlayers.add(p);
}
}
return visiblePlayers;
}
public List<DynmapPlayer> getVisiblePlayers() {
return getVisiblePlayers(null);
}
public List<DynmapPlayer> getHiddenPlayers() {
ArrayList<DynmapPlayer> hidden = new ArrayList<DynmapPlayer>();
DynmapPlayer[] onlinePlayers = online; /* Use copied list - we don't call from server thread */
boolean useWhitelist = configuration.getBoolean("display-whitelist", false);
for (int i = 0; i < onlinePlayers.length; i++) {
DynmapPlayer p = onlinePlayers[i];
if(p == null) continue;
String pname = p.getName().toLowerCase();
if (!(useWhitelist ^ hiddenPlayerNames.contains(pname))) {
if(invisibility_asserts.containsKey(pname)) {
hidden.add(p);
}
}
else if(!visibility_asserts.containsKey(pname)) {
hidden.add(p);
}
}
return hidden;
}
public boolean isVisiblePlayer(String p) {
p = p.toLowerCase();
boolean useWhitelist = configuration.getBoolean("display-whitelist", false);
return (!(useWhitelist ^ hiddenPlayerNames.contains(p))) && (!invisibility_asserts.containsKey(p));
}
/**
* Call this from server thread to update player list safely
*/
void updateOnlinePlayers(String skipone) {
DynmapPlayer[] players = server.getOnlinePlayers();
DynmapPlayer[] pl = new DynmapPlayer[players.length];
System.arraycopy(players, 0, pl, 0, pl.length);
if(skipone != null) {
for(int i = 0; i < pl.length; i++)
if(pl[i].getName().equals(skipone))
pl[i] = null;
}
online = pl;
}
}

View File

@ -0,0 +1,72 @@
package org.dynmap;
import static org.dynmap.JSONUtils.s;
import org.dynmap.common.DynmapListenerManager;
import org.dynmap.common.DynmapListenerManager.ChatEventListener;
import org.dynmap.common.DynmapListenerManager.EventType;
import org.dynmap.common.DynmapPlayer;
import org.json.simple.JSONObject;
public class SimpleWebChatComponent extends Component {
public SimpleWebChatComponent(final DynmapCore plugin, final ConfigurationNode configuration) {
super(plugin, configuration);
plugin.events.addListener("webchat", new Event.Listener<ChatEvent>() {
@Override
public void triggered(ChatEvent t) {
if(plugin.getServer().sendWebChatEvent(t.source, t.name, t.message)) {
String msg;
String msgfmt = plugin.configuration.getString("webmsgformat", null);
if(msgfmt != null) {
msgfmt = unescapeString(msgfmt);
msg = msgfmt.replace("%playername%", t.name).replace("%message%", t.message);
}
else {
msg = unescapeString(plugin.configuration.getString("webprefix", "\u00A72[WEB] ")) + t.name + ": " + unescapeString(plugin.configuration.getString("websuffix", "\u00A7f")) + t.message;
}
plugin.getServer().broadcastMessage(msg);
if (core.mapManager != null) {
core.mapManager.pushUpdate(new Client.ChatMessage("web", null, t.name, t.message, null));
}
}
}
});
plugin.events.addListener("buildclientconfiguration", new Event.Listener<JSONObject>() {
@Override
public void triggered(JSONObject t) {
s(t, "allowchat", configuration.getBoolean("allowchat", false));
}
});
if (configuration.getBoolean("allowchat", false)) {
plugin.listenerManager.addListener(EventType.PLAYER_CHAT, new ChatEventListener() {
@Override
public void chatEvent(DynmapPlayer p, String msg) {
if(core.disable_chat_to_web) return;
if(core.mapManager != null)
core.mapManager.pushUpdate(new Client.ChatMessage("player", "", p.getDisplayName(), msg, p.getName()));
}
});
plugin.listenerManager.addListener(EventType.PLAYER_JOIN, new DynmapListenerManager.PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
if(core.disable_chat_to_web) return;
if((core.mapManager != null) && (core.playerList != null) && (core.playerList.isVisiblePlayer(p.getName()))) {
core.mapManager.pushUpdate(new Client.PlayerJoinMessage(p.getDisplayName(), p.getName()));
}
}
});
plugin.listenerManager.addListener(EventType.PLAYER_QUIT, new DynmapListenerManager.PlayerEventListener() {
@Override
public void playerEvent(DynmapPlayer p) {
if(core.disable_chat_to_web) return;
if((core.mapManager != null) && (core.playerList != null) && (core.playerList.isVisiblePlayer(p.getName()))) {
core.mapManager.pushUpdate(new Client.PlayerQuitMessage(p.getDisplayName(), p.getName()));
}
}
});
}
}
}

View File

@ -0,0 +1,10 @@
package org.dynmap;
public class TestComponent extends Component {
public TestComponent(DynmapCore plugin, ConfigurationNode configuration) {
super(plugin, configuration);
Log.info("Hello! I'm a component that does stuff! Like saying what is in my configuration: " + configuration.getString("stuff"));
}
}

View File

@ -0,0 +1,121 @@
package org.dynmap;
import java.util.ArrayList;
import java.util.HashMap;
public class UpdateQueue {
public Object lock = new Object();
private HashMap<UpdateRec,UpdateRec> updateSet = new HashMap<UpdateRec,UpdateRec>();
private UpdateRec orderedlist = null; /* Oldest to youngest */
private static final long maxUpdateAge = 120000;
private static final long ageOutPeriod = 5000;
private long lastageout = 0;
private static class UpdateRec {
Client.Update u;
UpdateRec next;
UpdateRec prev;
@Override
public boolean equals(Object o) {
if(o instanceof UpdateRec)
return u.equals(((UpdateRec)o).u);
return false;
}
@Override
public int hashCode() {
return u.hashCode();
}
}
private void doAgeOut(long now) {
/* If we're due */
if((now < lastageout) || (now > (lastageout + ageOutPeriod))) {
lastageout = now;
long deadline = now - maxUpdateAge;
while((orderedlist != null) && (orderedlist.u.timestamp < deadline)) {
UpdateRec r = orderedlist;
updateSet.remove(r); /* Remove record from set */
if(r.next == r) {
orderedlist = null;
}
else {
orderedlist = r.next;
r.next.prev = r.prev;
r.prev.next = r.next;
}
r.next = r.prev = null;
}
}
}
public void pushUpdate(Client.Update obj) {
synchronized (lock) {
/* Do inside lock - prevent delay between time and actual work */
long now = System.currentTimeMillis();
doAgeOut(now); /* Consider age out */
UpdateRec r = new UpdateRec();
r.u = obj;
r.u.timestamp = now; // Use our timestamp: makes sure order is preserved
UpdateRec oldr = updateSet.remove(r); /* Try to remove redundant event */
if(oldr != null) { /* If found, remove from ordered list too */
if(oldr.next == oldr) { /* Only one? */
orderedlist = null;
}
else {
if(orderedlist == oldr) { /* We're oldest? */
orderedlist = oldr.next;
}
oldr.next.prev = oldr.prev;
oldr.prev.next = oldr.next;
}
oldr.next = oldr.prev = null;
}
updateSet.put(r, r);
/* Add to end of ordered list */
if(orderedlist == null) {
orderedlist = r;
r.next = r.prev = r;
}
else {
r.next = orderedlist;
r.prev = orderedlist.prev;
r.next.prev = r.prev.next = r;
}
}
}
private ArrayList<Client.Update> tmpupdates = new ArrayList<Client.Update>();
public Client.Update[] getUpdatedObjects(long since) {
Client.Update[] updates;
synchronized (lock) {
long now = System.currentTimeMillis();
doAgeOut(now); /* Consider age out */
tmpupdates.clear();
if(orderedlist != null) {
UpdateRec r = orderedlist.prev; /* Get newest */
while(r != null) {
if(r.u.timestamp >= since) {
tmpupdates.add(r.u);
if(r == orderedlist)
r = null;
else
r = r.prev;
}
else {
r = null;
}
}
}
// Reverse output.
updates = new Client.Update[tmpupdates.size()];
for (int i = 0; i < updates.length; i++) {
updates[i] = tmpupdates.get(updates.length-1-i);
}
}
return updates;
}
}

View File

@ -0,0 +1,358 @@
package org.dynmap;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.servlet.LoginServlet;
public class WebAuthManager {
private HashMap<String, String> pwdhash_by_userid = new HashMap<String, String>();
private HashMap<String, String> pending_registrations = new HashMap<String, String>();
private String hashsalt;
private File pfile;
public static final String WEBAUTHFILE = "webauth.txt";
private static final String HASHSALT = "$HASH_SALT$";
private static final String PWDHASH_PREFIX = "hash.";
private Random rnd = new Random();
private DynmapCore core;
public WebAuthManager(DynmapCore core) {
this.core = core;
pfile = new File(core.getDataFolder(), WEBAUTHFILE);
if(pfile.canRead()) {
FileReader rf = null;
try {
rf = new FileReader(pfile);
Properties p = new Properties();
p.load(rf);
hashsalt = p.getProperty(HASHSALT);
for(String k : p.stringPropertyNames()) {
if(k.equals(HASHSALT)) {
hashsalt = p.getProperty(k);
}
else if(k.startsWith(PWDHASH_PREFIX)) { /* Load password hashes */
pwdhash_by_userid.put(k.substring(PWDHASH_PREFIX.length()).toLowerCase(), p.getProperty(k));
}
}
} catch (IOException iox) {
Log.severe("Cannot read " + WEBAUTHFILE);
} finally {
if(rf != null) { try { rf.close(); } catch (IOException iox) {} }
}
}
if(hashsalt == null) { /* No hashsalt */
hashsalt = Long.toHexString(rnd.nextLong());
}
}
public boolean save() {
boolean success = false;
FileWriter fw = null;
try {
fw = new FileWriter(pfile);
Properties p = new Properties();
p.setProperty(HASHSALT, hashsalt); /* Save salt */
for(String k : pwdhash_by_userid.keySet()) {
p.setProperty(PWDHASH_PREFIX + k, pwdhash_by_userid.get(k));
}
p.store(fw, "DO NOT EDIT THIS FILE");
success = true;
} catch (IOException iox) {
Log.severe("Error writing " + WEBAUTHFILE);
} finally {
if(fw != null) { try { fw.close(); } catch (IOException iox) {} }
}
if(success)
core.events.trigger("loginupdated", null);
return success;
}
private String makeHash(String pwd) {
String check = hashsalt + pwd;
try {
byte[] checkbytes = check.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] rslt = md.digest(checkbytes);
String rslthash = "";
for(int i = 0; i < rslt.length; i++) {
rslthash += String.format("%02X", 0xFF & (int)rslt[i]);
}
return rslthash;
} catch (NoSuchAlgorithmException nsax) {
} catch (UnsupportedEncodingException uex) {
}
return null;
}
public boolean checkLogin(String uid, String pwd) {
uid = uid.toLowerCase();
if(uid.equals(LoginServlet.USERID_GUEST)) {
return true;
}
String hash = pwdhash_by_userid.get(uid);
if(hash == null) {
return false;
}
if(core.getServer().isPlayerBanned(uid)) {
return false;
}
String checkhash = makeHash(pwd);
return hash.equals(checkhash);
}
public boolean registerLogin(String uid, String pwd, String passcode) {
uid = uid.toLowerCase();
if(uid.equals(LoginServlet.USERID_GUEST)) {
return false;
}
if(core.getServer().isPlayerBanned(uid)) {
return false;
}
passcode = passcode.toLowerCase();
String kcode = pending_registrations.remove(uid);
if(kcode == null) {
return false;
}
if(!kcode.equals(passcode)) {
return false;
}
String hash = makeHash(pwd);
pwdhash_by_userid.put(uid, hash);
return save();
}
public boolean unregisterLogin(String uid) {
if(uid.equals(LoginServlet.USERID_GUEST)) {
return true;
}
uid = uid.toLowerCase();
pwdhash_by_userid.remove(uid);
return save();
}
public boolean isRegistered(String uid) {
if(uid.equals(LoginServlet.USERID_GUEST)) {
return false;
}
uid = uid.toLowerCase();
return pwdhash_by_userid.containsKey(uid);
}
boolean processCompletedRegister(String uid, String pc, String hash) {
uid = uid.toLowerCase();
if(uid.equals(LoginServlet.USERID_GUEST)) {
return false;
}
if(core.getServer().isPlayerBanned(uid)) {
return false;
}
String kcode = pending_registrations.remove(uid);
if(kcode == null) {
return false;
}
pc = pc.toLowerCase();
if(!kcode.equals(pc)) {
return false;
}
pwdhash_by_userid.put(uid, hash);
return save();
}
public static final boolean checkUserName(String name) {
int nlen = name.length();
if ((nlen > 0) && (nlen <= 16)) {
for (int i = 0; i < nlen; i++) {
if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".indexOf(name.charAt(i)) < 0) {
return false;
}
}
return true;
}
return false;
}
public boolean processWebRegisterCommand(DynmapCore core, DynmapCommandSender sender, DynmapPlayer player, String[] args) {
String uid = null;
boolean other = false;
if(args.length > 1) {
if(!core.checkPlayerPermission(sender, "webregister.other")) {
sender.sendMessage("Not authorized to set web login information for other players");
return true;
}
uid = args[1];
other = true;
}
else if (player == null) { /* Console? */
sender.sendMessage("Must provide user ID to register web login");
return true;
}
else {
uid = player.getName();
}
if (checkUserName(uid) == false) {
sender.sendMessage("Invalid user ID");
return true;
}
String regkey = String.format("%04d-%04d", rnd.nextInt(10000), rnd.nextInt(10000));
pending_registrations.put(uid.toLowerCase(), regkey.toLowerCase());
sender.sendMessage("Registration pending for user ID: " + uid);
sender.sendMessage("Registration code: " + regkey);
sender.sendMessage("Enter ID and code on registration web page (login.html) to complete registration");
if(other) {
DynmapPlayer p = core.getServer().getPlayer(uid);
if(p != null) {
p.sendMessage("The registration of your account for web access has been started.");
p.sendMessage("To complete the process, access the Login page on the Dynmap map");
p.sendMessage("Registration code: " + regkey);
p.sendMessage("The user ID must match your account ID, but the password should NOT be the same.");
}
}
core.events.trigger("loginupdated", null);
return true;
}
String getLoginPHP(boolean wrap) {
StringBuilder sb = new StringBuilder();
if (wrap) {
sb.append("<?php\n");
}
sb.append("$pwdsalt = '").append(hashsalt).append("';\n");
/* Create password hash */
sb.append("$pwdhash = array(\n");
for(String uid : pwdhash_by_userid.keySet()) {
sb.append(" \'").append(esc(uid)).append("\' => \'").append(esc(pwdhash_by_userid.get(uid))).append("\',\n");
}
sb.append(");\n");
/* Create registration table */
sb.append("$pendingreg = array(\n");
for(String uid : pending_registrations.keySet()) {
sb.append(" \'").append(esc(uid)).append("\' => \'").append(esc(pending_registrations.get(uid))).append("\',\n");
}
sb.append(");\n");
if (wrap) {
sb.append("?>\n");
}
return sb.toString();
}
public static String esc(String s) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '\\')
sb.append("\\\\");
else if(c == '\'')
sb.append("\\\'");
else
sb.append(c);
}
return sb.toString();
}
String getAccessPHP(boolean wrap) {
StringBuilder sb = new StringBuilder();
if (wrap) {
sb.append("<?php\n");
}
ArrayList<String> mid = new ArrayList<String>();
/* Create world access list */
sb.append("$worldaccess = array(\n");
for(DynmapWorld w : core.getMapManager().getWorlds()) {
if(w.isProtected()) {
String perm = "world." + w.getName();
sb.append(" \'").append(esc(w.getName())).append("\' => \'");
for(String uid : pwdhash_by_userid.keySet()) {
if(core.getServer().checkPlayerPermission(uid, perm)) {
sb.append("[").append(esc(uid)).append("]");
}
}
sb.append("\',\n");
}
for(MapType mt : w.maps) {
if(mt.isProtected()) {
mid.add(w.getName() + "." + mt.getPrefix());
}
}
}
sb.append(");\n");
/* Create map access list */
sb.append("$mapaccess = array(\n");
for(String id : mid) {
String perm = "map." + id;
sb.append(" \'").append(esc(id)).append("\' => \'");
for(String uid : pwdhash_by_userid.keySet()) {
if(core.getServer().checkPlayerPermission(uid, perm)) {
sb.append("[").append(esc(uid)).append("]");
}
}
sb.append("\',\n");
}
sb.append(");\n");
HashSet<String> cantseeall = new HashSet<String>();
String perm = "playermarkers.seeall";
sb.append("$seeallmarkers = \'");
for(String uid : pwdhash_by_userid.keySet()) {
if(core.getServer().checkPlayerPermission(uid, perm)) {
sb.append("[").append(esc(uid)).append("]");
}
else {
cantseeall.add(uid);
}
}
sb.append("\';\n");
/* Add visibility lists for each player that doesn't see everything */
sb.append("$playervisible = array(\n");
for(String id : cantseeall) {
id = id.toLowerCase();
Set<String> vis = core.getPlayersVisibleToPlayer(id);
if((vis.size() == 1) && vis.contains(id)) continue;
sb.append(" \'").append(esc(id)).append("\' => \'");
for(String uid : vis) {
sb.append("[").append(esc(uid)).append("]");
}
sb.append("\',\n");
}
sb.append(");\n");
core.getDefaultMapStorage().addPaths(sb, core);
if (wrap) {
sb.append("?>\n");
}
return sb.toString();
}
static String getDisabledAccessPHP(DynmapCore core, boolean wrap) {
StringBuilder sb = new StringBuilder();
if (wrap) {
sb.append("<?php\n");
}
core.getDefaultMapStorage().addPaths(sb, core);
if (wrap) {
sb.append("?>\n");
}
return sb.toString();
}
boolean pendingRegisters() {
return (pending_registrations.size() > 0);
}
Set<String> getUserIDs() {
HashSet<String> lst = new HashSet<String>();
lst.addAll(pwdhash_by_userid.keySet());
lst.addAll(pending_registrations.keySet());
return lst;
}
}

View File

@ -0,0 +1,39 @@
package org.dynmap.blockstate;
import org.dynmap.DynmapCore;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
// Handler for managing mapping of block states
public class BlockStateManager {
private static IBlockStateHandler DEFAULT = new MetadataBlockStateHandler();
private IBlockStateHandler[] blockHandlers = new IBlockStateHandler[DynmapCore.BLOCKTABLELEN];
public BlockStateManager() {
// Default to all meta for now
for (int i = 0; i < blockHandlers.length; i++) {
blockHandlers[i] = DEFAULT;
}
}
/**
* Get state count for given block ID
* @param blkid - Block ID
* @return state cnt
*/
public int getBlockStateCount(int blkid) {
if ((blkid >= 0) && (blkid < blockHandlers.length)) {
return blockHandlers[blkid].getBlockStateCount();
}
return DEFAULT.getBlockStateCount();
}
/**
* Get state for current block
* @param blkctx = block context
* @return state index
*/
public int getBlockStateIndex(MapDataContext mdc) {
DynmapBlockState blk = mdc.getBlockType();
return (blk != null) ? blk.stateIndex : 0;
}
}

View File

@ -0,0 +1,20 @@
package org.dynmap.blockstate;
import org.dynmap.renderer.MapDataContext;
/**
* Interface for block state handlers
*/
public interface IBlockStateHandler {
/**
* Return number of distinct blocks states
* @return state count
*/
public int getBlockStateCount();
/**
* Map current block to state
* @param mdc - current map data context
* @return state index
*/
public int getBlockStateIndex(MapDataContext mdc);
}

View File

@ -0,0 +1,14 @@
package org.dynmap.blockstate;
import org.dynmap.renderer.MapDataContext;
public class MetadataBlockStateHandler implements IBlockStateHandler {
@Override
public int getBlockStateCount() {
return 16; // Always 16 for metadata
}
@Override
public int getBlockStateIndex(MapDataContext mdc) {
return mdc.getBlockType().stateIndex;
}
}

View File

@ -0,0 +1,222 @@
package org.dynmap.common;
import org.dynmap.hdmap.HDBlockModels;
/* Generic biome mapping */
public class BiomeMap {
private static BiomeMap[] biome_by_index = new BiomeMap[1025];
public static final BiomeMap NULL = new BiomeMap(-1, "NULL", 0.5, 0.5, 0xFFFFFF, 0, 0);
public static final BiomeMap OCEAN = new BiomeMap(0, "OCEAN");
public static final BiomeMap PLAINS = new BiomeMap(1, "PLAINS", 0.8, 0.4);
public static final BiomeMap DESERT = new BiomeMap(2, "DESERT", 2.0, 0.0);
public static final BiomeMap EXTREME_HILLS = new BiomeMap(3, "EXTREME_HILLS", 0.2, 0.3);
public static final BiomeMap FOREST = new BiomeMap(4, "FOREST", 0.7, 0.8);
public static final BiomeMap TAIGA = new BiomeMap(5, "TAIGA", 0.05, 0.8);
public static final BiomeMap SWAMPLAND = new BiomeMap(6, "SWAMPLAND", 0.8, 0.9, 0xE0FFAE, 0x4E0E4E, 0x4E0E4E);
public static final BiomeMap RIVER = new BiomeMap(7, "RIVER");
public static final BiomeMap HELL = new BiomeMap(8, "HELL", 2.0, 0.0);
public static final BiomeMap SKY = new BiomeMap(9, "SKY");
public static final BiomeMap FROZEN_OCEAN = new BiomeMap(10, "FROZEN_OCEAN", 0.0, 0.5);
public static final BiomeMap FROZEN_RIVER = new BiomeMap(11, "FROZEN_RIVER", 0.0, 0.5);
public static final BiomeMap ICE_PLAINS = new BiomeMap(12, "ICE_PLAINS", 0.0, 0.5);
public static final BiomeMap ICE_MOUNTAINS = new BiomeMap(13, "ICE_MOUNTAINS", 0.0, 0.5);
public static final BiomeMap MUSHROOM_ISLAND = new BiomeMap(14, "MUSHROOM_ISLAND", 0.9, 1.0);
public static final BiomeMap MUSHROOM_SHORE = new BiomeMap(15, "MUSHROOM_SHORE", 0.9, 1.0);
public static final BiomeMap BEACH = new BiomeMap(16, "BEACH", 0.8, 0.4);
public static final BiomeMap DESERT_HILLS = new BiomeMap(17, "DESERT_HILLS", 2.0, 0.0);
public static final BiomeMap FOREST_HILLS = new BiomeMap(18, "FOREST_HILLS", 0.7, 0.8);
public static final BiomeMap TAIGA_HILLS = new BiomeMap(19, "TAIGA_HILLS", 0.05, 0.8);
public static final BiomeMap SMALL_MOUNTAINS = new BiomeMap(20, "SMALL_MOUNTAINS", 0.2, 0.8);
public static final BiomeMap JUNGLE = new BiomeMap(21, "JUNGLE", 1.2, 0.9);
public static final BiomeMap JUNGLE_HILLS = new BiomeMap(22, "JUNGLE_HILLS", 1.2, 0.9);
public static final int LAST_WELL_KNOWN = 22;
private double tmp;
private double rain;
private int watercolormult;
private int grassmult;
private int foliagemult;
private final String id;
private final int index;
private int biomeindex256; // Standard biome mapping index (for 256 x 256)
private boolean isDef;
private static boolean loadDone = false;
public static void loadWellKnownByVersion(String mcver) {
if (loadDone) return;
if (HDBlockModels.checkVersionRange(mcver, "1.7.0-")) {
new BiomeMap(23, "JUNGLE_EDGE", 0.95, 0.8);
new BiomeMap(24, "DEEP_OCEAN");
new BiomeMap(25, "STONE_BEACH", 0.2, 0.3);
new BiomeMap(26, "COLD_BEACH", 0.05, 0.3);
new BiomeMap(27, "BIRCH_FOREST", 0.6, 0.6);
new BiomeMap(28, "BIRCH_FOREST_HILLS", 0.6, 0.6);
new BiomeMap(29, "ROOFED_FOREST", 0.7, 0.8);
new BiomeMap(30, "COLD_TAIGA", -0.5, 0.4);
new BiomeMap(31, "COLD_TAIGA_HILLS", -0.5, 0.4);
new BiomeMap(32, "MEGA_TAIGA", 0.3, 0.8);
new BiomeMap(33, "MEGA_TAIGA_HILLS", 0.3, 0.8);
new BiomeMap(34, "EXTREME_HILLS_PLUS", 0.2, 0.3);
new BiomeMap(35, "SAVANNA", 1.2, 0.0);
new BiomeMap(36, "SAVANNA_PLATEAU", 1.0, 0.0);
new BiomeMap(37, "MESA", 2.0, 0.0);
new BiomeMap(38, "MESA_PLATEAU_FOREST", 2.0, 0.0);
new BiomeMap(39, "MESA_PLATEAU", 2.0, 0.0);
new BiomeMap(129, "SUNFLOWER_PLAINS", 0.8, 0.4);
new BiomeMap(130, "DESERT_MOUNTAINS", 2.0, 0.0);
new BiomeMap(131, "EXTREME_HILLS_MOUNTAINS", 0.2, 0.3);
new BiomeMap(132, "FLOWER_FOREST", 0.7, 0.8);
new BiomeMap(133, "TAIGA_MOUNTAINS", 0.05, 0.8);
new BiomeMap(134, "SWAMPLAND_MOUNTAINS", 0.8, 0.9, 0xE0FFAE, 0x4E0E4E, 0x4E0E4E);
new BiomeMap(140, "ICE_PLAINS_SPIKES", 0.0, 0.5);
new BiomeMap(149, "JUNGLE_MOUNTAINS", 1.2, 0.9);
new BiomeMap(151, "JUNGLE_EDGE_MOUNTAINS", 0.95, 0.8);
new BiomeMap(155, "BIRCH_FOREST_MOUNTAINS", 0.6, 0.6);
new BiomeMap(156, "BIRCH_FOREST_HILLS_MOUNTAINS", 0.6, 0.6);
new BiomeMap(157, "ROOFED_FOREST_MOUNTAINS", 0.7, 0.8);
new BiomeMap(158, "COLD_TAIGA_MOUNTAINS", -0.5, 0.4);
new BiomeMap(160, "MEGA_SPRUCE_TAIGA", 0.25, 0.8);
new BiomeMap(161, "MEGA_SPRUCE_TAIGA_HILLS", 0.3, 0.8);
new BiomeMap(162, "EXTREME_HILLS_PLUS_MOUNTAINS", 0.2, 0.3);
new BiomeMap(163, "SAVANNA_MOUNTAINS", 1.2, 0.0);
new BiomeMap(164, "SAVANNA_PLATEAU_MOUNTAINS", 1.0, 0.0);
new BiomeMap(165, "MESA_BRYCE", 2.0, 0.0);
new BiomeMap(166, "MESA_PLATEAU_FOREST_MOUNTAINS", 2.0, 0.0);
new BiomeMap(167, "MESA_PLATEAU_MOUNTAINS", 2.0, 0.0);
}
if (HDBlockModels.checkVersionRange(mcver, "1.9.0-")) {
new BiomeMap(127, "THE_VOID");
}
loadDone = true;
}
static {
for (int i = 0; i < 1024; i++) {
BiomeMap bm = BiomeMap.byBiomeID(i);
if (bm == null) {
bm = new BiomeMap(i, "BIOME_" + i);
bm.isDef = true;
}
}
}
private static boolean isUniqueID(String id) {
for(int i = 0; i < biome_by_index.length; i++) {
if(biome_by_index[i] == null) continue;
if(biome_by_index[i].id.equals(id))
return false;
}
return true;
}
private BiomeMap(int idx, String id, double tmp, double rain, int waterColorMultiplier, int grassmult, int foliagemult) {
/* Clamp values : we use raw values from MC code, which are clamped during color mapping only */
setTemperature(tmp);
setRainfall(rain);
this.watercolormult = waterColorMultiplier;
this.grassmult = grassmult;
this.foliagemult = foliagemult;
// Handle null biome
if (id == null) { id = "biome_" + idx; }
id = id.toUpperCase().replace(' ', '_');
if(isUniqueID(id) == false) {
id = id + "_" + idx;
}
this.id = id;
idx++; /* Insert one after ID value - null is zero index */
this.index = idx;
if(idx >= 0) {
biome_by_index[idx] = this;
}
}
public BiomeMap(int idx, String id) {
this(idx, id, 0.5, 0.5, 0xFFFFFF, 0, 0);
}
public BiomeMap(int idx, String id, double tmp, double rain) {
this(idx, id, tmp, rain, 0xFFFFFF, 0, 0);
}
private final int biomeLookup(int width) {
int w = width-1;
int t = (int)((1.0-tmp)*w);
int h = (int)((1.0 - (tmp*rain))*w);
return width*h + t;
}
public final int biomeLookup() {
return this.biomeindex256;
}
public final int getModifiedGrassMultiplier(int rawgrassmult) {
if(grassmult == 0)
return rawgrassmult;
else if(grassmult > 0xFFFFFF)
return grassmult & 0xFFFFFF;
else
return ((rawgrassmult & 0xfefefe) + grassmult) / 2;
}
public final int getModifiedFoliageMultiplier(int rawfoliagemult) {
if(foliagemult == 0)
return rawfoliagemult;
else if(foliagemult > 0xFFFFFF)
return foliagemult & 0xFFFFFF;
else
return ((rawfoliagemult & 0xfefefe) + foliagemult) / 2;
}
public final int getWaterColorMult() {
return watercolormult;
}
public final int ordinal() {
return index;
}
public static final BiomeMap byBiomeID(int idx) {
idx++;
if((idx >= 0) && (idx < biome_by_index.length))
return biome_by_index[idx];
else
return NULL;
}
public int getBiomeID() {
return index - 1; // Index of biome in MC biome table
}
public final String toString() {
return id;
}
public static final BiomeMap[] values() {
return biome_by_index;
}
public void setWaterColorMultiplier(int watercolormult) {
this.watercolormult = watercolormult;
}
public void setGrassColorMultiplier(int grassmult) {
this.grassmult = grassmult;
}
public void setFoliageColorMultiplier(int foliagemult) {
this.foliagemult = foliagemult;
}
public void setTemperature(double tmp) {
if(tmp < 0.0) tmp = 0.0;
if(tmp > 1.0) tmp = 1.0;
this.tmp = tmp;
this.biomeindex256 = this.biomeLookup(256);
}
public void setRainfall(double rain) {
if(rain < 0.0) rain = 0.0;
if(rain > 1.0) rain = 1.0;
this.rain = rain;
this.biomeindex256 = this.biomeLookup(256);
}
public final double getTemperature() {
return this.tmp;
}
public final double getRainfall() {
return this.rain;
}
public boolean isDefault() {
return isDef;
}
}

View File

@ -0,0 +1,37 @@
package org.dynmap.common;
public enum DynmapChatColor {
BLACK(0x0),
DARK_BLUE(0x1),
DARK_GREEN(0x2),
DARK_AQUA(0x3),
DARK_RED(0x4),
DARK_PURPLE(0x5),
GOLD(0x6),
GRAY(0x7),
DARK_GRAY(0x8),
BLUE(0x9),
GREEN(0xA),
AQUA(0xB),
RED(0xC),
LIGHT_PURPLE(0xD),
YELLOW(0xE),
WHITE(0xF);
private final String str;
private DynmapChatColor(final int code) {
this.str = String.format("\u00A7%x", code);
}
@Override
public String toString() {
return str;
}
public static String stripColor(final String input) {
if (input == null) {
return null;
}
return input.replaceAll("(?i)\u00A7[0-9A-Za-z]", "");
}
}

View File

@ -0,0 +1,31 @@
package org.dynmap.common;
public interface DynmapCommandSender {
/**
* Does command sender have given security privilege
* @param privid - privilege ID
* @return true if it does, false if it doesn't
*/
public boolean hasPrivilege(String privid);
/**
* Send given message to command sender
* @param msg - message to be sent (with color codes marked &amp;0 to &amp;F)
*/
public void sendMessage(String msg);
/**
* Test if command sender is still connected/online
* @return true if connected, false if not
*/
public boolean isConnected();
/**
* Is operator privilege
* @return true if operator
*/
public boolean isOp();
/**
* Test for permission node (no dynmap. prefix assumed)
* @param node - permission ID
* @return true if allowed
*/
public boolean hasPermissionNode(String node);
}

View File

@ -0,0 +1,146 @@
package org.dynmap.common;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.dynmap.Log;
/**
* Simple handler for managing event listeners and dispatch in a neutral fashion
*
*/
public class DynmapListenerManager {
private DynmapCore core;
public DynmapListenerManager(DynmapCore core) {
this.core = core;
}
public interface EventListener {
}
public interface WorldEventListener extends EventListener {
public void worldEvent(DynmapWorld w);
}
public interface PlayerEventListener extends EventListener {
public void playerEvent(DynmapPlayer p);
}
public interface ChatEventListener extends EventListener {
public void chatEvent(DynmapPlayer p, String msg);
}
public interface BlockEventListener extends EventListener {
public void blockEvent(int blkid, String w, int x, int y, int z);
}
public interface SignChangeEventListener extends EventListener {
public void signChangeEvent(int blkid, String w, int x, int y, int z, String[] lines, DynmapPlayer p);
}
public enum EventType {
WORLD_LOAD,
WORLD_UNLOAD,
WORLD_SPAWN_CHANGE,
PLAYER_JOIN,
PLAYER_QUIT,
PLAYER_BED_LEAVE,
PLAYER_CHAT,
BLOCK_BREAK,
SIGN_CHANGE
}
private Map<EventType, ArrayList<EventListener>> listeners = new EnumMap<EventType, ArrayList<EventListener>>(EventType.class);
public void addListener(EventType type, EventListener listener) {
synchronized(listeners) {
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) {
lst = new ArrayList<EventListener>();
listeners.put(type, lst);
core.getServer().requestEventNotification(type);
}
lst.add(listener);
}
}
public void processWorldEvent(EventType type, DynmapWorld w) {
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) return;
int sz = lst.size();
for(int i = 0; i < sz; i++) {
EventListener el = lst.get(i);
if(el instanceof WorldEventListener) {
try {
((WorldEventListener)el).worldEvent(w);
} catch (Throwable t) {
Log.warning("processWorldEvent(" + type + "," + w + ") - exception", t);
}
}
}
}
public void processPlayerEvent(EventType type, DynmapPlayer p) {
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) return;
int sz = lst.size();
for(int i = 0; i < sz; i++) {
EventListener el = lst.get(i);
if(el instanceof PlayerEventListener) {
try {
((PlayerEventListener)el).playerEvent(p);
} catch (Throwable t) {
Log.warning("processPlayerEvent(" + type + "," + p + ") - exception", t);
}
}
}
}
public void processChatEvent(EventType type, DynmapPlayer p, String msg) {
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) return;
int sz = lst.size();
for(int i = 0; i < sz; i++) {
EventListener el = lst.get(i);
if(el instanceof ChatEventListener) {
try {
((ChatEventListener)el).chatEvent(p, msg);
} catch (Throwable t) {
Log.warning("processChatEvent(" + type + "," + msg + ") - exception", t);
}
}
}
}
public void processBlockEvent(EventType type, int blkid, String world, int x, int y, int z)
{
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) return;
int sz = lst.size();
for(int i = 0; i < sz; i++) {
EventListener el = lst.get(i);
if(el instanceof BlockEventListener) {
try {
((BlockEventListener)el).blockEvent(blkid, world, x, y, z);
} catch (Throwable t) {
Log.warning("processBlockEvent(" + type + "," + blkid + "," + world + "," + x + "," + y + "," + z + ") - exception", t);
}
}
}
}
public void processSignChangeEvent(EventType type, int blkid, String world, int x, int y, int z, String[] lines, DynmapPlayer p)
{
ArrayList<EventListener> lst = listeners.get(type);
if(lst == null) return;
int sz = lst.size();
for(int i = 0; i < sz; i++) {
EventListener el = lst.get(i);
if(el instanceof SignChangeEventListener) {
try {
((SignChangeEventListener)el).signChangeEvent(blkid, world, x, y, z, lines, p);
} catch (Throwable t) {
Log.warning("processSignChangeEvent(" + type + "," + blkid + "," + world + "," + x + "," + y + "," + z + ") - exception", t);
}
}
}
}
/* Clean up registered listeners */
public void cleanup() {
for(ArrayList<EventListener> l : listeners.values())
l.clear();
listeners.clear();
}
}

View File

@ -0,0 +1,97 @@
package org.dynmap.common;
import java.net.InetSocketAddress;
import java.util.UUID;
import org.dynmap.DynmapLocation;
/**
* Player (server neutral) - represents online or offline player
*/
public interface DynmapPlayer extends DynmapCommandSender {
/**
* Get player ID
* @return ID (case insensitive)
*/
public String getName();
/**
* Get player display name
* @return display name
*/
public String getDisplayName();
/**
* Is player online?
* @return true if online
*/
public boolean isOnline();
/**
* Get current location of player
* @return location
*/
public DynmapLocation getLocation();
/**
* Get world ID of player
* @return id
*/
public String getWorld();
/**
* Get connected address for player
* @return connection address, or null if unknown
*/
public InetSocketAddress getAddress();
/**
* Check if player is sneaking
* @return true if sneaking
*/
public boolean isSneaking();
/**
* Get health
* @return health points
*/
public double getHealth();
/**
* Get armor points
* @return armor points
*/
public int getArmorPoints();
/**
* Get spawn bed location
* @return bed location, or null if none
*/
public DynmapLocation getBedSpawnLocation();
/**
* Get last login time
* @return UTC time (msec) of last login
*/
public long getLastLoginTime();
/**
* Get first login time
* @return UTC time (msec) of first login
*/
public long getFirstLoginTime();
/**
* Is invisible
* @return true if invisible
*/
public boolean isInvisible();
/**
* Get sort weight (ordered lowest to highest in player list: 0=default)
* @return sort weight
*/
public int getSortWeight();
/**
* Set sort weight (ordered lowest to highest in player list: 0=default)
* @param wt - sort weight
*/
public void setSortWeight(int wt);
/**
* Get skin URL for player
* @return URL, or null if not available
*/
public default String getSkinURL() { return null; }
/**
* Get player UUID
* Return UUID, or null if not available
*/
public default UUID getUUID() { return null; }
}

View File

@ -0,0 +1,244 @@
package org.dynmap.common;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.dynmap.DynmapChunk;
import org.dynmap.DynmapWorld;
import org.dynmap.common.DynmapListenerManager.EventType;
import org.dynmap.utils.MapChunkCache;
/**
* This interface defines a server-neutral interface for the DynmapCore and other neutral components to use to access server provided
* services. Platform-specific plugin must supply DynmapCore with an instance of an object implementing this interface.
*/
public abstract class DynmapServerInterface {
/**
* Schedule task to run on server-safe thread (one suitable for other server API calls)
* @param run - runnable method
* @param delay - delay in server ticks (50msec)
*/
public abstract void scheduleServerTask(Runnable run, long delay);
/**
* Call method on server-safe thread
* @param task - Callable method
* @param <T> - return value type for method called
* @return future for completion of call
*/
public abstract <T> Future<T> callSyncMethod(Callable<T> task);
/**
* Get list of online players
* @return list of online players
*/
public abstract DynmapPlayer[] getOnlinePlayers();
/**
* Request reload of plugin
*/
public abstract void reload();
/**
* Get active player
* @param name - player name
* @return player
*/
public abstract DynmapPlayer getPlayer(String name);
/**
* Get offline player
* @param name - player name
* @return player (offline or not)
*/
public abstract DynmapPlayer getOfflinePlayer(String name);
/**
* Get banned IPs
* @return set of banned IPs
*/
public abstract Set<String> getIPBans();
/**
* Get server name
* @return server name
*/
public abstract String getServerName();
/**
* Test if player ID is banned
* @param pid - player ID
* @return true if banned
*/
public abstract boolean isPlayerBanned(String pid);
/**
* Strip out chat color
* @param s - string to strip
* @return string stripped of color codes
*/
public abstract String stripChatColor(String s);
/**
* Request notificiation for given events (used by DynmapListenerManager)
* @param type - event type
* @return true if successful
*/
public abstract boolean requestEventNotification(EventType type);
/**
* Send notification of web chat message
* @param source - source
* @param name - name
* @param msg - message text
* @return true if not cancelled
*/
public abstract boolean sendWebChatEvent(String source, String name, String msg);
/**
* Broadcast message to players
* @param msg - message
*/
public abstract void broadcastMessage(String msg);
/**
* Get Biome ID lis
* @return list of biome IDs
*/
public abstract String[] getBiomeIDs();
/**
* Get snapshot cache hit rate
* @return hit rate
*/
public abstract double getCacheHitRate();
/**
* Reset cache stats
*/
public abstract void resetCacheStats();
/**
* Get world by name
* @param wname - world name
* @return world object, or null if not found
*/
public abstract DynmapWorld getWorldByName(String wname);
/**
* Test which of given set of permisssions a possibly offline user has
* @param player - player
* @param perms - set of permission IDs
* @return set of permission IDs allowed to player
*/
public abstract Set<String> checkPlayerPermissions(String player, Set<String> perms);
/**
* Test single permission attribute
* @param player - player
* @param perm - permission ID
* @return true if permitted
*/
public abstract boolean checkPlayerPermission(String player, String perm);
/**
* Render processor helper - used by code running on render threads to request chunk snapshot cache
* @param w - world
* @param chunks - list of chunks
* @param blockdata - include block data, if true
* @param highesty - include highest Y, if true
* @param biome - include biome data, if true
* @param rawbiome - include raw biome data, if true
* @return chunk map
*/
public abstract MapChunkCache createMapChunkCache(DynmapWorld w, List<DynmapChunk> chunks,
boolean blockdata, boolean highesty, boolean biome, boolean rawbiome);
/**
* Get maximum player count
* @return maximum online players
*/
public abstract int getMaxPlayers();
/**
* Get current player count
* @return number of online players
*/
public abstract int getCurrentPlayers();
/**
* Test if given mod is loaded (Forge)
* @param name - mod name
* @return true if mod loaded
*/
public boolean isModLoaded(String name) {
return false;
}
/**
* Get version of mod with given name
*
* @param name - name of mod
* @return version, or null of not found
*/
public String getModVersion(String name) {
return null;
}
/**
* Get block ID at given coordinate in given world (if chunk is loaded)
* @param wname - world name
* @param x - X coordinate
* @param y - Y coordinate
* @param z - Z coordinate
* @return block ID, or -1 if chunk at given coordinate isn't loaded
*/
public abstract int getBlockIDAt(String wname, int x, int y, int z);
/**
* Get current TPS for server (20.0 is nominal)
* @return ticks per second
*/
public abstract double getServerTPS();
/**
* Get address configured for server
*
* @return "" or null if none configured
*/
public abstract String getServerIP();
/**
* Get file/directory for given mod (for loading mod resources)
* @param mod - mod name
* @return file or directory, or null if not loaded
*/
public File getModContainerFile(String mod) {
return null;
}
/**
* Get mod list
* @return list of mods
*/
public List<String> getModList() {
return Collections.emptyList();
}
/**
* Get block ID map (modID:blockname, keyed by block ID)
* @return block ID map
*/
public Map<Integer, String> getBlockIDMap() {
return Collections.emptyMap();
}
/**
* Open resource (check all mods)
* @param modid - mod id
* @param rname - resource namep
* @return stream, or null
*/
public InputStream openResource(String modid, String rname) {
return null;
}
/**
* Get block unique ID map (module:blockid)
* @return block unique ID map
*/
public Map<String, Integer> getBlockUniqueIDMap() {
return Collections.emptyMap();
}
/**
* Get item unique ID map (module:itemid)
* @return item unique ID map
*/
public Map<String, Integer> getItemUniqueIDMap() {
return Collections.emptyMap();
}
/**
* Test if current thread is server thread
* @return true if server thread
*/
public boolean isServerThread() {
return false;
}
}

View File

@ -0,0 +1,31 @@
package org.dynmap.debug;
import java.util.ArrayList;
public class Debug {
private static ArrayList<Debugger> debuggers = new ArrayList<Debugger>();
public synchronized static void addDebugger(Debugger d) {
debuggers.add(d);
}
public synchronized static void removeDebugger(Debugger d) {
debuggers.remove(d);
}
public synchronized static void clearDebuggers() {
debuggers.clear();
}
public synchronized static void debug(String message) {
for(int i = 0; i < debuggers.size(); i++) debuggers.get(i).debug(message);
}
public synchronized static void error(String message) {
for(int i = 0; i < debuggers.size(); i++) debuggers.get(i).error(message);
}
public synchronized static void error(String message, Throwable thrown) {
for(int i = 0; i < debuggers.size(); i++) debuggers.get(i).error(message, thrown);
}
}

View File

@ -0,0 +1,9 @@
package org.dynmap.debug;
public interface Debugger {
void debug(String message);
void error(String message);
void error(String message, Throwable thrown);
}

View File

@ -0,0 +1,27 @@
package org.dynmap.debug;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
public class LogDebugger implements Debugger {
public LogDebugger(DynmapCore core, ConfigurationNode configuration) {
}
@Override
public void debug(String message) {
Log.info(message);
}
@Override
public void error(String message) {
Log.severe(message);
}
@Override
public void error(String message, Throwable thrown) {
Log.severe(message);
thrown.printStackTrace();
}
}

View File

@ -0,0 +1,21 @@
package org.dynmap.debug;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
public class NullDebugger implements Debugger {
public static final NullDebugger instance = new NullDebugger(null, null);
public NullDebugger(DynmapCore core, ConfigurationNode configuration) {
}
public void debug(String message) {
}
public void error(String message) {
}
public void error(String message, Throwable thrown) {
}
}

View File

@ -0,0 +1,310 @@
package org.dynmap.exporter;
import java.io.File;
import java.util.HashMap;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapLocation;
import org.dynmap.DynmapWorld;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.common.DynmapPlayer;
import org.dynmap.hdmap.HDShader;
/**
* Handler for export commands (/dynmapexp)
*/
public class DynmapExpCommands {
private HashMap<String, ExportContext> sessions = new HashMap<String, ExportContext>();
private static class ExportContext {
public String shader = "stdtexture";
public int xmin = Integer.MIN_VALUE;
public int ymin = Integer.MIN_VALUE;
public int zmin = Integer.MIN_VALUE;
public int xmax = Integer.MIN_VALUE;
public int ymax = Integer.MIN_VALUE;
public int zmax = Integer.MIN_VALUE;
public String world;
public boolean groupByChunk;
public boolean groupByBlockID;
public boolean groupByBlockIDData;
public boolean groupByTexture;
}
private String getContextID(DynmapCommandSender sender) {
String id = "<console>";
if (sender instanceof DynmapPlayer) {
id = ((DynmapPlayer)sender).getName();
}
return id;
}
private ExportContext getContext(DynmapCommandSender sender) {
String id = getContextID(sender);
ExportContext ctx = sessions.get(id);
if (ctx == null) {
ctx = new ExportContext();
sessions.put(id, ctx);
}
return ctx;
}
public boolean processCommand(DynmapCommandSender sender, String cmd, String commandLabel, String[] args, DynmapCore core) {
/* Re-parse args - handle doublequotes */
args = DynmapCore.parseArgs(args, sender);
if(args.length < 1)
return false;
if(!core.checkPlayerPermission(sender, "dynmapexp.export")) {
return true;
}
cmd = args[0];
boolean rslt = false;
ExportContext ctx = getContext(sender);
if(cmd.equalsIgnoreCase("set")) {
rslt = handleSetExport(sender, args, ctx, core);
}
else if (cmd.equalsIgnoreCase("radius")) {
rslt = handleRadius(sender, args, ctx, core);
}
else if (cmd.equalsIgnoreCase("pos0")) {
rslt = handlePosN(sender, args, ctx, core, 0);
}
else if (cmd.equalsIgnoreCase("pos1")) {
rslt = handlePosN(sender, args, ctx, core, 1);
}
else if (cmd.equalsIgnoreCase("export")) {
rslt = handleDoExport(sender, args, ctx, core);
}
else if(cmd.equalsIgnoreCase("reset")) {
rslt = handleResetExport(sender, args, ctx, core);
}
else if(cmd.equalsIgnoreCase("purge")) {
rslt = handlePurgeExport(sender, args, ctx, core);
}
else if(cmd.equalsIgnoreCase("info")) {
rslt = handleInfo(sender, args, ctx, core);
}
return rslt;
}
private boolean handleInfo(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
sender.sendMessage(String.format("Bounds: <%s,%s,%s> - <%s,%s,%s> on world '%s'", val(ctx.xmin), val(ctx.ymin), val(ctx.zmin),
val(ctx.xmax), val(ctx.ymax), val(ctx.zmax), ctx.world));
sender.sendMessage(String.format("groups: byChunk: %b, byBlockID: %b, byBlockIDData: %b, byTexture: %b", ctx.groupByChunk, ctx.groupByBlockID, ctx.groupByBlockIDData, ctx.groupByTexture));
return true;
}
private boolean handleSetExport(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
if (args.length < 3) {
sender.sendMessage(String.format("Bounds: <%s,%s,%s> - <%s,%s,%s> on world '%s'", val(ctx.xmin), val(ctx.ymin), val(ctx.zmin),
val(ctx.xmax), val(ctx.ymax), val(ctx.zmax), ctx.world));
return true;
}
for (int i = 1; i < (args.length-1); i += 2) {
try {
if (args[i].equals("x0")) {
ctx.xmin = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("x1")) {
ctx.xmax = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("y0")) {
ctx.ymin = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("y1")) {
ctx.ymax = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("z0")) {
ctx.zmin = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("z1")) {
ctx.zmax = Integer.parseInt(args[i+1]);
}
else if (args[i].equals("world")) {
DynmapWorld w = core.getWorld(args[i+1]);
if (w != null) {
ctx.world = args[i+1];
}
else {
sender.sendMessage("Invalid world '" + args[i+1] + "'");
return true;
}
}
else if (args[i].equals("shader")) {
HDShader s = MapManager.mapman.hdmapman.shaders.get(args[i+1]);
if (s == null) {
sender.sendMessage("Unknown shader '" + args[i+1] + "'");
return true;
}
ctx.shader = args[i+1];
}
else if (args[i].equals("byChunk")) {
ctx.groupByChunk = args[i+1].equalsIgnoreCase("true");
}
else if (args[i].equals("byBlockID")) {
ctx.groupByBlockID = args[i+1].equalsIgnoreCase("true");
}
else if (args[i].equals("byBlockIDData")) {
ctx.groupByBlockIDData = args[i+1].equalsIgnoreCase("true");
}
else if (args[i].equals("byTexture")) {
ctx.groupByTexture = args[i+1].equalsIgnoreCase("true");
}
else { // Unknown setting
sender.sendMessage("Unknown setting '" + args[i] + "'");
return true;
}
} catch (NumberFormatException nfx) {
sender.sendMessage("Invalid value for '" + args[i] + "' - " + args[i+1]);
return true;
}
}
return handleInfo(sender, args, ctx, core);
}
private boolean handleRadius(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
if ((sender instanceof DynmapPlayer) == false) { // Not a player
sender.sendMessage("Only usable by player");
return true;
}
DynmapPlayer plyr = (DynmapPlayer) sender;
DynmapLocation loc = plyr.getLocation();
DynmapWorld world = null;
if (loc != null) {
world = core.getWorld(loc.world);
}
if (world == null) {
sender.sendMessage("Location not found for player");
return true;
}
int radius = 16;
if (args.length >= 2) {
try {
radius = Integer.parseInt(args[1]);
if (radius < 0) {
sender.sendMessage("Invalid radius - " + args[1]);
return true;
}
} catch (NumberFormatException nfx) {
sender.sendMessage("Invalid radius - " + args[1]);
return true;
}
}
ctx.xmin = (int)Math.floor(loc.x) - radius;
ctx.xmax = (int)Math.ceil(loc.x) + radius;
ctx.zmin = (int)Math.floor(loc.z) - radius;
ctx.zmax = (int)Math.ceil(loc.z) + radius;
ctx.ymin = 0;
ctx.ymax = world.worldheight - 1;
ctx.world = world.getName();
return handleInfo(sender, args, ctx, core);
}
private boolean handlePosN(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core, int n) {
if ((sender instanceof DynmapPlayer) == false) { // Not a player
sender.sendMessage("Only usable by player");
return true;
}
DynmapPlayer plyr = (DynmapPlayer) sender;
DynmapLocation loc = plyr.getLocation();
DynmapWorld world = null;
if (loc != null) {
world = core.getWorld(loc.world);
}
if (world == null) {
sender.sendMessage("Location not found for player");
return true;
}
if (n == 0) {
ctx.xmin = (int)Math.floor(loc.x);
ctx.ymin = (int)Math.floor(loc.y);
ctx.zmin = (int)Math.floor(loc.z);
}
else {
ctx.xmax = (int)Math.floor(loc.x);
ctx.ymax = (int)Math.floor(loc.y);
ctx.zmax = (int)Math.floor(loc.z);
}
ctx.world = world.getName();
return handleInfo(sender, args, ctx, core);
}
private boolean handleDoExport(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
if ((ctx.world == null) || (ctx.xmin == Integer.MIN_VALUE) || (ctx.ymin == Integer.MIN_VALUE) ||
(ctx.zmin == Integer.MIN_VALUE) || (ctx.xmax == Integer.MIN_VALUE) || (ctx.ymax == Integer.MIN_VALUE) ||
(ctx.zmax == Integer.MIN_VALUE)) {
sender.sendMessage("Bounds not set");
return true;
}
DynmapWorld w = core.getWorld(ctx.world);
if (w == null) {
sender.sendMessage("Invalid world - " + ctx.world);
return true;
}
HDShader s = MapManager.mapman.hdmapman.shaders.get(ctx.shader);
if (s == null) {
sender.sendMessage("Invalid shader - " + ctx.shader);
return true;
}
String basename = "dynmapexp";
if (args.length > 1) {
basename = args[1];
}
basename = basename.replace('/', '_');
basename = basename.replace('\\', '_');
File f = new File(core.getExportFolder(), basename + ".zip");
int idx = 0;
String finalBasename = basename;
while (f.exists()) {
idx++;
finalBasename = basename + "_" + idx;
f = new File(core.getExportFolder(), finalBasename + ".zip");
}
sender.sendMessage("Exporting to " + f.getPath());
OBJExport exp = new OBJExport(f, s, w, core, finalBasename);
exp.setRenderBounds(ctx.xmin, ctx.ymin, ctx.zmin, ctx.xmax, ctx.ymax, ctx.zmax);
exp.setGroupEnabled(OBJExport.GROUP_CHUNK, ctx.groupByChunk);
exp.setGroupEnabled(OBJExport.GROUP_TEXTURE, ctx.groupByTexture);
exp.setGroupEnabled(OBJExport.GROUP_BLOCKID, ctx.groupByBlockID);
exp.setGroupEnabled(OBJExport.GROUP_BLOCKIDMETA, ctx.groupByBlockIDData);
MapManager.mapman.startOBJExport(exp, sender);
return true;
}
private boolean handleResetExport(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
sessions.remove(getContextID(sender));
return true;
}
private boolean handlePurgeExport(DynmapCommandSender sender, String[] args, ExportContext ctx, DynmapCore core) {
if (args.length > 1) {
String basename = args[1];
basename = basename.replace('/', '_');
basename = basename.replace('\\', '_');
File f = new File(core.getExportFolder(), basename + ".zip");
if (f.exists()) {
f.delete();
sender.sendMessage("Removed " + f.getPath());
}
else {
sender.sendMessage(f.getPath() + " not found");
}
}
return true;
}
private String val(int v) {
if (v == Integer.MIN_VALUE)
return "N/A";
else
return Integer.toString(v);
}
}

View File

@ -0,0 +1,647 @@
package org.dynmap.exporter;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.dynmap.DynmapChunk;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.hdmap.CustomBlockModel;
import org.dynmap.hdmap.HDBlockModels;
import org.dynmap.hdmap.HDBlockStateTextureMap;
import org.dynmap.hdmap.HDScaledBlockModels;
import org.dynmap.hdmap.HDShader;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.IndexedVector3D;
import org.dynmap.utils.IndexedVector3DList;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.dynmap.utils.PatchDefinition;
import org.dynmap.utils.PatchDefinitionFactory;
public class OBJExport {
private final File destZipFile; // Destination ZIP file
private final HDShader shader; // Shader to be used for textures
private final DynmapWorld world; // World to be rendered
private final DynmapCore core;
private final String basename;
private int minX, minY, minZ; // Minimum world coordinates to be rendered
private int maxX, maxY, maxZ; // Maximum world coordinates to be rendered
private static Charset UTF8 = Charset.forName("UTF-8");
private ZipOutputStream zos; // Output stream ZIP for result
private double originX, originY, originZ; // Origin for exported model
private double scale = 1.0; // Scale for exported model
private boolean centerOrigin = true; // Center at origin
private PatchDefinition[] defaultPathces; // Default patches for solid block, indexed by BlockStep.ordinal()
private HashSet<String> matIDs = new HashSet<String>(); // Set of defined material ids for RP
private static class Face {
String groupLine;
String faceLine;
}
private HashMap<String, List<Face>> facesByTexture = new HashMap<String, List<Face>>();
private static final int MODELSCALE = 16;
private static final double BLKSIZE = 1.0 / (double) MODELSCALE;
// Index of group settings
public static final int GROUP_CHUNK = 0;
public static final int GROUP_TEXTURE = 1;
public static final int GROUP_BLOCKID = 2;
public static final int GROUP_BLOCKIDMETA = 3;
public static final int GROUP_COUNT = 4;
private String[] group = new String[GROUP_COUNT];
private boolean[] enabledGroups = new boolean[GROUP_COUNT];
private String groupline = null;
// Vertex set
private IndexedVector3DList vertices;
// UV set
private IndexedVector3DList uvs;
// Scaled models
private HDScaledBlockModels models;
public static final int ROT0 = 0;
public static final int ROT90 = 1;
public static final int ROT180 = 2;
public static final int ROT270 = 3;
public static final int HFLIP = 4;
private static final double[][] pp = {
{ 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 0, 1, 1, 1, 1, 1, 0, 1, 0 },
{ 1, 0, 0, 0, 0, 0, 1, 1, 0 },
{ 0, 0, 1, 1, 0, 1, 0, 1, 1 },
{ 0, 0, 0, 0, 0, 1, 0, 1, 0 },
{ 1, 0, 1, 1, 0, 0, 1, 1, 1 }
};
/**
* Constructor for OBJ file export
* @param dest - destination file (ZIP)
* @param shader - shader to be used for coloring/texturing
* @param world - world to be rendered
* @param core - core object
* @param basename - base file name
*/
public OBJExport(File dest, HDShader shader, DynmapWorld world, DynmapCore core, String basename) {
destZipFile = dest;
this.shader = shader;
this.world = world;
this.core = core;
this.basename = basename;
this.defaultPathces = new PatchDefinition[6];
PatchDefinitionFactory fact = HDBlockModels.getPatchDefinitionFactory();
for (BlockStep s : BlockStep.values()) {
double[] p = pp[s.getFaceEntered()];
int ord = s.ordinal();
defaultPathces[ord] = fact.getPatch(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], 0, 1, 0, 1, 100, SideVisible.TOP, ord);
}
vertices = new IndexedVector3DList(new IndexedVector3DList.ListCallback() {
@Override
public void elementAdded(IndexedVector3DList list, IndexedVector3D newElement) {
try {
/* Minecraft XYZ maps to OBJ YZX */
addStringToExportedFile(String.format(Locale.US, "v %.4f %.4f %.4f\n",
(newElement.x - originX) * scale,
(newElement.y - originY) * scale,
(newElement.z - originZ) * scale
));
} catch (IOException iox) {
}
}
});
uvs = new IndexedVector3DList(new IndexedVector3DList.ListCallback() {
@Override
public void elementAdded(IndexedVector3DList list, IndexedVector3D newElement) {
try {
addStringToExportedFile(String.format(Locale.US, "vt %.4f %.4f\n", newElement.x, newElement.y));
} catch (IOException iox) {
}
}
});
// Get models
models = HDBlockModels.getModelsForScale(MODELSCALE);
}
/**
* Set render bounds
*
* @param minx - minimum X coord
* @param miny - minimum Y coord
* @param minz - minimum Z coord
* @param maxx - maximum X coord
* @param maxy - maximum Y coord
* @param maxz - maximum Z coord
*/
public void setRenderBounds(int minx, int miny, int minz, int maxx, int maxy, int maxz) {
if (minx < maxx) {
minX = minx; maxX = maxx;
}
else {
minX = maxx; maxX = minx;
}
if (miny < maxy) {
minY = miny; maxY = maxy;
}
else {
minY = maxy; maxY = miny;
}
if (minz < maxz) {
minZ = minz; maxZ = maxz;
}
else {
minZ = maxz; maxZ = minz;
}
if (minY < 0) minY = 0;
if (maxY >= world.worldheight) maxY = world.worldheight - 1;
if (centerOrigin) {
originX = (maxX + minX) / 2.0;
originY = minY;
originZ = (maxZ + minZ) / 2.0;
}
}
/**
* Set origin for exported model
* @param ox - origin x
* @param oy - origin y
* @param oz - origin z
*/
public void setOrigin(double ox, double oy, double oz) {
originX = ox;
originY = oy;
originZ = oz;
centerOrigin = false;
}
/**
* Set scale for exported model
* @param scale = scale
*/
public void setScale(double scale) {
this.scale = scale;
}
/**
* Process export
*
* @param sender - command sender: use for feedback messages
* @return true if successful, false if not
*/
public boolean processExport(DynmapCommandSender sender) {
boolean good = false;
try {
// Open ZIP file destination
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(destZipFile)));
List<DynmapChunk> requiredChunks = new ArrayList<DynmapChunk>();
int mincx = (minX >> 4);
int maxcx = (maxX + 15) >> 4;
int mincz = (minZ >> 4);
int maxcz = (maxZ + 15) >> 4;
boolean[] edgebits = new boolean[6];
startExportedFile(basename + ".obj");
// Add material library
addStringToExportedFile("mtllib " + basename + ".mtl\n");
// Loop through - do 8x8 chunks at a time (plus 1 border each way)
for (int cx = mincx; cx <= maxcx; cx += 4) {
for (int cz = mincz; cz <= maxcz; cz += 4) {
// Build chunk cache for block of chunks
requiredChunks.clear();
for (int i = -1; i < 5; i++) {
for (int j = -1; j < 5; j++) {
if (((cx+i) <= maxcx) && ((cz+j) <= maxcz) && ((cx+i) >= mincx) && ((cz+j) >= mincz)) {
requiredChunks.add(new DynmapChunk(cx + i, cz + j));
}
}
}
// Get the chunk buffer
MapChunkCache cache = core.getServer().createMapChunkCache(world, requiredChunks, true, false, true, false);
if (cache == null) {
throw new IOException("Error loading chunk cache");
}
MapIterator iter = cache.getIterator(minX, minY, minZ);
for (int x = cx * 16; (x < (cx * 16 + 64)) && (x <= maxX); x++) {
if (x < minX) x = minX;
edgebits[BlockStep.X_PLUS.ordinal()] = (x == minX);
edgebits[BlockStep.X_MINUS.ordinal()] = (x == maxX);
for (int z = cz * 16; (z < (cz * 16 + 64)) && (z <= maxZ); z++) {
if (z < minZ) z = minZ;
edgebits[BlockStep.Z_PLUS.ordinal()] = (z == minZ);
edgebits[BlockStep.Z_MINUS.ordinal()] = (z == maxZ);
iter.initialize(x, minY, z);
updateGroup(GROUP_CHUNK, "chunk" + (x >> 4) + "_" + (z >> 4));
// Do first (bottom)
edgebits[BlockStep.Y_MINUS.ordinal()] = true;
edgebits[BlockStep.Y_PLUS.ordinal()] = false;
DynmapBlockState blk = iter.getBlockType();
if (blk.isNotAir()) { // Not air
handleBlock(blk, iter, edgebits);
}
// Do middle
edgebits[BlockStep.Y_MINUS.ordinal()] = false;
for (int y = minY + 1; y < maxY; y++) {
iter.setY(y);
blk = iter.getBlockType();
if (blk.isNotAir()) { // Not air
handleBlock(blk, iter, edgebits);
}
}
// Do top
edgebits[BlockStep.Y_PLUS.ordinal()] = true;
iter.setY(maxY);
blk = iter.getBlockType();
if (blk.isNotAir()) { // Not air
handleBlock(blk, iter, edgebits);
}
}
}
// Output faces by texture
String grp = "";
for (String material : facesByTexture.keySet()) {
List<Face> faces = facesByTexture.get(material);
matIDs.add(material); // Record material use
addStringToExportedFile(String.format("usemtl %s\n", material));
for (Face face : faces) {
if ((face.groupLine != null) && (!face.groupLine.equals(grp))) {
grp = face.groupLine;
addStringToExportedFile(grp);
}
addStringToExportedFile(face.faceLine);
}
}
// Clear face table
facesByTexture.clear();
// Clean up vertices we've moved past
vertices.resetSet(minX, minY, minZ, cx * 16 + 64, maxY, cz * 16 + 64);
}
}
finishExportedFile();
// If shader provided, add shader content to ZIP
if (shader != null) {
sender.sendMessage("Adding textures from shader " + shader.getName());
shader.exportAsMaterialLibrary(sender, this);
sender.sendMessage("Texture export completed");
}
// And close the ZIP
zos.finish();
zos.close();
zos = null;
good = true;
sender.sendMessage("Export completed - " + destZipFile.getPath());
} catch (IOException iox) {
sender.sendMessage("Export failed: " + iox.getMessage());
} finally {
if (zos != null) {
try { zos.close(); } catch (IOException e) {}
zos = null;
destZipFile.delete();
}
}
return good;
}
/**
* Start adding file to export
* @param fname - path/name of file in destination zip
* @throws IOException if error starting file
*/
public void startExportedFile(String fname) throws IOException {
ZipEntry ze = new ZipEntry(fname);
zos.putNextEntry(ze);
}
/**
* Add bytes to current exported file
* @param buf - buffer with bytes
* @param off - offset of start
* @param len - length to be added
* @throws IOException if error adding to file
*/
public void addBytesToExportedFile(byte[] buf, int off, int len) throws IOException {
zos.write(buf, off, len);
}
/**
* Add string to curent exported file (UTF-8)
* @param str - string to be written
* @throws IOException if error adding to file
*/
public void addStringToExportedFile(String str) throws IOException {
byte[] b = str.getBytes(UTF8);
zos.write(b, 0, b.length);
}
/**
* Finish adding file to export
* @throws IOException if error completing file
*/
public void finishExportedFile() throws IOException {
zos.closeEntry();
}
/**
* Handle block at current iterator coord
* @param id - block ID
* @param iter - iterator
* @param edgebits - bit N corresponds to side N being an endge (forge render)
*/
private void handleBlock(DynmapBlockState blk, MapIterator map, boolean[] edgebits) throws IOException {
BlockStep[] steps = BlockStep.values();
int[] txtidx = null;
// See if the block has a patch model
RenderPatch[] patches = models.getPatchModel(blk);
/* If no patches, see if custom model */
if(patches == null) {
CustomBlockModel cbm = models.getCustomBlockModel(blk);
if(cbm != null) { /* If so, get our meshes */
patches = cbm.getMeshForBlock(map);
}
}
if (patches != null) {
steps = new BlockStep[patches.length];
txtidx = new int[patches.length];
for (int i = 0; i < txtidx.length; i++) {
txtidx[i] = ((PatchDefinition) patches[i]).getTextureIndex();
steps[i] = ((PatchDefinition) patches[i]).step;
}
}
else { // See if volumetric
short[] smod = models.getScaledModel(blk);
if (smod != null) {
patches = getScaledModelAsPatches(smod);
steps = new BlockStep[patches.length];
txtidx = new int[patches.length];
for (int i = 0; i < patches.length; i++) {
PatchDefinition pd = (PatchDefinition) patches[i];
steps[i] = pd.step;
txtidx[i] = pd.getTextureIndex();
}
}
}
// Set block ID and ID+meta groups
updateGroup(GROUP_BLOCKID, "blk" + blk.baseState.globalStateIndex);
updateGroup(GROUP_BLOCKIDMETA, "blk" + blk.globalStateIndex);
// Get materials for patches
String[] mats = shader.getCurrentBlockMaterials(blk, map, txtidx, steps);
if (patches != null) { // Patch based model?
for (int i = 0; i < patches.length; i++) {
addPatch((PatchDefinition) patches[i], map.getX(), map.getY(), map.getZ(), mats[i]);
}
}
else {
boolean opaque = HDBlockStateTextureMap.getTransparency(blk) == BlockTransparency.OPAQUE;
for (int face = 0; face < 6; face++) {
DynmapBlockState blk2 = map.getBlockTypeAt(BlockStep.oppositeValues[face]); // Get block in direction
// If we're not solid, or adjacent block is not solid, draw side
if ((!opaque) || blk2.isAir() || edgebits[face] || (HDBlockStateTextureMap.getTransparency(blk2) != BlockTransparency.OPAQUE)) {
addPatch(defaultPathces[face], map.getX(), map.getY(), map.getZ(), mats[face]);
}
}
}
}
private int[] getTextureUVs(PatchDefinition pd, int rot) {
int[] uv = new int[4];
if (rot == ROT0) {
uv[0] = uvs.getVectorIndex(pd.umin, pd.vmin, 0);
uv[1] = uvs.getVectorIndex(pd.umax, pd.vmin, 0);
uv[2] = uvs.getVectorIndex(pd.umax, pd.vmax, 0);
uv[3] = uvs.getVectorIndex(pd.umin, pd.vmax, 0);
}
else if (rot == ROT90) { // 90 degrees on texture
uv[0] = uvs.getVectorIndex(1.0 - pd.vmin, pd.umin, 0);
uv[1] = uvs.getVectorIndex(1.0 - pd.vmin, pd.umax, 0);
uv[2] = uvs.getVectorIndex(1.0 - pd.vmax, pd.umax, 0);
uv[3] = uvs.getVectorIndex(1.0 - pd.vmax, pd.umin, 0);
}
else if (rot == ROT180) { // 180 degrees on texture
uv[0] = uvs.getVectorIndex(1.0 - pd.umin, 1.0 - pd.vmin, 0);
uv[1] = uvs.getVectorIndex(1.0 - pd.umax, 1.0 - pd.vmin, 0);
uv[2] = uvs.getVectorIndex(1.0 - pd.umax, 1.0 - pd.vmax, 0);
uv[3] = uvs.getVectorIndex(1.0 - pd.umin, 1.0 - pd.vmax, 0);
}
else if (rot == ROT270) { // 270 degrees on texture
uv[0] = uvs.getVectorIndex(pd.vmin, 1.0 - pd.umin, 0);
uv[1] = uvs.getVectorIndex(pd.vmin, 1.0 - pd.umax, 0);
uv[2] = uvs.getVectorIndex(pd.vmax, 1.0 - pd.umax, 0);
uv[3] = uvs.getVectorIndex(pd.vmax, 1.0 - pd.umin, 0);
}
else if (rot == HFLIP) {
uv[0] = uvs.getVectorIndex(1.0 - pd.umin, pd.vmin, 0);
uv[1] = uvs.getVectorIndex(1.0 - pd.umax, pd.vmin, 0);
uv[2] = uvs.getVectorIndex(1.0 - pd.umax, pd.vmax, 0);
uv[3] = uvs.getVectorIndex(1.0 - pd.umin, pd.vmax, 0);
}
else {
uv[0] = uvs.getVectorIndex(pd.umin, pd.vmin, 0);
uv[1] = uvs.getVectorIndex(pd.umax, pd.vmin, 0);
uv[2] = uvs.getVectorIndex(pd.umax, pd.vmax, 0);
uv[3] = uvs.getVectorIndex(pd.umin, pd.vmax, 0);
}
return uv;
}
/**
* Add patch as face to output
*/
private void addPatch(PatchDefinition pd, double x, double y, double z, String material) throws IOException {
// No material? No face
if (material == null) {
return;
}
int rot = 0;
int rotidx = material.indexOf('@'); // Check for rotation modifier
if (rotidx >= 0) {
rot = material.charAt(rotidx+1) - '0'; // 0-3
material = material.substring(0, rotidx);
}
int[] v = new int[4];
int[] uv = getTextureUVs(pd, rot);
// Get offsets for U and V from origin
double ux = pd.xu - pd.x0;
double uy = pd.yu - pd.y0;
double uz = pd.zu - pd.z0;
double vx = pd.xv - pd.x0;
double vy = pd.yv - pd.y0;
double vz = pd.zv - pd.z0;
// Offset to origin corner
x = x + pd.x0;
y = y + pd.y0;
z = z + pd.z0;
// Origin corner, offset by umin, vmin
v[0] = vertices.getVectorIndex(x + ux*pd.umin + vx*pd.vmin, y + uy*pd.umin + vy*pd.vmin, z + uz*pd.umin + vz*pd.vmin);
uv[0] = uvs.getVectorIndex(pd.umin, pd.vmin, 0);
// Second is end of U (umax, vmin)
v[1] = vertices.getVectorIndex(x + ux*pd.umax + vx*pd.vmin, y + uy*pd.umax + vy*pd.vmin, z + uz*pd.umax + vz*pd.vmin);
uv[1] = uvs.getVectorIndex(pd.umax, pd.vmin, 0);
// Third is end of U+V (umax, vmax)
v[2] = vertices.getVectorIndex(x + ux*pd.umax + vx*pd.vmax, y + uy*pd.umax + vy*pd.vmax, z + uz*pd.umax + vz*pd.vmax);
uv[2] = uvs.getVectorIndex(pd.umax, pd.vmax, 0);
// Forth is end of V (umin, vmax)
v[3] = vertices.getVectorIndex(x + ux*pd.umin + vx*pd.vmax, y + uy*pd.umin + vy*pd.vmax, z + uz*pd.umin + vz*pd.vmax);
uv[3] = uvs.getVectorIndex(pd.umin, pd.vmax, 0);
// Add patch to file
addPatchToFile(v, uv, pd.sidevis, material, rot);
}
private void addPatchToFile(int[] v, int[] uv, SideVisible sv, String material, int rot) throws IOException {
List<Face> faces = facesByTexture.get(material);
if (faces == null) {
faces = new ArrayList<Face>();
facesByTexture.put(material, faces);
}
// If needed, rotate the UV sequence
if (rot == HFLIP) { // Flip horizonntal
int newuv[] = new int[uv.length];
for (int i = 0; i < uv.length; i++) {
newuv[i] = uv[i ^ 1];
}
uv = newuv;
}
else if (rot != ROT0) {
int newuv[] = new int[uv.length];
for (int i = 0; i < uv.length; i++) {
newuv[i] = uv[(i+4-rot) % uv.length];
}
uv = newuv;
}
Face f = new Face();
f.groupLine = updateGroup(GROUP_TEXTURE, material);
switch (sv) {
case TOP:
f.faceLine = String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[0], uv[0], v[1], uv[1], v[2], uv[2], v[3], uv[3]);
break;
case BOTTOM:
f.faceLine = String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[3], uv[3], v[2], uv[2], v[1], uv[1], v[0], uv[0]);
break;
case BOTH:
f.faceLine = String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[0], uv[0], v[1], uv[1], v[2], uv[2], v[3], uv[3]);
f.faceLine += String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[3], uv[3], v[2], uv[2], v[1], uv[1], v[0], uv[0]);
break;
case FLIP:
f.faceLine = String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[0], uv[0], v[1], uv[1], v[2], uv[2], v[3], uv[3]);
f.faceLine += String.format("f %d/%d %d/%d %d/%d %d/%d\n", v[3], uv[2], v[2], uv[3], v[1], uv[0], v[0], uv[1]);
break;
}
faces.add(f);
}
public Set<String> getMaterialIDs() {
return matIDs;
}
private static final boolean getSubblock(short[] mod, int x, int y, int z) {
if ((x >= 0) && (x < MODELSCALE) && (y >= 0) && (y < MODELSCALE) && (z >= 0) && (z < MODELSCALE)) {
return mod[MODELSCALE*MODELSCALE*y + MODELSCALE*z + x] != 0;
}
return false;
}
// Scan along X axis
private int scanX(short[] tmod, int x, int y, int z) {
int xlen = 0;
while (getSubblock(tmod, x+xlen, y, z)) {
xlen++;
}
return xlen;
}
// Scan along Z axis for rows matching given x length
private int scanZ(short[] tmod, int x, int y, int z, int xlen) {
int zlen = 0;
while (scanX(tmod, x, y, z+zlen) >= xlen) {
zlen++;
}
return zlen;
}
// Scan along Y axis for layers matching given X and Z lengths
private int scanY(short[] tmod, int x, int y, int z, int xlen, int zlen) {
int ylen = 0;
while (scanZ(tmod, x, y+ylen, z, xlen) >= zlen) {
ylen++;
}
return ylen;
}
private void addSubblock(short[] tmod, int x, int y, int z, List<RenderPatch> list) {
// Find dimensions of cuboid
int xlen = scanX(tmod, x, y, z);
int zlen = scanZ(tmod, x, y, z, xlen);
int ylen = scanY(tmod, x, y, z, xlen, zlen);
// Add equivalent of boxblock
CustomRenderer.addBox(HDBlockModels.getPatchDefinitionFactory(), list,
BLKSIZE * x, BLKSIZE * (x+xlen),
BLKSIZE * y, BLKSIZE * (y+ylen),
BLKSIZE * z, BLKSIZE * (z+zlen),
HDBlockModels.boxPatchList);
// And remove blocks from model (since we have them covered)
for (int xx = 0; xx < xlen; xx++) {
for (int yy = 0; yy < ylen; yy++) {
for (int zz = 0; zz < zlen; zz++) {
tmod[MODELSCALE*MODELSCALE*(y+yy) + MODELSCALE*(z+zz) + (x+xx)] = 0;
}
}
}
}
private PatchDefinition[] getScaledModelAsPatches(short[] mod) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
short[] tmod = Arrays.copyOf(mod, mod.length); // Make copy
for (int y = 0; y < MODELSCALE; y++) {
for (int z = 0; z < MODELSCALE; z++) {
for (int x = 0; x < MODELSCALE; x++) {
if (getSubblock(tmod, x, y, z)) { // If occupied, try to add to list
addSubblock(tmod, x, y, z, list);
}
}
}
}
PatchDefinition[] pd = new PatchDefinition[list.size()];
for (int i = 0; i < pd.length; i++) {
pd[i] = (PatchDefinition) list.get(i);
}
return pd;
}
private String updateGroup(int grpIndex, String newgroup) {
if (enabledGroups[grpIndex]) {
if (!newgroup.equals(group[grpIndex])) {
group[grpIndex] = newgroup;
String newline = "g";
for (int i = 0; i < GROUP_COUNT; i++) {
if (enabledGroups[i]) {
newline += " " + group[i];
}
}
newline += "\n";
groupline = newline;
}
}
return groupline;
}
public boolean getGroupEnabled(int grpIndex) {
if (grpIndex < enabledGroups.length) {
return enabledGroups[grpIndex];
}
else {
return false;
}
}
public void setGroupEnabled(int grpIndex, boolean set) {
if (grpIndex < enabledGroups.length) {
enabledGroups[grpIndex] = set;
}
}
public String getBaseName() {
return basename;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,270 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.BitSet;
import java.util.List;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
public class CaveHDShader implements HDShader {
private String name;
private boolean iflit;
private BitSet hiddenids = new BitSet();
private void setHidden(DynmapBlockState blk) {
hiddenids.set(blk.globalStateIndex);
}
private void setHidden(String blkname) {
DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(blkname);
if (bbs.isNotAir()) {
for (int i = 0; i < bbs.getStateCount(); i++) {
setHidden(bbs.getState(i));
}
}
}
private boolean isHidden(DynmapBlockState blk) {
return hiddenids.get(blk.globalStateIndex);
}
public CaveHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
iflit = configuration.getBoolean("onlyiflit", false);
setHidden(DynmapBlockState.AIR); /* Air is hidden always */
List<Object> hidden = configuration.getList("hiddennames");
if(hidden != null) {
for(Object o : hidden) {
if(o instanceof String) {
setHidden((String) o);
}
}
}
else {
setHidden(DynmapBlockState.LOG_BLOCK);
setHidden(DynmapBlockState.LEAVES_BLOCK);
setHidden(DynmapBlockState.GLASS_BLOCK);
setHidden(DynmapBlockState.WOODEN_DOOR_BLOCK);
setHidden(DynmapBlockState.IRON_DOOR_BLOCK);
setHidden(DynmapBlockState.SNOW_BLOCK);
setHidden(DynmapBlockState.ICE_BLOCK);
setHidden(DynmapBlockState.SNOW_LAYER_BLOCK);
}
}
@Override
public boolean isBiomeDataNeeded() {
return false;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return iflit;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color;
protected MapIterator mapiter;
protected HDMap map;
private boolean air;
private int yshift;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache) {
this.mapiter = mapiter;
this.map = map;
this.color = new Color();
int wheight = mapiter.getWorldHeight();
yshift = 0;
while(wheight > 128) {
wheight >>= 1;
yshift++;
}
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
@Override
public HDShader getShader() {
return CaveHDShader.this;
}
/**
* Get our map
*/
@Override
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
@Override
public HDLighting getLighting() {
return map.getLighting();
}
/**
* Reset renderer state for new ray
*/
@Override
public void reset(HDPerspectiveState ps) {
color.setTransparent();
air = true;
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
@Override
public boolean processBlock(HDPerspectiveState ps) {
DynmapBlockState blocktype = ps.getBlockState();
if (isHidden(blocktype)) {
blocktype = DynmapBlockState.AIR;
}
else {
air = false;
return false;
}
if (blocktype.isAir() && !air) {
if(iflit && (ps.getMapIterator().getBlockEmittedLight() == 0)) {
return false;
}
int cr, cg, cb;
int mult = 256;
int ys = mapiter.getY() >> yshift;
if (ys < 64) {
cr = 0;
cg = 64 + ys * 3;
cb = 255 - ys * 4;
} else {
cr = (ys - 64) * 4;
cg = 255;
cb = 0;
}
/* Figure out which color to use */
switch(ps.getLastBlockStep()) {
case X_PLUS:
case X_MINUS:
mult = 224;
break;
case Z_PLUS:
case Z_MINUS:
mult = 256;
break;
default:
mult = 160;
break;
}
cr = cr * mult / 256;
cg = cg * mult / 256;
cb = cb * mult / 256;
color.setRGBA(cr, cg, cb, 255);
return true;
}
return false;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
@Override
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
@Override
public void getRayColor(Color c, int index) {
c.setColor(color);
}
/**
* Clean up state object - called after last ray completed
*/
@Override
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspective
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new OurShaderState(mapiter, map, cache);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
return nulllist;
}
}

View File

@ -0,0 +1,58 @@
package org.dynmap.hdmap;
import java.util.Map;
import org.dynmap.Log;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
public class CustomBlockModel extends HDBlockModel {
public CustomRenderer render;
public CustomBlockModel(String blockname, int databits, String classname, Map<String,String> classparm, String blockset) {
super(blockname, databits, blockset);
try {
Class<?> cls = Class.forName(classname); /* Get class */
render = (CustomRenderer) cls.newInstance();
if(render.initializeRenderer(HDBlockModels.pdf, blockname, databits, classparm) == false) {
Log.severe("Error loading custom renderer - " + classname);
render = null;
}
else {
if(render.getTileEntityFieldsNeeded() != null) {
DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(blockname);
for(int i = 0; i < bbs.getStateCount(); i++) {
if ((databits & (1 << i)) != 0) {
DynmapBlockState bs = bbs.getState(i);
HDBlockModels.customModelsRequestingTileData.set(bs.globalStateIndex);
}
}
}
}
} catch (Exception x) {
Log.severe("Error loading custom renderer - " + classname, x);
render = null;
}
}
@Override
public int getTextureCount() {
return render.getMaximumTextureCount(HDBlockModels.pdf);
}
private static final RenderPatch[] empty_list = new RenderPatch[0];
public RenderPatch[] getMeshForBlock(MapDataContext ctx) {
if(render != null)
return render.getRenderPatchList(ctx);
else
return empty_list;
}
@Override
public void removed(DynmapBlockState blk) {
super.removed(blk);
HDBlockModels.customModelsRequestingTileData.clear(blk.globalStateIndex);
}
}

View File

@ -0,0 +1,58 @@
package org.dynmap.hdmap;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.json.simple.JSONObject;
import static org.dynmap.JSONUtils.s;
public class DefaultHDLighting implements HDLighting {
private String name;
public DefaultHDLighting(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
}
/* Get lighting name */
public String getName() { return name; }
/* Apply lighting to given pixel colors (1 outcolor if normal, 2 if night/day) */
public void applyLighting(HDPerspectiveState ps, HDShaderState ss, Color incolor, Color[] outcolor) {
for(int i = 0; i < outcolor.length; i++)
outcolor[i].setColor(incolor);
}
/* Test if Biome Data is needed for this renderer */
public boolean isBiomeDataNeeded() { return false; }
/* Test if raw biome temperature/rainfall data is needed */
public boolean isRawBiomeDataNeeded() { return false; }
/* Test if highest block Y data is needed */
public boolean isHightestBlockYDataNeeded() { return false; }
/* Tet if block type data needed */
public boolean isBlockTypeDataNeeded() { return false; }
/* Test if night/day is enabled for this renderer */
public boolean isNightAndDayEnabled() { return false; }
/* Test if sky light level needed */
public boolean isSkyLightLevelNeeded() { return false; }
/* Test if emitted light level needed */
public boolean isEmittedLightLevelNeeded() { return false; }
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "lighting", name);
s(mapObject, "nightandday", isNightAndDayEnabled());
}
@Override
public int[] getBrightnessTable(DynmapWorld world) {
return null;
}
}

View File

@ -0,0 +1,319 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import org.dynmap.Color;
import org.dynmap.ColorScheme;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.MapManager;
import org.dynmap.common.BiomeMap;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
public class DefaultHDShader implements HDShader {
private String name;
protected ColorScheme colorScheme;
protected boolean transparency; /* Is transparency support active? */
public enum BiomeColorOption {
NONE, BIOME, TEMPERATURE, RAINFALL
}
protected BiomeColorOption biomecolored = BiomeColorOption.NONE; /* Use biome for coloring */
public DefaultHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
colorScheme = ColorScheme.getScheme(core, configuration.getString("colorscheme", "default"));
transparency = configuration.getBoolean("transparency", true); /* Default on */
String biomeopt = configuration.getString("biomecolored", "none");
if(biomeopt.equals("biome")) {
biomecolored = BiomeColorOption.BIOME;
}
else if(biomeopt.equals("temperature")) {
biomecolored = BiomeColorOption.TEMPERATURE;
}
else if(biomeopt.equals("rainfall")) {
biomecolored = BiomeColorOption.RAINFALL;
}
else {
biomecolored = BiomeColorOption.NONE;
}
}
@Override
public boolean isBiomeDataNeeded() {
return biomecolored == BiomeColorOption.BIOME;
}
@Override
public boolean isRawBiomeDataNeeded() {
return (biomecolored == BiomeColorOption.RAINFALL) || (biomecolored == BiomeColorOption.TEMPERATURE);
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color[];
protected MapIterator mapiter;
protected HDMap map;
private Color tmpcolor[];
private int pixelodd;
private HDLighting lighting;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache) {
this.mapiter = mapiter;
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
tmpcolor = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
tmpcolor = new Color[] { new Color() };
}
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
public HDShader getShader() {
return DefaultHDShader.this;
}
/**
* Get our map
*/
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
pixelodd = (ps.getPixelX() & 0x3) + (ps.getPixelY()<<1);
}
protected Color[] getBlockColors(DynmapBlockState block) {
//TODO: this will not work right on 1.13+, but thwo whole colorscheme thing is broken anyway...
int blocktype = block.globalStateIndex / 16;
int blockdata = block.globalStateIndex & 0x0F;
if((blockdata != 0) && (colorScheme.datacolors[blocktype] != null))
return colorScheme.datacolors[blocktype][blockdata];
else
return colorScheme.colors[blocktype];
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
int i;
DynmapBlockState blocktype = ps.getBlockState();
if (blocktype.isAir())
return false;
Color[] colors = getBlockColors(blocktype);
if (colors != null) {
int seq;
int subalpha = ps.getSubmodelAlpha();
/* Figure out which color to use */
switch(ps.getLastBlockStep()) {
case X_PLUS:
case X_MINUS:
seq = 2;
break;
case Z_PLUS:
case Z_MINUS:
seq = 0;
break;
default:
//if(subalpha >= 0) /* We hit a block in a model */
// seq = 4; /* Use smooth top */
//else
if(((pixelodd + mapiter.getY()) & 0x03) == 0)
seq = 3;
else
seq = 1;
break;
}
Color c = colors[seq];
if (c.getAlpha() > 0) {
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, tmpcolor);
/* If we got alpha from subblock model, use it instead */
if(subalpha >= 0) {
for(int j = 0; j < tmpcolor.length; j++)
tmpcolor[j].setAlpha(Math.max(subalpha,tmpcolor[j].getAlpha()));
}
/* Blend color with accumulated color (weighted by alpha) */
if(!transparency) { /* No transparency support */
for(i = 0; i < color.length; i++)
color[i].setARGB(tmpcolor[i].getARGB() | 0xFF000000);
return true; /* We're done */
}
/* If no previous color contribution, use new color */
else if(color[0].isTransparent()) {
for(i = 0; i < color.length; i++)
color[i].setColor(tmpcolor[i]);
return (color[0].getAlpha() == 255);
}
/* Else, blend and generate new alpha */
else {
int alpha = color[0].getAlpha();
int alpha2 = tmpcolor[0].getAlpha() * (255-alpha) / 255;
int talpha = alpha + alpha2;
for(i = 0; i < color.length; i++)
color[i].setRGBA((tmpcolor[i].getRed()*alpha2 + color[i].getRed()*alpha) / talpha,
(tmpcolor[i].getGreen()*alpha2 + color[i].getGreen()*alpha) / talpha,
(tmpcolor[i].getBlue()*alpha2 + color[i].getBlue()*alpha) / talpha, talpha);
return (talpha >= 254); /* If only one short, no meaningful contribution left */
}
}
}
return false;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
}
private class OurBiomeShaderState extends OurShaderState {
private OurBiomeShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache) {
super(mapiter, map, cache);
}
@Override
protected Color[] getBlockColors(DynmapBlockState blk) {
BiomeMap bio = mapiter.getBiome();
if(bio != null)
return colorScheme.biomecolors[bio.ordinal()];
return null;
}
}
private class OurBiomeRainfallShaderState extends OurShaderState {
private OurBiomeRainfallShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache) {
super(mapiter, map, cache);
}
@Override
protected Color[] getBlockColors(DynmapBlockState blk) {
return colorScheme.getRainColor(mapiter.getBiome().getRainfall());
}
}
private class OurBiomeTempShaderState extends OurShaderState {
private OurBiomeTempShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache) {
super(mapiter, map, cache);
}
@Override
protected Color[] getBlockColors(DynmapBlockState blk) {
return colorScheme.getTempColor(mapiter.getBiome().getTemperature());
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of the perspecitve
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
switch(biomecolored) {
case NONE:
return new OurShaderState(mapiter, map, cache);
case BIOME:
return new OurBiomeShaderState(mapiter, map, cache);
case RAINFALL:
return new OurBiomeRainfallShaderState(mapiter, map, cache);
case TEMPERATURE:
return new OurBiomeTempShaderState(mapiter, map, cache);
}
return null;
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx,
BlockStep[] steps) {
return nulllist;
}
}

View File

@ -0,0 +1,36 @@
package org.dynmap.hdmap;
import org.dynmap.renderer.DynmapBlockState;
public abstract class HDBlockModel {
private String blockset;
/**
* Block definition - positions correspond to Bukkit coordinates (+X is south, +Y is up, +Z is west)
* @param blockname - block name
* @param databits - bitmap of block data bits matching this model (bit N is set if data=N would match)
* @param blockset - ID of block definition set
*/
protected HDBlockModel(String blockname, int databits, String blockset) {
this.blockset = blockset;
DynmapBlockState bblk = DynmapBlockState.getBaseStateByName(blockname);
if (bblk.isNotAir()) {
for (int i = 0; i < bblk.getStateCount(); i++) {
if((databits & (1<<i)) != 0) {
DynmapBlockState bs = bblk.getState(i);
HDBlockModel prev = HDBlockModels.models_by_id_data.put(bs.globalStateIndex, this);
if((prev != null) && (prev != this)) {
prev.removed(bs);
}
}
}
}
}
public String getBlockSet() {
return blockset;
}
public abstract int getTextureCount();
public void removed(DynmapBlockState blk) {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
package org.dynmap.hdmap;
import org.dynmap.utils.PatchDefinition;
public class HDBlockPatchModel extends HDBlockModel {
/* Patch model specific attributes */
private PatchDefinition[] patches;
private final int max_texture;
/**
* Block definition - positions correspond to Bukkit coordinates (+X is south, +Y is up, +Z is west)
* (for patch models)
* @param blockname - block name
* @param databits - bitmap of block data bits matching this model (bit N is set if data=N would match)
* @param patches - list of patches (surfaces composing model)
* @param blockset - ID of set of blocks defining model
*/
public HDBlockPatchModel(String blockname, int databits, PatchDefinition[] patches, String blockset) {
super(blockname, databits, blockset);
this.patches = patches;
int max = 0;
for(int i = 0; i < patches.length; i++) {
if((patches[i] != null) && (patches[i].textureindex > max))
max = patches[i].textureindex;
}
this.max_texture = max + 1;
}
/**
* Get patches for block model (if patch model)
* @return patches for model
*/
public final PatchDefinition[] getPatches() {
return patches;
}
/**
* Set patches for block
*/
public final void setPatches(PatchDefinition[] p) {
patches = p;
}
@Override
public int getTextureCount() {
return max_texture;
}
}

View File

@ -0,0 +1,193 @@
package org.dynmap.hdmap;
import java.util.Arrays;
import java.util.List;
import org.dynmap.hdmap.TexturePack;
import org.dynmap.Log;
import org.dynmap.hdmap.HDBlockModels;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.hdmap.TexturePack.ColorizingData;
import org.dynmap.renderer.CustomColorMultiplier;
import org.dynmap.renderer.DynmapBlockState;
public class HDBlockStateTextureMap {
private static HDBlockStateTextureMap[] texmaps = new HDBlockStateTextureMap[DynmapBlockState.getGlobalIndexMax()]; // List of texture maps, indexed by global state index
int faces[]; /* texture index of image for each face (indexed by BlockStep.ordinal() OR patch index) */
final byte[] layers; /* If layered, each index corresponds to faces index, and value is index of next layer */
final private String blockset;
final int colorMult;
final CustomColorMultiplier custColorMult;
final boolean stdrotate; // Marked for corrected to proper : stdrot=true
final private Integer colorMapping; // If non-null, color mapping texture
final BlockTransparency trans;
public static final HDBlockStateTextureMap BLANK = new HDBlockStateTextureMap();
// Default to a blank mapping
HDBlockStateTextureMap() {
blockset = null;
colorMult = 0;
custColorMult = null;
faces = new int[] { TexturePack.TILEINDEX_BLANK, TexturePack.TILEINDEX_BLANK, TexturePack.TILEINDEX_BLANK, TexturePack.TILEINDEX_BLANK, TexturePack.TILEINDEX_BLANK, TexturePack.TILEINDEX_BLANK };
layers = null;
stdrotate = true;
colorMapping = null;
trans = BlockTransparency.TRANSPARENT;
}
// Create block state map with given attributes
public HDBlockStateTextureMap(int[] faces, byte[] layers, int colorMult, CustomColorMultiplier custColorMult, String blockset, boolean stdrot, Integer colorIndex, BlockTransparency trans) {
this.faces = faces;
this.layers = layers;
this.colorMult = colorMult;
this.custColorMult = custColorMult;
this.blockset = blockset;
this.stdrotate = stdrot;
this.colorMapping = colorIndex;
this.trans = trans;
}
// Shallow copy state from another state map
public HDBlockStateTextureMap(HDBlockStateTextureMap map) {
this.faces = map.faces;
this.layers = map.layers;
this.blockset = map.blockset;
this.colorMult = map.colorMult;
this.custColorMult = map.custColorMult;
this.stdrotate = map.stdrotate;
this.colorMapping = map.colorMapping;
this.trans = map.trans;
}
// Get texture index for given face
public int getIndexForFace(int face) {
if ((faces != null) && (faces.length > face))
return faces[face];
return TexturePack.TILEINDEX_BLANK;
}
public void resizeFaces(int cnt) {
int[] newfaces = new int[cnt];
System.arraycopy(faces, 0, newfaces, 0, faces.length);
for(int i = faces.length; i < cnt; i++) {
newfaces[i] = TexturePack.TILEINDEX_BLANK;
}
faces = newfaces;
}
// Add block state to table, with given block IDs and state indexes
public void addToTable(List<String> blocknames, List<Integer> stateidx) {
/* Add entries to lookup table */
for (String blkname : blocknames) {
DynmapBlockState baseblk = DynmapBlockState.getBaseStateByName(blkname);
if (baseblk.isNotAir()) {
if (stateidx != null) {
for (Integer stateid : stateidx) {
DynmapBlockState bs = baseblk.getState(stateid);
if (bs.isAir()) {
Log.warning("Invalid texture block state: " + blkname + ":" + stateid);
continue;
}
if ((this.blockset != null) && (this.blockset.equals("core") == false)) {
HDBlockModels.resetIfNotBlockSet(bs, this.blockset);
}
copyToStateIndex(bs, this);
}
}
else { // Else, loop over all state IDs for given block
for (int stateid = 0; stateid < baseblk.getStateCount(); stateid++) {
DynmapBlockState bs = baseblk.getState(stateid);
if (bs.isAir()) {
Log.warning("Invalid texture block state: " + blkname + ":" + stateid);
continue;
}
if ((this.blockset != null) && (this.blockset.equals("core") == false)) {
HDBlockModels.resetIfNotBlockSet(bs, this.blockset);
}
copyToStateIndex(bs, this);
}
}
}
else {
Log.warning("Invalid texture block name: " + blkname);
}
}
}
private static final void resize(int newend) {
if (newend < texmaps.length) return;
HDBlockStateTextureMap[] newm = new HDBlockStateTextureMap[newend+1];
System.arraycopy(texmaps, 0, newm, 0, texmaps.length);
Arrays.fill(newm, texmaps.length, newm.length, HDBlockStateTextureMap.BLANK);
texmaps = newm;
}
// Initialize/reset block texture table
public static void initializeTable() {
Arrays.fill(texmaps, HDBlockStateTextureMap.BLANK);
}
// Lookup records by block state
public static final HDBlockStateTextureMap getByBlockState(DynmapBlockState blk) {
HDBlockStateTextureMap m = HDBlockStateTextureMap.BLANK;
try {
m = texmaps[blk.globalStateIndex];
} catch (ArrayIndexOutOfBoundsException x) {
resize(blk.globalStateIndex);
}
return m;
}
// Copy given block state to given state index
public static void copyToStateIndex(DynmapBlockState blk, HDBlockStateTextureMap map) {
resize(blk.globalStateIndex);
texmaps[blk.globalStateIndex] = new HDBlockStateTextureMap(map);
}
// Copy textures from source block ID to destination
public static void remapTexture(String dest, String src) {
DynmapBlockState dblk = DynmapBlockState.getBaseStateByName(dest);
DynmapBlockState sblk = DynmapBlockState.getBaseStateByName(src);
int mincnt = Math.min(dblk.getStateCount(), sblk.getStateCount());
for (int i = 0; i < mincnt; i++) {
int didx = dblk.getState(i).globalStateIndex;
int sidx = sblk.getState(i).globalStateIndex;
texmaps[didx] = new HDBlockStateTextureMap(texmaps[sidx]);
}
}
// Get by global state index
public static HDBlockStateTextureMap getByGlobalIndex(int gidx) {
HDBlockStateTextureMap m = HDBlockStateTextureMap.BLANK;
try {
m = texmaps[gidx];
} catch (ArrayIndexOutOfBoundsException x) {
resize(gidx);
}
return m;
}
// Get state by index
public final HDBlockStateTextureMap getStateMap(DynmapBlockState blk, int stateid) {
return getByGlobalIndex(blk.getState(stateid).globalStateIndex);
}
// Get transparency for given block ID
public static BlockTransparency getTransparency(DynmapBlockState blk) {
BlockTransparency trans = BlockTransparency.OPAQUE;
try {
trans = texmaps[blk.globalStateIndex].trans;
} catch (ArrayIndexOutOfBoundsException x) {
resize(blk.globalStateIndex);
}
return trans;
}
// Build copy of block colorization data
public static ColorizingData getColorizingData() {
ColorizingData map = new ColorizingData();
for (int j = 0; j < texmaps.length; j++) {
if (texmaps[j].colorMapping != null) {
map.setBlkStateValue(DynmapBlockState.getStateByGlobalIndex(j), texmaps[j].colorMapping);
}
}
return map;
}
}

View File

@ -0,0 +1,188 @@
package org.dynmap.hdmap;
import java.util.HashMap;
public class HDBlockVolumetricModel extends HDBlockModel {
/* Volumetric model specific attributes */
private long blockflags[];
private int nativeres;
private HashMap<Integer, short[]> scaledblocks;
/**
* Block definition - positions correspond to Bukkit coordinates (+X is south, +Y is up, +Z is west)
* (for volumetric models)
* @param blockname - block name
* @param databits - bitmap of block data bits matching this model (bit N is set if data=N would match)
* @param nativeres - native subblocks per edge of cube (up to 64)
* @param blockflags - array of native^2 long integers representing volume of block (bit X of element (nativeres*Y+Z) is set if that subblock is filled)
* if array is short, other elements area are assumed to be zero (fills from bottom of block up)
* @param blockset - ID of set of blocks defining model
*/
public HDBlockVolumetricModel(String blockname, int databits, int nativeres, long[] blockflags, String blockset) {
super(blockname, databits, blockset);
this.nativeres = nativeres;
this.blockflags = new long[nativeres * nativeres];
System.arraycopy(blockflags, 0, this.blockflags, 0, blockflags.length);
}
/**
* Test if given native block is filled (for volumetric model)
*
* @param x - X coordinate
* @param y - Y coordinate
* @param z - Z coordinate
* @return true if set, false if not
*/
public final boolean isSubblockSet(int x, int y, int z) {
return ((blockflags[nativeres*y+z] & (1 << x)) != 0);
}
/**
* Set subblock value (for volumetric model)
*
* @param x - X coordinate
* @param y - Y coordinate
* @param z - Z coordinate
* @param isset - true = set, false = clear
*/
public final void setSubblock(int x, int y, int z, boolean isset) {
if(isset)
blockflags[nativeres*y+z] |= (1 << x);
else
blockflags[nativeres*y+z] &= ~(1 << x);
}
/**
* Get scaled map of block: will return array of alpha levels, corresponding to how much of the
* scaled subblocks are occupied by the original blocks (indexed by Y*res*res + Z*res + X)
* @param res - requested scale (res subblocks per edge of block)
* @return array of alpha values (0-255), corresponding to resXresXres subcubes of block
*/
public short[] getScaledMap(int res) {
if(scaledblocks == null) { scaledblocks = new HashMap<Integer, short[]>(); }
short[] map = scaledblocks.get(Integer.valueOf(res));
if(map == null) {
map = new short[res*res*res];
if(res == nativeres) {
for(int i = 0; i < blockflags.length; i++) {
for(int j = 0; j < nativeres; j++) {
if((blockflags[i] & (1 << j)) != 0)
map[res*i+j] = 255;
}
}
}
/* If scaling from smaller sub-blocks to larger, each subblock contributes to 1-2 blocks
* on each axis: need to calculate crossovers for each, and iterate through smaller
* blocks to accumulate contributions
*/
else if(res > nativeres) {
int weights[] = new int[res];
int offsets[] = new int[res];
/* LCM of resolutions is used as length of line (res * nativeres)
* Each native block is (res) long, each scaled block is (nativeres) long
* Each scaled block overlaps 1 or 2 native blocks: starting with native block 'offsets[]' with
* 'weights[]' of its (res) width in the first, and the rest in the second
*/
for(int v = 0, idx = 0; v < res*nativeres; v += nativeres, idx++) {
offsets[idx] = (v/res); /* Get index of the first native block we draw from */
if((v+nativeres-1)/res == offsets[idx]) { /* If scaled block ends in same native block */
weights[idx] = nativeres;
}
else { /* Else, see how much is in first one */
weights[idx] = (offsets[idx] + res) - v;
weights[idx] = (offsets[idx]*res + res) - v;
}
}
/* Now, use weights and indices to fill in scaled map */
for(int y = 0, off = 0; y < res; y++) {
int ind_y = offsets[y];
int wgt_y = weights[y];
for(int z = 0; z < res; z++) {
int ind_z = offsets[z];
int wgt_z = weights[z];
for(int x = 0; x < res; x++, off++) {
int ind_x = offsets[x];
int wgt_x = weights[x];
int raw_w = 0;
for(int xx = 0; xx < 2; xx++) {
int wx = (xx==0)?wgt_x:(nativeres-wgt_x);
if(wx == 0) continue;
for(int yy = 0; yy < 2; yy++) {
int wy = (yy==0)?wgt_y:(nativeres-wgt_y);
if(wy == 0) continue;
for(int zz = 0; zz < 2; zz++) {
int wz = (zz==0)?wgt_z:(nativeres-wgt_z);
if(wz == 0) continue;
if(isSubblockSet(ind_x+xx, ind_y+yy, ind_z+zz)) {
raw_w += wx*wy*wz;
}
}
}
}
map[off] = (short)((255*raw_w) / (nativeres*nativeres*nativeres));
if(map[off] > 255) map[off] = 255;
if(map[off] < 0) map[off] = 0;
}
}
}
}
else { /* nativeres > res */
int weights[] = new int[nativeres];
int offsets[] = new int[nativeres];
/* LCM of resolutions is used as length of line (res * nativeres)
* Each native block is (res) long, each scaled block is (nativeres) long
* Each native block overlaps 1 or 2 scaled blocks: starting with scaled block 'offsets[]' with
* 'weights[]' of its (res) width in the first, and the rest in the second
*/
for(int v = 0, idx = 0; v < res*nativeres; v += res, idx++) {
offsets[idx] = (v/nativeres); /* Get index of the first scaled block we draw to */
if((v+res-1)/nativeres == offsets[idx]) { /* If native block ends in same scaled block */
weights[idx] = res;
}
else { /* Else, see how much is in first one */
weights[idx] = (offsets[idx]*nativeres + nativeres) - v;
}
}
/* Now, use weights and indices to fill in scaled map */
long accum[] = new long[map.length];
for(int y = 0; y < nativeres; y++) {
int ind_y = offsets[y];
int wgt_y = weights[y];
for(int z = 0; z < nativeres; z++) {
int ind_z = offsets[z];
int wgt_z = weights[z];
for(int x = 0; x < nativeres; x++) {
if(isSubblockSet(x, y, z)) {
int ind_x = offsets[x];
int wgt_x = weights[x];
for(int xx = 0; xx < 2; xx++) {
int wx = (xx==0)?wgt_x:(res-wgt_x);
if(wx == 0) continue;
for(int yy = 0; yy < 2; yy++) {
int wy = (yy==0)?wgt_y:(res-wgt_y);
if(wy == 0) continue;
for(int zz = 0; zz < 2; zz++) {
int wz = (zz==0)?wgt_z:(res-wgt_z);
if(wz == 0) continue;
accum[(ind_y+yy)*res*res + (ind_z+zz)*res + (ind_x+xx)] +=
wx*wy*wz;
}
}
}
}
}
}
}
for(int i = 0; i < map.length; i++) {
map[i] = (short)(accum[i]*255/nativeres/nativeres/nativeres);
if(map[i] > 255) map[i] = 255;
if(map[i] < 0) map[i] = 0;
}
}
scaledblocks.put(Integer.valueOf(res), map);
}
return map;
}
@Override
public int getTextureCount() {
return 6;
}
}

View File

@ -0,0 +1,30 @@
package org.dynmap.hdmap;
import org.dynmap.Color;
import org.dynmap.DynmapWorld;
import org.json.simple.JSONObject;
public interface HDLighting {
/* Get lighting name */
String getName();
/* Apply lighting to given pixel colors (1 outcolor if normal, 2 if night/day) */
void applyLighting(HDPerspectiveState ps, HDShaderState ss, Color incolor, Color[] outcolor);
/* Test if Biome Data is needed for this renderer */
boolean isBiomeDataNeeded();
/* Test if raw biome temperature/rainfall data is needed */
boolean isRawBiomeDataNeeded();
/* Test if highest block Y data is needed */
boolean isHightestBlockYDataNeeded();
/* Tet if block type data needed */
boolean isBlockTypeDataNeeded();
/* Test if night/day is enabled for this renderer */
boolean isNightAndDayEnabled();
/* Test if sky light level needed */
boolean isSkyLightLevelNeeded();
/* Test if emitted light level needed */
boolean isEmittedLightLevelNeeded();
/* Add shader's contributions to JSON for map object */
void addClientConfiguration(JSONObject mapObject);
/* Get brightness table for given world */
int[] getBrightnessTable(DynmapWorld world);
}

View File

@ -0,0 +1,465 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.a;
import static org.dynmap.JSONUtils.s;
import java.util.ArrayList;
import java.util.List;
import org.dynmap.Client;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapChunk;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.MapTile;
import org.dynmap.MapType;
import org.dynmap.storage.MapStorage;
import org.dynmap.storage.MapStorageTile;
import org.dynmap.storage.MapStorageTileEnumCB;
import org.dynmap.utils.TileFlags;
import org.json.simple.JSONObject;
public class HDMap extends MapType {
private String name;
private String prefix;
private HDPerspective perspective;
private HDShader shader;
private HDLighting lighting;
// private ConfigurationNode configuration;
private int mapzoomout;
private String imgfmtstring;
private MapType.ImageFormat imgformat;
private int bgcolornight;
private int bgcolorday;
private String title;
private String icon;
private String bg_cfg;
private String bg_day_cfg;
private String bg_night_cfg;
private String append_to_world;
private int mapzoomin;
private int boostzoom;
public DynmapCore core;
public static final String IMGFORMAT_PNG = "png";
public static final String IMGFORMAT_JPG = "jpg";
public HDMap(DynmapCore core, ConfigurationNode configuration) {
this.core = core;
name = configuration.getString("name", null);
if(name == null) {
Log.severe("HDMap missing required attribute 'name' - disabled");
return;
}
String perspectiveid = configuration.getString("perspective", "default");
perspective = MapManager.mapman.hdmapman.perspectives.get(perspectiveid);
if(perspective == null) {
/* Try to use default */
perspective = MapManager.mapman.hdmapman.perspectives.get("default");
if(perspective == null) {
Log.severe("HDMap '"+name+"' loaded invalid perspective '" + perspectiveid + "' - map disabled");
name = null;
return;
}
else {
Log.severe("HDMap '"+name+"' loaded invalid perspective '" + perspectiveid + "' - using 'default' perspective");
}
}
String shaderid = configuration.getString("shader", "default");
shader = MapManager.mapman.hdmapman.shaders.get(shaderid);
if(shader == null) {
shader = MapManager.mapman.hdmapman.shaders.get("default");
if(shader == null) {
Log.severe("HDMap '"+name+"' loading invalid shader '" + shaderid + "' - map disabled");
name = null;
return;
}
else {
Log.severe("HDMap '"+name+"' loading invalid shader '" + shaderid + "' - using 'default' shader");
}
}
String lightingid = configuration.getString("lighting", "default");
lighting = MapManager.mapman.hdmapman.lightings.get(lightingid);
if(lighting == null) {
lighting = MapManager.mapman.hdmapman.lightings.get("default");
if(lighting == null) {
Log.severe("HDMap '"+name+"' loading invalid lighting '" + lighting + "' - map disabled");
name = null;
return;
}
else {
Log.severe("HDMap '"+name+"' loading invalid lighting '" + lighting + "' - using 'default' lighting");
}
}
prefix = configuration.getString("prefix", name);
/* Compute extra zoom outs needed for this map */
double scale = perspective.getScale();
mapzoomout = 0;
while(scale >= 1.0) {
mapzoomout++;
scale = scale / 2.0;
}
imgfmtstring = configuration.getString("image-format", "default");
if(imgfmtstring.equals("default")) {
imgformat = ImageFormat.fromID(core.getDefImageFormat());
}
else {
imgformat = ImageFormat.fromID(imgfmtstring);
}
if(imgformat == null) {
Log.severe("HDMap '"+name+"' set invalid image-format: " + imgfmtstring);
imgformat = ImageFormat.FORMAT_PNG;
}
/* Get color info */
String c = configuration.getString("background");
if(c != null) {
bgcolorday = bgcolornight = parseColor(c);
}
c = configuration.getString("backgroundday");
if(c != null) {
bgcolorday = parseColor(c);
}
c = configuration.getString("backgroundnight");
if(c != null) {
bgcolornight = parseColor(c);
}
if(imgformat != ImageFormat.FORMAT_PNG) { /* If JPG, set background color opacity */
bgcolorday |= 0xFF000000;
bgcolornight |= 0xFF000000;
}
this.title = configuration.getString("title", name);
this.icon = configuration.getString("icon");
this.bg_cfg = configuration.getString("background");
this.bg_day_cfg = configuration.getString("backgroundday");
this.bg_night_cfg = configuration.getString("backgroundnight");
this.mapzoomin = configuration.getInteger("mapzoomin", 2);
this.mapzoomout = configuration.getInteger("mapzoomout", this.mapzoomout);
this.boostzoom = configuration.getInteger("boostzoom", 0);
if(this.boostzoom < 0) this.boostzoom = 0;
if(this.boostzoom > 3) this.boostzoom = 3;
// Map zoom in must be at least as big as boost zoom
if (this.boostzoom > this.mapzoomin) {
this.mapzoomin = this.boostzoom;
}
this.append_to_world = configuration.getString("append_to_world", "");
setProtected(configuration.getBoolean("protected", false));
setTileUpdateDelay(configuration.getInteger("tileupdatedelay", -1));
}
public ConfigurationNode saveConfiguration() {
ConfigurationNode cn = super.saveConfiguration();
cn.put("title", title);
if(icon != null)
cn.put("icon", icon);
cn.put("prefix", prefix);
if(perspective != null)
cn.put("perspective", perspective.getName());
if(shader != null)
cn.put("shader", shader.getName());
if(lighting != null)
cn.put("lighting", lighting.getName());
cn.put("image-format", imgfmtstring);
cn.put("mapzoomin", mapzoomin);
cn.put("mapzoomout", mapzoomout);
cn.put("boostzoom", boostzoom);
if(bg_cfg != null)
cn.put("background", bg_cfg);
if(bg_day_cfg != null)
cn.put("backgroundday", bg_day_cfg);
if(bg_night_cfg != null)
cn.put("backgroundnight", bg_night_cfg);
cn.put("append_to_world", append_to_world);
cn.put("protected", isProtected());
if(this.tileupdatedelay > 0) {
cn.put("tileupdatedelay", this.tileupdatedelay);
}
return cn;
}
public final HDShader getShader() { return shader; }
public final HDPerspective getPerspective() { return perspective; }
public final HDLighting getLighting() { return lighting; }
public final int getBoostZoom() { return boostzoom; }
@Override
public List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int x, int y, int z) {
return perspective.getTileCoords(w, x, y, z);
}
@Override
public List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int minx, int miny, int minz, int maxx, int maxy, int maxz) {
return perspective.getTileCoords(w, minx, miny, minz, maxx, maxy, maxz);
}
@Override
public MapTile[] getAdjecentTiles(MapTile tile) {
return perspective.getAdjecentTiles(tile);
}
@Override
public List<DynmapChunk> getRequiredChunks(MapTile tile) {
return perspective.getRequiredChunks(tile);
}
/* Return number of zoom levels needed by this map (before extra levels from extrazoomout) */
public int getMapZoomOutLevels() {
return mapzoomout;
}
@Override
public String getName() {
return name;
}
@Override
public String getPrefix() {
return prefix;
}
/* Get maps rendered concurrently with this map in this world */
public List<MapType> getMapsSharingRender(DynmapWorld w) {
ArrayList<MapType> maps = new ArrayList<MapType>();
for(MapType mt : w.maps) {
if(mt instanceof HDMap) {
HDMap hdmt = (HDMap)mt;
if((hdmt.perspective == this.perspective) && (hdmt.boostzoom == this.boostzoom)) { /* Same perspective */
maps.add(hdmt);
}
}
}
return maps;
}
/* Get names of maps rendered concurrently with this map type in this world */
public List<String> getMapNamesSharingRender(DynmapWorld w) {
ArrayList<String> lst = new ArrayList<String>();
for(MapType mt : w.maps) {
if(mt instanceof HDMap) {
HDMap hdmt = (HDMap)mt;
if((hdmt.perspective == this.perspective) && (hdmt.boostzoom == this.boostzoom)) { /* Same perspective */
if(hdmt.lighting.isNightAndDayEnabled())
lst.add(hdmt.getName() + "(night/day)");
else
lst.add(hdmt.getName());
}
}
}
return lst;
}
@Override
public ImageFormat getImageFormat() { return imgformat; }
@Override
public void buildClientConfiguration(JSONObject worldObject, DynmapWorld world) {
JSONObject o = new JSONObject();
s(o, "type", "HDMapType");
s(o, "name", name);
s(o, "title", title);
s(o, "icon", icon);
s(o, "prefix", prefix);
s(o, "background", bg_cfg);
s(o, "backgroundday", bg_day_cfg);
s(o, "backgroundnight", bg_night_cfg);
s(o, "bigmap", true);
s(o, "mapzoomout", (world.getExtraZoomOutLevels()+mapzoomout));
s(o, "mapzoomin", mapzoomin);
s(o, "boostzoom", boostzoom);
s(o, "protected", isProtected());
s(o, "image-format", imgformat.getFileExt());
if(append_to_world.length() > 0)
s(o, "append_to_world", append_to_world);
perspective.addClientConfiguration(o);
shader.addClientConfiguration(o);
lighting.addClientConfiguration(o);
a(worldObject, "maps", o);
}
private static int parseColor(String c) {
int v = 0;
if(c.startsWith("#")) {
c = c.substring(1);
if(c.length() == 3) { /* #rgb */
try {
v = Integer.valueOf(c, 16);
} catch (NumberFormatException nfx) {
return 0;
}
v = 0xFF000000 | ((v & 0xF00) << 12) | ((v & 0x0F0) << 8) | ((v & 0x00F) << 4);
}
else if(c.length() == 6) { /* #rrggbb */
try {
v = Integer.valueOf(c, 16);
} catch (NumberFormatException nfx) {
return 0;
}
v = 0xFF000000 | (v & 0xFFFFFF);
}
}
return v;
}
public int getBackgroundARGBDay() {
return bgcolorday;
}
public int getBackgroundARGBNight() {
return bgcolornight;
}
public void purgeOldTiles(final DynmapWorld world, final TileFlags rendered) {
final MapStorage ms = world.getMapStorage();
ms.enumMapTiles(world, this, new MapStorageTileEnumCB() {
@Override
public void tileFound(MapStorageTile tile, ImageEncoding fmt) {
if (fmt != getImageFormat().getEncoding()) { // Wrong format? toss it
/* Otherwise, delete tile */
tile.delete();
}
else if (tile.zoom == 1) { // First tier zoom? sensitive to newly rendered tiles
// If any were rendered, already triggered (and still needed
if (rendered.getFlag(tile.x, tile.y) || rendered.getFlag(tile.x+1, tile.y) ||
rendered.getFlag(tile.x, tile.y-1) || rendered.getFlag(tile.x+1, tile.y-1)) {
return;
}
tile.enqueueZoomOutUpdate();
}
else if (tile.zoom == 0) {
if (rendered.getFlag(tile.x, tile.y)) { /* If we rendered this tile, its good */
return;
}
/* Otherwise, delete tile */
tile.delete();
/* Push updates, clear hash code, and signal zoom tile update */
MapManager.mapman.pushUpdate(world, new Client.Tile(tile.getURI()));
tile.enqueueZoomOutUpdate();
}
}
});
}
public String getTitle() {
return title;
}
public int getMapZoomIn() {
return mapzoomin;
}
public String getIcon() {
return (icon == null)?"":icon;
}
public boolean setPrefix(String s) {
if(!s.equals(prefix)) {
prefix = s;
return true;
}
return false;
}
public boolean setTitle(String s) {
if(!s.equals(title)) {
title = s;
return true;
}
return false;
}
public boolean setAppendToWorld(String s) {
if(!s.equals(append_to_world)) {
append_to_world = s;
return true;
}
return false;
}
public String getAppendToWorld() {
return append_to_world;
}
public boolean setMapZoomIn(int mzi) {
if(mzi != mapzoomin) {
mapzoomin = mzi;
return true;
}
return false;
}
public boolean setMapZoomOut(int mzi) {
if(mzi != mapzoomout) {
mapzoomout = mzi;
return true;
}
return false;
}
public boolean setBoostZoom(int mzi) {
if(mzi != this.boostzoom) {
this.boostzoom = mzi;
return true;
}
return false;
}
public boolean setPerspective(HDPerspective p) {
if(perspective != p) {
perspective = p;
return true;
}
return false;
}
public boolean setShader(HDShader p) {
if(shader != p) {
shader = p;
return true;
}
return false;
}
public boolean setLighting(HDLighting p) {
if(lighting != p) {
lighting = p;
return true;
}
return false;
}
public boolean setImageFormatSetting(String f) {
if(imgfmtstring.equals(f) == false) {
MapType.ImageFormat newfmt;
if(f.equals("default"))
newfmt = MapType.ImageFormat.fromID(core.getDefImageFormat());
else
newfmt = MapType.ImageFormat.fromID(f);
if(newfmt != null) {
imgformat = newfmt;
imgfmtstring = f;
return true;
}
}
return false;
}
public String getImageFormatSetting() {
return imgfmtstring;
}
public boolean setIcon(String v) {
if("".equals(v)) v = null;
icon = v;
return true;
}
@Override
public void addMapTiles(List<MapTile> list, DynmapWorld w, int tx, int ty) {
list.add(new HDMapTile(w, this.perspective, tx, ty, boostzoom));
}
private static final ImageVariant[] dayVariant = { ImageVariant.STANDARD, ImageVariant.DAY };
@Override
public ImageVariant[] getVariants() {
if (lighting.isNightAndDayEnabled())
return dayVariant;
return super.getVariants();
}
}

View File

@ -0,0 +1,192 @@
package org.dynmap.hdmap;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.MapType;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
public class HDMapManager {
public HashMap<String, HDShader> shaders = new HashMap<String, HDShader>();
public HashMap<String, HDPerspective> perspectives = new HashMap<String, HDPerspective>();
public HashMap<String, HDLighting> lightings = new HashMap<String, HDLighting>();
public HashSet<HDMap> maps = new HashSet<HDMap>();
public HashMap<String, ArrayList<HDMap>> maps_by_world_perspective = new HashMap<String, ArrayList<HDMap>>();
public static boolean waterlightingfix;
public static boolean biomeshadingfix;
public void loadHDShaders(DynmapCore core) {
Log.verboseinfo("Loading shaders...");
/* Update mappings, if needed */
TexturePack.handleBlockAlias();
File f = new File(core.getDataFolder(), "shaders.txt");
if(!core.updateUsingDefaultResource("/shaders.txt", f, "shaders")) {
return;
}
ConfigurationNode shadercfg = new ConfigurationNode(f);
shadercfg.load();
for(HDShader shader : shadercfg.<HDShader>createInstances("shaders", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(shader.getName() == null) continue;
shaders.put(shader.getName(), shader);
}
/* Load custom shaders, if file is defined - or create empty one if not */
f = new File(core.getDataFolder(), "custom-shaders.txt");
core.createDefaultFileFromResource("/custom-shaders.txt", f);
if(f.exists()) {
ConfigurationNode customshadercfg = new ConfigurationNode(f);
customshadercfg.load();
for(HDShader shader : customshadercfg.<HDShader>createInstances("shaders", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(shader.getName() == null) continue;
shaders.put(shader.getName(), shader);
}
}
Log.info("Loaded " + shaders.size() + " shaders.");
}
public void loadHDPerspectives(DynmapCore core) {
Log.verboseinfo("Loading perspectives...");
// Update mappings, if needed
HDBlockModels.handleBlockAlias();
File f = new File(core.getDataFolder(), "perspectives.txt");
if(!core.updateUsingDefaultResource("/perspectives.txt", f, "perspectives")) {
return;
}
ConfigurationNode perspectivecfg = new ConfigurationNode(f);
perspectivecfg.load();
for(HDPerspective perspective : perspectivecfg.<HDPerspective>createInstances("perspectives", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(perspective.getName() == null) continue;
perspectives.put(perspective.getName(), perspective);
}
/* Load custom perspectives, if file is defined - or create empty one if not */
f = new File(core.getDataFolder(), "custom-perspectives.txt");
core.createDefaultFileFromResource("/custom-perspectives.txt", f);
if(f.exists()) {
perspectivecfg = new ConfigurationNode(f);
perspectivecfg.load();
for(HDPerspective perspective : perspectivecfg.<HDPerspective>createInstances("perspectives", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(perspective.getName() == null) continue;
perspectives.put(perspective.getName(), perspective);
}
}
Log.info("Loaded " + perspectives.size() + " perspectives.");
}
public void loadHDLightings(DynmapCore core) {
Log.verboseinfo("Loading lightings...");
File f = new File(core.getDataFolder(), "lightings.txt");
if(!core.updateUsingDefaultResource("/lightings.txt", f, "lightings")) {
return;
}
ConfigurationNode lightingcfg = new ConfigurationNode(f);
lightingcfg.load();
for(HDLighting lighting : lightingcfg.<HDLighting>createInstances("lightings", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(lighting.getName() == null) continue;
lightings.put(lighting.getName(), lighting);
}
/* Load custom lightings, if file is defined - or create empty one if not */
f = new File(core.getDataFolder(), "custom-lightings.txt");
core.createDefaultFileFromResource("/custom-lightings.txt", f);
if(f.exists()) {
lightingcfg = new ConfigurationNode(f);
lightingcfg.load();
for(HDLighting lighting : lightingcfg.<HDLighting>createInstances("lightings", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
if(lighting.getName() == null) continue;
lightings.put(lighting.getName(), lighting);
}
}
Log.info("Loaded " + lightings.size() + " lightings.");
}
/**
* Initialize shader states for all shaders for given tile
*
* @param tile - tile to init
* @param cache - chunk cache
* @param mapiter - map iterator
* @param mapname - map name
* @param scale - map scale
* @return array of shader states for all associated shaders
*/
public HDShaderState[] getShaderStateForTile(HDMapTile tile, MapChunkCache cache, MapIterator mapiter, String mapname, int scale) {
DynmapWorld w = MapManager.mapman.worldsLookup.get(tile.getDynmapWorld().getName());
if(w == null) {
return new HDShaderState[0];
}
ArrayList<HDShaderState> shaders = new ArrayList<HDShaderState>();
for(MapType map : w.maps) {
if(map instanceof HDMap) {
HDMap hdmap = (HDMap)map;
if((hdmap.getPerspective() == tile.perspective) && (hdmap.getBoostZoom() == tile.boostzoom)) {
/* If limited to one map, and this isn't it, skip */
if((mapname != null) && (!hdmap.getName().equals(mapname)))
continue;
shaders.add(hdmap.getShader().getStateInstance(hdmap, cache, mapiter, scale));
}
}
}
return shaders.toArray(new HDShaderState[shaders.size()]);
}
private static final int BIOMEDATAFLAG = 0;
private static final int HIGHESTZFLAG = 1;
private static final int RAWBIOMEFLAG = 2;
private static final int BLOCKTYPEFLAG = 3;
public boolean isBiomeDataNeeded(HDMapTile t) {
return getCachedFlags(t)[BIOMEDATAFLAG];
}
public boolean isHightestBlockYDataNeeded(HDMapTile t) {
return getCachedFlags(t)[HIGHESTZFLAG];
}
public boolean isRawBiomeDataNeeded(HDMapTile t) {
return getCachedFlags(t)[RAWBIOMEFLAG];
}
public boolean isBlockTypeDataNeeded(HDMapTile t) {
return getCachedFlags(t)[BLOCKTYPEFLAG];
}
private HashMap<String, boolean[]> cached_data_flags_by_world_perspective = new HashMap<String, boolean[]>();
private boolean[] getCachedFlags(HDMapTile t) {
String w = t.getDynmapWorld().getName();
String k = w + "/" + t.perspective.getName();
boolean[] flags = cached_data_flags_by_world_perspective.get(k);
if(flags != null)
return flags;
flags = new boolean[4];
cached_data_flags_by_world_perspective.put(k, flags);
DynmapWorld dw = MapManager.mapman.worldsLookup.get(w);
if(dw == null) return flags;
for(MapType map : dw.maps) {
if(map instanceof HDMap) {
HDMap hdmap = (HDMap)map;
if(hdmap.getPerspective() == t.perspective) {
HDShader sh = hdmap.getShader();
HDLighting lt = hdmap.getLighting();
flags[BIOMEDATAFLAG] |= sh.isBiomeDataNeeded() | lt.isBiomeDataNeeded();
flags[HIGHESTZFLAG] |= sh.isHightestBlockYDataNeeded() | lt.isHightestBlockYDataNeeded();
flags[RAWBIOMEFLAG] |= sh.isRawBiomeDataNeeded() | lt.isRawBiomeDataNeeded();
flags[BLOCKTYPEFLAG] |= sh.isBlockTypeDataNeeded() | lt.isBlockTypeDataNeeded();
}
}
}
return flags;
}
}

View File

@ -0,0 +1,93 @@
package org.dynmap.hdmap;
import org.dynmap.DynmapChunk;
import org.dynmap.DynmapWorld;
import org.dynmap.MapManager;
import java.util.List;
import org.dynmap.MapTile;
import org.dynmap.utils.MapChunkCache;
public class HDMapTile extends MapTile {
public final HDPerspective perspective;
public final int tx, ty; /* Tile X and Tile Y are in tile coordinates (pixels/tile-size) */
public final int boostzoom;
public HDMapTile(DynmapWorld world, HDPerspective perspective, int tx, int ty, int boostzoom) {
super(world);
this.perspective = perspective;
this.tx = tx;
this.ty = ty;
this.boostzoom = boostzoom;
}
public HDMapTile(DynmapWorld world, String parm) throws Exception {
super(world);
String[] parms = parm.split(",");
if(parms.length < 3) throw new Exception("wrong parameter count");
this.tx = Integer.parseInt(parms[0]);
this.ty = Integer.parseInt(parms[1]);
this.perspective = MapManager.mapman.hdmapman.perspectives.get(parms[2]);
if(this.perspective == null) throw new Exception("invalid perspective");
if(parms.length > 3)
this.boostzoom = Integer.parseInt(parms[3]);
else
this.boostzoom = 0;
}
@Override
protected String saveTileData() {
return String.format("%d,%d,%s,%d", tx, ty, perspective.getName(), boostzoom);
}
@Override
public int hashCode() {
return tx ^ ty ^ perspective.hashCode() ^ world.hashCode() ^ boostzoom;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof HDMapTile) {
return equals((HDMapTile) obj);
}
return false;
}
public boolean equals(HDMapTile o) {
return o.tx == tx && o.ty == ty && (perspective == o.perspective) && (o.world == world) && (o.boostzoom == boostzoom);
}
@Override
public String toString() {
return world.getName() + ":" + perspective.getName() + "," + tx + "," + ty + ":" + boostzoom;
}
@Override
public boolean isBiomeDataNeeded() { return MapManager.mapman.hdmapman.isBiomeDataNeeded(this); }
@Override
public boolean isHightestBlockYDataNeeded() { return MapManager.mapman.hdmapman.isHightestBlockYDataNeeded(this); }
@Override
public boolean isRawBiomeDataNeeded() { return MapManager.mapman.hdmapman.isRawBiomeDataNeeded(this); }
@Override
public boolean isBlockTypeDataNeeded() { return MapManager.mapman.hdmapman.isBlockTypeDataNeeded(this); }
public boolean render(MapChunkCache cache, String mapname) {
return perspective.render(cache, this, mapname);
}
public List<DynmapChunk> getRequiredChunks() {
return perspective.getRequiredChunks(this);
}
public MapTile[] getAdjecentTiles() {
return perspective.getAdjecentTiles(this);
}
public int tileOrdinalX() { return tx; }
public int tileOrdinalY() { return ty; }
}

View File

@ -0,0 +1,40 @@
package org.dynmap.hdmap;
import java.util.List;
import org.dynmap.DynmapChunk;
import org.dynmap.DynmapWorld;
import org.dynmap.MapTile;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.TileFlags;
import org.dynmap.utils.Vector3D;
import org.json.simple.JSONObject;
public interface HDPerspective {
/* Get name of perspective */
String getName();
/* Get tiles invalidated by change at given location */
List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int x, int y, int z);
/* Get tiles invalidated by change at given volume, defined by 2 opposite corner locations */
List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int minx, int miny, int minz, int maxx, int maxy, int maxz);
/* Get tiles adjacent to given tile */
MapTile[] getAdjecentTiles(MapTile tile);
/* Get chunks needed for given tile */
List<DynmapChunk> getRequiredChunks(MapTile tile);
/* Render given tile */
boolean render(MapChunkCache cache, HDMapTile tile, String mapname);
public boolean isBiomeDataNeeded();
public boolean isHightestBlockYDataNeeded();
public boolean isRawBiomeDataNeeded();
public boolean isBlockTypeDataNeeded();
double getScale();
int getModelScale();
public void addClientConfiguration(JSONObject mapObject);
public void transformWorldToMapCoord(Vector3D input, Vector3D rslt);
public int hashCode();
}

View File

@ -0,0 +1,92 @@
package org.dynmap.hdmap;
import org.dynmap.utils.MapIterator;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.Vector3D;
import org.dynmap.utils.LightLevels;
public interface HDPerspectiveState {
/**
* Get light levels - only available if shader requested it
* @param ll - light levels (filled in when returned)
*/
void getLightLevels(LightLevels ll);
/**
* Get sky light level - only available if shader requested it
* @param step - last step
* @param ll - light levels (filled in when returned)
*/
void getLightLevelsAtStep(BlockStep step, LightLevels ll);
/**
* Get current block state
* @return block
*/
DynmapBlockState getBlockState();
/**
* Get direction of last block step
* @return last step direction
*/
BlockStep getLastBlockStep();
/**
* Get perspective scale
* @return scale
*/
double getScale();
/**
* Get start of current ray, in world coordinates
* @return start of ray
*/
Vector3D getRayStart();
/**
* Get end of current ray, in world coordinates
* @return end of ray
*/
Vector3D getRayEnd();
/**
* Get pixel X coordinate
* @return x coordinate
*/
int getPixelX();
/**
* Get pixel Y coordinate
* @return y coordinate
*/
int getPixelY();
/**
* Return submodel alpha value (-1 if no submodel rendered)
* @return alpha value
*/
int getSubmodelAlpha();
/**
* Return subblock coordinates of current ray position
* @return coordinates of ray
*/
int[] getSubblockCoord();
/**
* Get map iterator
* @return iterator
*/
MapIterator getMapIterator();
/**
* Get current texture index
* @return texture index
*/
int getTextureIndex();
/**
* Get current U of patch intercept
* @return U in patch
*/
double getPatchU();
/**
* Get current V of patch intercept
* @return V in patch
*/
double getPatchV();
/**
* Light level cache
* @param idx - index of light level (0-3)
* @return light level
*/
LightLevels getCachedLightLevels(int idx);
}

View File

@ -0,0 +1,85 @@
package org.dynmap.hdmap;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.PatchDefinition;
public class HDScaledBlockModels {
private short[][] modelvectors;
private PatchDefinition[][] patches;
private CustomBlockModel[] custom;
public HDScaledBlockModels(int scale) {
short[][] blockmodels = new short[DynmapBlockState.getGlobalIndexMax()][];
PatchDefinition[][] patches = new PatchDefinition[DynmapBlockState.getGlobalIndexMax()][];
CustomBlockModel[] custom = new CustomBlockModel[DynmapBlockState.getGlobalIndexMax()];
for(Integer gidx : HDBlockModels.models_by_id_data.keySet()) {
HDBlockModel m = HDBlockModels.models_by_id_data.get(gidx);
if(m instanceof HDBlockVolumetricModel) {
HDBlockVolumetricModel vm = (HDBlockVolumetricModel)m;
short[] smod = vm.getScaledMap(scale);
/* See if scaled model is full block : much faster to not use it if it is */
if(smod != null) {
boolean keep = false;
for(int i = 0; (!keep) && (i < smod.length); i++) {
if(smod[i] == 0) keep = true;
}
if(keep) {
blockmodels[gidx] = smod;
}
else {
blockmodels[gidx] = null;
}
}
}
else if(m instanceof HDBlockPatchModel) {
HDBlockPatchModel pm = (HDBlockPatchModel)m;
patches[gidx] = pm.getPatches();
}
else if(m instanceof CustomBlockModel) {
CustomBlockModel cbm = (CustomBlockModel)m;
custom[gidx] = cbm;
}
}
this.modelvectors = blockmodels;
this.patches = patches;
this.custom = custom;
}
public final short[] getScaledModel(DynmapBlockState blk) {
short[] m = null;
try {
m = modelvectors[blk.globalStateIndex];
} catch (ArrayIndexOutOfBoundsException aioobx) {
short[][] newmodels = new short[blk.globalStateIndex+1][];
System.arraycopy(modelvectors, 0, newmodels, 0, modelvectors.length);
modelvectors = newmodels;
}
return m;
}
public PatchDefinition[] getPatchModel(DynmapBlockState blk) {
PatchDefinition[] p = null;
try {
p = patches[blk.globalStateIndex];
} catch (ArrayIndexOutOfBoundsException aioobx) {
PatchDefinition[][] newpatches = new PatchDefinition[blk.globalStateIndex+1][];
System.arraycopy(patches, 0, newpatches, 0, patches.length);
patches = newpatches;
}
return p;
}
public CustomBlockModel getCustomBlockModel(DynmapBlockState blk) {
CustomBlockModel m = null;
try {
m = custom[blk.globalStateIndex];
} catch (ArrayIndexOutOfBoundsException aioobx) {
CustomBlockModel[] newcustom = new CustomBlockModel[blk.globalStateIndex+1];
System.arraycopy(custom, 0, newcustom, 0, custom.length);
custom = newcustom;
}
return m;
}
}

View File

@ -0,0 +1,43 @@
package org.dynmap.hdmap;
import java.io.IOException;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
public interface HDShader {
/* Get shader name */
String getName();
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale
* @return state object to use for all rays in tile
*/
HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale);
/* Test if Biome Data is needed for this renderer */
boolean isBiomeDataNeeded();
/* Test if raw biome temperature/rainfall data is needed */
boolean isRawBiomeDataNeeded();
/* Test if highest block Y data is needed */
boolean isHightestBlockYDataNeeded();
/* Tet if block type data needed */
boolean isBlockTypeDataNeeded();
/* Test if sky light level needed */
boolean isSkyLightLevelNeeded();
/* Test if emitted light level needed */
boolean isEmittedLightLevelNeeded();
/* Add shader's contributions to JSON for map object */
void addClientConfiguration(JSONObject mapObject);
/* Export shader as material library */
void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport exp) throws IOException;
/* Get materials for each patch on the current block (with +N for N*90 degree rotations) */
String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps);
}

View File

@ -0,0 +1,62 @@
package org.dynmap.hdmap;
import org.dynmap.Color;
import org.dynmap.utils.DynLongHashMap;
/**
* This interface is used to define the operational state of a renderer during raytracing
* All method should be considered performance critical
*/
public interface HDShaderState {
/**
* Get our shader
* @return shader
*/
HDShader getShader();
/**
* Get our lighting
* @return lighting
*/
HDLighting getLighting();
/**
* Get our map
* @return map
*/
HDMap getMap();
/**
* Reset renderer state for new ray - passes in pixel coordinate for ray
* @param ps - perspective state
*/
void reset(HDPerspectiveState ps);
/**
* Process next ray step - called for each block on route
* @param ps - perspective state
* @return true if ray is done, false if ray needs to continue
*/
boolean processBlock(HDPerspectiveState ps);
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
* @param ps - perspective state
*/
void rayFinished(HDPerspectiveState ps);
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
void getRayColor(Color c, int index);
/**
* Clean up state object - called after last ray completed
*/
void cleanup();
/**
* Get CTM texture cache
* @return texture cache
*/
DynLongHashMap getCTMTextureCache();
/**
* Get lighting table
* @return array of lighting values
*/
int[] getLightingTable();
}

View File

@ -0,0 +1,241 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.HashMap;
import java.util.TreeSet;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
public class InhabitedHDShader implements HDShader {
private final String name;
private final long filllevel[]; /* Values for colors */
private final Color fillcolor[];
private Color readColor(String id, ConfigurationNode cfg) {
String lclr = cfg.getString(id, null);
if((lclr != null) && (lclr.startsWith("#"))) {
try {
int c = Integer.parseInt(lclr.substring(1), 16);
return new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF);
} catch (NumberFormatException nfx) {
Log.severe("Invalid color value: " + lclr + " for '" + id + "'");
}
}
return null;
}
public InhabitedHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
HashMap<Long, Color> map = new HashMap<Long, Color>();
for (String key : configuration.keySet()) {
if (key.startsWith("color")) {
try {
long val = Long.parseLong(key.substring(5));
Color clr = readColor(key, configuration);
map.put(val, clr);
} catch (NumberFormatException nfx) {
}
}
}
TreeSet<Long> keys = new TreeSet<Long>(map.keySet());
filllevel = new long[keys.size()];
fillcolor = new Color[keys.size()];
int idx = 0;
for (Long k : keys) {
filllevel[idx] = k;
fillcolor[idx] = map.get(k);
idx++;
}
}
@Override
public boolean isBiomeDataNeeded() {
return false;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color[];
private Color c;
protected HDMap map;
private HDLighting lighting;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
}
c = new Color();
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
public HDShader getShader() {
return InhabitedHDShader.this;
}
/**
* Get our map
*/
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
if (ps.getBlockState().isAir()) {
return false;
}
long ts = ps.getMapIterator().getInhabitedTicks() / 1200; // Get time, in minutes
// Find top of range
boolean match = false;
for (int i = 0; i < filllevel.length; i++) {
if (ts < filllevel[i]) { // Found it
if (i > 0) { // Middle? Interpolate
int interp = (int) ((256 * (ts - filllevel[i-1])) / (filllevel[i] - filllevel[i-1]));
int red = (interp * fillcolor[i].getRed()) + ((256 - interp) * fillcolor[i-1].getRed());
int green = (interp * fillcolor[i].getGreen()) + ((256 - interp) * fillcolor[i-1].getGreen());
int blue = (interp * fillcolor[i].getBlue()) + ((256 - interp) * fillcolor[i-1].getBlue());
c.setRGBA(red / 256, green / 256, blue / 256, 255);
}
else { // Else, use color
c.setColor(fillcolor[i]);
}
match = true;
break;
}
}
if (!match) {
c.setColor(fillcolor[fillcolor.length-1]);
}
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, color);
return true;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspecitve
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new OurShaderState(mapiter, map, cache, scale);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
return nulllist;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,293 @@
package org.dynmap.hdmap;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.DynmapWorld;
import org.dynmap.MapManager;
import org.dynmap.utils.LightLevels;
import org.dynmap.utils.BlockStep;
public class ShadowHDLighting extends DefaultHDLighting {
protected final int defLightingTable[]; /* index=skylight level, value = 256 * scaling value */
protected final int lightscale[]; /* scale skylight level (light = lightscale[skylight] */
protected final boolean night_and_day; /* If true, render both day (prefix+'-day') and night (prefix) tiles */
protected final boolean smooth;
protected final boolean useWorldBrightnessTable;
public ShadowHDLighting(DynmapCore core, ConfigurationNode configuration) {
super(core, configuration);
double shadowweight = configuration.getDouble("shadowstrength", 0.0);
// See if we're using world's lighting table, or our own
useWorldBrightnessTable = configuration.getBoolean("use-brightness-table", MapManager.mapman.useBrightnessTable());
defLightingTable = new int[16];
defLightingTable[15] = 256;
/* Normal brightness weight in MC is a 20% relative dropoff per step */
for(int i = 14; i >= 0; i--) {
double v = defLightingTable[i+1] * (1.0 - (0.2 * shadowweight));
defLightingTable[i] = (int)v;
if(defLightingTable[i] > 256) defLightingTable[i] = 256;
if(defLightingTable[i] < 0) defLightingTable[i] = 0;
}
int v = configuration.getInteger("ambientlight", -1);
if(v < 0) v = 15;
if(v > 15) v = 15;
night_and_day = configuration.getBoolean("night-and-day", false);
lightscale = new int[16];
for(int i = 0; i < 16; i++) {
if(i < (15-v))
lightscale[i] = 0;
else
lightscale[i] = i - (15-v);
}
smooth = configuration.getBoolean("smooth-lighting", MapManager.mapman.getSmoothLighting());
}
private void applySmoothLighting(HDPerspectiveState ps, HDShaderState ss, Color incolor, Color[] outcolor, int[] shadowscale) {
int[] xyz = ps.getSubblockCoord();
int scale = (int)ps.getScale();
int mid = scale/2;
BlockStep s1, s2;
int w1, w2;
/* Figure out which directions to look */
switch(ps.getLastBlockStep()) {
case X_MINUS:
case X_PLUS:
if(xyz[1] < mid) {
s1 = BlockStep.Y_MINUS;
w1 = mid - xyz[1];
}
else {
s1 = BlockStep.Y_PLUS;
w1 = xyz[1] - mid;
}
if(xyz[2] < mid) {
s2 = BlockStep.Z_MINUS;
w2 = mid - xyz[2];
}
else {
s2 = BlockStep.Z_PLUS;
w2 = xyz[2] - mid;
}
break;
case Z_MINUS:
case Z_PLUS:
if(xyz[0] < mid) {
s1 = BlockStep.X_MINUS;
w1 = mid - xyz[0];
}
else {
s1 = BlockStep.X_PLUS;
w1 = xyz[0] - mid;
}
if(xyz[1] < mid) {
s2 = BlockStep.Y_MINUS;
w2 = mid - xyz[1];
}
else {
s2 = BlockStep.Y_PLUS;
w2 = xyz[1] - mid;
}
break;
default:
if(xyz[0] < mid) {
s1 = BlockStep.X_MINUS;
w1 = mid - xyz[0];
}
else {
s1 = BlockStep.X_PLUS;
w1 = xyz[0] - mid;
}
if(xyz[2] < mid) {
s2 = BlockStep.Z_MINUS;
w2 = mid - xyz[2];
}
else {
s2 = BlockStep.Z_PLUS;
w2 = xyz[2] - mid;
}
break;
}
/* Now get the 3 needed light levels */
LightLevels skyemit0 = ps.getCachedLightLevels(0);
ps.getLightLevels(skyemit0);
LightLevels skyemit1 = ps.getCachedLightLevels(1);
ps.getLightLevelsAtStep(s1, skyemit1);
LightLevels skyemit2 = ps.getCachedLightLevels(2);
ps.getLightLevelsAtStep(s2, skyemit2);
/* Get light levels */
int ll0 = getLightLevel(skyemit0, true);
int ll1 = getLightLevel(skyemit1, true);
int weight = 0;
if(ll1 < ll0)
weight -= w1;
else if(ll1 > ll0)
weight += w1;
int ll2 = getLightLevel(skyemit2, true);
if(ll2 < ll0)
weight -= w2;
else if(ll2 > ll0)
weight += w2;
outcolor[0].setColor(incolor);
int cscale = 256;
if(weight == 0) {
cscale = shadowscale[ll0];
}
else if(weight < 0) { /* If negative, interpolate down */
weight = -weight;
if(ll0 > 0) {
cscale = (shadowscale[ll0] * (scale-weight) + shadowscale[ll0-1] * weight)/scale;
}
else {
cscale = shadowscale[ll0];
}
}
else {
if(ll0 < 15) {
cscale = (shadowscale[ll0] * (scale-weight) + shadowscale[ll0+1] * weight)/scale;
}
else {
cscale = shadowscale[ll0];
}
}
if(cscale < 256) {
Color c = outcolor[0];
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
(c.getBlue() * cscale) >> 8, c.getAlpha());
}
if(outcolor.length > 1) {
ll0 = getLightLevel(skyemit0, false);
ll1 = getLightLevel(skyemit1, false);
weight = 0;
if(ll1 < ll0)
weight -= w1;
else if(ll1 > ll0)
weight += w1;
ll2 = getLightLevel(skyemit2, false);
if(ll2 < ll0)
weight -= w2;
else if(ll2 > ll0)
weight += w2;
outcolor[1].setColor(incolor);
cscale = 256;
if(weight == 0) {
cscale = shadowscale[ll0];
}
else if(weight < 0) { /* If negative, interpolate down */
weight = -weight;
if(ll0 > 0) {
cscale = (shadowscale[ll0] * (scale-weight) + shadowscale[ll0-1] * weight)/scale;
}
else {
cscale = shadowscale[ll0];
}
}
else {
if(ll0 < 15) {
cscale = (shadowscale[ll0] * (scale-weight) + shadowscale[ll0+1] * weight)/scale;
}
else {
cscale = shadowscale[ll0];
}
}
if(cscale < 256) {
Color c = outcolor[1];
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
(c.getBlue() * cscale) >> 8, c.getAlpha());
}
}
}
private final int getLightLevel(final LightLevels ll, boolean useambient) {
int lightlevel;
/* If ambient light, adjust base lighting for it */
if(useambient)
lightlevel = lightscale[ll.sky];
else
lightlevel = ll.sky;
/* If we're below max, see if emitted light helps */
if(lightlevel < 15) {
lightlevel = Math.max(ll.emitted, lightlevel);
}
return lightlevel;
}
/* Apply lighting to given pixel colors (1 outcolor if normal, 2 if night/day) */
public void applyLighting(HDPerspectiveState ps, HDShaderState ss, Color incolor, Color[] outcolor) {
int[] shadowscale = null;
if(smooth) {
shadowscale = ss.getLightingTable();
if (shadowscale == null) {
shadowscale = defLightingTable;
}
applySmoothLighting(ps, ss, incolor, outcolor, shadowscale);
return;
}
LightLevels ll = null;
int lightlevel = 15, lightlevel_day = 15;
/* If processing for shadows, use sky light level as base lighting */
if(defLightingTable != null) {
shadowscale = ss.getLightingTable();
if (shadowscale == null) {
shadowscale = defLightingTable;
}
ll = ps.getCachedLightLevels(0);
ps.getLightLevels(ll);
lightlevel = lightlevel_day = ll.sky;
}
/* If ambient light, adjust base lighting for it */
lightlevel = lightscale[lightlevel];
/* If we're below max, see if emitted light helps */
if((lightlevel < 15) || (lightlevel_day < 15)) {
int emitted = ll.emitted;
lightlevel = Math.max(emitted, lightlevel);
lightlevel_day = Math.max(emitted, lightlevel_day);
}
/* Figure out our color, with lighting if needed */
outcolor[0].setColor(incolor);
if(lightlevel < 15) {
shadowColor(outcolor[0], lightlevel, shadowscale);
}
if(outcolor.length > 1) {
if(lightlevel_day == lightlevel) {
outcolor[1].setColor(outcolor[0]);
}
else {
outcolor[1].setColor(incolor);
if(lightlevel_day < 15) {
shadowColor(outcolor[1], lightlevel_day, shadowscale);
}
}
}
}
private final void shadowColor(Color c, int lightlevel, int[] shadowscale) {
int scale = shadowscale[lightlevel];
if(scale < 256)
c.setRGBA((c.getRed() * scale) >> 8, (c.getGreen() * scale) >> 8,
(c.getBlue() * scale) >> 8, c.getAlpha());
}
/* Test if night/day is enabled for this renderer */
public boolean isNightAndDayEnabled() { return night_and_day; }
/* Test if sky light level needed */
public boolean isSkyLightLevelNeeded() { return true; }
/* Test if emitted light level needed */
public boolean isEmittedLightLevelNeeded() { return true; }
@Override
public int[] getBrightnessTable(DynmapWorld world) {
if (useWorldBrightnessTable) {
return world.getBrightnessTable();
}
else {
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
package org.dynmap.hdmap;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.LightLevels;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
public class TexturePackHDCaveShader extends TexturePackHDShader {
private int maxskylevel;
private int minemittedlevel;
class CaveShaderState extends TexturePackHDShader.ShaderState {
private boolean ready;
private LightLevels ll = new LightLevels();
protected CaveShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
super(mapiter, map, cache, scale);
}
@Override
public void reset(HDPerspectiveState ps) {
super.reset(ps);
ready = false;
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
if(ready)
return super.processBlock(ps);
if((ps.getLastBlockStep() == BlockStep.Y_MINUS) && ps.getBlockState().isAir()) { /* In air? */
ps.getLightLevels(ll);
if((ll.sky <= maxskylevel) && (ll.emitted > minemittedlevel)) {
ready = true;
}
}
return false;
}
}
public TexturePackHDCaveShader(DynmapCore core, ConfigurationNode configuration) {
super(core, configuration);
maxskylevel = configuration.getInteger("max-sky-light", 0);
minemittedlevel = configuration.getInteger("min-emitted-light", 1);
}
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new CaveShaderState(mapiter, map, cache, scale);
}
}

View File

@ -0,0 +1,357 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.BitSet;
import java.util.List;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
public class TexturePackHDShader implements HDShader {
private final String tpname;
private final String name;
private TexturePack tp;
private boolean did_tp_load = false;
private final boolean biome_shaded;
private final boolean bettergrass;
private final int gridscale;
private final DynmapCore core;
private final BitSet hiddenids;
public TexturePackHDShader(DynmapCore core, ConfigurationNode configuration) {
tpname = configuration.getString("texturepack", "minecraft");
name = configuration.getString("name", tpname);
this.core = core;
biome_shaded = configuration.getBoolean("biomeshaded", true);
bettergrass = configuration.getBoolean("better-grass", MapManager.mapman.getBetterGrass());
gridscale = configuration.getInteger("grid-scale", 0);
List<Object> hidden = configuration.getList("hiddenids");
if(hidden != null) {
hiddenids = new BitSet();
for(Object o : hidden) {
if(o instanceof Integer) {
int v = ((Integer)o);
hiddenids.set(v);
}
}
}
else {
hiddenids = null;
}
}
private final TexturePack getTexturePack() {
if (!did_tp_load) {
tp = TexturePack.getTexturePack(this.core, this.tpname);
if(tp == null) {
Log.severe("Error: shader '" + name + "' cannot load texture pack '" + tpname + "'");
}
did_tp_load = true;
}
return tp;
}
@Override
public boolean isBiomeDataNeeded() {
return biome_shaded;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
class ShaderState implements HDShaderState {
final private Color color[];
final private Color tmpcolor[];
final private Color c;
final protected MapIterator mapiter;
final protected HDMap map;
final private TexturePack scaledtp;
final private HDLighting lighting;
private DynmapBlockState lastblk;
final boolean do_biome_shading;
final boolean do_better_grass;
DynLongHashMap ctm_cache;
final int[] lightingTable;
protected ShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
this.mapiter = mapiter;
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
tmpcolor = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
tmpcolor = new Color[] { new Color() };
}
c = new Color();
TexturePack tp = getTexturePack();
if (tp != null)
scaledtp = tp.resampleTexturePack(scale);
else
scaledtp = null;
/* Biome raw data only works on normal worlds at this point */
do_biome_shading = biome_shaded; // && (cache.getWorld().getEnvironment() == Environment.NORMAL);
do_better_grass = bettergrass;
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
@Override
public HDShader getShader() {
return TexturePackHDShader.this;
}
/**
* Get our map
*/
@Override
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
@Override
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
@Override
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
lastblk = DynmapBlockState.AIR;
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
DynmapBlockState blocktype = ps.getBlockState();
if ((hiddenids != null) && hiddenids.get(blocktype.globalStateIndex)) {
blocktype = DynmapBlockState.AIR;
}
DynmapBlockState lastblocktype = lastblk;
lastblk = blocktype;
if (blocktype.isAir()) {
return false;
}
/* Get color from textures */
if (scaledtp != null) {
scaledtp.readColor(ps, mapiter, c, blocktype, lastblocktype, ShaderState.this);
}
if (c.getAlpha() > 0) {
/* Scale brightness depending upon face */
if (this.lightingTable != null) {
switch(ps.getLastBlockStep()) {
case X_MINUS:
case X_PLUS:
/* 60% brightness */
c.blendColor(0xFF999999);
break;
case Y_MINUS:
// 95% for even
if((mapiter.getY() & 0x01) == 0) {
c.blendColor(0xFFF3F3F3);
}
break;
case Y_PLUS:
/* 50%*/
c.blendColor(0xFF808080);
break;
case Z_MINUS:
case Z_PLUS:
default:
/* 80%*/
c.blendColor(0xFFCDCDCD);
break;
}
}
else {
switch(ps.getLastBlockStep()) {
case X_MINUS:
case X_PLUS:
/* 60% brightness */
c.blendColor(0xFFA0A0A0);
break;
case Y_MINUS:
case Y_PLUS:
/* 85% brightness for even, 90% for even*/
if((mapiter.getY() & 0x01) == 0)
c.blendColor(0xFFD9D9D9);
else
c.blendColor(0xFFE6E6E6);
break;
default:
break;
}
}
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, tmpcolor);
/* If grid scale, add it */
if(gridscale > 0) {
int xx = mapiter.getX() % gridscale;
int zz = mapiter.getZ() % gridscale;
if(((xx == 0) && ((zz & 2) == 0)) || ((zz == 0) && ((xx & 2) == 0))) {
for(int i = 0; i < tmpcolor.length; i++) {
int v = tmpcolor[i].getARGB();
tmpcolor[i].setARGB((v & 0xFF000000) | ((v & 0xFEFEFE) >> 1) | 0x808080);
}
}
}
/* If no previous color contribution, use new color */
if(color[0].isTransparent()) {
for(int i = 0; i < color.length; i++)
color[i].setColor(tmpcolor[i]);
return (color[0].getAlpha() == 255);
}
/* Else, blend and generate new alpha */
else {
int alpha = color[0].getAlpha();
int alpha2 = tmpcolor[0].getAlpha() * (255-alpha) / 255;
int talpha = alpha + alpha2;
if(talpha > 0)
for(int i = 0; i < color.length; i++)
color[i].setRGBA((tmpcolor[i].getRed()*alpha2 + color[i].getRed()*alpha) / talpha,
(tmpcolor[i].getGreen()*alpha2 + color[i].getGreen()*alpha) / talpha,
(tmpcolor[i].getBlue()*alpha2 + color[i].getBlue()*alpha) / talpha, talpha);
else
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
return (talpha >= 254); /* If only one short, no meaningful contribution left */
}
}
return false;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
if (ctm_cache != null) {
ctm_cache.clear();
ctm_cache = null;
}
}
@Override
public DynLongHashMap getCTMTextureCache() {
if (ctm_cache == null) {
ctm_cache = new DynLongHashMap();
}
return ctm_cache;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspective
* @return state object to use for all rays in tile
*/
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new ShaderState(mapiter, map, cache, scale);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
if (tp == null) {
getTexturePack(); // Make sure its loaded
}
if (tp != null) {
tp.exportAsOBJMaterialLibrary(out, out.getBaseName());
return;
}
throw new IOException("Export unsupported - invalid texture pack");
}
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
if (tp == null) {
getTexturePack(); // Make sure its loaded
}
if (tp != null) {
return tp.getCurrentBlockMaterials(blk, mapiter, txtidx, steps);
}
return new String[txtidx.length];
}
}

View File

@ -0,0 +1,171 @@
package org.dynmap.hdmap;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.common.DynmapServerInterface;
public class TexturePackLoader {
private ZipFile zf;
private File tpdir;
private DynmapServerInterface dsi;
private static final String RESOURCEPATH = "texturepacks/standard";
private static class ModSource {
ZipFile zf;
File directory;
}
private HashMap<String, ModSource> src_by_mod = new HashMap<String, ModSource>();
public TexturePackLoader(File tp, DynmapCore core) {
if (tp.isFile() && tp.canRead()) {
try {
zf = new ZipFile(tp);
} catch (IOException e) {
Log.severe("Error opening texture pack - " + tp.getPath());
}
}
else if (tp.isDirectory() && tp.canRead()) {
tpdir = tp;
}
else {
Log.info("Texture pack not found - " + tp.getPath());
}
dsi = core.getServer();
}
public InputStream openTPResource(String rname, String altname) {
InputStream is = openTPResource(rname);
if (is == null) {
if (altname != null) {
is = openTPResource(altname);
}
}
return is;
}
public InputStream openTPResource(String rname) {
return openModTPResource(rname, null);
}
public InputStream openModTPResource(String rname, String modname) {
try {
if (zf != null) {
ZipEntry ze = zf.getEntry(rname);
if ((ze != null) && (!ze.isDirectory())) {
return zf.getInputStream(ze);
}
}
else if (tpdir != null) {
File f = new File(tpdir, rname);
if (f.isFile() && f.canRead()) {
return new FileInputStream(f);
}
}
} catch (IOException iox) {
}
// Fall through - load as resource from mod, if possible, or from jar
InputStream is = dsi.openResource(modname, rname);
if (is != null) {
return is;
}
if (modname != null) {
ModSource ms = src_by_mod.get(modname);
if (ms == null) {
File f = dsi.getModContainerFile(modname);
ms = new ModSource();
if (f != null) {
if (f.isFile()) {
try {
ms.zf = new ZipFile(f);
} catch (IOException e) {
}
}
else {
ms.directory = f;
}
}
src_by_mod.put(modname, ms);
}
try {
if (ms.zf != null) {
ZipEntry ze = ms.zf.getEntry(rname);
if ((ze != null) && (!ze.isDirectory())) {
is = ms.zf.getInputStream(ze);
}
}
else if (ms.directory != null) {
File f = new File(ms.directory, rname);
if (f.isFile() && f.canRead()) {
is = new FileInputStream(f);
}
}
} catch (IOException iox) {
}
}
if (is == null) {
is = getClass().getClassLoader().getResourceAsStream(RESOURCEPATH + "/" + rname);
}
if ((is == null) && (modname != null)) {
Log.warning("Resource " + rname + " for mod " + modname + " not found");
}
return is;
}
public void close() {
if(zf != null) {
try { zf.close(); } catch (IOException iox) {}
zf = null;
}
for (ModSource ms : src_by_mod.values()) {
if (ms.zf != null) {
try { ms.zf.close(); } catch (IOException iox) {}
}
}
src_by_mod.clear();
}
public void closeResource(InputStream is) {
try {
if (is != null)
is.close();
} catch (IOException iox) {
}
}
public Set<String> getEntries() {
HashSet<String> rslt = new HashSet<String>();
if (zf != null) {
Enumeration<? extends ZipEntry> lst = zf.entries();
while(lst.hasMoreElements()) {
rslt.add(lst.nextElement().getName());
}
}
if (tpdir != null) {
addFiles(rslt, tpdir, "");
}
return rslt;
}
private void addFiles(HashSet<String> files, File dir, String path) {
File[] listfiles = dir.listFiles();
if(listfiles == null) return;
for(File f : listfiles) {
String fn = f.getName();
if(fn.equals(".") || (fn.equals(".."))) continue;
if(f.isFile()) {
files.add(path + "/" + fn);
}
else if(f.isDirectory()) {
addFiles(files, f, path + "/" + f.getName());
}
}
}
}

View File

@ -0,0 +1,349 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.BitSet;
import java.util.List;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.dynmap.utils.BlockStep;
import org.json.simple.JSONObject;
public class TopoHDShader implements HDShader {
private final String name;
private final Color linecolor; /* Color for topo lines */
private final Color fillcolor[]; /* Color for nontopo surfaces */
private final Color watercolor;
private BitSet hiddenids;
private final int linespacing;
private Color readColor(String id, ConfigurationNode cfg) {
String lclr = cfg.getString(id, null);
if((lclr != null) && (lclr.startsWith("#"))) {
try {
int c = Integer.parseInt(lclr.substring(1), 16);
return new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF);
} catch (NumberFormatException nfx) {
Log.severe("Invalid color value: " + lclr + " for '" + id + "'");
}
}
return null;
}
public TopoHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
fillcolor = new Color[256]; /* Color by Y */
/* Load defined colors from parameters */
for(int i = 0; i < 256; i++) {
fillcolor[i] = readColor("color" + i, configuration);
}
linecolor = readColor("linecolor", configuration);
watercolor = readColor("watercolor", configuration);
float wateralpha = configuration.getFloat("wateralpha", 1.0F);
if (wateralpha < 1.0) {
watercolor.setAlpha((int)(255 * wateralpha));
}
/* Now, interpolate missing colors */
if(fillcolor[0] == null) {
fillcolor[0] = new Color(0, 0, 0);
}
if(fillcolor[255] == null) {
fillcolor[255] = new Color(255, 255, 255);
}
int starty = 0;
for(int i = 1; i < 256; i++) {
if(fillcolor[i] != null) { /* Found color? */
int delta = i - starty;
Color c0 = fillcolor[starty];
Color c1 = fillcolor[i];
/* Interpolate missing colors since last one */
for(int j = 1; j < delta; j++) {
fillcolor[starty + j] = new Color((c0.getRed()*(delta-j) + c1.getRed()*j)/delta, (c0.getGreen()*(delta-j) + c1.getGreen()*j)/delta, (c0.getBlue()*(delta-j) + c1.getBlue()*j)/delta);
}
starty = i; /* New start color */
}
}
hiddenids = new BitSet();
setHidden(DynmapBlockState.AIR_BLOCK); /* Air is hidden always */
List<Object> hidden = configuration.getList("hiddennames");
if(hidden != null) {
for(Object o : hidden) {
if(o instanceof String) {
setHidden((String) o);
}
}
}
linespacing = configuration.getInteger("linespacing", 1);
}
private void setHidden(String bn) {
DynmapBlockState bs = DynmapBlockState.getBaseStateByName(bn);
for (int i = 0; i < bs.getStateCount(); i++) {
DynmapBlockState b = bs.getState(i);
hiddenids.set(b.globalStateIndex);
}
}
@Override
public boolean isBiomeDataNeeded() {
return false;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color[];
private Color tmpcolor[];
private Color c;
protected MapIterator mapiter;
protected HDMap map;
private HDLighting lighting;
private int scale;
private int heightshift; /* Divide to keep in 0-127 range of colors */
private boolean inWater;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
this.mapiter = mapiter;
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
tmpcolor = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
tmpcolor = new Color[] { new Color() };
}
this.scale = scale;
c = new Color();
/* Compute divider for Y - to map to existing color range */
int wh = mapiter.getWorldHeight();
heightshift = 0;
while(wh > 256) {
heightshift++;
wh >>= 1;
}
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
inWater = false;
}
/**
* Get our shader
*/
public HDShader getShader() {
return TopoHDShader.this;
}
/**
* Get our map
*/
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
inWater = false;
}
private final boolean isHidden(DynmapBlockState blk) {
return hiddenids.get(blk.globalStateIndex);
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
DynmapBlockState blocktype = ps.getBlockState();
if (isHidden(blocktype)) {
return false;
}
/* See if we're close to an edge */
int[] xyz = ps.getSubblockCoord();
// Only color lines when spacing is matched
Color lcolor = ((ps.getMapIterator().getY() % linespacing) == 0)?linecolor:null;
/* See which face we're on (only do lines on top face) */
switch(ps.getLastBlockStep()) {
case Y_MINUS:
case Y_PLUS:
if((lcolor != null) &&
(((xyz[0] == 0) && (isHidden(mapiter.getBlockTypeAt(BlockStep.X_MINUS)))) ||
((xyz[0] == (scale-1)) && (isHidden(mapiter.getBlockTypeAt(BlockStep.X_PLUS)))) ||
((xyz[2] == 0) && (isHidden(mapiter.getBlockTypeAt(BlockStep.Z_MINUS)))) ||
((xyz[2] == (scale-1)) && (isHidden(mapiter.getBlockTypeAt(BlockStep.Z_PLUS)))))) {
c.setColor(lcolor);
inWater = false;
}
else if ((watercolor != null) && blocktype.isWater()) {
if (!inWater) {
c.setColor(watercolor);
inWater = true;
}
else {
return false;
}
}
else {
c.setColor(fillcolor[mapiter.getY() >> heightshift]);
inWater = false;
}
break;
default:
if((lcolor != null) && (xyz[1] == (scale-1))) {
c.setColor(lcolor);
inWater = false;
}
else if ((watercolor != null) && blocktype.isWater()) {
if (!inWater) {
c.setColor(watercolor);
inWater = true;
}
else {
return false;
}
}
else {
c.setColor(fillcolor[mapiter.getY() >> heightshift]);
inWater = false;
}
break;
}
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, tmpcolor);
/* If no previous color contribution, use new color */
if(color[0].isTransparent()) {
for(int i = 0; i < color.length; i++)
color[i].setColor(tmpcolor[i]);
return (color[0].getAlpha() == 255);
}
/* Else, blend and generate new alpha */
else {
int alpha = color[0].getAlpha();
int alpha2 = tmpcolor[0].getAlpha() * (255-alpha) / 255;
int talpha = alpha + alpha2;
if(talpha > 0)
for(int i = 0; i < color.length; i++)
color[i].setRGBA((tmpcolor[i].getRed()*alpha2 + color[i].getRed()*alpha) / talpha,
(tmpcolor[i].getGreen()*alpha2 + color[i].getGreen()*alpha) / talpha,
(tmpcolor[i].getBlue()*alpha2 + color[i].getBlue()*alpha) / talpha, talpha);
else
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
return (talpha >= 254); /* If only one short, no meaningful contribution left */
}
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspecitve
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new OurShaderState(mapiter, map, cache, scale);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
return nulllist;
}
}

View File

@ -0,0 +1,29 @@
package org.dynmap.hdmap.colormult;
import org.dynmap.renderer.CustomColorMultiplier;
import org.dynmap.renderer.MapDataContext;
/**
* Twilight Forest banded wood color multiplier
*/
public class TFBandedWoodColorMultiplier extends CustomColorMultiplier {
public TFBandedWoodColorMultiplier() {
}
@Override
public int getColorMultiplier(MapDataContext ctx) {
int x = ctx.getX();
int y = ctx.getY();
int z = ctx.getZ();
int value = x * 31 + y * 15 + z * 33;
if ((value & 0x100) != 0) {
value = 255 - (value & 0xFF);
}
value &= 255;
value >>= 1;
value |= 128;
return (value << 16) | (value << 8) | value;
}
}

View File

@ -0,0 +1,32 @@
package org.dynmap.hdmap.colormult;
import org.dynmap.renderer.CustomColorMultiplier;
import org.dynmap.renderer.MapDataContext;
/**
* Twilight Forest magic leaf color multiplier
*/
public class TFMagicLeafColorMultiplier extends CustomColorMultiplier {
public TFMagicLeafColorMultiplier() {
}
@Override
public int getColorMultiplier(MapDataContext ctx) {
int x = ctx.getX();
int y = ctx.getY();
int z = ctx.getZ();
int fade = x * 16 + y * 16 + z * 16;
if ((fade & 0x100) != 0) {
fade = 255 - (fade & 0xFF);
}
fade &= 255;
float spring = (255 - fade) / 255.0F;
float fall = fade / 255.0F;
int red = (int)(spring * 106.0F + fall * 251.0F);
int green = (int)(spring * 156.0F + fall * 108.0F);
int blue = (int)(spring * 23.0F + fall * 27.0F);
return (red << 16) | (green << 8) | blue;
}
}

View File

@ -0,0 +1,39 @@
package org.dynmap.hdmap.colormult;
import org.dynmap.renderer.CustomColorMultiplier;
import org.dynmap.renderer.MapDataContext;
/**
* Twilight Forest special leaf color multiplier
*/
public class TFSpecialLeafColorMultiplier extends CustomColorMultiplier {
public TFSpecialLeafColorMultiplier() {
}
@Override
public int getColorMultiplier(MapDataContext ctx) {
int x = ctx.getX();
int y = ctx.getY();
int z = ctx.getZ();
int r = (x * 32) + (y * 16);
if((r & 0x100) != 0) {
r = 0xFF - (r & 0xFF);
}
r &= 0xFF;
int g = (y * 32) + (z * 16);
if((g & 0x100) != 0) {
g = 0xFF - (g & 0xFF);
}
g ^= 0xFF; // Probably bug in TwilightForest - needed to match
int b = (x * 16) + (z * 32);
if((b & 0x100) != 0) {
b = 0xFF - (b & 0xFF);
}
b &= 0xFF;
return (r << 16) | (g << 8) | b;
}
}

View File

@ -0,0 +1,75 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
/**
* Simple renderer for creating a model representing a normal cube (texture-wise), but with reductions in the X, Y and/or Z ranges
*/
public class BoxRenderer extends CustomRenderer {
// Models for rotation values
private RenderPatch[] model;
// Patch index ordering, corresponding to BlockStep ordinal order
private static final int patchlist[] = { 1, 4, 2, 5, 0, 3 };
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
double xmin = 0.0, xmax = 1.0;
double ymin = 0.0, ymax = 1.0;
double zmin = 0.0, zmax = 1.0;
/* Check limits */
String lim = custparm.get("xmin");
if (lim != null) {
xmin = Double.valueOf(lim);
if (xmin < 0.0) xmin = 0.0;
}
lim = custparm.get("xmax");
if (lim != null) {
xmax = Double.valueOf(lim);
if (xmax > 1.0) xmax = 1.0;
}
lim = custparm.get("ymin");
if (lim != null) {
ymin = Double.valueOf(lim);
if (ymin < 0.0) ymin = 0.0;
}
lim = custparm.get("ymax");
if (lim != null) {
ymax = Double.valueOf(lim);
if (ymax > 1.0) ymax = 1.0;
}
lim = custparm.get("zmin");
if (lim != null) {
zmin = Double.valueOf(lim);
if (zmin < 0.0) zmin = 0.0;
}
lim = custparm.get("zmax");
if (lim != null) {
zmax = Double.valueOf(lim);
if (zmax > 1.0) zmax = 1.0;
}
/* Now, build box model */
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
CustomRenderer.addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patchlist);
model = list.toArray(new RenderPatch[patchlist.length]);
return true;
}
@Override
public int getMaximumTextureCount() {
return 6;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
return model;
}
}

View File

@ -0,0 +1,96 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class CTMVertTextureRenderer extends CustomRenderer {
private static final int TEXTURE_BOTTOM = 0;
private static final int TEXTURE_TOP = 1;
private static final int TEXTURE_SIDE_NO_NEIGHBOR = 2;
private static final int TEXTURE_SIDE_ABOVE = 3;
private static final int TEXTURE_SIDE_BELOW = 4;
private static final int TEXTURE_SIDE_BOTH = 5;
private DynmapBlockState blk;
private RenderPatch[] mesh_no_neighbor;
private RenderPatch[] mesh_above_neighbor;
private RenderPatch[] mesh_below_neighbor;
private RenderPatch[] mesh_both_neighbor;
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
blk = DynmapBlockState.getBaseStateByName(blkname);
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
/* Build no neighbors patches */
addBox(rpf, list, 0, 1, 0, 1, 0, 1, new int[] { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDE_NO_NEIGHBOR, TEXTURE_SIDE_NO_NEIGHBOR, TEXTURE_SIDE_NO_NEIGHBOR, TEXTURE_SIDE_NO_NEIGHBOR });
mesh_no_neighbor = list.toArray(new RenderPatch[6]);
/* Build above neighbor patches */
list.clear();
addBox(rpf, list, 0, 1, 0, 1, 0, 1, new int[] { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDE_ABOVE, TEXTURE_SIDE_ABOVE, TEXTURE_SIDE_ABOVE, TEXTURE_SIDE_ABOVE });
mesh_above_neighbor = list.toArray(new RenderPatch[6]);
/* Build below neighbor patches */
list.clear();
addBox(rpf, list, 0, 1, 0, 1, 0, 1, new int[] { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDE_BELOW, TEXTURE_SIDE_BELOW, TEXTURE_SIDE_BELOW, TEXTURE_SIDE_BELOW });
mesh_below_neighbor = list.toArray(new RenderPatch[6]);
/* Build both neighbor patches */
list.clear();
addBox(rpf, list, 0, 1, 0, 1, 0, 1, new int[] { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDE_BOTH, TEXTURE_SIDE_BOTH, TEXTURE_SIDE_BOTH, TEXTURE_SIDE_BOTH });
mesh_both_neighbor = list.toArray(new RenderPatch[6]);
return true;
}
@Override
public int getMaximumTextureCount() {
return 6;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext mapDataCtx) {
DynmapBlockState bs = mapDataCtx.getBlockType();
int meta = bs.stateIndex;
boolean above = false;
DynmapBlockState id_above = mapDataCtx.getBlockTypeAt(0, 1, 0);
if (id_above.baseState == blk) { /* MIght match */
int id_meta = id_above.stateIndex;
if (meta == id_meta) {
above = true;
}
}
boolean below = false;
DynmapBlockState id_below = mapDataCtx.getBlockTypeAt(0, -1, 0);
if (id_below.baseState == blk) { /* MIght match */
int id_meta = id_below.stateIndex;
if (meta == id_meta) {
below = true;
}
}
RenderPatch[] mesh;
if (above) {
if (below) {
mesh = this.mesh_both_neighbor;
}
else {
mesh = this.mesh_above_neighbor;
}
}
else {
if (below) {
mesh = this.mesh_below_neighbor;
}
else {
mesh = this.mesh_no_neighbor;
}
}
return mesh;
}
}

View File

@ -0,0 +1,150 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
/**
* Simple renderer for handling single and double chests
*/
public class ChestRenderer extends CustomRenderer {
private enum ChestData {
SINGLE_WEST, SINGLE_SOUTH, SINGLE_EAST, SINGLE_NORTH, LEFT_WEST, LEFT_SOUTH, LEFT_EAST, LEFT_NORTH, RIGHT_WEST, RIGHT_SOUTH, RIGHT_EAST, RIGHT_NORTH
};
// Models, indexed by ChestData.ordinal()
private RenderPatch[][] models = new RenderPatch[ChestData.values().length][];
private static final double OFF1 = 1.0 / 16.0;
private static final double OFF14 = 14.0 / 16.0;
private static final double OFF15 = 15.0 / 16.0;
private static final int[] SINGLE_PATCHES = { 5, 0, 1, 2, 4, 3 };
private static final int[] LEFT_PATCHES = { 14, 6, 10, 11, 12, 8 };
private static final int[] RIGHT_PATCHES = { 15, 7, 10, 11, 13, 9 };
private boolean double_chest = false;
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
double_chest = !("false".equals(custparm.get("doublechest")));
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
// Build single chest patch model
CustomRenderer.addBox(rpf, list, OFF1, OFF15, 0, OFF14, OFF1, OFF15, SINGLE_PATCHES);
models[ChestData.SINGLE_SOUTH.ordinal()] = list.toArray(new RenderPatch[list.size()]);
// Rotate to other orientations
models[ChestData.SINGLE_EAST.ordinal()] = new RenderPatch[6];
models[ChestData.SINGLE_NORTH.ordinal()] = new RenderPatch[6];
models[ChestData.SINGLE_WEST.ordinal()] = new RenderPatch[6];
for (int i = 0; i < 6; i++) {
models[ChestData.SINGLE_WEST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 90, 0, SINGLE_PATCHES[i]);
models[ChestData.SINGLE_NORTH.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 180, 0, SINGLE_PATCHES[i]);
models[ChestData.SINGLE_EAST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 270, 0, SINGLE_PATCHES[i]);
}
if (double_chest) {
// Build left half model for double chest
list.clear();
CustomRenderer.addBox(rpf, list, OFF1, 1, 0, OFF14, OFF1, OFF15, LEFT_PATCHES);
models[ChestData.LEFT_SOUTH.ordinal()] = list.toArray(new RenderPatch[list.size()]);
// Rotate to other orientations
models[ChestData.LEFT_EAST.ordinal()] = new RenderPatch[6];
models[ChestData.LEFT_NORTH.ordinal()] = new RenderPatch[6];
models[ChestData.LEFT_WEST.ordinal()] = new RenderPatch[6];
for (int i = 0; i < 6; i++) {
models[ChestData.LEFT_WEST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 90, 0, LEFT_PATCHES[i]);
models[ChestData.LEFT_NORTH.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 180, 0, LEFT_PATCHES[i]);
models[ChestData.LEFT_EAST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 270, 0, LEFT_PATCHES[i]);
}
// Build right half model for double chest
list.clear();
CustomRenderer.addBox(rpf, list, 0, OFF15, 0, OFF14, OFF1, OFF15, RIGHT_PATCHES);
models[ChestData.RIGHT_SOUTH.ordinal()] = list.toArray(new RenderPatch[list.size()]);
// Rotate to other orientations
models[ChestData.RIGHT_EAST.ordinal()] = new RenderPatch[6];
models[ChestData.RIGHT_NORTH.ordinal()] = new RenderPatch[6];
models[ChestData.RIGHT_WEST.ordinal()] = new RenderPatch[6];
for (int i = 0; i < 6; i++) {
models[ChestData.RIGHT_WEST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 90, 0, RIGHT_PATCHES[i]);
models[ChestData.RIGHT_NORTH.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 180, 0, RIGHT_PATCHES[i]);
models[ChestData.RIGHT_EAST.ordinal()][i] = rpf.getRotatedPatch(list.get(i), 0, 270, 0, RIGHT_PATCHES[i]);
}
}
return true;
}
@Override
public int getMaximumTextureCount() {
if (double_chest) {
return 16;
}
else {
return 6;
}
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
DynmapBlockState blktype = ctx.getBlockType().baseState;
if (!double_chest) {
blktype = DynmapBlockState.AIR; // Force mismatch - always single
}
int blkdata = blktype.stateIndex; /* Get block data */
ChestData cd = ChestData.SINGLE_NORTH; /* Default to single facing north */
switch(blkdata) { /* First, use orientation data */
case 2: /* North */
if(ctx.getBlockTypeAt(-1, 0, 0).baseState == blktype) {
cd = ChestData.LEFT_NORTH;
}
else if(ctx.getBlockTypeAt(1, 0, 0).baseState == blktype) {
cd = ChestData.RIGHT_NORTH;
}
else {
cd = ChestData.SINGLE_NORTH;
}
break;
case 4: /* West */
if(ctx.getBlockTypeAt(0, 0, -1).baseState == blktype) {
cd = ChestData.RIGHT_WEST;
}
else if(ctx.getBlockTypeAt(0, 0, 1).baseState == blktype) {
cd = ChestData.LEFT_WEST;
}
else {
cd = ChestData.SINGLE_WEST;
}
break;
case 5: /* East */
if(ctx.getBlockTypeAt(0, 0, -1).baseState == blktype) {
cd = ChestData.LEFT_EAST;
}
else if(ctx.getBlockTypeAt(0, 0, 1).baseState == blktype) {
cd = ChestData.RIGHT_EAST;
}
else {
cd = ChestData.SINGLE_EAST;
}
break;
case 3: /* South */
default:
if(ctx.getBlockTypeAt(-1, 0, 0).baseState == blktype) {
cd = ChestData.RIGHT_SOUTH;
}
else if(ctx.getBlockTypeAt(1, 0, 0).baseState == blktype) {
cd = ChestData.LEFT_SOUTH;
}
else {
cd = ChestData.SINGLE_SOUTH;
}
break;
}
return models[cd.ordinal()];
}
}

View File

@ -0,0 +1,101 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.Log;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
/**
* Renderer for allowing creation of a set of one or more cuboid or crossed-patch blocks, with independent textures for each
* Each cuboid is provided with two corner offsets within the cube (xmin,ymin,zmin) and (xmax,ymax,zmax), and a list of 6 patch indexes (default is 0,1,2,3,4,5 - order is bottom,top,xmin,xmax,zmin,zmax)
* Each crossed-patch is provided with two corner offsets within the cube (xmin,ymin,zmin) and (xmax,ymax,zmax), and a single patch index (default is 0)
*/
public class CuboidRenderer extends CustomRenderer {
private RenderPatch[] model;
private int textureCount;
private static final int[] crossedPatchDefault = { 0 };
private static final int[] cuboidPatchDefault = { 0, 1, 2, 3, 4, 5 };
private static final double clamp(double f) {
if (f < 0.0) { f = 0.0; }
if (f > 1.0) { f = 1.0; }
return f;
}
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
// Loop through parameters
for (String key : custparm.keySet()) {
String v = custparm.get(key);
if (key.startsWith("cuboid") || key.startsWith("cross")) {
boolean isCrossed = key.startsWith("cross");
String[] toks = v.split("/");
if (toks.length < 2) { // Must be at least two
Log.severe("Invalid cuboid token value: " + v);
return false;
}
String[] mins = toks[0].split(":");
String[] maxs = toks[1].split(":");
if ((mins.length < 3) || (maxs.length < 3)) {
Log.severe("Invalid number of fields: " + v);
return false;
}
double xmin = clamp(Double.parseDouble(mins[0]));
double ymin = clamp(Double.parseDouble(mins[1]));
double zmin = clamp(Double.parseDouble(mins[2]));
double xmax = clamp(Double.parseDouble(maxs[0]));
double ymax = clamp(Double.parseDouble(maxs[1]));
double zmax = clamp(Double.parseDouble(maxs[2]));
int[] patches = (isCrossed)?crossedPatchDefault:cuboidPatchDefault;
if (toks.length > 2) {
patches = new int[isCrossed?1:6];
String[] pidx = toks[2].split(":");
for (int j = 0; j < patches.length; j++) {
if (j >= pidx.length) {
patches[j] = Integer.parseInt(pidx[pidx.length-1]);
}
else {
patches[j] = Integer.parseInt(pidx[j]);
}
if (patches[j] >= textureCount) {
textureCount= patches[j] + 1;
}
}
}
// Build crossed patches
if (isCrossed) {
RenderPatch VertX1Z0ToX0Z1 = rpf.getPatch(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, Math.min(xmin, zmin), Math.max(xmax, zmax),
ymin, ymax, SideVisible.FLIP, patches[0]);
RenderPatch VertX1Z0ToX0Z1_90 = rpf.getRotatedPatch(VertX1Z0ToX0Z1, 0, 90, 0, patches[0]);
list.add(VertX1Z0ToX0Z1);
list.add(VertX1Z0ToX0Z1_90);
}
else {
CustomRenderer.addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patches);
}
}
}
model = list.toArray(new RenderPatch[list.size()]);
return true;
}
@Override
public int getMaximumTextureCount() {
return textureCount;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
return model;
}
}

View File

@ -0,0 +1,206 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
/**
* Standard door renderer : two textures (top, bottom)
*/
public class DoorRenderer extends CustomRenderer {
public static final int TXT_TOP = 0;
public static final int TXT_BOTTOM = 1;
// Indexed by combined meta
private RenderPatch[][] models = new RenderPatch[32][];
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
int[] txt = new int[6];
for (int combined_meta = 0; combined_meta < 32; combined_meta++) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
/* Get textures for each side */
for (int side = 0; side < 6; side++) {
txt[side] = sideAndMetaToTexture(combined_meta, side);
}
double[] bounds = getBoundsByMeta(combined_meta);
double xmin = bounds[0];
double zmin = bounds[1];
double xmax = bounds[2];
double zmax = bounds[3];
/* Add bottom */
list.add(rpf.getPatch(0, 0, 0, 1, 0, 0, 0, 0, 1, xmin, xmax, zmin, zmax, SideVisible.TOP, txt[0] & 1));
/* Add top */
list.add(rpf.getPatch(0, 1, 1, 1, 1, 1, 0, 1, 0, xmin, xmax, 1-zmax, 1-zmin, SideVisible.TOP, txt[1] & 1));
/* Add minZ side */
if ((txt[2] & 2) != 0) {
list.add(rpf.getPatch(0, 0, zmin, 1, 0, zmin, 0, 1, zmin, xmin, xmax, 0, 1, SideVisible.BOTTOM, txt[2] & 1));
}
else {
list.add(rpf.getPatch(1, 0, zmin, 0, 0, zmin, 1, 1, zmin, 1-xmax, 1-xmin, 0, 1, SideVisible.TOP, txt[2] & 1));
}
/* Add maxZ side */
if ((txt[3] & 2) != 0) {
list.add(rpf.getPatch(1, 0, zmax, 0, 0, zmax, 1, 1, zmax, 1-xmax, 1-xmin, 0, 1, SideVisible.BOTTOM, txt[3] & 1));
}
else {
list.add(rpf.getPatch(0, 0, zmax, 1, 0, zmax, 0, 1, zmax, xmin, xmax, 0, 1, SideVisible.TOP, txt[3] & 1));
}
/* Add minX side */
if ((txt[4] & 2) != 0) {
list.add(rpf.getPatch(xmin, 0, 1, xmin, 0, 0, xmin, 1, 1, 1-zmax, 1-zmin, 0, 1, SideVisible.BOTTOM, txt[4] & 1));
}
else {
list.add(rpf.getPatch(xmin, 0, 0, xmin, 0, 1, xmin, 1, 0, zmin, zmax, 0, 1, SideVisible.TOP, txt[4] & 1));
}
/* Add maxX side */
if ((txt[5] & 2) != 0) {
list.add(rpf.getPatch(xmax, 0, 0, xmax, 0, 1, xmax, 1, 0, zmin, zmax, 0, 1, SideVisible.BOTTOM, txt[5] & 1));
}
else {
list.add(rpf.getPatch(xmax, 0, 1, xmax, 0, 0, xmax, 1, 1, 1-zmax, 1-zmin, 0, 1, SideVisible.TOP, txt[5] & 1));
}
models[combined_meta] = list.toArray(new RenderPatch[6]);
}
return true;
}
@Override
public int getMaximumTextureCount() {
return 2;
}
private int getCombinedMeta(MapDataContext ctx, int meta) {
boolean isTop = (meta & 0x8) != 0;
int bottom;
int top;
if (isTop) {
bottom = ctx.getBlockTypeAt(0, -1, 0).stateIndex;
top = meta;
}
else {
bottom = meta;
top = ctx.getBlockTypeAt(0, 1, 0).stateIndex;
}
boolean isOpen = (top & 1) != 0;
return (bottom & 7) | (isTop ? 8 : 0) | (isOpen ? 16 : 0);
}
// Return 0 = top, 1 = bottom, 2 = top-flipped, 3 = bottom-flipped
private int sideAndMetaToTexture(int combined_meta, int side) {
if (side != 1 && side != 0) {
int direction = combined_meta & 3;
boolean flag = (combined_meta & 4) != 0;
boolean flag1 = false;
boolean flag2 = (combined_meta & 8) != 0;
if (flag) {
if (direction == 0 && side == 2) {
flag1 = !flag1;
} else if (direction == 1 && side == 5) {
flag1 = !flag1;
} else if (direction == 2 && side == 3) {
flag1 = !flag1;
} else if (direction == 3 && side == 4) {
flag1 = !flag1;
}
} else {
if (direction == 0 && side == 5) {
flag1 = !flag1;
} else if (direction == 1 && side == 3) {
flag1 = !flag1;
} else if (direction == 2 && side == 4) {
flag1 = !flag1;
} else if (direction == 3 && side == 2) {
flag1 = !flag1;
}
if ((combined_meta & 16) != 0) {
flag1 = !flag1;
}
}
return flag2 ? (flag1 ? 2 : 0) : (flag1 ? 3 : 1);
} else {
return 1;
}
}
private static final double[][] bounds = {
{ 0.0, 0.0, 1.0, 0.1875 },
{ 0.0, 0.9125, 1.0, 1.0 },
{ 0.0, 0.0, 0.1875, 1.0 },
{ 0.9125, 0.0, 1.0, 1.0 }
};
private double[] getBoundsByMeta(int meta) {
int direction = meta & 3;
boolean flag = (meta & 4) != 0;
boolean flag1 = (meta & 16) != 0;
switch (direction) {
case 0:
if (flag) {
if (!flag1) {
return bounds[0];
}
else {
return bounds[1];
}
}
else {
return bounds[2];
}
case 1:
if (flag) {
if (!flag1) {
return bounds[3];
}
else {
return bounds[2];
}
}
else {
return bounds[0];
}
case 2:
if (flag) {
if (!flag1) {
return bounds[1];
}
else {
return bounds[0];
}
}
else {
return bounds[3];
}
case 3:
if (flag) {
if (!flag1) {
return bounds[2];
}
else {
return bounds[3];
}
}
else {
return bounds[1];
}
}
return bounds[0];
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int meta = ctx.getBlockType().stateIndex; // Get our meta
int combined_meta = getCombinedMeta(ctx, meta);
return models[combined_meta];
}
}

View File

@ -0,0 +1,110 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class FenceGateBlockRenderer extends CustomRenderer {
private static final int TEXTURE_SIDES = 0;
private static final int TEXTURE_TOP = 1;
private static final int TEXTURE_BOTTOM = 2;
// Meshes, indexed by connection combination (bit 2=open(1)/close(0), bit 0-1=0(south),1(west),2(north),3(east))
private RenderPatch[][] meshes = new RenderPatch[8][];
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
buildMeshes(rpf);
return true;
}
@Override
public int getMaximumTextureCount() {
return 3;
}
private static final int[] patchlist = { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES };
private void addBox(RenderPatchFactory rpf, List<RenderPatch> list, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) {
addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patchlist);
}
private void buildMeshes(RenderPatchFactory rpf) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
for(int dat = 0; dat < 8; dat++) {
// Add posts
if ((dat & 1) == 0) {
addBox(rpf, list, 0.0, 0.125, 0.3125, 1.0, 0.4375, 0.5625);
addBox(rpf, list, 0.875, 1.0, 0.3125, 1.0, 0.4375, 0.5625);
if ((dat & 4) == 0) { // If closed
addBox(rpf, list, 0.375, 0.625, 0.375, 0.9375, 0.4375, 0.5625);
addBox(rpf, list, 0.625, 0.875, 0.375, 0.5625, 0.4375, 0.5625);
addBox(rpf, list, 0.625, 0.875, 0.75, 0.9375, 0.4375, 0.5625);
addBox(rpf, list, 0.125, 0.375, 0.375f, 0.5625, 0.4375, 0.5625);
addBox(rpf, list, 0.125, 0.375, 0.75, 0.9375, 0.4375, 0.5625);
}
else if ((dat & 3) == 0) {
addBox(rpf, list, 0.0, 0.125, 0.375, 0.9375, 0.8125, 0.9375);
addBox(rpf, list, 0.875, 1.0, 0.375, 0.9375, 0.8125, 0.9375);
addBox(rpf, list, 0.0, 0.125, 0.375, 0.5625, 0.5625, 0.8125);
addBox(rpf, list, 0.875, 1.0, 0.375, 0.5625, 0.5625, 0.8125);
addBox(rpf, list, 0.0, 0.125, 0.75, 0.9375, 0.5625, 0.8125);
addBox(rpf, list, 0.875, 1.0, 0.75, 0.9375, 0.5625, 0.8125);
}
else {
addBox(rpf, list, 0.0, 0.125, 0.375, 0.9375, 0.0625, 0.1875);
addBox(rpf, list, 0.875, 1.0, 0.375, 0.9375, 0.0625, 0.1875);
addBox(rpf, list, 0.0, 0.125, 0.375, 0.5625, 0.1875, 0.4375);
addBox(rpf, list, 0.875, 1.0, 0.375, 0.5625, 0.1875, 0.4375);
addBox(rpf, list, 0.0, 0.125, 0.75, 0.9375, 0.1875, 0.4375);
addBox(rpf, list, 0.875, 1.0, 0.75, 0.9375, 0.1875, 0.4375);
}
}
else {
addBox(rpf, list, 0.4375, 0.5625, 0.3125, 1.0, 0.0, 0.125);
addBox(rpf, list, 0.4375, 0.5625, 0.3125, 1.0, 0.875, 1.0);
if ((dat & 4) == 0) { // If closed
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.9375, 0.375, 0.625);
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.5625, 0.625, 0.875);
addBox(rpf, list, 0.4375, 0.5625, 0.75, 0.9375, 0.625, 0.875);
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.5625, 0.125, 0.375);
addBox(rpf, list, 0.4375, 0.5625, 0.75, 0.9375, 0.125, 0.375);
}
else if ((dat & 3) == 3) {
addBox(rpf, list, 0.8125, 0.9375, 0.375, 0.9375, 0.0, 0.125);
addBox(rpf, list, 0.8125, 0.9375, 0.375, 0.9375, 0.875, 1.0);
addBox(rpf, list, 0.5625, 0.8125, 0.375, 0.5625, 0.0, 0.125);
addBox(rpf, list, 0.5625, 0.8125, 0.375, 0.5625, 0.875, 1.0);
addBox(rpf, list, 0.5625, 0.8125, 0.75, 0.9375, 0.0, 0.125);
addBox(rpf, list, 0.5625, 0.8125, 0.75, 0.9375, 0.875, 1.0);
}
else {
addBox(rpf, list, 0.0625, 0.1875, 0.375, 0.9375, 0.0, 0.125);
addBox(rpf, list, 0.0625, 0.1875, 0.375, 0.9375, 0.875, 1.0);
addBox(rpf, list, 0.1875, 0.4375, 0.375, 0.5625, 0.0, 0.125);
addBox(rpf, list, 0.1875, 0.4375, 0.375, 0.5625, 0.875, 1.0);
addBox(rpf, list, 0.1875, 0.4375, 0.75, 0.9375, 0.0, 0.125);
addBox(rpf, list, 0.1875, 0.4375, 0.75, 0.9375, 0.875, 1.0);
}
}
meshes[dat] = list.toArray(new RenderPatch[list.size()]);
list.clear();
}
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int meta = ctx.getBlockType().stateIndex;
return meshes[meta & 0x7];
}
}

View File

@ -0,0 +1,177 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import org.dynmap.hdmap.HDBlockStateTextureMap;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class FenceWallBlockRenderer extends CustomRenderer {
private static final int TEXTURE_SIDES = 0;
private static final int TEXTURE_TOP = 1;
private static final int TEXTURE_BOTTOM = 2;
private boolean check_yplus;
private BitSet link_ids = new BitSet();
private static final int SIDE_XP = 0x1;
private static final int SIDE_XN = 0x2;
private static final int SIDE_X = SIDE_XN | SIDE_XP;
private static final int SIDE_ZP = 0x4;
private static final int SIDE_ZN = 0x8;
private static final int SIDE_Z = SIDE_ZN | SIDE_ZP;
private static final int SIDE_YP = 0x10;
// Meshes, indexed by connection combination (bit 0=X+, bit 1=X-, bit 2=Z+, bit 3=Z-, bit 4=Y+)
private RenderPatch[][] meshes = new RenderPatch[32][];
private void addIDs(String bn) {
DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(bn);
if (bbs.isNotAir()) {
for (int i = 0; i < bbs.getStateCount(); i++) {
DynmapBlockState bs = bbs.getState(i);
link_ids.set(bs.globalStateIndex);
}
}
}
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
addIDs(blkname);
/* Build models, based on type of fence/wall we're set to be */
String type = custparm.get("type");
if((type != null) && (type.equals("wall"))) {
buildWallMeshes(rpf);
check_yplus = true;
}
else {
buildFenceMeshes(rpf);
}
for(int i = 0; true; i++) {
String lid = custparm.get("link" + i);
if(lid == null) break;
addIDs(lid);
}
return true;
}
@Override
public int getMaximumTextureCount() {
return 3;
}
private static final int[] patchlist = { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES };
private void addBox(RenderPatchFactory rpf, List<RenderPatch> list, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) {
addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patchlist);
}
private void buildFenceMeshes(RenderPatchFactory rpf) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
for(int dat = 0; dat < 16; dat++) {
/* Add center post */
addBox(rpf, list, 0.375, 0.625, 0.0, 1.0, 0.375, 0.625);
switch(dat & SIDE_X) {
case SIDE_XP: // Just X+
addBox(rpf, list, 0.625, 1.0, 0.375, 0.5625, 0.4375, 0.5625);
addBox(rpf, list, 0.625, 1.0, 0.75, 0.9275, 0.4375, 0.5625);
break;
case SIDE_XN: // Just X-
addBox(rpf, list, 0.0, 0.375, 0.375, 0.5625, 0.4375, 0.5625);
addBox(rpf, list, 0.0, 0.375, 0.75, 0.9275, 0.4375, 0.5625);
break;
case SIDE_X: // X- and X+
addBox(rpf, list, 0.0, 1.0, 0.375, 0.5625, 0.4375, 0.5625);
addBox(rpf, list, 0.0, 1.0, 0.75, 0.9275, 0.4375, 0.5625);
break;
}
switch(dat & SIDE_Z) {
case SIDE_ZP: // Just Z+
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.5625, 0.625, 1.0);
addBox(rpf, list, 0.4375, 0.5625, 0.75, 0.9275, 0.625, 1.0);
break;
case SIDE_ZN: // Just Z-
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.5625, 0.0, 0.375);
addBox(rpf, list, 0.4375, 0.5625, 0.75, 0.9275, 0.0, 0.375);
break;
case SIDE_Z: // Z- and Z+
addBox(rpf, list, 0.4375, 0.5625, 0.375, 0.5625, 0.0, 1.0);
addBox(rpf, list, 0.4375, 0.5625, 0.75, 0.9275, 0.0, 1.0);
break;
}
meshes[dat] = list.toArray(new RenderPatch[list.size()]);
list.clear();
}
}
private void buildWallMeshes(RenderPatchFactory rpf) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
for(int dat = 0; dat < 32; dat++) {
boolean need_post = ((dat & 0xF) == 0) || ((dat & 0x10) == 0x10);
switch(dat & SIDE_X) {
case SIDE_XP: // Just X+
addBox(rpf, list, 0.75, 1.0, 0.0, 0.8125, 0.3125, 0.6875);
need_post = true;
break;
case SIDE_XN: // Just X-
addBox(rpf, list, 0.0, 0.25, 0.0, 0.8125, 0.3125, 0.6875);
need_post = true;
break;
case SIDE_X: // X- and X+
addBox(rpf, list, 0.0, 1.0, 0.0, 0.8125, 0.3125, 0.6875);
break;
}
switch(dat & SIDE_Z) {
case SIDE_ZP: // Just Z+
addBox(rpf, list, 0.3125, 0.6875, 0.0, 0.8125, 0.75, 1.0);
need_post = true;
break;
case SIDE_ZN: // Just Z-
addBox(rpf, list, 0.3125, 0.6875, 0.0, 0.8125, 0.0, 0.25);
need_post = true;
break;
case SIDE_Z: // Z- and Z+
addBox(rpf, list, 0.3125, 0.6875, 0.0, 0.8125, 0.0, 1.0);
break;
}
if(need_post) {
addBox(rpf, list, 0.25, 0.75, 0.0, 1.0, 0.25, 0.75);
}
meshes[dat] = list.toArray(new RenderPatch[list.size()]);
list.clear();
}
}
private static int[][] sides = {
{ 1, 0, 0, SIDE_XP },
{ -1, 0, 0, SIDE_XN },
{ 0, 0, 1, SIDE_ZP },
{ 0, 0, -1, SIDE_ZN }
};
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
/* Build connection map - check each axis */
int connect = 0;
for(int i = 0; i < sides.length; i++) {
DynmapBlockState blk = ctx.getBlockTypeAt(sides[i][0], sides[i][1], sides[i][2]);
if (blk.isAir()) continue;
if (link_ids.get(blk.globalStateIndex) || (HDBlockStateTextureMap.getTransparency(blk) == BlockTransparency.OPAQUE)) {
connect |= sides[i][3];
}
}
if(check_yplus) {
if (ctx.getBlockTypeAt(0, 1, 0).isNotAir()) {
connect |= SIDE_YP;
}
}
return meshes[connect];
}
}

View File

@ -0,0 +1,251 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import org.dynmap.Log;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class FrameRenderer extends CustomRenderer {
// Map of block ID sets for linking blocks
private static Map<String, BitSet> linked_ids_by_set = new HashMap<String, BitSet>();
// Set of linked blocks
private BitSet linked_ids;
// Diameter of frame/wore (1.0 = full block)
private double diameter;
// Models for connection graph (bit0=X+,bit1=X-,bit2=Y+,bit3=Y-,bit4=Z+,bit5=Z-), by texture index
private RenderPatch[][][] models = new RenderPatch[64][][];
// Base index (based on force parameter)
private int base_index = 0;
// Texture index map
private int[] txtIndex;
// Texture offset - added to texture key
private int txtOffset = 0;
// Indexing attribute
private String idx_attrib = null;
// Texture map ID, if being used
private String map_id = null;
// Texture count
private int txtCount = 0;
// Texture default index (if indexed and not found)
private int txtDefIndex = -1;
private String[] tileEntityAttribs = null;
private void addIDs(String blkname) {
DynmapBlockState bbs = DynmapBlockState.getBaseStateByName(blkname);
if (bbs.isNotAir()) {
for (int i = 0; i < bbs.getStateCount(); i++) {
DynmapBlockState bs = bbs.getState(i);
linked_ids.set(bs.globalStateIndex);
}
}
}
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
String linkset = custparm.get("linkset");
if(linkset == null) linkset = "default";
linked_ids = linked_ids_by_set.get(linkset); /* Get our set */
if(linked_ids == null) {
linked_ids = new BitSet();
linked_ids_by_set.put(linkset, linked_ids);
}
addIDs(blkname); // Add us to set
// Get diameter
String dia = custparm.get("diameter");
if(dia == null) dia = "0.5";
try {
diameter = Double.parseDouble(dia);
} catch (NumberFormatException nfx) {
diameter = 0.5;
Log.severe("Error: diameter must be number between 0.0 and 1.0");
}
if((diameter <= 0.0) || (diameter >= 1.0)) {
Log.severe("Error: diameter must be number between 0.0 and 1.0");
diameter = 0.5;
}
// Process other link block IDs
for(String k : custparm.keySet()) {
if(k.startsWith("link")) {
addIDs(custparm.get(k));
}
}
// Check for axis force
String force = custparm.get("force");
if(force != null) {
String v = "xXyYzZ";
for(int i = 0; i < v.length(); i++) {
if(force.indexOf(v.charAt(i)) >= 0) {
base_index |= (1 << i);
}
}
}
/* See if index attribute defined */
String idx = custparm.get("textureIndex");
if(idx != null) {
txtOffset = 0;
String txt_off = custparm.get("textureOffset");
if(txt_off != null) {
txtOffset = Integer.valueOf(txt_off);
}
idx_attrib = idx;
String txt_def = custparm.get("textureDefault");
if(txt_def != null) {
txtDefIndex = Integer.valueOf(txt_def);
}
map_id = custparm.get("textureMap");
if(map_id == null) { /* If no map, indexes are explicit */
ArrayList<Integer> map = new ArrayList<Integer>();
for(int id = 0; ; id++) {
String v = custparm.get("index" + id);
if(v == null) break;
map.add(Integer.valueOf(v));
}
txtIndex = new int[map.size()];
for(int id = 0; id < txtIndex.length; id++) {
txtIndex[id] = map.get(id).intValue() + txtOffset;
}
txtCount = txtIndex.length;
}
tileEntityAttribs = new String[1];
tileEntityAttribs[0] = idx_attrib;
}
else {
txtIndex = new int[1];
txtCount = 1;
}
return true;
}
@Override
public int getMaximumTextureCount(RenderPatchFactory rpf) {
if(map_id != null) {
if(txtCount == 0) {
txtCount = rpf.getTextureCountFromMap(map_id);
if(txtCount < 0) txtCount = 0;
}
}
return txtCount;
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tileEntityAttribs;
}
private RenderPatch[] buildModel(RenderPatchFactory rpf, int idx, int txt_idx) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
int[] sides = { txt_idx,txt_idx,txt_idx,txt_idx,txt_idx,txt_idx };
/* If we have an X axis match */
if((idx & 0x3) != 0) {
addBox(rpf, list,
((idx & 1) != 0)?0.0:(0.5-diameter/2.0),
((idx & 2) != 0)?1.0:(0.5+diameter/2.0),
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
sides);
}
/* If we have an Y axis match */
if((idx & 0xC) != 0) {
addBox(rpf, list,
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
((idx & 0x4) != 0)?0.0:(0.5-diameter/2.0),
((idx & 0x8) != 0)?1.0:(0.5+diameter/2.0),
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
sides);
}
/* If we have an Z axis match, or no links */
if(((idx & 0x30) != 0) || (idx == 0)) {
addBox(rpf, list,
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
(0.5 - diameter/2.0),
(0.5 + diameter/2.0),
((idx & 0x10) != 0)?0.0:(0.5-diameter/2.0),
((idx & 0x20) != 0)?1.0:(0.5+diameter/2.0),
sides);
}
return list.toArray(new RenderPatch[list.size()]);
}
private static final int[] x_off = { -1, 1, 0, 0, 0, 0 };
private static final int[] y_off = { 0, 0, -1, 1, 0, 0 };
private static final int[] z_off = { 0, 0, 0, 0, -1, 1 };
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int textureIdx = 0;
if((map_id != null) && (txtCount == 0)) {
txtCount = ctx.getPatchFactory().getTextureCountFromMap(map_id);
}
/* See if we have texture index */
if(idx_attrib != null) {
Object idxv = ctx.getBlockTileEntityField(idx_attrib);
if(idxv instanceof Number) {
int val = ((Number)idxv).intValue();
if(map_id != null) { /* If texture map, look up value there */
textureIdx = ctx.getPatchFactory().getTextureIndexFromMap(map_id, val - txtOffset);
if((textureIdx < 0) && (txtDefIndex >= 0))
textureIdx = ctx.getPatchFactory().getTextureIndexFromMap(map_id, txtDefIndex);
if(textureIdx < 0)
textureIdx = 0;
}
else {
for(int i = 0; i < txtIndex.length; i++) {
if(val == txtIndex[i]) {
textureIdx = i;
break;
}
}
}
}
}
int idx = base_index;
for(int i = 0; i < x_off.length; i++) {
if((idx & (1 << i)) != 0) continue;
DynmapBlockState blk = ctx.getBlockTypeAt(x_off[i], y_off[i], z_off[i]);
if(linked_ids.get(blk.globalStateIndex)) {
idx |= (1 << i);
}
}
RenderPatch[][] row = models[idx];
/* If row not found, add it */
if(row == null) {
row = new RenderPatch[txtCount][];
models[idx] = row;
}
/* If model not found, create it */
RenderPatch[] model = null;
if(textureIdx < row.length)
model = row[textureIdx];
if(model == null) {
model = buildModel(ctx.getPatchFactory(), idx, textureIdx);
if(textureIdx >= row.length) {
row = Arrays.copyOf(row, textureIdx+1);
models[idx] = row;
}
row[textureIdx] = model;
}
return model;
}
}

View File

@ -0,0 +1,378 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class ImmibisMicroRenderer extends CustomRenderer {
private static final String[] tileFields = { "ICMP" };
/* Defined texture indexes
* 1 = stone
* 2 = grass
* 3 = dirt
* 4 = cobblestone
* 5 = planks:0
* 6 = planks:1
* 7 = planks:2
* 8 = planks:3
* 9 = bedrock
* 10 = sand
* 11 = gravel
* 12 = oreGold
* 13 = oreIron
* 14 = oreCoal
* 15 = wood:0
* 16 = wood:1
* 17 = wood:2
* 18 = wood:3
* 19 = leaves:0
* 20 = leaves:1
* 21 = leaves:2
* 22 = leaves:3
* 23 = sponge
* 24 = glass
* 25 = oreLapis
* 26 = blockLapis
* 27 = dispenser
* 28 = sandStone
* 29 = music
* 30 = pistonStickyBase
* 31 = pistonBase
* 32 = cloth:0
* 33 = cloth:1
* 34 = cloth:2
* 35 = cloth:3
* 36 = cloth:4
* 37 = cloth:5
* 38 = cloth:6
* 39 = cloth:7
* 40 = cloth:8
* 41 = cloth:9
* 42 = cloth:10
* 43 = cloth:11
* 44 = cloth:12
* 45 = cloth:13
* 46 = cloth:14
* 47 = cloth:15
* 48 = blockGold
* 49 = blockIron
* 50 = brick
* 51 = tnt
* 52 = bookShelf
* 53 = cobblestoneMossy
* 54 = obsidian
* 55 = mobSpawner
* 56 = oreDiamond
* 57 = blockDiamond
* 58 = workbench
* 59 = furnaceIdle
* 60 = oreRedstone
* 61 = blockSnow
* 62 = blockClay
* 63 = jukebox
* 64 = pumpkin
* 65 = netherrack
* 66 = slowSand
* 67 = glowStone
* 68 = pumpkinLantern
* 69 = stoneBrick
* 70 = melon
* 71 = mycelium
* 72 = netherBrick
* 73 = whiteStone:0
* 74 = whiteStone:1
* 75 = oreEmerald
* 76 = blockEmerald
* 77 = commandBlock
* 78 = sandStone:1
* 79 = sandStone:2
* 80 = redstoneLampIdle
* 81 = stoneBrick:1
* 82 = stoneBrick:2
* 83 = stoneBrick:3
* 84 = blockRedstone
* 85 = oreNetherQuartz
* 86 = blockNetherQuartz:0
* 87 = blockNetherQuartz:1
* 88 = blockNetherQuartz:2
* 89 = dropper
*/
private static final int NUM_TEXTURES = 102;
/* Texture index = material index in RP */
private static final int materialTextureMap[][] = {
{ 0 }, // 0 = ?
{ 0 }, // 1 = Stone (stone:0)
{ 0 }, // 2 = N/A
{ 1 }, // 3 = dirt
{ 2 }, // 4 = cobblestone
{ 3 }, // 5 = planks:0
{ 4 }, // 6 = planks:1
{ 5 }, // 7 = planks:2
{ 6 }, // 8 = planks:3
{ 7 }, // 9 = bedrock
{ 8 }, // 10 = sand
{ 9 }, // 11 = gravel
{ 10 }, // 12 = oreGold
{ 11 }, // 13 = oreIron
{ 12 }, // 14 = oreCoal
{ 13, 13, 14, 14, 14, 14 }, // 15 = wood:0
{ 13, 13, 15, 15, 15, 15 }, // 16 = wood:1
{ 13, 13, 16, 16, 16, 16 }, // 17 = wood:2
{ 13, 13, 17, 17, 17, 17 }, // 18 = wood:3
{ 0 }, // 19 = N/A
{ 0 }, // 20 = N/A
{ 0 }, // 21 = N/A
{ 0 }, // 22 = N/A
{ 18 }, // 23 = sponge
{ 19 }, // 24 = glass
{ 20 }, // 25 = oreLapis
{ 21 }, // 26 = blockLapis
{ 22, 22, 23, 24, 24, 24 }, // 27 = dispenser
{ 25, 26, 27, 27, 27, 27 }, // 28 = sandStone:0
{ 28 }, // 29 = music
{ 29, 30, 31, 31, 31, 31 }, // 30 = pistonStickyBase
{ 32, 33, 31, 31, 31, 31 } , // 31 = pistonBase
{ 34 }, // 32 = cloth:0
{ 35 }, // 33 = cloth:1
{ 36 }, // 34 = cloth:2
{ 37 }, // 35 = cloth:3
{ 38 }, // 36 = cloth:4
{ 39 }, // 37 = cloth:5
{ 40 }, // 38 = cloth:6
{ 41 }, // 39 = cloth:7
{ 42 }, // 40 = cloth:8
{ 43 }, // 41 = cloth:9
{ 44 }, // 42 = cloth:10
{ 45 }, // 43 = cloth:11
{ 46 }, // 44 = cloth:12
{ 47 }, // 45 = cloth:13
{ 48 }, // 46 = cloth:14
{ 49 }, // 47 = cloth:15
{ 50 }, // 48 = blockGold
{ 51 }, // 49 = blockIron
{ 52 }, // 50 = brick
{ 53 }, // 51 = tnt
{ 3, 3, 54, 54, 54, 54 }, // 52 = bookShelf
{ 55 }, // 53 = cobblestoneMossy
{ 56 }, // 54 = obsidian
{ 57 }, // 55 = mobSpawner
{ 58 }, // 56 = oreDiamond
{ 59 }, // 57 = blockDiamond
{ 60, 60, 61, 61, 62, 62 }, // 58 = workbench
{ 22, 22, 63, 64, 64, 64 }, // 59 = furnaceIdle
{ 65 }, // 60 = oreRedstone
{ 66 }, // 61 = blockSnow
{ 67 }, // 62 = blockClay
{ 68, 68, 69, 69, 69, 69 }, // 63 = jukebox
{ 70, 70, 71, 71, 71, 71 }, // 64 = pumpkin
{ 72 }, // 65 = netherrack
{ 73 }, // 66 = slowSand
{ 74 }, // 67 = glowStone
{ 70, 70, 75, 71, 71, 71 }, // 68 = pumpkinLantern
{ 76 }, // 69 = stoneBrick:0
{ 77, 77, 78, 78, 78, 78 }, // 70 = melon
{ 1, 79, 80, 80, 80, 80 }, // 71 = mycelium
{ 81 }, // 72 = netherBrick
{ 82 }, // 73 = whiteStone:0
{ 82 }, // 74 = whiteStone:1
{ 83 }, // 75 = oreEmerald
{ 84 }, // 76 = blockEmerald
{ 85 }, // 77 = commandBlock
{ 26, 26, 86, 86, 86, 86 }, // 78 = sandStone:1
{ 26, 26, 87, 87, 87, 87 }, // 79 = sandStone:2
{ 88 }, // 80 = redstoneLampIdle
{ 89 }, // 81 = stoneBrick:1
{ 90 }, // 82 = stoneBrick:2
{ 91 }, // 83 = stoneBrick:3
{ 92 }, // 84 = blockRedstone
{ 93 }, // 85 = oreNetherQuartz
{ 94, 95, 96, 96, 96, 96 }, // 86 = blockNetherQuartz:0
{ 94, 97, 98, 98, 98, 98 }, // 87 = blockNetherQuartz:1
{ 99, 99, 100, 100, 100, 100 }, // 88 = blockNetherQuartz:2
{ 22, 101, 22, 22, 22, 22 } // 89 = dropper
};
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
/* Flesh out sides map */
for(int i = 0; i < materialTextureMap.length; i++) {
if(materialTextureMap[i].length < 6) {
int[] sides = new int[6];
Arrays.fill(sides, materialTextureMap[i][0]);
materialTextureMap[i] = sides;
}
}
return true;
}
@Override
public int getMaximumTextureCount() {
return NUM_TEXTURES;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
Object v = ctx.getBlockTileEntityField("ICMP");
/* Build patch list */
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
if ((v != null) && (v instanceof List)) {
List<?> lv = (List<?>) v;
for (Object lval : lv) {
if (lval instanceof Map) {
Map<?, ?> mv = (Map<?,?>) lval;
Integer type = (Integer)mv.get("type");
Byte pos = (Byte)mv.get("pos");
if ((type != null) && (pos != null)) {
addPatchesFor(ctx.getPatchFactory(), list, type, pos);
}
}
}
}
return list.toArray(new RenderPatch[list.size()]);
}
private boolean isHollow(int shape, int thickness) {
return (shape == 3);
}
private double getThickness(int shape, int thickness) {
return (0.125 * thickness);
}
private enum AxisPos {
CENTER,
NEGATIVE,
POSITIVE,
SPAN
};
private static final AxisPos axes_by_pos[][] = {
{ AxisPos.CENTER, AxisPos.CENTER, AxisPos.CENTER }, // Centre
{ AxisPos.NEGATIVE, AxisPos.SPAN, AxisPos.SPAN }, // FaceNX
{ AxisPos.POSITIVE, AxisPos.SPAN, AxisPos.SPAN }, // FacePX
{ AxisPos.SPAN, AxisPos.NEGATIVE, AxisPos.SPAN }, // FaceNY
{ AxisPos.SPAN, AxisPos.POSITIVE, AxisPos.SPAN }, // FacePY
{ AxisPos.SPAN, AxisPos.SPAN, AxisPos.NEGATIVE }, // FaceNZ
{ AxisPos.SPAN, AxisPos.SPAN, AxisPos.POSITIVE }, // FacePZ
{ AxisPos.NEGATIVE, AxisPos.NEGATIVE, AxisPos.SPAN }, // EdgeNXNY
{ AxisPos.NEGATIVE, AxisPos.POSITIVE, AxisPos.SPAN }, // EdgeNXPY
{ AxisPos.POSITIVE, AxisPos.NEGATIVE, AxisPos.SPAN }, // EdgePXNY
{ AxisPos.POSITIVE, AxisPos.POSITIVE, AxisPos.SPAN }, // EdgePXPY
{ AxisPos.NEGATIVE, AxisPos.SPAN, AxisPos.NEGATIVE }, // EdgeNXNZ
{ AxisPos.NEGATIVE, AxisPos.SPAN, AxisPos.POSITIVE }, // EdgeNXPZ
{ AxisPos.POSITIVE, AxisPos.SPAN, AxisPos.NEGATIVE }, // EdgePXNZ
{ AxisPos.POSITIVE, AxisPos.SPAN, AxisPos.POSITIVE }, // EdgePXPZ
{ AxisPos.SPAN, AxisPos.NEGATIVE, AxisPos.NEGATIVE }, // EdgeNYNZ
{ AxisPos.SPAN, AxisPos.NEGATIVE, AxisPos.POSITIVE }, // EdgeNYPZ
{ AxisPos.SPAN, AxisPos.POSITIVE, AxisPos.NEGATIVE }, // EdgePYNZ
{ AxisPos.SPAN, AxisPos.POSITIVE, AxisPos.POSITIVE }, // EdgePYPZ
{ AxisPos.NEGATIVE, AxisPos.NEGATIVE, AxisPos.NEGATIVE }, // CornerNXNYNZ
{ AxisPos.NEGATIVE, AxisPos.NEGATIVE, AxisPos.POSITIVE }, // CornerNXNYPZ
{ AxisPos.NEGATIVE, AxisPos.POSITIVE, AxisPos.NEGATIVE }, // CornerNXPYNZ
{ AxisPos.NEGATIVE, AxisPos.POSITIVE, AxisPos.POSITIVE }, // CornerNXPYPZ
{ AxisPos.POSITIVE, AxisPos.NEGATIVE, AxisPos.NEGATIVE }, // CornerPXNYNZ
{ AxisPos.POSITIVE, AxisPos.NEGATIVE, AxisPos.POSITIVE }, // CornerPXNYPZ
{ AxisPos.POSITIVE, AxisPos.POSITIVE, AxisPos.NEGATIVE }, // CornerPXPYNZ
{ AxisPos.POSITIVE, AxisPos.POSITIVE, AxisPos.POSITIVE }, // CornerPXPYPZ
{ AxisPos.SPAN, AxisPos.CENTER, AxisPos.CENTER }, // PostX
{ AxisPos.CENTER, AxisPos.SPAN, AxisPos.CENTER }, // PostY
{ AxisPos.CENTER, AxisPos.CENTER, AxisPos.SPAN } // PostZ
};
private double getAxisMin(AxisPos ap, double thick) {
switch (ap) {
case POSITIVE:
return 1.0 - thick;
case CENTER:
return 0.5 - (thick / 2.0);
default:
return 0.0;
}
}
private double getAxisMax(AxisPos ap, double thick) {
switch (ap) {
case NEGATIVE:
return thick;
case CENTER:
return 0.5 + (thick / 2.0);
default:
return 1.0;
}
}
private void addPatchesFor(RenderPatchFactory rpf, ArrayList<RenderPatch> list, int type, int pos) {
int[] sides;
int material = (type >> 6) & 0xFFFF; // Get material type
int shape = (type >> 3) & 0x7; // Get shape
int thickness = (type & 0x7) + 1; // Get thickness
if((material < 0) || (material >= materialTextureMap.length)) {
material = 0;
}
if ((pos < 0) || (pos >= axes_by_pos.length)) {
pos = 0;
}
sides = materialTextureMap[material]; /* Get sides map for texture */
double thick = getThickness(shape, thickness);
if (thick <= 0.0) return;
if (thick > 1.0) thick = 1.0;
/* If a hollow block, handle specially */
if(isHollow(shape, thickness)) {
switch(pos) {
case 1: /* X min cover */
CustomRenderer.addBox(rpf, list, 0, thick, 0, 0.75, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0.75, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0.25, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0, 0.25, 0.25, 1, sides);
break;
case 2: /* X max cover */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 0.75, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0.75, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0.25, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 0.25, 0.25, 1, sides);
break;
case 3: /* Bottom cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, thick, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, thick, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0, thick, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0, thick, 0.25, 1, sides);
break;
case 4: /* Top cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 1-thick, 1, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 1-thick, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 1-thick, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 1-thick, 1, 0.25, 1, sides);
break;
case 5: /* Z min cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, 0.25, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, 0.75, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0.75, 1, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0.25, 1, 0, thick, sides);
break;
case 6: /* Z max cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, 0.25, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, 0.75, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0.75, 1, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0.25, 1, 1-thick, 1, sides);
break;
}
}
else {
CustomRenderer.addBox(rpf, list, getAxisMin(axes_by_pos[pos][0], thick), getAxisMax(axes_by_pos[pos][0], thick), getAxisMin(axes_by_pos[pos][1], thick), getAxisMax(axes_by_pos[pos][1], thick), getAxisMin(axes_by_pos[pos][2], thick), getAxisMax(axes_by_pos[pos][2], thick), sides);
}
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tileFields;
}
}

View File

@ -0,0 +1,108 @@
package org.dynmap.hdmap.renderer;
import java.util.Map;
import org.dynmap.hdmap.HDBlockStateTextureMap;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
/*
* Glass pane / iron fence renderer
*/
public class PaneRenderer extends CustomRenderer {
private static final int TEXTURE_FACE = 0;
private static final int TEXTURE_EDGE = 1;
private static final int SIDE_XP = 0x4;
private static final int SIDE_XN = 0x1;
private static final int SIDE_ZP = 0x8;
private static final int SIDE_ZN = 0x2;
// Meshes, indexed by connection combination (bit 0=X-, bit 1=Z-, bit 2=X+, bit 3=Z+)
private RenderPatch[][] meshes = new RenderPatch[16][];
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
buildPatches(rpf);
return true;
}
private void buildPatches(RenderPatchFactory rpf) {
RenderPatch VertX05 = rpf.getPatch(0.5, 0.0, 1.0, 0.5, 0.0, 0.0, 0.5, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, SideVisible.BOTH, TEXTURE_FACE);
RenderPatch VertX05_90 = rpf.getRotatedPatch(VertX05, 0, 90, 0, TEXTURE_FACE);
RenderPatch VertX05_180 = rpf.getRotatedPatch(VertX05, 0, 180, 0, TEXTURE_FACE);
RenderPatch VertX05_270 = rpf.getRotatedPatch(VertX05, 0, 270, 0, TEXTURE_FACE);
RenderPatch VertX05Left = rpf.getPatch(0.5, 0.0, 1.0, 0.5, 0.0, 0.0, 0.5, 1.0, 1.0, 0.0, 0.5, 0.0, 1.0, SideVisible.BOTH, TEXTURE_FACE);
RenderPatch VertX05Left_90 = rpf.getRotatedPatch(VertX05Left, 0, 90, 0, TEXTURE_FACE);
RenderPatch VertX05Left_180 = rpf.getRotatedPatch(VertX05Left, 0, 180, 0, TEXTURE_FACE);
RenderPatch VertX05Left_270 = rpf.getRotatedPatch(VertX05Left, 0, 270, 0, TEXTURE_FACE);
RenderPatch VertX05Strip = rpf.getPatch(0.5, 0.0, 1.0, 0.5, 0.0, 0.0, 0.5, 1.0, 1.0, 0.4375, 0.5625, 0.0, 1.0, SideVisible.BOTH, TEXTURE_EDGE);
RenderPatch VertX05Strip_90 = rpf.getRotatedPatch(VertX05Strip, 0, 90, 0, TEXTURE_EDGE);
RenderPatch VertX05Strip_180 = rpf.getRotatedPatch(VertX05Strip, 0, 180, 0, TEXTURE_EDGE);
RenderPatch VertX05Strip_270 = rpf.getRotatedPatch(VertX05Strip, 0, 270, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStrip = rpf.getPatch(0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.4375, 0.5625, 0.0, 1.0, SideVisible.BOTH, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStrip_90 = rpf.getRotatedPatch(HorizY100ZTopStrip, 0, 90, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStrip_180 = rpf.getRotatedPatch(HorizY100ZTopStrip, 0, 180, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStrip_270 = rpf.getRotatedPatch(HorizY100ZTopStrip, 0, 270, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStripTop = rpf.getPatch(0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.4375, 0.5625, 0.5, 1.0, SideVisible.BOTH, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStripTop_90 = rpf.getRotatedPatch(HorizY100ZTopStripTop, 0, 90, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStripTop_180 = rpf.getRotatedPatch(HorizY100ZTopStripTop, 0, 180, 0, TEXTURE_EDGE);
RenderPatch HorizY100ZTopStripTop_270 = rpf.getRotatedPatch(HorizY100ZTopStripTop, 0, 270, 0, TEXTURE_EDGE);
meshes[0] = meshes[15] = new RenderPatch[] { VertX05, VertX05_90, HorizY100ZTopStrip, HorizY100ZTopStrip_90 };
meshes[1] = new RenderPatch[] { VertX05Left_90, HorizY100ZTopStripTop_90, VertX05Strip };
meshes[2] = new RenderPatch[] { VertX05Left_180, HorizY100ZTopStripTop_180, VertX05Strip_90 };
meshes[3] = new RenderPatch[] { VertX05Left_90, HorizY100ZTopStripTop_90, VertX05Left_180, HorizY100ZTopStripTop_180 };
meshes[4] = new RenderPatch[] { VertX05Left_270, HorizY100ZTopStripTop_270, VertX05Strip_180 };
meshes[5] = new RenderPatch[] { VertX05_90, HorizY100ZTopStrip_90 };
meshes[6] = new RenderPatch[] { VertX05Left_180, HorizY100ZTopStripTop_180, VertX05Left_270, HorizY100ZTopStripTop_270 };
meshes[7] = new RenderPatch[] { VertX05_90, HorizY100ZTopStrip_90, VertX05Left_180, HorizY100ZTopStripTop_180 };
meshes[8] = new RenderPatch[] { VertX05Left, HorizY100ZTopStripTop, VertX05Strip_270 };
meshes[9] = new RenderPatch[] { VertX05Left, HorizY100ZTopStripTop, VertX05Left_90, HorizY100ZTopStripTop_90 };
meshes[10] = new RenderPatch[] { VertX05, HorizY100ZTopStrip };
meshes[11] = new RenderPatch[] { VertX05, HorizY100ZTopStrip, VertX05Left_90, HorizY100ZTopStripTop_90 };
meshes[12] = new RenderPatch[] { VertX05Left_270, HorizY100ZTopStripTop_270, VertX05Left, HorizY100ZTopStripTop };
meshes[13] = new RenderPatch[] { VertX05_270, HorizY100ZTopStrip_270, VertX05Left, HorizY100ZTopStripTop };
meshes[14] = new RenderPatch[] { VertX05_180, HorizY100ZTopStrip_180, VertX05Left_270, HorizY100ZTopStripTop_270 };
}
@Override
public int getMaximumTextureCount() {
return 2;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
/* Build connection map - check each axis */
int blockdata = 0;
DynmapBlockState t;
DynmapBlockState type = ctx.getBlockType();
/* Check north */
t = ctx.getBlockTypeAt(-1, 0, 0);
if ((t == type) || t.is(DynmapBlockState.GLASS_BLOCK) || (HDBlockStateTextureMap.getTransparency(t) == BlockTransparency.OPAQUE)) {
blockdata |= SIDE_XN;
}
/* Look east */
t = ctx.getBlockTypeAt(0, 0, -1);
if ((t == type) || t.is(DynmapBlockState.GLASS_BLOCK) || (HDBlockStateTextureMap.getTransparency(t) == BlockTransparency.OPAQUE)) {
blockdata |= SIDE_ZN;
}
/* Look south */
t = ctx.getBlockTypeAt(1, 0, 0);
if ((t == type) || t.is(DynmapBlockState.GLASS_BLOCK) || (HDBlockStateTextureMap.getTransparency(t) == BlockTransparency.OPAQUE)) {
blockdata |= SIDE_XP;
}
/* Look west */
t = ctx.getBlockTypeAt(0, 0, 1);
if ((t == type) || t.is(DynmapBlockState.GLASS_BLOCK) || (HDBlockStateTextureMap.getTransparency(t) == BlockTransparency.OPAQUE)) {
blockdata |= SIDE_ZP;
}
return meshes[blockdata];
}
}

View File

@ -0,0 +1,67 @@
package org.dynmap.hdmap.renderer;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
/*
* Plant (crossed texture) renderer : includes option for tall plants (index derived from meta below block)
*/
public class PlantRenderer extends CustomRenderer {
// Meshes, indexed by metadata
private RenderPatch[][] meshes;
private boolean metaFromBelow = false;
private int metaCnt = 16;
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
String idxsrc = custparm.get("metasrc");
if ((idxsrc != null) && (idxsrc.equals("below"))) {
metaFromBelow = true;
}
String maxmeta = custparm.get("metacnt");
if (maxmeta != null) {
metaCnt = Integer.parseInt(maxmeta, 10);
if (metaCnt > 16) metaCnt = 16;
}
buildPatches(rpf);
return true;
}
private void buildPatches(RenderPatchFactory rpf) {
meshes = new RenderPatch[metaCnt][];
for (int txtid = 0; txtid < metaCnt; txtid++) {
RenderPatch VertX1Z0ToX0Z1 = rpf.getPatch(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, SideVisible.FLIP, txtid);
RenderPatch VertX1Z0ToX0Z1_90 = rpf.getRotatedPatch(VertX1Z0ToX0Z1, 0, 90, 0, txtid);
meshes[txtid] = new RenderPatch[] { VertX1Z0ToX0Z1, VertX1Z0ToX0Z1_90 };
}
}
@Override
public int getMaximumTextureCount() {
return metaCnt;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int idx = 0;
if (metaFromBelow) {
idx = ctx.getBlockTypeAt(0, -1, 0).stateIndex;
}
else {
idx = ctx.getBlockType().stateIndex;
}
if (idx >= metaCnt) {
idx = 0;
}
return meshes[idx];
}
}

View File

@ -0,0 +1,401 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class RPMicroRenderer extends CustomRenderer {
private static final String[] tileFields = { "cvs", "cvm" };
/* Defined texture indexes
* 0 = Cobblestone (cobblestone:0)
* 1 = Stone (stone:0)
* 2 = Wood planks (Oak) (planks:0)
* 3 = Sandstone (sandStone:0)
* 4 = Mossy Stone (cobblestoneMossy:0)
* 5 = Brick (brick:0)
* 6 = Obsidian (obsidian:0)
* 7 = Glass (glass:0)
* 8 = Dirt (dirt:0)
* 9 = Clay (blockClay:0)
* 10 = Bookshelf (bookShelf:0)
* 11 = Netherrack (87:0)
* 12 = Wood (Oak) (wood:0)
* 13 = Wood (Spruce) (wood:1)
* 14 = Wood (Birch) (wood:2)
* 15 = Soul Sand (slowSand:0)
* 16 = Polished Stone (stairSingle:0)
* 17 = Iron (blockSteel:0)
* 18 = Gold (blockGold:0)
* 19 = Diamond (blockDiamond:0)
* 20 = Lapis Lazuli (blockLapis:0)
* 21 = Snow (blockSnow:0)
* 22 = Pumpkin (pumpkin:0)
* 23 = Stone Brick (stoneBrick:0)
* 24 = Stone Brick (stoneBrick:1)
* 25 = Stone Brick (stoneBrick:2)
* 26 = Nether Brick (netherBrick:0)
* 27 = Stone Brick (stoneBrick:3)
* 28 = Wooden Planks (planks:1)
* 29 = Wooden Planks (planks:2)
* 30 = Wooden Planks (planks:3)
* 31 = Sandstone (sandStone:1)
* 32 = Wool (cloth:0)
* 33 = Wool (cloth:1)
* 34 = Wool (cloth:2)
* 35 = Wool (cloth:3)
* 36 = Wool (cloth:4)
* 37 = Wool (cloth:5)
* 38 = Wool (cloth:6)
* 39 = Wool (cloth:7)
* 40 = Wool (cloth:8)
* 41 = Wool (cloth:9)
* 42 = Wool (cloth:10)
* 43 = Wool (cloth:11)
* 44 = Wool (cloth:12)
* 45 = Wool (cloth:13)
* 46 = Wool (cloth:14)
* 47 = Wool (cloth:15)
* 48 = Marble (blockStone:0)
* 49 = Basalt (blockStone:1)
* 50 = Marble Brick (blockStone:2)
* 51 = Basalt Cobblestone (blockStone:3)
* 52 = Basalt Brick (blockStone:4)
* 53 = Rubberwood (blockLogs:0)
* 54 = Ruby Block (blockStorage:0)
* 55 = Emerald Block (blockStorage:1)
* 56 = Sapphire Block (blockStorage:2)
* 57 = Sandstone (sandStone:2)
* 58 = Wood (wood:3)
* 59 = Sandstone bottom
* 60 = Sandstone top
* 61 = Log top/bottom
* 62 = Stone step top
* 63 = Pumpkin top/bottom
* 64 = Rubberwood log top/bottom
* 65 =Chiseled Basalt Brick (blockStone:5)
* 66 = Basalt Paver (blockStone:6)
* 67 = Silver Block (blockStorage:3)
* 68 = Tin Block (blockStorage:4)
* 69 = Copper Block (blockStorage:5)
*/
private static final int NUM_TEXTURES = 70;
/* Texture index = material index in RP */
private static final int materialTextureMap[][] = {
{ 0 }, // 0 = Cobblestone (cobblestone:0)
{ 1 }, // 1 = Stone (stone:0)
{ 2 }, // 2 = Wood planks (Oak) (planks:0)
{ 59, 60, 3, 3, 3, 3 }, // 3 = Sandstone (sandStone:0)
{ 4 }, // 4 = Mossy Stone (cobblestoneMossy:0)
{ 5 }, // 5 = Brick (brick:0)
{ 6 }, // 6 = Obsidian (obsidian:0)
{ 7 }, // 7 = Glass (glass:0)
{ 8 }, // 8 = Dirt (dirt:0)
{ 9 }, // 9 = Clay (blockClay:0)
{ 2, 2, 10, 10, 10, 10 }, // 10 = Bookshelf (bookShelf:0)
{ 11 }, // 11 = Netherrack (87:0)
{ 61, 61, 12, 12, 12, 12 }, // 12 = Wood (Oak) (wood:0)
{ 61, 61, 13, 13, 13, 13 }, // 13 = Wood (Spruce) (wood:1)
{ 61, 61, 14, 14, 14, 14 }, // 14 = Wood (Birch) (wood:2)
{ 15 }, // 15 = Soul Sand (slowSand:0)
{ 62, 62, 16, 16, 16, 16 }, // 16 = Polished Stone (stairSingle:0)
{ 17 }, // 17 = Iron (blockSteel:0)
{ 18 }, // 18 = Gold (blockGold:0)
{ 19 }, // 19 = Diamond (blockDiamond:0)
{ 20 }, // 20 = Lapis Lazuli (blockLapis:0)
{ 21 }, // 21 = Snow (blockSnow:0)
{ 63, 63, 22, 22, 22, 22 }, // 22 = Pumpkin (pumpkin:0)
{ 23 }, // 23 = Stone Brick (stoneBrick:0)
{ 24 }, // 24 = Stone Brick (stoneBrick:1)
{ 25 }, // 25 = Stone Brick (stoneBrick:2)
{ 26 }, // 26 = Nether Brick (netherBrick:0)
{ 27 }, // 27 = Stone Brick (stoneBrick:3)
{ 28 }, // 28 = Wooden Planks (planks:1)
{ 29 }, // 29 = Wooden Planks (planks:2)
{ 30 }, // 30 = Wooden Planks (planks:3)
{ 60, 60, 31, 31, 31, 31 }, // 31 = Sandstone (sandStone:1)
{ 32 }, // 32 = Wool (cloth:0)
{ 33 }, // 33 = Wool (cloth:1)
{ 34 }, // 34 = Wool (cloth:2)
{ 35 }, // 35 = Wool (cloth:3)
{ 36 }, // 36 = Wool (cloth:4)
{ 37 }, // 37 = Wool (cloth:5)
{ 38 }, // 38 = Wool (cloth:6)
{ 39 }, // 39 = Wool (cloth:7)
{ 40 }, // 40 = Wool (cloth:8)
{ 41 }, // 41 = Wool (cloth:9)
{ 42 }, // 42 = Wool (cloth:10)
{ 43 }, // 43 = Wool (cloth:11)
{ 44 }, // 44 = Wool (cloth:12)
{ 45 }, // 45 = Wool (cloth:13)
{ 46 }, // 46 = Wool (cloth:14)
{ 47 }, // 47 = Wool (cloth:15)
{ 48 }, // 48 = Marble (blockStone:0)
{ 49 }, // 49 = Basalt (blockStone:1)
{ 50 }, // 50 = Marble Brick (blockStone:2)
{ 51 }, // 51 = Basalt Cobblestone (blockStone:3)
{ 52 }, // 52 = Basalt Brick (blockStone:4)
{ 64, 64, 53, 53, 53, 53 }, // 53 = Rubberwood (blockLogs:0)
{ 54 }, // 54 = Ruby Block (blockStorage:0)
{ 55 }, // 55 = Emerald Block (blockStorage:1)
{ 56 }, // 56 = Sapphire Block (blockStorage:2)
{ 65 }, // 57 = Chiseled Basalt Brick (blockStone:5)
{ 66 }, // 58 = Basalt Paver (blockStone:6)
{ 0 }, // 59
{ 0 }, // 60
{ 0 }, // 61
{ 0 }, // 62
{ 0 }, // 63
{ 60, 60, 57, 57, 57, 57 }, // 64 = Sandstone (sandStone:2)
{ 61, 61, 58, 58, 58, 58 }, // 65 = Wood (wood:3)
{ 67 }, // 66 = Silver Block (blockStorage:3)
{ 68 }, // 67 = Tin Block (blockStorage:4)
{ 69 } // 68 = Copper Block (blockStorage:5)
};
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
/* Flesh out sides map */
for(int i = 0; i < materialTextureMap.length; i++) {
if(materialTextureMap[i].length < 6) {
int[] sides = new int[6];
Arrays.fill(sides, materialTextureMap[i][0]);
materialTextureMap[i] = sides;
}
}
return true;
}
@Override
public int getMaximumTextureCount() {
return NUM_TEXTURES;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int covermask = 0;
byte[] covervals = new byte[2*29];
Object v = ctx.getBlockTileEntityField("cvm");
if(v instanceof Integer) {
covermask = ((Integer)v).intValue();
}
v = ctx.getBlockTileEntityField("cvs");
if(v instanceof byte[]) {
covervals = (byte[])v;
}
// String s = String.format("[%d,%d,%d]:cvm=%08x,cvs=", ctx.getX(), ctx.getY(), ctx.getZ(), covermask);
// for(int i = 0; i < covervals.length; i+=2) {
// s += String.format("%02x%02x:", covervals[i], covervals[i+1]);
// }
// Log.info(s);
/* Build patch list */
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
for(int i = 0, off = 0; i < 29; i++) {
if ((covermask & (1 << i)) != 0) {
addPatchesFor(ctx.getPatchFactory(), list, i, covervals[off], covervals[off+1]);
off += 2;
}
}
return list.toArray(new RenderPatch[list.size()]);
}
private static final double[] thick_0_5 = { 0.125, 0.25, 0.5, 0.125, 0.25, 0.5, 0.375, 0.625, 0.75, 0.875, 0.375, 0.625, 0.75, 0.875 };
private static final double[] thick_6_25 = { 0.125, 0.25, 0.5, 0.375, 0.625, 0.75, 0.875 };
private static final double[] thick_26_28 = { 0.125, 0.25, 0.375 };
private boolean isHollow(int shape, int thickness) {
if(shape < 6) {
switch(thickness) {
case 3:
case 4:
case 5:
case 10:
case 11:
case 12:
case 13:
return true;
}
}
return false;
}
private double getThickness(int shape, int thickness) {
double[] v;
if(shape < 6) {
v = thick_0_5;
if(thickness < v.length)
return v[thickness];
else
return 1.0;
}
else if((shape >= 26) && (shape < 29)) {
v = thick_26_28;
if(thickness < v.length)
return v[thickness];
else
return v[0];
}
else {
v = thick_6_25;
if(thickness < v.length)
return v[thickness];
else
return 1.0;
}
}
private void addPatchesFor(RenderPatchFactory rpf, ArrayList<RenderPatch> list, int shape, int material, int thickness) {
int[] sides;
if((material < 0) || (material >= materialTextureMap.length)) {
material = 0;
}
sides = materialTextureMap[material]; /* Get sides map for texture */
double thick = getThickness(shape, thickness);
/* If a hollow block, handle specially */
if(isHollow(shape, thickness)) {
switch(shape) {
case 0: /* Bottom cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, thick, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, thick, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0, thick, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0, thick, 0.25, 1, sides);
break;
case 1: /* Top cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 1-thick, 1, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 1-thick, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 1-thick, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 1-thick, 1, 0.25, 1, sides);
break;
case 2: /* Z min cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, 0.25, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, 0.75, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0.75, 1, 0, thick, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0.25, 1, 0, thick, sides);
break;
case 3: /* Z max cover */
CustomRenderer.addBox(rpf, list, 0, 0.75, 0, 0.25, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0.75, 1, 0, 0.75, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0.25, 1, 0.75, 1, 1-thick, 1, sides);
CustomRenderer.addBox(rpf, list, 0, 0.25, 0.25, 1, 1-thick, 1, sides);
break;
case 4: /* X min cover */
CustomRenderer.addBox(rpf, list, 0, thick, 0, 0.75, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0.75, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0.25, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 0, thick, 0, 0.25, 0.25, 1, sides);
break;
case 5: /* X max cover */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 0.75, 0, 0.25, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0.75, 1, 0, 0.75, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0.25, 1, 0.75, 1, sides);
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 0.25, 0.25, 1, sides);
break;
}
}
else {
switch(shape) {
case 0: /* Bottom cover */
CustomRenderer.addBox(rpf, list, 0, 1, 0, thick, 0, 1, sides);
break;
case 1: /* Top cover */
CustomRenderer.addBox(rpf, list, 0, 1, 1-thick, 1.0, 0, 1, sides);
break;
case 2: /* Z min cover */
CustomRenderer.addBox(rpf, list, 0, 1, 0, 1, 0, thick, sides);
break;
case 3: /* Z max cover */
CustomRenderer.addBox(rpf, list, 0, 1, 0, 1, 1.0-thick, 1, sides);
break;
case 4: /* X min cover */
CustomRenderer.addBox(rpf, list, 0, thick, 0, 1, 0, 1, sides);
break;
case 5: /* X max cover */
CustomRenderer.addBox(rpf, list, 1.0-thick, 1, 0, 1, 0, 1, sides);
break;
case 6: /* Xmin, Ymin, Zmin corner */
CustomRenderer.addBox(rpf, list, 0, thick, 0, thick, 0, thick, sides);
break;
case 7: /* Xmin, Ymin, Zmax corner */
CustomRenderer.addBox(rpf, list, 0, thick, 0, thick, 1-thick, 1, sides);
break;
case 8: /* Xmax, Ymin, Zmin corner */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, thick, 0, thick, sides);
break;
case 9: /* Xmax, Ymin, Zmax corner */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, thick, 1-thick, 1, sides);
break;
case 10: /* Xmin, Ymax, Zmin corner */
CustomRenderer.addBox(rpf, list, 0, thick, 1-thick, 1, 0, thick, sides);
break;
case 11: /* Xmin, Ymax, Zmax corner */
CustomRenderer.addBox(rpf, list, 0, thick, 1-thick, 1, 1-thick, 1, sides);
break;
case 12: /* Xmax, Ymax, Zmin corner */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 1-thick, 1, 0, thick, sides);
break;
case 13: /* Xmax, Ymax, Zmax corner */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 1-thick, 1, 1-thick, 1, sides);
break;
case 14: /* Zmin, Ymin Strip */
CustomRenderer.addBox(rpf, list, 0, 1, 0, thick, 0, thick, sides);
break;
case 15: /* Zmax, Ymin Strip */
CustomRenderer.addBox(rpf, list, 0, 1, 0, thick, 1-thick, 1, sides);
break;
case 16: /* Xmin, Ymin Strip */
CustomRenderer.addBox(rpf, list, 0, thick, 0, thick, 0, 1, sides);
break;
case 17: /* Xmax, Ymin Strip */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, thick, 0, 1, sides);
break;
case 18: /* Xmin, Zmin Strip */
CustomRenderer.addBox(rpf, list, 0, thick, 0, 1, 0, thick, sides);
break;
case 19: /* Xmin, Zmax Strip */
CustomRenderer.addBox(rpf, list, 0, thick, 0, 1, 1-thick, 1, sides);
break;
case 20: /* Xmax, Zmin Strip */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 1, 0, thick, sides);
break;
case 21: /* Xmax, Zmax Strip */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 0, 1, 1-thick, 1, sides);
break;
case 22: /* Zmin, Ymax Strip */
CustomRenderer.addBox(rpf, list, 0, 1, 1-thick, 1, 0, thick, sides);
break;
case 23: /* Zmax, Ymax Strip */
CustomRenderer.addBox(rpf, list, 0, 1, 1-thick, 1, 1-thick, 1, sides);
break;
case 24: /* Xmin, Ymax Strip */
CustomRenderer.addBox(rpf, list, 0, thick, 1-thick, 1, 0, 1, sides);
break;
case 25: /* Xmax, Ymax Strip */
CustomRenderer.addBox(rpf, list, 1-thick, 1, 1-thick, 1, 0, 1, sides);
break;
case 26: /* Pillar Y */
CustomRenderer.addBox(rpf, list, 0.5-thick, 0.5+thick, 0, 1, 0.5-thick, 0.5+thick, sides);
break;
case 27: /* Pillar Z */
CustomRenderer.addBox(rpf, list, 0.5-thick, 0.5+thick, 0.5-thick, 0.5+thick, 0, 1, sides);
break;
case 28: /* Pillar X */
CustomRenderer.addBox(rpf, list, 0, 1, 0.5-thick, 0.5+thick, 0.5-thick, 0.5+thick, sides);
break;
default:
CustomRenderer.addBox(rpf, list, 0, 1, 0, 1, 0, 1, sides);
break;
}
}
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tileFields;
}
}

View File

@ -0,0 +1,108 @@
package org.dynmap.hdmap.renderer;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class RPRotatedBoxRenderer extends CustomRenderer {
// From RenderContext.java in RP2 (for rotateTexturesNew())
private static final int[][] rotTable = {
{ 0, 1, 2, 3, 4, 5, 0, 112347, 0 },
{ 0, 1, 4, 5, 3, 2, 45, 112320, 27 },
{ 0, 1, 3, 2, 5, 4, 27, 112347, 0 },
{ 0, 1, 5, 4, 2, 3, 54, 112320, 27 },
{ 1, 0, 2, 3, 5, 4, 112347, 112347, 0 },
{ 1, 0, 4, 5, 2, 3, 112374, 112320, 27 },
{ 1, 0, 3, 2, 4, 5, 112320, 112347, 0 },
{ 1, 0, 5, 4, 3, 2, 112365, 112320, 27 },
{ 4, 5, 0, 1, 2, 3, 217134, 1728, 110619 },
{ 3, 2, 0, 1, 4, 5, 220014, 0, 112347 },
{ 5, 4, 0, 1, 3, 2, 218862, 1728, 110619 },
{ 2, 3, 0, 1, 5, 4, 220590, 0, 112347 },
{ 4, 5, 1, 0, 3, 2, 188469, 1728, 110619 },
{ 3, 2, 1, 0, 5, 4, 191349, 0, 112347 },
{ 5, 4, 1, 0, 2, 3, 190197, 1728, 110619 },
{ 2, 3, 1, 0, 4, 5, 191925, 0, 112347 },
{ 4, 5, 3, 2, 0, 1, 2944, 110619, 1728 },
{ 3, 2, 5, 4, 0, 1, 187264, 27, 112320 },
{ 5, 4, 2, 3, 0, 1, 113536, 110619, 1728 },
{ 2, 3, 4, 5, 0, 1, 224128, 27, 112320 },
{ 4, 5, 2, 3, 1, 0, 3419, 110619, 1728 },
{ 3, 2, 4, 5, 1, 0, 187739, 27, 112320 },
{ 5, 4, 3, 2, 1, 0, 114011, 110619, 1728 },
{ 2, 3, 5, 4, 1, 0, 224603, 27, 112320 } };
private int rotalg[] = new int[16]; // Rotaton algorithm (0=orientTextures,1=orientTexturesNew,2=fixed-at-0, 3=rotateTextures)
// Models for rotation values
private RenderPatch[][] models;
private String[] tileEntityAttribs = { "rot" };
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
models = new RenderPatch[rotTable.length][];
for(int i = 0; i < 16; i++) {
String v = custparm.get("rotalg" + i);
if(v != null) {
rotalg[i] = Integer.parseInt(v);
}
}
return true;
}
@Override
public int getMaximumTextureCount() {
return 6;
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tileEntityAttribs;
}
private static final int rotgrid[][] = {
{ 270, 180, 0, 90 }, // Bottom
{ 270, 180, 0, 90 }, // Top
{ 0, 270, 90, 180 }, // Z-
{ 0, 270, 90, 180 }, // Z+
{ 0, 270, 90, 180 }, // X-
{ 0, 270, 90, 180 } }; // X+
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
Object rot = ctx.getBlockTileEntityField("rot");
int idx = 0;
if(rot instanceof Number) {
idx = ((Number)rot).intValue();
}
switch (rotalg[ctx.getBlockType().stateIndex]) {
case 0: // Base rotation
idx = idx * 4; // Map to equivalent index in "new" algorithm map
break;
case 2: // Fixed to zero
idx = 0;
break;
case 3: // rotateTextures
if(idx > 3) idx = 0;
break;
}
if((idx < 0) || (idx >= models.length)) {
idx = 0;
}
if(models[idx] == null) {
models[idx] = new RenderPatch[6];
int v = rotTable[idx][6];
for (int side = 0; side < 6; side++) {
models[idx][side] = this.getSidePatch(ctx.getPatchFactory(), side, rotgrid[side][(v >> (3*side)) & 0x3], rotTable[idx][side]);
}
}
return models[idx];
}
}

View File

@ -0,0 +1,65 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.Map;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
import org.dynmap.renderer.RenderPatchFactory.SideVisible;
public class RPSupportFrameRenderer extends RPMicroRenderer {
private int frame_txt_side;
private int frame_txt_edge;
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
frame_txt_side = super.getMaximumTextureCount(); /* Get index for side and edge textures */
frame_txt_edge = frame_txt_side + 1;
return true;
}
@Override
public int getMaximumTextureCount() {
return super.getMaximumTextureCount() + 2;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int covermask = 0;
Object v = ctx.getBlockTileEntityField("cvm");
if(v instanceof Integer) {
covermask = ((Integer)v).intValue();
}
RenderPatchFactory rpf = ctx.getPatchFactory();
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
/* Use mask to add right sides first */
/* Add top */
list.add(rpf.getPatch(0, 1.001, 1, 1, 1.001, 1, 0, 1.001, 0, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x02) != 0)?frame_txt_edge:frame_txt_side));
/* Add bottom */
list.add(rpf.getPatch(0, -0.001, 1, 1, -0.001, 1, 0, -0.001, 0, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x01) != 0)?frame_txt_edge:frame_txt_side));
/* Add minX side */
list.add(rpf.getPatch(-0.001, 0, 0, -0.001, 0, 1, -0.001, 1, 0, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x10) != 0)?frame_txt_edge:frame_txt_side));
/* Add maxX side */
list.add(rpf.getPatch(1.001, 0, 1, 1.001, 0, 0, 1.001, 1, 1, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x20) != 0)?frame_txt_edge:frame_txt_side));
/* Add minZ side */
list.add(rpf.getPatch(1, 0, -0.001, 0, 0, -0.001, 1, 1, -0.001, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x04) != 0)?frame_txt_edge:frame_txt_side));
/* Add maxZ side */
list.add(rpf.getPatch(0, 0, 1.001, 1, 0, 1.001, 0, 1, 1.001, 0, 1, 0, 1, SideVisible.BOTH,
((covermask & 0x08) != 0)?frame_txt_edge:frame_txt_side));
/* Get patches from any microblocks */
if((covermask & 0x3FFFFFFF) != 0) {
RenderPatch[] rp = super.getRenderPatchList(ctx);
for(int i = 0; i < rp.length; i++) {
list.add(rp[i]);
}
}
return list.toArray(new RenderPatch[list.size()]);
}
}

View File

@ -0,0 +1,131 @@
package org.dynmap.hdmap.renderer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class RailCraftSlabBlockRenderer extends CustomRenderer {
private static final int TEXTURE_SIDES = 0;
private static final int TEXTURE_TOP = 1;
private static final int TEXTURE_BOTTOM = 2;
private static BitSet stair_ids = new BitSet();
// Array of meshes for normal steps - index = (0=bottom, 1=top, 2=double)
private RenderPatch[][] stepmeshes = new RenderPatch[3][];
private int textsetcnt = 0;
private String[] tilefields = null;
private String[] texturemap;
private void setID(String bname) {
DynmapBlockState bss = DynmapBlockState.getBaseStateByName(bname);
if (bss.isNotAir()) {
for (int i = 0; i < bss.getStateCount(); i++) {
DynmapBlockState bs = bss.getState(i);
stair_ids.set(bs.globalStateIndex);
}
}
}
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
setID(blkname); /* Mark block as a stair */
/* Build step meshes */
for(int i = 0; i < 3; i++) {
stepmeshes[i] = buildStepMeshes(rpf, i);
}
String cnt = custparm.get("texturecnt");
if(cnt != null)
textsetcnt = Integer.parseInt(cnt);
else
textsetcnt = 16;
tilefields = new String[] { "bottom", "top" };
texturemap = new String[textsetcnt];
for (int i = 0; i < textsetcnt; i++) {
texturemap[i] = custparm.get("textmap" + i);
if (texturemap[i] == null) {
texturemap[i] = Integer.toString(i);
}
}
return true;
}
@Override
public int getMaximumTextureCount() {
return textsetcnt;
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tilefields;
}
private static final int[] patchlist = { TEXTURE_BOTTOM, TEXTURE_TOP, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES, TEXTURE_SIDES };
private void addBox(RenderPatchFactory rpf, List<RenderPatch> list, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) {
addBox(rpf, list, xmin, xmax, ymin, ymax, zmin, zmax, patchlist);
}
private RenderPatch[] buildStepMeshes(RenderPatchFactory rpf, int dat) {
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
switch (dat) {
case 0:
addBox(rpf, list, 0, 1, 0.0, 0.5, 0, 1);
break;
case 1:
addBox(rpf, list, 0, 1, 0.5, 1, 0, 1);
break;
case 2:
addBox(rpf, list, 0, 1, 0, 1, 0, 1);
break;
}
return list.toArray(new RenderPatch[list.size()]);
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int idx = 0;
Object o = ctx.getBlockTileEntityField("bottom");
Object o2 = ctx.getBlockTileEntityField("top");
Object txtid = o;
if (o == null) {
txtid = o2;
}
if (txtid instanceof String) {
String os = (String) txtid;
for (int i = 0; i < texturemap.length; i++) {
if (os.equals(texturemap[i])) {
idx = i;
break;
}
}
}
if((idx < 0) || (idx >= textsetcnt)) {
idx = 0;
}
RenderPatch[] rp = this.stepmeshes[0];
if (o2 != null) {
if (o != null) {
rp = this.stepmeshes[2];
}
else {
rp = this.stepmeshes[1];
}
}
RenderPatch[] rp2 = new RenderPatch[rp.length];
for(int i = 0; i < rp.length; i++) {
rp2[i] = ctx.getPatchFactory().getRotatedPatch(rp[i], 0, 0, 0, idx);
}
return rp2;
}
}

View File

@ -0,0 +1,72 @@
package org.dynmap.hdmap.renderer;
import java.util.Map;
import org.dynmap.Log;
import org.dynmap.renderer.CustomRenderer;
import org.dynmap.renderer.MapDataContext;
import org.dynmap.renderer.RenderPatch;
import org.dynmap.renderer.RenderPatchFactory;
public class RailCraftTrackRenderer extends CustomRenderer {
private String[] tileEntityAttribs = { "trackId" };
private RenderPatch[][] basepatches;
private int maxTrackId;
@Override
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, int blockdatamask, Map<String,String> custparm) {
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
return false;
String cnt = custparm.get("maxTrackId"); // How many defined track IDs
if(cnt != null) {
maxTrackId = Integer.parseInt(cnt);
}
else {
maxTrackId = 35;
}
String patchid = custparm.get("patch");
if(patchid == null) {
Log.severe("Missing patch ID");
return false;
}
basepatches = new RenderPatch[maxTrackId+1][];
basepatches[0] = new RenderPatch[] { rpf.getNamedPatch(patchid, 0) };
if(basepatches[0][0] == null) {
Log.severe("Error getting patch : " + patchid);
return false;
}
for(int i = 1; i <= maxTrackId; i++) {
basepatches[i] = new RenderPatch[] { rpf.getRotatedPatch(basepatches[0][0], 0, 0, 0, i) };
}
return true;
}
@Override
public int getMaximumTextureCount() {
return maxTrackId + 1;
}
@Override
public String[] getTileEntityFieldsNeeded() {
return tileEntityAttribs;
}
@Override
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
int trackId = 0;
Object idxv = ctx.getBlockTileEntityField("trackId");
if(idxv instanceof Number) {
trackId = ((Number)idxv).intValue();
}
/* Clamp track ID */
if(trackId > maxTrackId) {
trackId = 0;
}
/* Return patch */
return basepatches[trackId];
}
}

Some files were not shown because too many files have changed in this diff Show More