package com.boydti.fawe;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Commands;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.visualization.VisualQueue;
import com.boydti.fawe.regions.general.plot.PlotSquaredFeature;
import com.boydti.fawe.util.*;
import com.boydti.fawe.util.chat.ChatManager;
import com.boydti.fawe.util.chat.PlainChatManager;
import com.boydti.fawe.util.cui.CUI;
import com.boydti.fawe.util.metrics.BStats;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.factory.DefaultTransformParser;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.session.request.Request;
import javax.annotation.Nullable;
import javax.management.InstanceAlreadyExistsException;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* [ WorldEdit action]
* |
* \|/
* [ EditSession ] - The change is processed (area restrictions, change limit, block type)
* |
* \|/
* [Block change] - A block change from some location
* |
* \|/
* [ Set Queue ] - The SetQueue manages the implementation specific queue
* |
* \|/
* [ Fawe Queue] - A queue of chunks - check if the queue has the chunk for a change
* |
* \|/
* [ Fawe Chunk Implementation ] - Otherwise create a new FaweChunk object which is a wrapper around the Chunk object
* |
* \|/
* [ Execution ] - When done, the queue then sets the blocks for the chunk, performs lighting updates and sends the chunk packet to the clients
*
* Why it's faster:
* - The chunk is modified directly rather than through the API
* \ Removes some overhead, and means some processing can be done async
* - Lighting updates are performed on the chunk level rather than for every block
* \ e.g. A blob of stone: only the visible blocks need to have the lighting calculated
* - Block changes are sent with a chunk packet
* \ A chunk packet is generally quicker to create and smaller for large world edits
* - No physics updates
* \ Physics updates are slow, and are usually performed on each block
* - Block data shortcuts
* \ Some known blocks don't need to have the data set or accessed (e.g. air is never going to have data)
* - Remove redundant extents
* \ Up to 11 layers of extents can be removed
* - History bypassing
* \ FastMode bypasses history and means blocks in the world don't need to be checked and recorded
*/
public class Fawe {
/**
* The FAWE instance;
*/
private static Fawe INSTANCE;
/**
* TPS timer
*/
private final FaweTimer timer;
private FaweVersion version;
private VisualQueue visualQueue;
private Updater updater;
private TextureUtil textures;
private DefaultTransformParser transformParser;
private ChatManager chatManager = new PlainChatManager();
private BStats stats;
/**
* Get the implementation specific class
*
* @return
*/
@SuppressWarnings("unchecked")
public static T imp() {
return INSTANCE != null ? (T) INSTANCE.IMP : null;
}
/**
* Get the implementation independent class
*
* @return
*/
public static Fawe get() {
return INSTANCE;
}
/**
* Setup Fawe
*
* @param implementation
* @throws InstanceAlreadyExistsException
*/
public static void set(final IFawe implementation) throws InstanceAlreadyExistsException, IllegalArgumentException {
if (INSTANCE != null) {
throw new InstanceAlreadyExistsException("FAWE has already been initialized with: " + INSTANCE.IMP);
}
if (implementation == null) {
throw new IllegalArgumentException("Implementation may not be null.");
}
INSTANCE = new Fawe(implementation);
}
public static void debugPlain(String s) {
if (INSTANCE != null) {
INSTANCE.IMP.debug(s);
} else {
System.out.println(BBC.stripColor(BBC.color(s)));
}
}
/**
* Write something to the console
*
* @param s
*/
public static void debug(Object s) {
Actor actor = Request.request().getActor();
if (actor != null && actor.isPlayer()) {
actor.print(BBC.color(BBC.PREFIX.original() + " " + s));
return;
}
debugPlain(BBC.PREFIX.original() + " " + s);
}
/**
* The platform specific implementation
*/
private final IFawe IMP;
private Thread thread = Thread.currentThread();
private Fawe(final IFawe implementation) {
this.INSTANCE = this;
this.IMP = implementation;
this.thread = Thread.currentThread();
/*
* Implementation dependent stuff
*/
this.setupConfigs();
TaskManager.IMP = this.IMP.getTaskManager();
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.IMP.PATHS.HISTORY), TimeUnit.DAYS.toMillis(Settings.IMP.HISTORY.DELETE_AFTER_DAYS), false);
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.IMP.PATHS.CLIPBOARD), TimeUnit.DAYS.toMillis(Settings.IMP.CLIPBOARD.DELETE_AFTER_DAYS), false);
}
});
if (Settings.IMP.METRICS) {
try {
this.stats = new BStats();
this.IMP.startMetrics();
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
stats.start();
}
}, 1);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
/*
* Instance independent stuff
*/
this.setupMemoryListener();
this.timer = new FaweTimer();
Fawe.this.IMP.setupVault();
File jar = MainUtil.getJarFile();
// TODO FIXME remove extrablocks.json
// File extraBlocks = MainUtil.copyFile(jar, "extrablocks.json", null);
// if (extraBlocks != null && extraBlocks.exists()) {
// TaskManager.IMP.task(() -> {
// try {
// BundledBlockData.getInstance().loadFromResource();
// BundledBlockData.getInstance().add(extraBlocks.toURI().toURL(), true);
// } catch (Throwable ignore) {
// ignore.printStackTrace();
// Fawe.debug("Invalid format: extrablocks.json");
// }
// });
// }
// Delayed worldedit setup
TaskManager.IMP.later(() -> {
try {
transformParser = new DefaultTransformParser(getWorldEdit());
visualQueue = new VisualQueue(3);
WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers());
WEManager.IMP.managers.add(new PlotSquaredFeature());
Fawe.debug("Plugin 'PlotSquared' found. Using it now.");
} catch (Throwable e) {}
}, 0);
TaskManager.IMP.repeat(timer, 1);
if (!Settings.IMP.UPDATE.equalsIgnoreCase("false")) {
// Delayed updating
updater = new Updater();
TaskManager.IMP.async(() -> update());
TaskManager.IMP.repeatAsync(() -> update(), 36000);
}
}
public void onDisable() {
if (stats != null) {
stats.close();
}
}
private boolean update() {
if (updater != null) {
updater.getUpdate(IMP.getPlatform(), getVersion());
return true;
}
return false;
}
public CUI getCUI(Actor actor) {
FawePlayer