commit
99e2d81419
4988 changed files with 226473 additions and 759 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -1,11 +1,12 @@
|
||||||
# Eclipse stuff
|
# Eclipse stuff
|
||||||
/.classpath
|
.classpath
|
||||||
/.project
|
.project
|
||||||
/.settings
|
.settings
|
||||||
|
|
||||||
# netbeans
|
# netbeans
|
||||||
/nbproject
|
/nbproject
|
||||||
|
|
||||||
|
|
||||||
# we use maven!
|
# we use maven!
|
||||||
/build.xml
|
/build.xml
|
||||||
|
|
||||||
|
|
@ -24,3 +25,5 @@
|
||||||
# Mac filesystem dust
|
# Mac filesystem dust
|
||||||
/.DS_Store
|
/.DS_Store
|
||||||
/dependency-reduced-pom.xml
|
/dependency-reduced-pom.xml
|
||||||
|
|
||||||
|
/.gradle
|
||||||
|
|
|
||||||
1
DynmapCore/.gitignore
vendored
Normal file
1
DynmapCore/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
||||||
1
DynmapCore/bin/.gitignore
vendored
Normal file
1
DynmapCore/bin/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/main/
|
||||||
53
DynmapCore/build.gradle
Normal file
53
DynmapCore/build.gradle
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
description = "DynmapCore"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(path: ':DynmapCoreAPI', configuration: 'shadow')
|
||||||
|
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 'shaders.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
DynmapCore/src/.gitignore
vendored
Normal file
1
DynmapCore/src/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
||||||
161
DynmapCore/src/main/java/org/dynmap/AsynchronousQueue.java
Normal file
161
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ChatEvent.java
Normal file
12
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Client.java
Normal file
275
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ClientComponent.java
Normal file
67
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ClientUpdateComponent.java
Normal file
176
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ClientUpdateEvent.java
Normal file
17
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Color.java
Normal file
89
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ColorScheme.java
Normal file
286
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Component.java
Normal file
21
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ComponentManager.java
Normal file
47
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/ConfigurationNode.java
Normal file
447
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/DynmapChunk.java
Normal file
22
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/DynmapCore.java
Normal file
2425
DynmapCore/src/main/java/org/dynmap/DynmapCore.java
Normal file
File diff suppressed because it is too large
Load diff
16
DynmapCore/src/main/java/org/dynmap/DynmapLocation.java
Normal file
16
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/DynmapMapCommands.java
Normal file
635
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/DynmapWorld.java
Normal file
575
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Event.java
Normal file
62
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Events.java
Normal file
44
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Handler.java
Normal file
5
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/JSONUtils.java
Normal file
72
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/Log.java
Normal file
84
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/MapManager.java
Normal file
1753
DynmapCore/src/main/java/org/dynmap/MapManager.java
Normal file
File diff suppressed because it is too large
Load diff
63
DynmapCore/src/main/java/org/dynmap/MapTile.java
Normal file
63
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/MapType.java
Normal file
206
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/MapTypeState.java
Normal file
275
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/MarkersComponent.java
Normal file
306
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/PlayerFaces.java
Normal file
226
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/PlayerList.java
Normal file
188
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/TestComponent.java
Normal file
10
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/UpdateQueue.java
Normal file
121
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/WebAuthManager.java
Normal file
358
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java
Normal file
222
DynmapCore/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();
|
||||||
|
}
|
||||||
|
}
|
||||||
97
DynmapCore/src/main/java/org/dynmap/common/DynmapPlayer.java
Normal file
97
DynmapCore/src/main/java/org/dynmap/common/DynmapPlayer.java
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.dynmap.common;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.dynmap.DynmapLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player (server neutral) - represents online or offline player
|
||||||
|
*/
|
||||||
|
public interface DynmapPlayer extends DynmapCommandSender {
|
||||||
|
/**
|
||||||
|
* Get player ID
|
||||||
|
* @return ID (case insensitive)
|
||||||
|
*/
|
||||||
|
public String getName();
|
||||||
|
/**
|
||||||
|
* Get player display name
|
||||||
|
* @return display name
|
||||||
|
*/
|
||||||
|
public String getDisplayName();
|
||||||
|
/**
|
||||||
|
* Is player online?
|
||||||
|
* @return true if online
|
||||||
|
*/
|
||||||
|
public boolean isOnline();
|
||||||
|
/**
|
||||||
|
* Get current location of player
|
||||||
|
* @return location
|
||||||
|
*/
|
||||||
|
public DynmapLocation getLocation();
|
||||||
|
/**
|
||||||
|
* Get world ID of player
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
public String getWorld();
|
||||||
|
/**
|
||||||
|
* Get connected address for player
|
||||||
|
* @return connection address, or null if unknown
|
||||||
|
*/
|
||||||
|
public InetSocketAddress getAddress();
|
||||||
|
/**
|
||||||
|
* Check if player is sneaking
|
||||||
|
* @return true if sneaking
|
||||||
|
*/
|
||||||
|
public boolean isSneaking();
|
||||||
|
/**
|
||||||
|
* Get health
|
||||||
|
* @return health points
|
||||||
|
*/
|
||||||
|
public double getHealth();
|
||||||
|
/**
|
||||||
|
* Get armor points
|
||||||
|
* @return armor points
|
||||||
|
*/
|
||||||
|
public int getArmorPoints();
|
||||||
|
/**
|
||||||
|
* Get spawn bed location
|
||||||
|
* @return bed location, or null if none
|
||||||
|
*/
|
||||||
|
public DynmapLocation getBedSpawnLocation();
|
||||||
|
/**
|
||||||
|
* Get last login time
|
||||||
|
* @return UTC time (msec) of last login
|
||||||
|
*/
|
||||||
|
public long getLastLoginTime();
|
||||||
|
/**
|
||||||
|
* Get first login time
|
||||||
|
* @return UTC time (msec) of first login
|
||||||
|
*/
|
||||||
|
public long getFirstLoginTime();
|
||||||
|
/**
|
||||||
|
* Is invisible
|
||||||
|
* @return true if invisible
|
||||||
|
*/
|
||||||
|
public boolean isInvisible();
|
||||||
|
/**
|
||||||
|
* Get sort weight (ordered lowest to highest in player list: 0=default)
|
||||||
|
* @return sort weight
|
||||||
|
*/
|
||||||
|
public int getSortWeight();
|
||||||
|
/**
|
||||||
|
* Set sort weight (ordered lowest to highest in player list: 0=default)
|
||||||
|
* @param wt - sort weight
|
||||||
|
*/
|
||||||
|
public void setSortWeight(int wt);
|
||||||
|
/**
|
||||||
|
* Get skin URL for player
|
||||||
|
* @return URL, or null if not available
|
||||||
|
*/
|
||||||
|
public default String getSkinURL() { return null; }
|
||||||
|
/**
|
||||||
|
* Get player UUID
|
||||||
|
* Return UUID, or null if not available
|
||||||
|
*/
|
||||||
|
public default UUID getUUID() { return null; }
|
||||||
|
}
|
||||||
|
|
@ -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
DynmapCore/src/main/java/org/dynmap/debug/Debug.java
Normal file
31
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/debug/Debugger.java
Normal file
9
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/debug/LogDebugger.java
Normal file
27
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/debug/NullDebugger.java
Normal file
21
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/exporter/OBJExport.java
Normal file
647
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java
Normal file
1544
DynmapCore/src/main/java/org/dynmap/hdmap/CTMTexturePack.java
Normal file
File diff suppressed because it is too large
Load diff
270
DynmapCore/src/main/java/org/dynmap/hdmap/CaveHDShader.java
Normal file
270
DynmapCore/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,59 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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.isEmpty() || databits.get(i)) {
|
||||||
|
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
DynmapCore/src/main/java/org/dynmap/hdmap/DefaultHDShader.java
Normal file
319
DynmapCore/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockModel.java
Normal file
38
DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockModel.java
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
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, BitSet databits, String blockset) {
|
||||||
|
this.blockset = blockset;
|
||||||
|
DynmapBlockState bblk = DynmapBlockState.getBaseStateByName(blockname);
|
||||||
|
if (bblk.isNotAir()) {
|
||||||
|
for (int i = 0; i < bblk.getStateCount(); i++) {
|
||||||
|
if (databits.isEmpty() || databits.get(i)) {
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
1019
DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockModels.java
Normal file
1019
DynmapCore/src/main/java/org/dynmap/hdmap/HDBlockModels.java
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
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, BitSet 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,194 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet stateidx) {
|
||||||
|
/* Add entries to lookup table */
|
||||||
|
for (String blkname : blocknames) {
|
||||||
|
DynmapBlockState baseblk = DynmapBlockState.getBaseStateByName(blkname);
|
||||||
|
if (baseblk.isNotAir()) {
|
||||||
|
if (stateidx != null) {
|
||||||
|
for (int stateid = stateidx.nextSetBit(0); stateid >= 0; stateid = stateidx.nextSetBit(stateid+1)) {
|
||||||
|
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,189 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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
DynmapCore/src/main/java/org/dynmap/hdmap/HDLighting.java
Normal file
30
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/hdmap/HDMap.java
Normal file
465
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/hdmap/HDMapManager.java
Normal file
192
DynmapCore/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
DynmapCore/src/main/java/org/dynmap/hdmap/HDMapTile.java
Normal file
93
DynmapCore/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; }
|
||||||
|
|
||||||
|
}
|
||||||
40
DynmapCore/src/main/java/org/dynmap/hdmap/HDPerspective.java
Normal file
40
DynmapCore/src/main/java/org/dynmap/hdmap/HDPerspective.java
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.dynmap.DynmapChunk;
|
||||||
|
import org.dynmap.DynmapWorld;
|
||||||
|
import org.dynmap.MapTile;
|
||||||
|
import org.dynmap.utils.MapChunkCache;
|
||||||
|
import org.dynmap.utils.TileFlags;
|
||||||
|
import org.dynmap.utils.Vector3D;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
public interface HDPerspective {
|
||||||
|
/* Get name of perspective */
|
||||||
|
String getName();
|
||||||
|
/* Get tiles invalidated by change at given location */
|
||||||
|
List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int x, int y, int z);
|
||||||
|
/* Get tiles invalidated by change at given volume, defined by 2 opposite corner locations */
|
||||||
|
List<TileFlags.TileCoord> getTileCoords(DynmapWorld w, int minx, int miny, int minz, int maxx, int maxy, int maxz);
|
||||||
|
/* Get tiles adjacent to given tile */
|
||||||
|
MapTile[] getAdjecentTiles(MapTile tile);
|
||||||
|
/* Get chunks needed for given tile */
|
||||||
|
List<DynmapChunk> getRequiredChunks(MapTile tile);
|
||||||
|
/* Render given tile */
|
||||||
|
boolean render(MapChunkCache cache, HDMapTile tile, String mapname);
|
||||||
|
|
||||||
|
public boolean isBiomeDataNeeded();
|
||||||
|
public boolean isHightestBlockYDataNeeded();
|
||||||
|
public boolean isRawBiomeDataNeeded();
|
||||||
|
public boolean isBlockTypeDataNeeded();
|
||||||
|
|
||||||
|
double getScale();
|
||||||
|
int getModelScale();
|
||||||
|
|
||||||
|
public void addClientConfiguration(JSONObject mapObject);
|
||||||
|
|
||||||
|
public void transformWorldToMapCoord(Vector3D input, Vector3D rslt);
|
||||||
|
|
||||||
|
public int hashCode();
|
||||||
|
}
|
||||||
|
|
@ -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
DynmapCore/src/main/java/org/dynmap/hdmap/HDShader.java
Normal file
43
DynmapCore/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);
|
||||||
|
}
|
||||||
62
DynmapCore/src/main/java/org/dynmap/hdmap/HDShaderState.java
Normal file
62
DynmapCore/src/main/java/org/dynmap/hdmap/HDShaderState.java
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import org.dynmap.Color;
|
||||||
|
import org.dynmap.utils.DynLongHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is used to define the operational state of a renderer during raytracing
|
||||||
|
* All method should be considered performance critical
|
||||||
|
*/
|
||||||
|
public interface HDShaderState {
|
||||||
|
/**
|
||||||
|
* Get our shader
|
||||||
|
* @return shader
|
||||||
|
*/
|
||||||
|
HDShader getShader();
|
||||||
|
/**
|
||||||
|
* Get our lighting
|
||||||
|
* @return lighting
|
||||||
|
*/
|
||||||
|
HDLighting getLighting();
|
||||||
|
/**
|
||||||
|
* Get our map
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
HDMap getMap();
|
||||||
|
/**
|
||||||
|
* Reset renderer state for new ray - passes in pixel coordinate for ray
|
||||||
|
* @param ps - perspective state
|
||||||
|
*/
|
||||||
|
void reset(HDPerspectiveState ps);
|
||||||
|
/**
|
||||||
|
* Process next ray step - called for each block on route
|
||||||
|
* @param ps - perspective state
|
||||||
|
* @return true if ray is done, false if ray needs to continue
|
||||||
|
*/
|
||||||
|
boolean processBlock(HDPerspectiveState ps);
|
||||||
|
/**
|
||||||
|
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
|
||||||
|
* @param ps - perspective state
|
||||||
|
*/
|
||||||
|
void rayFinished(HDPerspectiveState ps);
|
||||||
|
/**
|
||||||
|
* Get result color - get resulting color for ray
|
||||||
|
* @param c - object to store color value in
|
||||||
|
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
|
||||||
|
*/
|
||||||
|
void getRayColor(Color c, int index);
|
||||||
|
/**
|
||||||
|
* Clean up state object - called after last ray completed
|
||||||
|
*/
|
||||||
|
void cleanup();
|
||||||
|
/**
|
||||||
|
* Get CTM texture cache
|
||||||
|
* @return texture cache
|
||||||
|
*/
|
||||||
|
DynLongHashMap getCTMTextureCache();
|
||||||
|
/**
|
||||||
|
* Get lighting table
|
||||||
|
* @return array of lighting values
|
||||||
|
*/
|
||||||
|
int[] getLightingTable();
|
||||||
|
}
|
||||||
241
DynmapCore/src/main/java/org/dynmap/hdmap/InhabitedHDShader.java
Normal file
241
DynmapCore/src/main/java/org/dynmap/hdmap/InhabitedHDShader.java
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import static org.dynmap.JSONUtils.s;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.dynmap.Color;
|
||||||
|
import org.dynmap.ConfigurationNode;
|
||||||
|
import org.dynmap.DynmapCore;
|
||||||
|
import org.dynmap.Log;
|
||||||
|
import org.dynmap.MapManager;
|
||||||
|
import org.dynmap.common.DynmapCommandSender;
|
||||||
|
import org.dynmap.exporter.OBJExport;
|
||||||
|
import org.dynmap.renderer.DynmapBlockState;
|
||||||
|
import org.dynmap.utils.BlockStep;
|
||||||
|
import org.dynmap.utils.DynLongHashMap;
|
||||||
|
import org.dynmap.utils.MapChunkCache;
|
||||||
|
import org.dynmap.utils.MapIterator;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
public class InhabitedHDShader implements HDShader {
|
||||||
|
private final String name;
|
||||||
|
private final long filllevel[]; /* Values for colors */
|
||||||
|
private final Color fillcolor[];
|
||||||
|
|
||||||
|
private Color readColor(String id, ConfigurationNode cfg) {
|
||||||
|
String lclr = cfg.getString(id, null);
|
||||||
|
if((lclr != null) && (lclr.startsWith("#"))) {
|
||||||
|
try {
|
||||||
|
int c = Integer.parseInt(lclr.substring(1), 16);
|
||||||
|
return new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF);
|
||||||
|
} catch (NumberFormatException nfx) {
|
||||||
|
Log.severe("Invalid color value: " + lclr + " for '" + id + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public InhabitedHDShader(DynmapCore core, ConfigurationNode configuration) {
|
||||||
|
name = (String) configuration.get("name");
|
||||||
|
HashMap<Long, Color> map = new HashMap<Long, Color>();
|
||||||
|
for (String key : configuration.keySet()) {
|
||||||
|
if (key.startsWith("color")) {
|
||||||
|
try {
|
||||||
|
long val = Long.parseLong(key.substring(5));
|
||||||
|
Color clr = readColor(key, configuration);
|
||||||
|
map.put(val, clr);
|
||||||
|
} catch (NumberFormatException nfx) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TreeSet<Long> keys = new TreeSet<Long>(map.keySet());
|
||||||
|
filllevel = new long[keys.size()];
|
||||||
|
fillcolor = new Color[keys.size()];
|
||||||
|
int idx = 0;
|
||||||
|
for (Long k : keys) {
|
||||||
|
filllevel[idx] = k;
|
||||||
|
fillcolor[idx] = map.get(k);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBiomeDataNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRawBiomeDataNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHightestBlockYDataNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBlockTypeDataNeeded() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSkyLightLevelNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmittedLightLevelNeeded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OurShaderState implements HDShaderState {
|
||||||
|
private Color color[];
|
||||||
|
private Color c;
|
||||||
|
protected HDMap map;
|
||||||
|
private HDLighting lighting;
|
||||||
|
final int[] lightingTable;
|
||||||
|
|
||||||
|
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
|
||||||
|
this.map = map;
|
||||||
|
this.lighting = map.getLighting();
|
||||||
|
if(lighting.isNightAndDayEnabled()) {
|
||||||
|
color = new Color[] { new Color(), new Color() };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
color = new Color[] { new Color() };
|
||||||
|
}
|
||||||
|
c = new Color();
|
||||||
|
if (MapManager.mapman.useBrightnessTable()) {
|
||||||
|
lightingTable = cache.getWorld().getBrightnessTable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lightingTable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get our shader
|
||||||
|
*/
|
||||||
|
public HDShader getShader() {
|
||||||
|
return InhabitedHDShader.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get our map
|
||||||
|
*/
|
||||||
|
public HDMap getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get our lighting
|
||||||
|
*/
|
||||||
|
public HDLighting getLighting() {
|
||||||
|
return lighting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset renderer state for new ray
|
||||||
|
*/
|
||||||
|
public void reset(HDPerspectiveState ps) {
|
||||||
|
for(int i = 0; i < color.length; i++)
|
||||||
|
color[i].setTransparent();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Process next ray step - called for each block on route
|
||||||
|
* @return true if ray is done, false if ray needs to continue
|
||||||
|
*/
|
||||||
|
public boolean processBlock(HDPerspectiveState ps) {
|
||||||
|
if (ps.getBlockState().isAir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long ts = ps.getMapIterator().getInhabitedTicks() / 1200; // Get time, in minutes
|
||||||
|
// Find top of range
|
||||||
|
boolean match = false;
|
||||||
|
for (int i = 0; i < filllevel.length; i++) {
|
||||||
|
if (ts < filllevel[i]) { // Found it
|
||||||
|
if (i > 0) { // Middle? Interpolate
|
||||||
|
int interp = (int) ((256 * (ts - filllevel[i-1])) / (filllevel[i] - filllevel[i-1]));
|
||||||
|
int red = (interp * fillcolor[i].getRed()) + ((256 - interp) * fillcolor[i-1].getRed());
|
||||||
|
int green = (interp * fillcolor[i].getGreen()) + ((256 - interp) * fillcolor[i-1].getGreen());
|
||||||
|
int blue = (interp * fillcolor[i].getBlue()) + ((256 - interp) * fillcolor[i-1].getBlue());
|
||||||
|
c.setRGBA(red / 256, green / 256, blue / 256, 255);
|
||||||
|
}
|
||||||
|
else { // Else, use color
|
||||||
|
c.setColor(fillcolor[i]);
|
||||||
|
}
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
c.setColor(fillcolor[fillcolor.length-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle light level, if needed */
|
||||||
|
lighting.applyLighting(ps, this, c, color);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
|
||||||
|
*/
|
||||||
|
public void rayFinished(HDPerspectiveState ps) {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get result color - get resulting color for ray
|
||||||
|
* @param c - object to store color value in
|
||||||
|
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
|
||||||
|
*/
|
||||||
|
public void getRayColor(Color c, int index) {
|
||||||
|
c.setColor(color[index]);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Clean up state object - called after last ray completed
|
||||||
|
*/
|
||||||
|
public void cleanup() {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public DynLongHashMap getCTMTextureCache() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int[] getLightingTable() {
|
||||||
|
return lightingTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get renderer state object for use rendering a tile
|
||||||
|
* @param map - map being rendered
|
||||||
|
* @param cache - chunk cache containing data for tile to be rendered
|
||||||
|
* @param mapiter - iterator used when traversing rays in tile
|
||||||
|
* @param scale - scale of perspecitve
|
||||||
|
* @return state object to use for all rays in tile
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
|
||||||
|
return new OurShaderState(mapiter, map, cache, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add shader's contributions to JSON for map object */
|
||||||
|
public void addClientConfiguration(JSONObject mapObject) {
|
||||||
|
s(mapObject, "shader", name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
|
||||||
|
throw new IOException("Export unsupported");
|
||||||
|
}
|
||||||
|
private static final String[] nulllist = new String[0];
|
||||||
|
@Override
|
||||||
|
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
|
||||||
|
return nulllist;
|
||||||
|
}
|
||||||
|
}
|
||||||
1343
DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java
Normal file
1343
DynmapCore/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java
Normal file
File diff suppressed because it is too large
Load diff
293
DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java
Normal file
293
DynmapCore/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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3673
DynmapCore/src/main/java/org/dynmap/hdmap/TexturePack.java
Normal file
3673
DynmapCore/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;
|
||||||
|
protected 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,44 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import org.dynmap.ConfigurationNode;
|
||||||
|
import org.dynmap.DynmapCore;
|
||||||
|
import org.dynmap.renderer.DynmapBlockState;
|
||||||
|
import org.dynmap.utils.MapChunkCache;
|
||||||
|
import org.dynmap.utils.MapIterator;
|
||||||
|
|
||||||
|
public class TexturePackHDUnderwaterShader extends TexturePackHDShader {
|
||||||
|
|
||||||
|
class UnderwaterShaderState extends TexturePackHDShader.ShaderState {
|
||||||
|
private boolean ready;
|
||||||
|
private DynmapBlockState full_water;
|
||||||
|
|
||||||
|
protected UnderwaterShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
|
||||||
|
super(mapiter, map, cache, scale);
|
||||||
|
full_water = DynmapBlockState.getBaseStateByName(DynmapBlockState.WATER_BLOCK);
|
||||||
|
}
|
||||||
|
@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) {
|
||||||
|
DynmapBlockState bs = ps.getBlockState();
|
||||||
|
if (bs.isWater() || bs.isWaterlogged()) {
|
||||||
|
ready = true;
|
||||||
|
this.lastblk = full_water;
|
||||||
|
}
|
||||||
|
return ready ? super.processBlock(ps) : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public TexturePackHDUnderwaterShader(DynmapCore core, ConfigurationNode configuration) {
|
||||||
|
super(core, configuration);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
|
||||||
|
return new UnderwaterShaderState(mapiter, map, cache, scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
171
DynmapCore/src/main/java/org/dynmap/hdmap/TexturePackLoader.java
Normal file
171
DynmapCore/src/main/java/org/dynmap/hdmap/TexturePackLoader.java
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
package org.dynmap.hdmap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import org.dynmap.DynmapCore;
|
||||||
|
import org.dynmap.Log;
|
||||||
|
import org.dynmap.common.DynmapServerInterface;
|
||||||
|
|
||||||
|
public class TexturePackLoader {
|
||||||
|
private ZipFile zf;
|
||||||
|
private File tpdir;
|
||||||
|
private DynmapServerInterface dsi;
|
||||||
|
private static final String RESOURCEPATH = "texturepacks/standard";
|
||||||
|
|
||||||
|
private static class ModSource {
|
||||||
|
ZipFile zf;
|
||||||
|
File directory;
|
||||||
|
}
|
||||||
|
private HashMap<String, ModSource> src_by_mod = new HashMap<String, ModSource>();
|
||||||
|
|
||||||
|
public TexturePackLoader(File tp, DynmapCore core) {
|
||||||
|
if (tp.isFile() && tp.canRead()) {
|
||||||
|
try {
|
||||||
|
zf = new ZipFile(tp);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.severe("Error opening texture pack - " + tp.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tp.isDirectory() && tp.canRead()) {
|
||||||
|
tpdir = tp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.info("Texture pack not found - " + tp.getPath());
|
||||||
|
}
|
||||||
|
dsi = core.getServer();
|
||||||
|
}
|
||||||
|
public InputStream openTPResource(String rname, String altname) {
|
||||||
|
InputStream is = openTPResource(rname);
|
||||||
|
if (is == null) {
|
||||||
|
if (altname != null) {
|
||||||
|
is = openTPResource(altname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
public InputStream openTPResource(String rname) {
|
||||||
|
return openModTPResource(rname, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream openModTPResource(String rname, String modname) {
|
||||||
|
try {
|
||||||
|
if (zf != null) {
|
||||||
|
ZipEntry ze = zf.getEntry(rname);
|
||||||
|
if ((ze != null) && (!ze.isDirectory())) {
|
||||||
|
return zf.getInputStream(ze);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tpdir != null) {
|
||||||
|
File f = new File(tpdir, rname);
|
||||||
|
if (f.isFile() && f.canRead()) {
|
||||||
|
return new FileInputStream(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException iox) {
|
||||||
|
}
|
||||||
|
// Fall through - load as resource from mod, if possible, or from jar
|
||||||
|
InputStream is = dsi.openResource(modname, rname);
|
||||||
|
if (is != null) {
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
if (modname != null) {
|
||||||
|
ModSource ms = src_by_mod.get(modname);
|
||||||
|
if (ms == null) {
|
||||||
|
File f = dsi.getModContainerFile(modname);
|
||||||
|
ms = new ModSource();
|
||||||
|
if (f != null) {
|
||||||
|
if (f.isFile()) {
|
||||||
|
try {
|
||||||
|
ms.zf = new ZipFile(f);
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ms.directory = f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src_by_mod.put(modname, ms);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (ms.zf != null) {
|
||||||
|
ZipEntry ze = ms.zf.getEntry(rname);
|
||||||
|
if ((ze != null) && (!ze.isDirectory())) {
|
||||||
|
is = ms.zf.getInputStream(ze);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ms.directory != null) {
|
||||||
|
File f = new File(ms.directory, rname);
|
||||||
|
if (f.isFile() && f.canRead()) {
|
||||||
|
is = new FileInputStream(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException iox) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is == null) {
|
||||||
|
is = getClass().getClassLoader().getResourceAsStream(RESOURCEPATH + "/" + rname);
|
||||||
|
}
|
||||||
|
if ((is == null) && (modname != null)) {
|
||||||
|
Log.warning("Resource " + rname + " for mod " + modname + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
public void close() {
|
||||||
|
if(zf != null) {
|
||||||
|
try { zf.close(); } catch (IOException iox) {}
|
||||||
|
zf = null;
|
||||||
|
}
|
||||||
|
for (ModSource ms : src_by_mod.values()) {
|
||||||
|
if (ms.zf != null) {
|
||||||
|
try { ms.zf.close(); } catch (IOException iox) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src_by_mod.clear();
|
||||||
|
}
|
||||||
|
public void closeResource(InputStream is) {
|
||||||
|
try {
|
||||||
|
if (is != null)
|
||||||
|
is.close();
|
||||||
|
} catch (IOException iox) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Set<String> getEntries() {
|
||||||
|
HashSet<String> rslt = new HashSet<String>();
|
||||||
|
if (zf != null) {
|
||||||
|
Enumeration<? extends ZipEntry> lst = zf.entries();
|
||||||
|
while(lst.hasMoreElements()) {
|
||||||
|
rslt.add(lst.nextElement().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tpdir != null) {
|
||||||
|
addFiles(rslt, tpdir, "");
|
||||||
|
}
|
||||||
|
return rslt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFiles(HashSet<String> files, File dir, String path) {
|
||||||
|
File[] listfiles = dir.listFiles();
|
||||||
|
if(listfiles == null) return;
|
||||||
|
for(File f : listfiles) {
|
||||||
|
String fn = f.getName();
|
||||||
|
if(fn.equals(".") || (fn.equals(".."))) continue;
|
||||||
|
if(f.isFile()) {
|
||||||
|
files.add(path + "/" + fn);
|
||||||
|
}
|
||||||
|
else if(f.isDirectory()) {
|
||||||
|
addFiles(files, f, path + "/" + f.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
349
DynmapCore/src/main/java/org/dynmap/hdmap/TopoHDShader.java
Normal file
349
DynmapCore/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,76 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,97 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,151 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,102 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,207 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,111 @@
|
||||||
|
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.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, BitSet 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, BitSet 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,162 @@
|
||||||
|
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 FenceWallBlockStateRenderer 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 static final int SIDE_XP = 0x1; // East
|
||||||
|
private static final int SIDE_XN = 0x2; // West
|
||||||
|
private static final int SIDE_X = SIDE_XN | SIDE_XP;
|
||||||
|
private static final int SIDE_ZP = 0x4; // South
|
||||||
|
private static final int SIDE_ZN = 0x8; // North
|
||||||
|
private static final int SIDE_Z = SIDE_ZN | SIDE_ZP;
|
||||||
|
private static final int SIDE_YP = 0x10; // Up
|
||||||
|
|
||||||
|
// 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][];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, BitSet blockdatamask, Map<String,String> custparm) {
|
||||||
|
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
|
||||||
|
return false;
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
int idx = ctx.getBlockType().stateIndex;
|
||||||
|
int off = 0;
|
||||||
|
if(check_yplus) { // Wall?
|
||||||
|
if ((idx & 0x20) == 0) off += SIDE_XP; // East connected
|
||||||
|
if ((idx & 0x10) == 0) off += SIDE_ZN; // North connected
|
||||||
|
if ((idx & 0x08) == 0) off += SIDE_ZP; // South connected
|
||||||
|
if ((idx & 0x04) == 0) off += SIDE_YP; // Up connected
|
||||||
|
if ((idx & 0x01) == 0) off += SIDE_XN; // West connected
|
||||||
|
}
|
||||||
|
else { // Fence
|
||||||
|
if ((idx & 0x10) == 0) off += SIDE_XP; // East connected
|
||||||
|
if ((idx & 0x08) == 0) off += SIDE_ZN; // North connected
|
||||||
|
if ((idx & 0x04) == 0) off += SIDE_ZP; // South connected
|
||||||
|
if ((idx & 0x01) == 0) off += SIDE_XN; // West connected
|
||||||
|
}
|
||||||
|
return meshes[off];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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, BitSet 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,64 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class HeadRenderer extends CustomRenderer {
|
||||||
|
private static final int NUM_DIRECTIONS = 16;
|
||||||
|
|
||||||
|
private RenderPatch basemesh[];
|
||||||
|
private RenderPatch meshes[][] = new RenderPatch[NUM_DIRECTIONS][];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initializeRenderer(RenderPatchFactory rpf, String blkname, BitSet blockdatamask, Map<String,String> custparm) {
|
||||||
|
if(!super.initializeRenderer(rpf, blkname, blockdatamask, custparm))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ArrayList<RenderPatch> list = new ArrayList<RenderPatch>();
|
||||||
|
list.add(rpf.getPatch(0.75, 0.0, 0.25, 0.25, 0.0, 0.25, 0.75, 0.0, 0.75, 0, 1, 0, 1, SideVisible.TOP, 0));
|
||||||
|
list.add(rpf.getPatch(0.75, 0.5, 0.25, 0.25, 0.5, 0.25, 0.75, 0.5, 0.75, 0, 1, 0, 1, SideVisible.TOP, 0));
|
||||||
|
RenderPatch side = rpf.getPatch(0.75, 0.0, 0.25, 0.25, 0.0, 0.25, 0.75, 0.5, 0.25, 0, 1, 0, 1, SideVisible.TOP, 0);
|
||||||
|
RenderPatch side2 = rpf.getRotatedPatch(side, 0, 90, 0, 0);
|
||||||
|
RenderPatch side3 = rpf.getRotatedPatch(side, 0, 180, 0, 0);
|
||||||
|
RenderPatch side4 = rpf.getRotatedPatch(side, 0, 270, 0, 0);
|
||||||
|
list.add(side4);
|
||||||
|
list.add(side);
|
||||||
|
list.add(side2);
|
||||||
|
list.add(side3);
|
||||||
|
basemesh = list.toArray(new RenderPatch[list.size()]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaximumTextureCount() {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int faces[] = { 0, 1, 2, 3, 4, 5 };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderPatch[] getRenderPatchList(MapDataContext ctx) {
|
||||||
|
int idx = ctx.getBlockType().stateIndex;
|
||||||
|
while(idx < 0) idx += NUM_DIRECTIONS; // Normalize (bad values from some mods)
|
||||||
|
if(idx < meshes.length) {
|
||||||
|
if(meshes[idx] == null) {
|
||||||
|
RenderPatchFactory rpf = ctx.getPatchFactory();
|
||||||
|
RenderPatch[] rp = new RenderPatch[basemesh.length];
|
||||||
|
for(int i = 0; i < rp.length; i++) {
|
||||||
|
rp[i] = rpf.getRotatedPatch(basemesh[i], 0, 45*idx/2, 0, faces[i]);
|
||||||
|
}
|
||||||
|
meshes[idx] = rp;
|
||||||
|
}
|
||||||
|
return meshes[idx];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return meshes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,379 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,109 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,68 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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,402 @@
|
||||||
|
package org.dynmap.hdmap.renderer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.BitSet;
|
||||||
|
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, BitSet 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue