MCPE port is now async
This commit is contained in:
parent
b3787d4dbe
commit
9e297dc34a
@ -68,7 +68,6 @@ public class FaweBukkit implements IFawe, Listener {
|
||||
debug(" - This is only a recommendation");
|
||||
debug("==============================");
|
||||
}
|
||||
|
||||
} catch (final Throwable e) {
|
||||
MainUtil.handleError(e);
|
||||
Bukkit.getServer().shutdown();
|
||||
|
@ -41,10 +41,10 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
|
||||
*/
|
||||
public CharFaweChunk(FaweQueue parent, int x, int z) {
|
||||
super(parent, x, z);
|
||||
this.ids = new char[16][];
|
||||
this.count = new short[16];
|
||||
this.air = new short[16];
|
||||
this.relight = new short[16];
|
||||
this.ids = new char[HEIGHT >> 4][];
|
||||
this.count = new short[HEIGHT >> 4];
|
||||
this.air = new short[HEIGHT >> 4];
|
||||
this.relight = new short[HEIGHT >> 4];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,7 +91,7 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
|
||||
|
||||
public int getTotalCount() {
|
||||
int total = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < count.length; i++) {
|
||||
total += Math.min(4096, this.count[i]);
|
||||
}
|
||||
return total;
|
||||
@ -104,7 +104,7 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
|
||||
return Short.MAX_VALUE;
|
||||
}
|
||||
int total = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < relight.length; i++) {
|
||||
total += this.relight[i];
|
||||
}
|
||||
return total;
|
||||
|
@ -150,7 +150,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
|
||||
|
||||
@Override
|
||||
public void setTile(int x, int y, int z, CompoundTag tag) {
|
||||
if ((y > 255) || (y < 0)) {
|
||||
if ((y >= FaweChunk.HEIGHT) || (y < 0)) {
|
||||
return;
|
||||
}
|
||||
int cx = x >> 4;
|
||||
@ -161,7 +161,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
|
||||
|
||||
@Override
|
||||
public void setEntity(int x, int y, int z, CompoundTag tag) {
|
||||
if ((y > 255) || (y < 0)) {
|
||||
if ((y >= FaweChunk.HEIGHT) || (y < 0)) {
|
||||
return;
|
||||
}
|
||||
int cx = x >> 4;
|
||||
@ -172,7 +172,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
|
||||
|
||||
@Override
|
||||
public void removeEntity(int x, int y, int z, UUID uuid) {
|
||||
if ((y > 255) || (y < 0)) {
|
||||
if ((y >= FaweChunk.HEIGHT) || (y < 0)) {
|
||||
return;
|
||||
}
|
||||
int cx = x >> 4;
|
||||
|
@ -59,7 +59,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
|
||||
boolean relight = false;
|
||||
boolean[] fix = new boolean[16];
|
||||
boolean sky = hasSky();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < chunk.ids.length; i++) {
|
||||
if ((sky && ((chunk.getAir(i) & 4095) != 0 || (chunk.getCount(i) & 4095) != 0)) || chunk.getRelight(i) != 0) {
|
||||
relight = true;
|
||||
fix[i] = true;
|
||||
|
@ -14,6 +14,7 @@ public abstract class FaweChunk<T> {
|
||||
|
||||
private FaweQueue parent;
|
||||
private int x,z;
|
||||
public static int HEIGHT = 256;
|
||||
|
||||
private final ArrayDeque<Runnable> tasks = new ArrayDeque<Runnable>();
|
||||
|
||||
@ -123,7 +124,7 @@ public abstract class FaweChunk<T> {
|
||||
|
||||
public char[][] getCombinedIdArrays() {
|
||||
char[][] ids = new char[16][];
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int y = 0; y < HEIGHT >> 4; y++) {
|
||||
int y4 = y >> 4;
|
||||
short[][] i1 = FaweCache.CACHE_J[y];
|
||||
for (int z = 0; z < 16; z++) {
|
||||
@ -151,7 +152,7 @@ public abstract class FaweChunk<T> {
|
||||
* @param data
|
||||
*/
|
||||
public void fill(int id, byte data) {
|
||||
fillCuboid(0, 15, 0, 255, 0, 15, id, data);
|
||||
fillCuboid(0, 15, 0, HEIGHT - 1, 0, 15, id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,10 +27,12 @@ import java.util.Map;
|
||||
public class FastWorldEditExtent extends AbstractDelegateExtent {
|
||||
|
||||
private final FaweQueue queue;
|
||||
private final int maxY;
|
||||
|
||||
public FastWorldEditExtent(final World world, FaweQueue queue) {
|
||||
super(world);
|
||||
this.queue = queue;
|
||||
this.maxY = world.getMaxY();
|
||||
}
|
||||
|
||||
public FaweQueue getQueue() {
|
||||
@ -116,266 +118,8 @@ public class FastWorldEditExtent extends AbstractDelegateExtent {
|
||||
|
||||
@Override
|
||||
public boolean setBlock(int x, int y, int z, final BaseBlock block) throws WorldEditException {
|
||||
switch (y) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
case 40:
|
||||
case 41:
|
||||
case 42:
|
||||
case 43:
|
||||
case 44:
|
||||
case 45:
|
||||
case 46:
|
||||
case 47:
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57:
|
||||
case 58:
|
||||
case 59:
|
||||
case 60:
|
||||
case 61:
|
||||
case 62:
|
||||
case 63:
|
||||
case 64:
|
||||
case 65:
|
||||
case 66:
|
||||
case 67:
|
||||
case 68:
|
||||
case 69:
|
||||
case 70:
|
||||
case 71:
|
||||
case 72:
|
||||
case 73:
|
||||
case 74:
|
||||
case 75:
|
||||
case 76:
|
||||
case 77:
|
||||
case 78:
|
||||
case 79:
|
||||
case 80:
|
||||
case 81:
|
||||
case 82:
|
||||
case 83:
|
||||
case 84:
|
||||
case 85:
|
||||
case 86:
|
||||
case 87:
|
||||
case 88:
|
||||
case 89:
|
||||
case 90:
|
||||
case 91:
|
||||
case 92:
|
||||
case 93:
|
||||
case 94:
|
||||
case 95:
|
||||
case 96:
|
||||
case 97:
|
||||
case 98:
|
||||
case 99:
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
case 103:
|
||||
case 104:
|
||||
case 105:
|
||||
case 106:
|
||||
case 107:
|
||||
case 108:
|
||||
case 109:
|
||||
case 110:
|
||||
case 111:
|
||||
case 112:
|
||||
case 113:
|
||||
case 114:
|
||||
case 115:
|
||||
case 116:
|
||||
case 117:
|
||||
case 118:
|
||||
case 119:
|
||||
case 120:
|
||||
case 121:
|
||||
case 122:
|
||||
case 123:
|
||||
case 124:
|
||||
case 125:
|
||||
case 126:
|
||||
case 127:
|
||||
case 128:
|
||||
case 129:
|
||||
case 130:
|
||||
case 131:
|
||||
case 132:
|
||||
case 133:
|
||||
case 134:
|
||||
case 135:
|
||||
case 136:
|
||||
case 137:
|
||||
case 138:
|
||||
case 139:
|
||||
case 140:
|
||||
case 141:
|
||||
case 142:
|
||||
case 143:
|
||||
case 144:
|
||||
case 145:
|
||||
case 146:
|
||||
case 147:
|
||||
case 148:
|
||||
case 149:
|
||||
case 150:
|
||||
case 151:
|
||||
case 152:
|
||||
case 153:
|
||||
case 154:
|
||||
case 155:
|
||||
case 156:
|
||||
case 157:
|
||||
case 158:
|
||||
case 159:
|
||||
case 160:
|
||||
case 161:
|
||||
case 162:
|
||||
case 163:
|
||||
case 164:
|
||||
case 165:
|
||||
case 166:
|
||||
case 167:
|
||||
case 168:
|
||||
case 169:
|
||||
case 170:
|
||||
case 171:
|
||||
case 172:
|
||||
case 173:
|
||||
case 174:
|
||||
case 175:
|
||||
case 176:
|
||||
case 177:
|
||||
case 178:
|
||||
case 179:
|
||||
case 180:
|
||||
case 181:
|
||||
case 182:
|
||||
case 183:
|
||||
case 184:
|
||||
case 185:
|
||||
case 186:
|
||||
case 187:
|
||||
case 188:
|
||||
case 189:
|
||||
case 190:
|
||||
case 191:
|
||||
case 192:
|
||||
case 193:
|
||||
case 194:
|
||||
case 195:
|
||||
case 196:
|
||||
case 197:
|
||||
case 198:
|
||||
case 199:
|
||||
case 200:
|
||||
case 201:
|
||||
case 202:
|
||||
case 203:
|
||||
case 204:
|
||||
case 205:
|
||||
case 206:
|
||||
case 207:
|
||||
case 208:
|
||||
case 209:
|
||||
case 210:
|
||||
case 211:
|
||||
case 212:
|
||||
case 213:
|
||||
case 214:
|
||||
case 215:
|
||||
case 216:
|
||||
case 217:
|
||||
case 218:
|
||||
case 219:
|
||||
case 220:
|
||||
case 221:
|
||||
case 222:
|
||||
case 223:
|
||||
case 224:
|
||||
case 225:
|
||||
case 226:
|
||||
case 227:
|
||||
case 228:
|
||||
case 229:
|
||||
case 230:
|
||||
case 231:
|
||||
case 232:
|
||||
case 233:
|
||||
case 234:
|
||||
case 235:
|
||||
case 236:
|
||||
case 237:
|
||||
case 238:
|
||||
case 239:
|
||||
case 240:
|
||||
case 241:
|
||||
case 242:
|
||||
case 243:
|
||||
case 244:
|
||||
case 245:
|
||||
case 246:
|
||||
case 247:
|
||||
case 248:
|
||||
case 249:
|
||||
case 250:
|
||||
case 251:
|
||||
case 252:
|
||||
case 253:
|
||||
case 254:
|
||||
case 255:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
if (y >= maxY) {
|
||||
return false;
|
||||
}
|
||||
final short id = (short) block.getId();
|
||||
switch (id) {
|
||||
|
@ -26,6 +26,7 @@ shadowJar {
|
||||
include(dependency('com.google.code.gson:gson:2.2.4'))
|
||||
include(dependency('org.yaml:snakeyaml:1.16'))
|
||||
include(dependency('com.google.guava:guava:17.0'))
|
||||
include(dependency(':core'))
|
||||
}
|
||||
archiveName = "${parent.name}-${project.name}-${parent.version}.jar"
|
||||
destinationDir = file '../target'
|
||||
|
@ -1,84 +0,0 @@
|
||||
package com.boydti.fawe.nukkit;
|
||||
|
||||
import com.boydti.fawe.IFawe;
|
||||
import com.boydti.fawe.object.FaweCommand;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FaweNukkit implements IFawe {
|
||||
@Override
|
||||
public void debug(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDirectory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupCommand(String label, FaweCommand cmd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FawePlayer wrap(Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupVault() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskManager getTaskManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweQueue getNewQueue(String world, boolean fast) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldName(World world) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<FaweMaskManager> getMaskManagers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMetrics() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatform() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBlocksHubApi() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import java.util.Map.Entry;
|
||||
/**
|
||||
* Converts between Jcn.nukkit.nbt.tag. and Minecraft cn.nukkit.nbt.tag. classes.
|
||||
*/
|
||||
final class NBTConverter {
|
||||
public final class NBTConverter {
|
||||
|
||||
private NBTConverter() {
|
||||
}
|
||||
|
@ -39,14 +39,14 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class NukkitPlatform extends AbstractPlatform implements MultiUserPlatform {
|
||||
public class NukkitPlatform extends AbstractPlatform implements MultiUserPlatform {
|
||||
|
||||
private final NukkitWorldEdit mod;
|
||||
private final NukkitTaskManager taskManager;
|
||||
private boolean hookingEvents = false;
|
||||
private NukkitCommandManager commandManager;
|
||||
|
||||
NukkitPlatform(NukkitWorldEdit mod) {
|
||||
public NukkitPlatform(NukkitWorldEdit mod) {
|
||||
this.mod = mod;
|
||||
this.commandManager = new NukkitCommandManager(mod.getServer().getCommandMap());
|
||||
this.taskManager = new NukkitTaskManager(mod);
|
||||
@ -93,6 +93,10 @@ class NukkitPlatform extends AbstractPlatform implements MultiUserPlatform {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public NukkitTaskManager getTaskManager() {
|
||||
return taskManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Player matchPlayer(Player player) {
|
||||
|
@ -2,10 +2,11 @@ package com.boydti.fawe.nukkit.core;
|
||||
|
||||
import cn.nukkit.plugin.Plugin;
|
||||
import cn.nukkit.scheduler.TaskHandler;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NukkitTaskManager {
|
||||
public class NukkitTaskManager extends TaskManager{
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
@ -13,19 +14,22 @@ public class NukkitTaskManager {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int repeat(final Runnable r, final int interval) {
|
||||
TaskHandler task = this.plugin.getServer().getScheduler().scheduleRepeatingTask(r, interval, false);
|
||||
return task.getTaskId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int repeatAsync(final Runnable r, final int interval) {
|
||||
TaskHandler task = this.plugin.getServer().getScheduler().scheduleRepeatingTask(r, interval, true);
|
||||
return task.getTaskId();
|
||||
}
|
||||
|
||||
public AtomicInteger index = new AtomicInteger(0);
|
||||
public HashMap<Integer, Integer> tasks = new HashMap<>();
|
||||
private AtomicInteger index = new AtomicInteger(0);
|
||||
private HashMap<Integer, Integer> tasks = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void async(final Runnable r) {
|
||||
if (r == null) {
|
||||
return;
|
||||
@ -33,6 +37,7 @@ public class NukkitTaskManager {
|
||||
this.plugin.getServer().getScheduler().scheduleTask(r, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void task(final Runnable r) {
|
||||
if (r == null) {
|
||||
return;
|
||||
@ -40,6 +45,7 @@ public class NukkitTaskManager {
|
||||
this.plugin.getServer().getScheduler().scheduleTask(r, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void later(final Runnable r, final int delay) {
|
||||
if (r == null) {
|
||||
return;
|
||||
@ -47,10 +53,12 @@ public class NukkitTaskManager {
|
||||
this.plugin.getServer().getScheduler().scheduleDelayedTask(r, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void laterAsync(final Runnable r, final int delay) {
|
||||
this.plugin.getServer().getScheduler().scheduleDelayedTask(r, delay, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(final int task) {
|
||||
if (task != -1) {
|
||||
this.plugin.getServer().getScheduler().cancelTask(task);
|
||||
|
@ -19,6 +19,7 @@ import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.world.biome.BaseBiome;
|
||||
import com.sk89q.worldedit.world.registry.LegacyWorldData;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
@ -246,7 +247,7 @@ public class NukkitWorld extends LocalWorld {
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return 255;
|
||||
return 127;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -261,7 +262,7 @@ public class NukkitWorld extends LocalWorld {
|
||||
|
||||
@Override
|
||||
public WorldData getWorldData() {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
return LegacyWorldData.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,6 +24,8 @@ import cn.nukkit.Player;
|
||||
import cn.nukkit.command.Command;
|
||||
import cn.nukkit.command.CommandSender;
|
||||
import cn.nukkit.plugin.PluginBase;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.nukkit.optimization.FaweNukkit;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.sk89q.util.yaml.YAMLProcessor;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
@ -31,7 +33,6 @@ import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.event.platform.CommandEvent;
|
||||
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Logger;
|
||||
@ -65,9 +66,8 @@ public class NukkitWorldEdit extends PluginBase {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
// TODO load FAWE
|
||||
logger = Logger.getLogger(NukkitWorldEdit.class.getCanonicalName());
|
||||
File file = new File(getDataFolder(), "config.yml");
|
||||
File file = new File(getDataFolder(), "config-basic.yml");
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
@ -77,6 +77,7 @@ public class NukkitWorldEdit extends PluginBase {
|
||||
this.platform = new NukkitPlatform(this);
|
||||
getServer().getPluginManager().registerEvents(new WorldEditListener(this), this);
|
||||
WorldEdit.getInstance().getPlatformManager().register(platform);
|
||||
Fawe.set(new FaweNukkit(this));
|
||||
logger.info("WorldEdit for Nukkit (version " + getInternalVersion() + ") is loaded");
|
||||
WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent());
|
||||
} catch (Throwable e) {
|
||||
@ -151,7 +152,7 @@ public class NukkitWorldEdit extends PluginBase {
|
||||
*
|
||||
* @return the WorldEdit platform
|
||||
*/
|
||||
public Platform getPlatform() {
|
||||
public NukkitPlatform getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,121 @@
|
||||
package com.boydti.fawe.nukkit.optimization;
|
||||
|
||||
import cn.nukkit.Player;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.IFawe;
|
||||
import com.boydti.fawe.nukkit.core.NukkitWorldEdit;
|
||||
import com.boydti.fawe.nukkit.optimization.queue.NukkitQueue;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.FaweCommand;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.regions.FaweMaskManager;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class FaweNukkit implements IFawe {
|
||||
|
||||
private final NukkitWorldEdit plugin;
|
||||
|
||||
public FaweNukkit(NukkitWorldEdit mod) {
|
||||
this.plugin = mod;
|
||||
FaweChunk.HEIGHT = 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String s) {
|
||||
plugin.getWELogger().log(Level.INFO, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDirectory() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupCommand(String label, final FaweCommand cmd) {
|
||||
plugin.getServer().getCommandMap().register(label, new NukkitCommand(cmd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FawePlayer wrap(Object obj) {
|
||||
if (obj.getClass() == String.class) {
|
||||
String name = (String) obj;
|
||||
FawePlayer existing = Fawe.get().getCachedPlayer(name);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
return new FaweNukkitPlayer(getPlugin().getServer().getPlayer(name));
|
||||
} else if (obj instanceof Player) {
|
||||
Player player = (Player) obj;
|
||||
FawePlayer existing = Fawe.get().getCachedPlayer(player.getName());
|
||||
return existing != null ? existing : new FaweNukkitPlayer(player);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public NukkitWorldEdit getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupVault() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskManager getTaskManager() {
|
||||
return plugin.getPlatform().getTaskManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweQueue getNewQueue(String world, boolean fast) {
|
||||
return new NukkitQueue(this, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldName(World world) {
|
||||
return world.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<FaweMaskManager> getMaskManagers() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMetrics() {
|
||||
Metrics metrics = new Metrics(plugin);
|
||||
metrics.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatform() {
|
||||
return "Nukkit-" + plugin.getServer().getNukkitVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID(String name) {
|
||||
try {
|
||||
return UUID.fromString(name);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(UUID uuid) {
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBlocksHubApi() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.boydti.fawe.nukkit.optimization;
|
||||
|
||||
import cn.nukkit.Player;
|
||||
import cn.nukkit.command.ConsoleCommandSender;
|
||||
import cn.nukkit.level.Location;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.nukkit.core.NukkitPlatform;
|
||||
import com.boydti.fawe.nukkit.core.NukkitPlayer;
|
||||
import com.boydti.fawe.object.FaweLocation;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FaweNukkitPlayer extends FawePlayer<Player> {
|
||||
|
||||
private static ConsoleCommandSender console;
|
||||
|
||||
public FaweNukkitPlayer(final Player parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.parent.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return this.parent.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String perm) {
|
||||
return this.parent.hasPermission(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermission(final String perm, final boolean flag) {
|
||||
this.parent.addAttachment(Fawe.<FaweNukkit> imp().getPlugin()).setPermission("fawe.bypass", flag);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resetTitle() {
|
||||
sendTitle("","");
|
||||
}
|
||||
|
||||
public void sendTitle(String title, String sub) {
|
||||
throw new UnsupportedOperationException("Titles are not implemented in MCPE!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
this.parent.sendMessage(BBC.color(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(final String cmd) {
|
||||
Fawe.<FaweNukkit> imp().getPlugin().getServer().dispatchCommand(parent, cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweLocation getLocation() {
|
||||
final Location loc = this.parent.getLocation();
|
||||
return new FaweLocation(loc.getLevel().getName(), loc.getFloorX(), loc.getFloorY(), loc.getFloorZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.sk89q.worldedit.entity.Player getPlayer() {
|
||||
return new NukkitPlayer((NukkitPlatform) Fawe.<FaweNukkit> imp().getPlugin().getPlatform(), parent);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,580 @@
|
||||
package com.boydti.fawe.nukkit.optimization;
|
||||
|
||||
import cn.nukkit.plugin.Plugin;
|
||||
import cn.nukkit.plugin.PluginDescription;
|
||||
import cn.nukkit.utils.LogLevel;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
/**
|
||||
* The current revision number.
|
||||
*/
|
||||
private static final int REVISION = 7;
|
||||
/**
|
||||
* The base url of the metrics domain.
|
||||
*/
|
||||
private static final String BASE_URL = "http://report.mcstats.org";
|
||||
/**
|
||||
* The url used to report a server's status.
|
||||
*/
|
||||
private static final String REPORT_URL = "/plugin/%s";
|
||||
/**
|
||||
* Interval of time to ping (in minutes).
|
||||
*/
|
||||
private static final int PING_INTERVAL = 15;
|
||||
/**
|
||||
* The plugin this metrics submits for.
|
||||
*/
|
||||
private final Plugin plugin;
|
||||
/**
|
||||
* All of the custom graphs to submit to metrics.
|
||||
*/
|
||||
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
|
||||
/**
|
||||
* Unique server id.
|
||||
*/
|
||||
private final String guid;
|
||||
/**
|
||||
* Debug mode.
|
||||
*/
|
||||
private final boolean debug;
|
||||
/**
|
||||
* The scheduled task.
|
||||
*/
|
||||
private volatile int taskId = -1;
|
||||
|
||||
public Metrics(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
this.guid = UUID.randomUUID().toString();
|
||||
this.debug = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* GZip compress a string of bytes.
|
||||
*
|
||||
* @param input
|
||||
*
|
||||
* @return byte[] the file as a byte array
|
||||
*/
|
||||
public static byte[] gzip(String input) {
|
||||
FastByteArrayOutputStream baos = new FastByteArrayOutputStream();
|
||||
GZIPOutputStream gzos = null;
|
||||
try {
|
||||
gzos = new GZIPOutputStream(baos);
|
||||
gzos.write(input.getBytes("UTF-8"));
|
||||
} catch (IOException e) {
|
||||
MainUtil.handleError(e);
|
||||
} finally {
|
||||
if (gzos != null) {
|
||||
try {
|
||||
gzos.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a json encoded key/value pair to the given string builder.
|
||||
*
|
||||
* @param json
|
||||
* @param key
|
||||
* @param value
|
||||
*
|
||||
*/
|
||||
private static void appendJSONPair(StringBuilder json, String key, String value) {
|
||||
boolean isValueNumeric = false;
|
||||
try {
|
||||
if (value.equals("0") || !value.endsWith("0")) {
|
||||
Double.parseDouble(value);
|
||||
isValueNumeric = true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
isValueNumeric = false;
|
||||
}
|
||||
if (json.charAt(json.length() - 1) != '{') {
|
||||
json.append(',');
|
||||
}
|
||||
json.append(escapeJSON(key));
|
||||
json.append(':');
|
||||
if (isValueNumeric) {
|
||||
json.append(value);
|
||||
} else {
|
||||
json.append(escapeJSON(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a string to create a valid JSON string
|
||||
*
|
||||
* @param text
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private static String escapeJSON(String text) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append('"');
|
||||
for (int index = 0; index < text.length(); index++) {
|
||||
char chr = text.charAt(index);
|
||||
switch (chr) {
|
||||
case '"':
|
||||
case '\\':
|
||||
builder.append('\\');
|
||||
builder.append(chr);
|
||||
break;
|
||||
case '\b':
|
||||
builder.append("\\b");
|
||||
break;
|
||||
case '\t':
|
||||
builder.append("\\t");
|
||||
break;
|
||||
case '\n':
|
||||
builder.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
builder.append("\\r");
|
||||
break;
|
||||
default:
|
||||
if (chr < ' ') {
|
||||
String t = "000" + Integer.toHexString(chr);
|
||||
builder.append("\\u" + t.substring(t.length() - 4));
|
||||
} else {
|
||||
builder.append(chr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
builder.append('"');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode text as UTF-8
|
||||
*
|
||||
* @param text the text to encode
|
||||
*
|
||||
* @return the encoded text, as UTF-8
|
||||
*/
|
||||
private static String urlEncode(String text) throws UnsupportedEncodingException {
|
||||
return URLEncoder.encode(text, "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
|
||||
* website. Plotters can be added to the graph object returned.
|
||||
*
|
||||
* @param name The name of the graph
|
||||
*
|
||||
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
|
||||
*/
|
||||
public Graph createGraph(String name) {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Graph name cannot be null");
|
||||
}
|
||||
// Construct the graph object
|
||||
Graph graph = new Graph(name);
|
||||
// Now we can add our graph
|
||||
this.graphs.add(graph);
|
||||
// and return back
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
|
||||
*
|
||||
* @param graph The name of the graph
|
||||
*/
|
||||
public void addGraph(Graph graph) {
|
||||
if (graph == null) {
|
||||
throw new IllegalArgumentException("Graph cannot be null");
|
||||
}
|
||||
this.graphs.add(graph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
|
||||
* initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
|
||||
* ticks.
|
||||
*
|
||||
* @return True if statistics measuring is running, otherwise false.
|
||||
*/
|
||||
public boolean start() {
|
||||
// Is metrics already running?
|
||||
if (this.taskId != -1) {
|
||||
return true;
|
||||
}
|
||||
// Begin hitting the server with glorious data
|
||||
this.taskId = TaskManager.IMP.repeatAsync(new Runnable() {
|
||||
private boolean firstPost = true;
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
postPlugin(!this.firstPost);
|
||||
// After the first post we set firstPost to
|
||||
// false
|
||||
// Each post thereafter will be a ping
|
||||
this.firstPost = false;
|
||||
} catch (IOException e) {
|
||||
MainUtil.handleError(e);
|
||||
if (Metrics.this.debug) {
|
||||
plugin.getLogger().log(LogLevel.INFO, "[Metrics] " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, PING_INTERVAL * 1200);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void enable() {
|
||||
// Enable Task, if it is not running
|
||||
if (this.taskId == -1) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
|
||||
*
|
||||
*/
|
||||
public void disable() {
|
||||
// Disable Task, if it is running
|
||||
if (this.taskId != -1) {
|
||||
TaskManager.IMP.cancel(this.taskId);
|
||||
this.taskId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the File object of the config file that should be used to store
|
||||
* data such as the GUID and opt-out status.
|
||||
*
|
||||
* @return the File object for the config file
|
||||
*/
|
||||
public File getConfigFile() {
|
||||
// I believe the easiest way to get the base folder (e.g craftbukkit set
|
||||
// via -P) for plugins to use
|
||||
// is to abuse the plugin object we already have
|
||||
// plugin.getDataFolder() => base/plugins/PluginA/
|
||||
// pluginsFolder => base/plugins/
|
||||
// The base is not necessarily relative to the startup directory.
|
||||
File pluginsFolder = this.plugin.getDataFolder().getParentFile();
|
||||
// return => base/plugins/PluginMetrics/config.yml
|
||||
return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method that posts a plugin to the metrics website.
|
||||
*/
|
||||
private void postPlugin(boolean isPing) throws IOException {
|
||||
// Server software specific section
|
||||
PluginDescription description = this.plugin.getDescription();
|
||||
String pluginName = description.getName();
|
||||
boolean onlineMode = false;
|
||||
String pluginVersion = description.getVersion();
|
||||
String serverVersion = plugin.getServer().getNukkitVersion();
|
||||
int playersOnline = plugin.getServer().getOnlinePlayers().size();
|
||||
// END server software specific section -- all code below does not use
|
||||
// any code outside of this class / Java
|
||||
// Construct the post data
|
||||
StringBuilder json = new StringBuilder(1024);
|
||||
json.append('{');
|
||||
// The plugin's description file containing all of the plugin data such as name, version, author, etc
|
||||
appendJSONPair(json, "guid", this.guid);
|
||||
appendJSONPair(json, "plugin_version", pluginVersion);
|
||||
appendJSONPair(json, "server_version", serverVersion);
|
||||
appendJSONPair(json, "players_online", Integer.toString(playersOnline));
|
||||
// New data as of R6
|
||||
String osname = System.getProperty("os.name");
|
||||
String osarch = System.getProperty("os.arch");
|
||||
String osversion = System.getProperty("os.version");
|
||||
String java_version = System.getProperty("java.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
// normalize os arch .. amd64 -> x86_64
|
||||
if (osarch.equals("amd64")) {
|
||||
osarch = "x86_64";
|
||||
}
|
||||
appendJSONPair(json, "osname", osname);
|
||||
appendJSONPair(json, "osarch", osarch);
|
||||
appendJSONPair(json, "osversion", osversion);
|
||||
appendJSONPair(json, "cores", Integer.toString(coreCount));
|
||||
appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0");
|
||||
appendJSONPair(json, "java_version", java_version);
|
||||
// If we're pinging, append it
|
||||
if (isPing) {
|
||||
appendJSONPair(json, "ping", "1");
|
||||
}
|
||||
if (!this.graphs.isEmpty()) {
|
||||
synchronized (this.graphs) {
|
||||
json.append(',');
|
||||
json.append('"');
|
||||
json.append("graphs");
|
||||
json.append('"');
|
||||
json.append(':');
|
||||
json.append('{');
|
||||
boolean firstGraph = true;
|
||||
for (Graph graph : this.graphs) {
|
||||
StringBuilder graphJson = new StringBuilder();
|
||||
graphJson.append('{');
|
||||
for (Plotter plotter : graph.getPlotters()) {
|
||||
appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue()));
|
||||
}
|
||||
graphJson.append('}');
|
||||
if (!firstGraph) {
|
||||
json.append(',');
|
||||
}
|
||||
json.append(escapeJSON(graph.getName()));
|
||||
json.append(':');
|
||||
json.append(graphJson);
|
||||
firstGraph = false;
|
||||
}
|
||||
json.append('}');
|
||||
}
|
||||
}
|
||||
// close json
|
||||
json.append('}');
|
||||
// Create the url
|
||||
URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName)));
|
||||
// Connect to the website
|
||||
URLConnection connection;
|
||||
// Mineshafter creates a socks proxy, so we can safely bypass it
|
||||
// It does not reroute POST requests so we need to go around it
|
||||
if (isMineshafterPresent()) {
|
||||
connection = url.openConnection(Proxy.NO_PROXY);
|
||||
} else {
|
||||
connection = url.openConnection();
|
||||
}
|
||||
byte[] uncompressed = json.toString().getBytes();
|
||||
byte[] compressed = gzip(json.toString());
|
||||
// Headers
|
||||
connection.addRequestProperty("User-Agent", "MCStats/" + REVISION);
|
||||
connection.addRequestProperty("Content-Type", "application/json");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", Integer.toString(compressed.length));
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.setDoOutput(true);
|
||||
if (this.debug) {
|
||||
Fawe.debug("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length);
|
||||
}
|
||||
try {
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(compressed);
|
||||
os.flush();
|
||||
}
|
||||
String response;
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
response = reader.readLine();
|
||||
if (this.debug) {
|
||||
Fawe.debug("[Metrics] Response for " + pluginName + ": " + response);
|
||||
}
|
||||
}
|
||||
if (response == null || response.startsWith("ERR") || response.startsWith("7")) {
|
||||
if (response == null) {
|
||||
response = "null";
|
||||
} else if (response.startsWith("7")) {
|
||||
response = response.substring(response.startsWith("7,") ? 2 : 1);
|
||||
}
|
||||
throw new IOException(response);
|
||||
} else {
|
||||
// Is this the first update this hour?
|
||||
if ("1".equals(response) || response.contains("This is your first update this hour")) {
|
||||
synchronized (this.graphs) {
|
||||
for (Graph graph : this.graphs) {
|
||||
for (Plotter plotter : graph.getPlotters()) {
|
||||
plotter.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (this.debug) {
|
||||
MainUtil.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
|
||||
*
|
||||
* @return true if mineshafter is installed on the server
|
||||
*/
|
||||
private boolean isMineshafterPresent() {
|
||||
try {
|
||||
Class.forName("mineshafter.MineServer");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom graph on the website
|
||||
*/
|
||||
public static class Graph {
|
||||
|
||||
/**
|
||||
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
|
||||
* rejected
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* The set of plotters that are contained within this graph
|
||||
*/
|
||||
private final Set<Plotter> plotters = new LinkedHashSet<>();
|
||||
|
||||
private Graph(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the graph's name
|
||||
*
|
||||
* @return the Graph's name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plotter to the graph, which will be used to plot entries
|
||||
*
|
||||
* @param plotter the plotter to add to the graph
|
||||
*/
|
||||
public void addPlotter(Plotter plotter) {
|
||||
this.plotters.add(plotter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a plotter from the graph
|
||||
*
|
||||
* @param plotter the plotter to remove from the graph
|
||||
*/
|
||||
public void removePlotter(Plotter plotter) {
|
||||
this.plotters.remove(plotter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
|
||||
*
|
||||
* @return an unmodifiable {@link java.util.Set} of the plotter objects
|
||||
*/
|
||||
public Set<Plotter> getPlotters() {
|
||||
return Collections.unmodifiableSet(this.plotters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (!(object instanceof Graph)) {
|
||||
return false;
|
||||
}
|
||||
Graph graph = (Graph) object;
|
||||
return graph.name.equals(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
|
||||
*/
|
||||
protected void onOptOut() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used to collect custom data for a plugin
|
||||
*/
|
||||
public abstract static class Plotter {
|
||||
|
||||
/**
|
||||
* The plot's name
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Construct a plotter with the default plot name
|
||||
*/
|
||||
public Plotter() {
|
||||
this("Default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a plotter with a specific plot name
|
||||
*
|
||||
* @param name the name of the plotter to use, which will show up on the website
|
||||
*/
|
||||
public Plotter(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value for the plotted point. Since this function defers to an external function it may or may
|
||||
* not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
|
||||
* from any thread so care should be taken when accessing resources that need to be synchronized.
|
||||
*
|
||||
* @return the current value for the point to be plotted.
|
||||
*/
|
||||
public abstract int getValue();
|
||||
|
||||
/**
|
||||
* Get the column name for the plotted point
|
||||
*
|
||||
* @return the plotted point's column name
|
||||
*/
|
||||
public String getColumnName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the website graphs have been updated
|
||||
*/
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getColumnName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (!(object instanceof Plotter)) {
|
||||
return false;
|
||||
}
|
||||
Plotter plotter = (Plotter) object;
|
||||
return plotter.name.equals(this.name) && plotter.getValue() == getValue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.boydti.fawe.nukkit.optimization;
|
||||
|
||||
import cn.nukkit.command.Command;
|
||||
import cn.nukkit.command.CommandSender;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.object.FaweCommand;
|
||||
import com.boydti.fawe.object.FawePlayer;
|
||||
|
||||
public class NukkitCommand extends Command {
|
||||
|
||||
private final FaweCommand cmd;
|
||||
|
||||
public NukkitCommand(final FaweCommand cmd) {
|
||||
super(cmd.getPerm());
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String label, String[] args) {
|
||||
final FawePlayer plr = Fawe.imp().wrap(sender);
|
||||
if (!sender.hasPermission(this.cmd.getPerm()) && !sender.isOp()) {
|
||||
BBC.NO_PERM.send(plr, this.cmd.getPerm());
|
||||
return true;
|
||||
}
|
||||
this.cmd.executeSafe(plr, args);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
package com.boydti.fawe.nukkit.optimization.queue;
|
||||
|
||||
import cn.nukkit.blockentity.BlockEntity;
|
||||
import cn.nukkit.level.Level;
|
||||
import cn.nukkit.level.format.generic.BaseFullChunk;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.example.CharFaweChunk;
|
||||
import com.boydti.fawe.nukkit.core.NBTConverter;
|
||||
import com.boydti.fawe.nukkit.core.NukkitUtil;
|
||||
import com.boydti.fawe.object.FaweQueue;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.LocalWorld;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.world.biome.BaseBiome;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NukkitChunk extends CharFaweChunk<BaseFullChunk> {
|
||||
|
||||
|
||||
/**
|
||||
* A FaweSections object represents a chunk and the blocks that you wish to change in it.
|
||||
*
|
||||
* @param parent
|
||||
* @param x
|
||||
* @param z
|
||||
*/
|
||||
public NukkitChunk(FaweQueue parent, int x, int z) {
|
||||
super(parent, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFullChunk getNewChunk() {
|
||||
return ((NukkitQueue) getParent()).getWorld().getChunk(getX(), getZ());
|
||||
}
|
||||
|
||||
private int layer = -1;
|
||||
private int index;
|
||||
private boolean place = true;
|
||||
|
||||
public void execute(long start) {
|
||||
int recommended = 25 + NukkitQueue.ALLOCATE;
|
||||
boolean more = true;
|
||||
NukkitQueue parent = (NukkitQueue) getParent();
|
||||
Level world = ((NukkitQueue) getParent()).getWorld();
|
||||
world.clearCache(true);
|
||||
final BaseFullChunk chunk = (world.getChunk(getX(), getZ(), true));
|
||||
char[][] sections = getCombinedIdArrays();
|
||||
if (layer == -1) {
|
||||
// Biomes
|
||||
if (layer == 0) {
|
||||
final int[][] biomes = getBiomeArray();
|
||||
if (biomes != null) {
|
||||
final LocalWorld lw = NukkitUtil.getLocalWorld(world);
|
||||
final int X = getX() << 4;
|
||||
final int Z = getZ() << 4;
|
||||
final BaseBiome bb = new BaseBiome(0);
|
||||
int last = 0;
|
||||
for (int x = 0; x < 16; x++) {
|
||||
final int[] array = biomes[x];
|
||||
if (array == null) {
|
||||
continue;
|
||||
}
|
||||
for (int z = 0; z < 16; z++) {
|
||||
final int biome = array[z];
|
||||
if (biome == 0) {
|
||||
continue;
|
||||
}
|
||||
if (last != biome) {
|
||||
last = biome;
|
||||
bb.setId(biome);
|
||||
}
|
||||
lw.setBiome(new Vector2D(X + x, Z + z), bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (index != 0) {
|
||||
if (place) {
|
||||
layer--;
|
||||
} else {
|
||||
layer++;
|
||||
}
|
||||
}
|
||||
mainloop:
|
||||
do {
|
||||
if (place) {
|
||||
if (++layer >= sections.length) {
|
||||
place = false;
|
||||
layer = sections.length - 1;
|
||||
}
|
||||
} else if (--layer < 0) {
|
||||
more = false;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
// Efficiently merge sections
|
||||
int changes = getCount(layer);
|
||||
int lighting = getRelight(layer);
|
||||
if (changes == 0) {
|
||||
continue;
|
||||
}
|
||||
final char[] newArray = sections[layer];
|
||||
if (newArray == null) {
|
||||
continue;
|
||||
}
|
||||
final byte[] cacheX = FaweCache.CACHE_X[layer];
|
||||
final short[] cacheY = FaweCache.CACHE_Y[layer];
|
||||
final byte[] cacheZ = FaweCache.CACHE_Z[layer];
|
||||
boolean checkTime = !((getAir(layer) == 4096 || (getCount(layer) == 4096 && getAir(layer) == 0) || (getCount(layer) == getAir(layer))) && getRelight(layer) == 0);
|
||||
if (!checkTime) {
|
||||
ArrayList<Thread> threads = new ArrayList<Thread>();
|
||||
for (int k = 0; k < 16; k++) {
|
||||
final int l = k << 8;
|
||||
final int y = cacheY[l];
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int m = l; m < l + 256; m++) {
|
||||
char combined = newArray[m];
|
||||
switch (combined) {
|
||||
case 0:
|
||||
continue;
|
||||
case 1:
|
||||
if (!place) {
|
||||
int x = cacheX[m];
|
||||
int z = cacheZ[m];
|
||||
chunk.setBlockId(x, y, z, 0);
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
if (place) {
|
||||
int x = cacheX[m];
|
||||
int z = cacheZ[m];
|
||||
int id = combined >> 4;
|
||||
chunk.setBlockId(x, y, z, id);
|
||||
chunk.setBlockData(x, y, z, (combined & 0xF));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
threads.add(thread);
|
||||
thread.start();
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
} else {
|
||||
for (;index < 4096; index++) {
|
||||
int j = place ? index : 4095 - index;
|
||||
char combined = newArray[j];
|
||||
switch (combined) {
|
||||
case 0:
|
||||
continue;
|
||||
case 1:
|
||||
if (!place) {
|
||||
int x = cacheX[j];
|
||||
int z = cacheZ[j];
|
||||
int y = cacheY[j];
|
||||
chunk.setBlockId(x, y, z, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
int id = combined >> 4;
|
||||
boolean light = FaweCache.hasLight(id);
|
||||
if (light) {
|
||||
if (place) {
|
||||
continue;
|
||||
}
|
||||
} else if (!place) {
|
||||
continue;
|
||||
}
|
||||
if (light != place) {
|
||||
int data = combined & 0xF;
|
||||
int x = cacheX[j];
|
||||
int z = cacheZ[j];
|
||||
int y = cacheY[j];
|
||||
chunk.setBlockId(x, y, z, id);
|
||||
chunk.setBlockData(x, y, z, data);
|
||||
if (FaweCache.hasNBT(id)) {
|
||||
CompoundTag tile = getTile(x, y, z);
|
||||
if (tile != null) {
|
||||
cn.nukkit.nbt.tag.CompoundTag tag = (cn.nukkit.nbt.tag.CompoundTag) NBTConverter.toNative(tile);
|
||||
chunk.addBlockEntity(new BlockEntity(chunk, tag) {
|
||||
@Override
|
||||
public boolean isBlockEntityValid() {
|
||||
return getBlock().getId() == id;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (checkTime && System.currentTimeMillis() - start > recommended) {
|
||||
index++;
|
||||
break mainloop;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
} catch (final Throwable e) {
|
||||
MainUtil.handleError(e);
|
||||
}
|
||||
} while (System.currentTimeMillis() - start < recommended);
|
||||
if (more || place) {
|
||||
this.addToQueue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
package com.boydti.fawe.nukkit.optimization.queue;
|
||||
|
||||
import cn.nukkit.Player;
|
||||
import cn.nukkit.blockentity.BlockEntity;
|
||||
import cn.nukkit.level.Level;
|
||||
import cn.nukkit.level.Position;
|
||||
import cn.nukkit.level.format.generic.BaseFullChunk;
|
||||
import cn.nukkit.math.Vector3;
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.example.CharFaweChunk;
|
||||
import com.boydti.fawe.example.NMSMappedFaweQueue;
|
||||
import com.boydti.fawe.nukkit.core.NBTConverter;
|
||||
import com.boydti.fawe.nukkit.optimization.FaweNukkit;
|
||||
import com.boydti.fawe.object.FaweChunk;
|
||||
import com.boydti.fawe.object.RunnableVal;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NukkitQueue extends NMSMappedFaweQueue<Level, BaseFullChunk, BaseFullChunk, BaseFullChunk> {
|
||||
private final FaweNukkit faweNukkit;
|
||||
private final Level world;
|
||||
|
||||
public static int ALLOCATE;
|
||||
public static double TPS_TARGET = 18.5;
|
||||
private static int LIGHT_MASK = 0x739C0;
|
||||
|
||||
public NukkitQueue(FaweNukkit fn, String world) {
|
||||
super(world);
|
||||
this.faweNukkit = fn;
|
||||
this.world = faweNukkit.getPlugin().getServer().getLevelByName(world);
|
||||
if (Settings.QUEUE.EXTRA_TIME_MS != Integer.MIN_VALUE) {
|
||||
ALLOCATE = Settings.QUEUE.EXTRA_TIME_MS;
|
||||
Settings.QUEUE.EXTRA_TIME_MS = Integer.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
public FaweNukkit getFaweNukkit() {
|
||||
return faweNukkit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(FaweChunk fc) {
|
||||
if (super.execute(fc)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshChunk(FaweChunk fs) {
|
||||
NukkitChunk fc = (NukkitChunk) fs;
|
||||
Collection<Player> players = faweNukkit.getPlugin().getServer().getOnlinePlayers().values();
|
||||
int view = faweNukkit.getPlugin().getServer().getViewDistance();
|
||||
for (Player player : players) {
|
||||
Position pos = player.getPosition();
|
||||
int pcx = pos.getFloorX() >> 4;
|
||||
int pcz = pos.getFloorZ() >> 4;
|
||||
if (Math.abs(pcx - fs.getX()) > view || Math.abs(pcz - fs.getZ()) > view) {
|
||||
continue;
|
||||
}
|
||||
world.requestChunk(fs.getX(), fs.getZ(), player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharFaweChunk getPrevious(CharFaweChunk fs, BaseFullChunk sections, Map<?, ?> tiles, Collection<?>[] entities, Set<UUID> createdEntities, boolean all) throws Exception {
|
||||
return fs;
|
||||
}
|
||||
|
||||
private int skip;
|
||||
|
||||
@Override
|
||||
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
|
||||
if (skip > 0) {
|
||||
skip--;
|
||||
fc.addToQueue();
|
||||
return true;
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
((NukkitChunk) fc).execute(start);
|
||||
if (System.currentTimeMillis() - start > 50 || Fawe.get().getTPS() < TPS_TARGET) {
|
||||
skip = 10;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSaveFolder() {
|
||||
return new File(world.getFolderName() + File.separator + "region");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSky() {
|
||||
return world.getDimension() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullbright(BaseFullChunk sections) {
|
||||
for (int y = 0; y < 128; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
sections.setBlockSkyLight(x, y, z, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLighting(BaseFullChunk sections, RelightMode mode, boolean hasSky) {
|
||||
for (int y = 0; y < 128; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
sections.setBlockSkyLight(x, y, z, 0);
|
||||
sections.setBlockLight(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector3 mutable = new Vector3();
|
||||
private Vector3 getMutable(int x, int y, int z) {
|
||||
mutable.x = x;
|
||||
mutable.y = y;
|
||||
mutable.z = z;
|
||||
return mutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relight(int x, int y, int z) {
|
||||
world.updateAllLight(getMutable(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relightBlock(int x, int y, int z) {
|
||||
world.updateBlockLight(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relightSky(int x, int y, int z) {
|
||||
world.updateBlockSkyLight(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLight(BaseFullChunk chunkSection, int x, int y, int z, int value) {
|
||||
chunkSection.setBlockSkyLight(x & 15, y & 15, z & 15, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockLight(BaseFullChunk chunkSection, int x, int y, int z, int value) {
|
||||
chunkSection.setBlockLight(x & 15, y & 15, z & 15, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getTileEntity(BaseFullChunk baseChunk, int x, int y, int z) {
|
||||
BlockEntity entity = baseChunk.getTile(x & 15, y, z & 15);
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
cn.nukkit.nbt.tag.CompoundTag nbt = entity.namedTag;
|
||||
return NBTConverter.fromNative(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFullChunk getChunk(Level level, int x, int z) {
|
||||
return (BaseFullChunk) level.getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getImpWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkLoaded(Level level, int x, int z) {
|
||||
return level.isChunkLoaded(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerateChunk(Level level, int x, int z) {
|
||||
level.regenerateChunk(x, z);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk getFaweChunk(int x, int z) {
|
||||
return new NukkitChunk(this, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadChunk(Level level, int x, int z, boolean generate) {
|
||||
return level.loadChunk(x, z, generate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFullChunk getCachedSections(Level level, int cx, int cz) {
|
||||
BaseFullChunk chunk = (BaseFullChunk) world.getChunk(cx, cz);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCombinedId4Data(BaseFullChunk chunkSection, int x, int y, int z) {
|
||||
int id = chunkSection.getBlockId(x & 15, y & 15, z & 15);
|
||||
if (FaweCache.hasData(id)) {
|
||||
int data = chunkSection.getBlockData(x & 15, y & 15, z & 15);
|
||||
return (id << 4) + data;
|
||||
} else {
|
||||
return (id << 4);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(BaseFullChunk sections, int x, int y, int z) {
|
||||
return sections.getBlockSkyLight(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmmittedLight(BaseFullChunk sections, int x, int y, int z) {
|
||||
return sections.getBlockLight(x & 15, y & 15, z & 15);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user