mirror of
https://github.com/webbukkit/dynmap.git
synced 2025-02-11 01:11:35 +01:00
Add dynmap-core to common build
This commit is contained in:
parent
f66e63fe45
commit
805a7aa799
@ -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
1
dynmap-core/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build/
|
52
dynmap-core/build.gradle
Normal file
52
dynmap-core/build.gradle
Normal 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
1
dynmap-core/src/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.DS_Store
|
161
dynmap-core/src/main/java/org/dynmap/AsynchronousQueue.java
Normal file
161
dynmap-core/src/main/java/org/dynmap/AsynchronousQueue.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
12
dynmap-core/src/main/java/org/dynmap/ChatEvent.java
Normal file
12
dynmap-core/src/main/java/org/dynmap/ChatEvent.java
Normal 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;
|
||||
}
|
||||
}
|
275
dynmap-core/src/main/java/org/dynmap/Client.java
Normal file
275
dynmap-core/src/main/java/org/dynmap/Client.java
Normal 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);
|
||||
}
|
||||
}
|
67
dynmap-core/src/main/java/org/dynmap/ClientComponent.java
Normal file
67
dynmap-core/src/main/java/org/dynmap/ClientComponent.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
176
dynmap-core/src/main/java/org/dynmap/ClientUpdateComponent.java
Normal file
176
dynmap-core/src/main/java/org/dynmap/ClientUpdateComponent.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
17
dynmap-core/src/main/java/org/dynmap/ClientUpdateEvent.java
Normal file
17
dynmap-core/src/main/java/org/dynmap/ClientUpdateEvent.java
Normal 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;
|
||||
}
|
||||
}
|
89
dynmap-core/src/main/java/org/dynmap/Color.java
Normal file
89
dynmap-core/src/main/java/org/dynmap/Color.java
Normal 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;
|
||||
}
|
||||
}
|
286
dynmap-core/src/main/java/org/dynmap/ColorScheme.java
Normal file
286
dynmap-core/src/main/java/org/dynmap/ColorScheme.java
Normal 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();
|
||||
}
|
||||
}
|
21
dynmap-core/src/main/java/org/dynmap/Component.java
Normal file
21
dynmap-core/src/main/java/org/dynmap/Component.java
Normal 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;
|
||||
}
|
||||
}
|
47
dynmap-core/src/main/java/org/dynmap/ComponentManager.java
Normal file
47
dynmap-core/src/main/java/org/dynmap/ComponentManager.java
Normal 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;
|
||||
}
|
||||
}
|
447
dynmap-core/src/main/java/org/dynmap/ConfigurationNode.java
Normal file
447
dynmap-core/src/main/java/org/dynmap/ConfigurationNode.java
Normal 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
|
||||
}
|
||||
|
||||
}
|
22
dynmap-core/src/main/java/org/dynmap/DynmapChunk.java
Normal file
22
dynmap-core/src/main/java/org/dynmap/DynmapChunk.java
Normal 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);
|
||||
}
|
||||
}
|
2425
dynmap-core/src/main/java/org/dynmap/DynmapCore.java
Normal file
2425
dynmap-core/src/main/java/org/dynmap/DynmapCore.java
Normal file
File diff suppressed because it is too large
Load Diff
16
dynmap-core/src/main/java/org/dynmap/DynmapLocation.java
Normal file
16
dynmap-core/src/main/java/org/dynmap/DynmapLocation.java
Normal 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;
|
||||
}
|
||||
}
|
635
dynmap-core/src/main/java/org/dynmap/DynmapMapCommands.java
Normal file
635
dynmap-core/src/main/java/org/dynmap/DynmapMapCommands.java
Normal 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;
|
||||
}
|
||||
}
|
575
dynmap-core/src/main/java/org/dynmap/DynmapWorld.java
Normal file
575
dynmap-core/src/main/java/org/dynmap/DynmapWorld.java
Normal 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;
|
||||
}
|
||||
}
|
62
dynmap-core/src/main/java/org/dynmap/Event.java
Normal file
62
dynmap-core/src/main/java/org/dynmap/Event.java
Normal 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);
|
||||
}
|
||||
}
|
44
dynmap-core/src/main/java/org/dynmap/Events.java
Normal file
44
dynmap-core/src/main/java/org/dynmap/Events.java
Normal 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);
|
||||
}
|
||||
}
|
5
dynmap-core/src/main/java/org/dynmap/Handler.java
Normal file
5
dynmap-core/src/main/java/org/dynmap/Handler.java
Normal file
@ -0,0 +1,5 @@
|
||||
package org.dynmap;
|
||||
|
||||
public interface Handler<T> {
|
||||
void handle(T t);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
72
dynmap-core/src/main/java/org/dynmap/JSONUtils.java
Normal file
72
dynmap-core/src/main/java/org/dynmap/JSONUtils.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
84
dynmap-core/src/main/java/org/dynmap/Log.java
Normal file
84
dynmap-core/src/main/java/org/dynmap/Log.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
1753
dynmap-core/src/main/java/org/dynmap/MapManager.java
Normal file
1753
dynmap-core/src/main/java/org/dynmap/MapManager.java
Normal file
File diff suppressed because it is too large
Load Diff
63
dynmap-core/src/main/java/org/dynmap/MapTile.java
Normal file
63
dynmap-core/src/main/java/org/dynmap/MapTile.java
Normal 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;
|
||||
}
|
||||
}
|
206
dynmap-core/src/main/java/org/dynmap/MapType.java
Normal file
206
dynmap-core/src/main/java/org/dynmap/MapType.java
Normal 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;
|
||||
}
|
||||
}
|
275
dynmap-core/src/main/java/org/dynmap/MapTypeState.java
Normal file
275
dynmap-core/src/main/java/org/dynmap/MapTypeState.java
Normal 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;
|
||||
}
|
||||
}
|
306
dynmap-core/src/main/java/org/dynmap/MarkersComponent.java
Normal file
306
dynmap-core/src/main/java/org/dynmap/MarkersComponent.java
Normal 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 */
|
||||
}
|
||||
}
|
226
dynmap-core/src/main/java/org/dynmap/PlayerFaces.java
Normal file
226
dynmap-core/src/main/java/org/dynmap/PlayerFaces.java
Normal 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();
|
||||
}
|
||||
}
|
188
dynmap-core/src/main/java/org/dynmap/PlayerList.java
Normal file
188
dynmap-core/src/main/java/org/dynmap/PlayerList.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
10
dynmap-core/src/main/java/org/dynmap/TestComponent.java
Normal file
10
dynmap-core/src/main/java/org/dynmap/TestComponent.java
Normal 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"));
|
||||
}
|
||||
|
||||
}
|
121
dynmap-core/src/main/java/org/dynmap/UpdateQueue.java
Normal file
121
dynmap-core/src/main/java/org/dynmap/UpdateQueue.java
Normal 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;
|
||||
}
|
||||
}
|
358
dynmap-core/src/main/java/org/dynmap/WebAuthManager.java
Normal file
358
dynmap-core/src/main/java/org/dynmap/WebAuthManager.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
222
dynmap-core/src/main/java/org/dynmap/common/BiomeMap.java
Normal file
222
dynmap-core/src/main/java/org/dynmap/common/BiomeMap.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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]", "");
|
||||
}
|
||||
}
|
@ -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 &0 to &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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
31
dynmap-core/src/main/java/org/dynmap/debug/Debug.java
Normal file
31
dynmap-core/src/main/java/org/dynmap/debug/Debug.java
Normal 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);
|
||||
}
|
||||
}
|
9
dynmap-core/src/main/java/org/dynmap/debug/Debugger.java
Normal file
9
dynmap-core/src/main/java/org/dynmap/debug/Debugger.java
Normal 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);
|
||||
}
|
27
dynmap-core/src/main/java/org/dynmap/debug/LogDebugger.java
Normal file
27
dynmap-core/src/main/java/org/dynmap/debug/LogDebugger.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
21
dynmap-core/src/main/java/org/dynmap/debug/NullDebugger.java
Normal file
21
dynmap-core/src/main/java/org/dynmap/debug/NullDebugger.java
Normal 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) {
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
647
dynmap-core/src/main/java/org/dynmap/exporter/OBJExport.java
Normal file
647
dynmap-core/src/main/java/org/dynmap/exporter/OBJExport.java
Normal 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;
|
||||
}
|
||||
}
|
1544
dynmap-core/src/main/java/org/dynmap/hdmap/CTMTexturePack.java
Normal file
1544
dynmap-core/src/main/java/org/dynmap/hdmap/CTMTexturePack.java
Normal file
File diff suppressed because it is too large
Load Diff
270
dynmap-core/src/main/java/org/dynmap/hdmap/CaveHDShader.java
Normal file
270
dynmap-core/src/main/java/org/dynmap/hdmap/CaveHDShader.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
319
dynmap-core/src/main/java/org/dynmap/hdmap/DefaultHDShader.java
Normal file
319
dynmap-core/src/main/java/org/dynmap/hdmap/DefaultHDShader.java
Normal 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;
|
||||
}
|
||||
}
|
36
dynmap-core/src/main/java/org/dynmap/hdmap/HDBlockModel.java
Normal file
36
dynmap-core/src/main/java/org/dynmap/hdmap/HDBlockModel.java
Normal 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) {
|
||||
}
|
||||
}
|
1002
dynmap-core/src/main/java/org/dynmap/hdmap/HDBlockModels.java
Normal file
1002
dynmap-core/src/main/java/org/dynmap/hdmap/HDBlockModels.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
30
dynmap-core/src/main/java/org/dynmap/hdmap/HDLighting.java
Normal file
30
dynmap-core/src/main/java/org/dynmap/hdmap/HDLighting.java
Normal 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);
|
||||
}
|
465
dynmap-core/src/main/java/org/dynmap/hdmap/HDMap.java
Normal file
465
dynmap-core/src/main/java/org/dynmap/hdmap/HDMap.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
192
dynmap-core/src/main/java/org/dynmap/hdmap/HDMapManager.java
Normal file
192
dynmap-core/src/main/java/org/dynmap/hdmap/HDMapManager.java
Normal 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;
|
||||
}
|
||||
}
|
93
dynmap-core/src/main/java/org/dynmap/hdmap/HDMapTile.java
Normal file
93
dynmap-core/src/main/java/org/dynmap/hdmap/HDMapTile.java
Normal 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; }
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
43
dynmap-core/src/main/java/org/dynmap/hdmap/HDShader.java
Normal file
43
dynmap-core/src/main/java/org/dynmap/hdmap/HDShader.java
Normal 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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
1320
dynmap-core/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java
Normal file
1320
dynmap-core/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java
Normal file
File diff suppressed because it is too large
Load Diff
293
dynmap-core/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java
Normal file
293
dynmap-core/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
3640
dynmap-core/src/main/java/org/dynmap/hdmap/TexturePack.java
Normal file
3640
dynmap-core/src/main/java/org/dynmap/hdmap/TexturePack.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
349
dynmap-core/src/main/java/org/dynmap/hdmap/TopoHDShader.java
Normal file
349
dynmap-core/src/main/java/org/dynmap/hdmap/TopoHDShader.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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()];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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()]);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user