Skip to content

Instantly share code, notes, and snippets.

@Exerosis
Created February 20, 2018 18:52
Show Gist options
  • Save Exerosis/f482b02985bc7f006f1aa4dd7f9eac84 to your computer and use it in GitHub Desktop.
Save Exerosis/f482b02985bc7f006f1aa4dd7f9eac84 to your computer and use it in GitHub Desktop.
package me.exerosis.debug;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.nio.ByteBuffer.allocate;
import static java.nio.channels.DatagramChannel.open;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
public class DebugServer implements Closeable {
private final Map<Number, BiConsumer<ByteBuffer, SocketAddress>> handlers = new HashMap<>();
private final ExecutorService executor = newSingleThreadExecutor();
private final DatagramChannel channel;
private final ByteBuffer buffer;
public DebugServer(Number port, Number length) throws IOException {
this(port, length, ByteBuffer::get);
}
public DebugServer(Number port, Number length, Function<ByteBuffer, Number> opcodes) throws IOException {
buffer = allocate(length.intValue());
buffer.clear();
channel = open();
channel.configureBlocking(true);
channel.bind(new InetSocketAddress(port.intValue()));
executor.submit(() -> {
while (channel.isOpen()) {
SocketAddress address = channel.receive(buffer);
buffer.flip();
handlers.get(opcodes.apply(buffer)).accept(buffer, address);
buffer.clear();
}
return null;
});
}
public void send(SocketAddress address, Number opcode, Consumer<ByteBuffer> packet) {
try {
synchronized (buffer) {
buffer.clear();
buffer.put(opcode.byteValue());
packet.accept(buffer);
buffer.flip();
channel.send(buffer, address);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void receive(Number opcode, BiConsumer<ByteBuffer, SocketAddress> handler) {
handlers.put(opcode, handler);
}
public void respond(Number opcode, Function<ByteBuffer, Consumer<ByteBuffer>> handler) {
respond(opcode, (request, address) -> handler.apply(request));
}
public void respond(Number opcode, BiFunction<ByteBuffer, SocketAddress, Consumer<ByteBuffer>> handler) {
receive(opcode, (request, address) -> {
Consumer<ByteBuffer> response = handler.apply(request, address);
buffer.clear();
buffer.put(opcode.byteValue());
response.accept(request);
buffer.flip();
try {
channel.send(buffer, new InetSocketAddress("localhost", ((InetSocketAddress) address).getPort()));
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
public void close() throws IOException {
channel.close();
executor.shutdown();
}
}
package me.exerosis.debug;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.lang.String.format;
import static java.util.Arrays.fill;
import static me.exerosis.debug.Main.Error.*;
import static org.bukkit.Bukkit.getPluginManager;
public class Main extends JavaPlugin {
private static final byte OP_TOGGLE = 0;
private static final byte OP_LIST = 1;
private static final byte OP_CHECK = 2;
private static DebugServer debugServer;
public static void main(String[] args) {
onEnabled();
}
static public void onEnabled() {
try {
debugServer = new DebugServer(25566, 512);
} catch (IOException e) {
e.printStackTrace();
}
//--Toggle Plugins--
debugServer.respond(OP_TOGGLE, request -> {
byte count = request.get();
byte[] errors = new byte[count];
fill(errors, NONE.code);
for (int i = 0; i < count; i++) {
String name = getString(request);
long delay = request.getLong();
boolean unload = request.get() == 0;
try {
Plugin plugin = getPluginManager().getPlugin(name);
if (plugin == null)
errors[i] = NOT_FOUND.handle(name);
if (unload)
getPluginManager().enablePlugin(plugin);
else
getPluginManager().disablePlugin(plugin);
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
errors[i] = INTERRUPTED.handle(name);
}
} catch (Exception e) {
errors[i] = OTHER.handle(name);
}
}
return response -> response.put(errors);
});
//--List Plugins--
debugServer.respond(OP_LIST, request -> {
System.out.println("List incomming");
String filter = getString(request);
return response -> {
putString(response, "Test plugin1");
response.put((byte) 1);
putString(response, "Test plugin2");
response.put((byte) 0);
putString(response, "Test plugin3");
response.put((byte) 1);
};
/*return response -> stream(Bukkit.getPluginManager().getPlugins())
.filter(plugin -> plugin.getName().contains(filter))
.forEach(plugin -> {
putString(response, plugin.getName());
response.put((byte) (plugin.isEnabled() ? 1 : 0));
});*/
});
//--Check Plugin Enabled--
debugServer.respond(OP_CHECK, request -> {
String name = getString(request);
Plugin plugin = Bukkit.getPluginManager().getPlugin(name);
return response -> {
if (plugin == null) {
response.put(NOT_FOUND.handle(name));
response.put((byte) -1);
} else {
response.put((byte) -1);
response.put((byte) (plugin.isEnabled() ? 1 : 0));
}
};
});
}
@Override
public void onDisable() {
try {
debugServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
enum Error {
NONE(0, null),
NOT_FOUND(1, "\nNo such plugin\n\tPlugin: %s"),
INTERRUPTED(2, "\nThread interrupted while unloading plugin.\n\tPlugin: %s"),
OTHER(3, "\nUnknown error while attempting to unload plugin.\n\tPlugin: %s");
byte code;
String message;
Error(Number code, String message) {
this.code = code.byteValue();
this.message = message;
}
byte handle(String plugin) {
if (message != null)
System.err.println(format(message, plugin));
return code;
}
}
private static void putString(ByteBuffer buffer, String value) {
putString(buffer, value, length -> buffer.put(length.byteValue()));
}
private static void putString(ByteBuffer buffer, String value, Consumer<Integer> length) {
byte[] bytes = value.getBytes();
length.accept(bytes.length);
buffer.put(bytes);
}
private static String getString(ByteBuffer buffer) {
return getString(buffer, ByteBuffer::get);
}
private static String getString(ByteBuffer buffer, Function<ByteBuffer, Number> length) {
byte[] bytes = new byte[length.apply(buffer).intValue()];
buffer.get(bytes);
return new String(bytes);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment