Created
February 20, 2018 18:52
-
-
Save Exerosis/f482b02985bc7f006f1aa4dd7f9eac84 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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