Skip to content

Instantly share code, notes, and snippets.

@Exerosis
Created February 20, 2018 18:52
Show Gist options
  • Save Exerosis/07df700f2c295c71ac997b474c71729f to your computer and use it in GitHub Desktop.
Save Exerosis/07df700f2c295c71ac997b474c71729f to your computer and use it in GitHub Desktop.
import java.nio.ByteBuffer
import java.nio.channels.DatagramChannel
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.lang.Integer.parseInt
import static java.lang.String.format
import static java.nio.ByteBuffer.allocateDirect
import static java.nio.channels.DatagramChannel.open
import static java.util.concurrent.Executors.newSingleThreadExecutor
import static java.util.concurrent.ThreadLocalRandom.current
group 'me.exerosis.debugging'
version '1.0.0'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.3.2'
compile files("/libs/tools.jar")
}
def connection = new DebugConnection(25569)
task listPlugins() {
connection.list(new InetSocketAddress("localhost", parseInt(port)), filter, { plugins ->
plugins.each { pl ->
println 'Plugins'
println '===[Name]=====[Enabled]==='
println "${pl.name()} ${pl.isEnabled()}"
}
})
}
class DebugConnection {
private final byte OP_TOGGLE = 0
private final byte OP_LIST = 1
private final byte OP_CHECK = 2
private final DebugServer server
DebugConnection(Number port) {
server = new DebugServer(port, 512)
}
private static String ERROR_CHECK = "\tPlugin check failed.\n\tPlugin: %s\n\tCode: %s"
private final Map<String, Plugin> plugins = new HashMap<>();
Plugin getPlugin(SocketAddress address, String name) {
return plugins.computeIfAbsent(name, { plugin ->
Plugin pl = new Plugin(name) {
@Override
boolean setEnabled(boolean enabled, Number delay) {
return false
}
@Override
boolean isEnabled(boolean refresh) {
if (refresh) {
server.request(OP_CHECK, address, { request ->
putString(request, name);
return { response ->
byte error = response.get();
if (error != -1)
throw new RuntimeException(format(ERROR_CHECK, name, error))
enabled = response.get() == 1;
}
})
}
return enabled
}
}
pl.isEnabled(true)
return pl;
})
}
void toggle(SocketAddress address, Consumer<Boolean> callback, PluginAction... actions) {
server.request(OP_TOGGLE, address, { request ->
request.put((byte) actions.length)
actions.each { action ->
putString(request, action.name)
request.putLong(action.delay)
request.put((byte) (action.enable ? 1 : 0))
}
return { response ->
byte[] errors = new byte[actions.length]
response.get(errors)
errors.each { code ->
if (code != -1) {
callback.accept(false)
return;
}
}
callback.accept(true)
}
})
}
void list(SocketAddress address, String filter, Consumer<List<Plugin>> callback) {
server.request(OP_LIST, address, { request ->
println 'test0'
putString(request, filter)
return { response ->
List<Plugin> plugins = new ArrayList<>()
byte length;
while ((length = response.get()) != 0) {
byte[] bytes = new byte[length]
response.get(bytes)
plugins.add(getPlugin(address, new String(bytes)))
}
callback.accept(plugins)
}
})
Thread.sleep(10_000)
}
static abstract class Plugin {
private final String name
private boolean enabled = false
private Plugin(String name) {
this.name = name
}
abstract boolean setEnabled(boolean enabled, Number delay = 5_000)
abstract boolean isEnabled(boolean refresh = true)
String name() {
return name
}
void enable(Number delay = 5_000) {
if (!enabled && setEnabled(true, delay))
enabled = true
}
void disable(Number delay = 5_000) {
if (enabled && setEnabled(false, delay))
enabled = false
}
void toggle(Number delay = 5_000) {
if (setEnabled(!enabled, delay))
enabled = !enabled
}
}
static class PluginAction {
private final boolean enable
private final long delay
private final String name
private PluginAction(String name, boolean enable, Number delay) {
this.name = name
this.delay = delay.longValue()
this.enable = enable
}
}
static void putString(ByteBuffer buffer, String string, Consumer<Number> length = { buffer.put(it.byteValue()) }) {
length.accept(string.length())
buffer.put(string.bytes)
}
}
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
DebugServer(Number port, Number length) throws IOException {
this(port, length, { buffer ->
buffer.put(length.byteValue())
})
}
DebugServer(Number port, Number length, Function<ByteBuffer, Number> opcodes) throws IOException {
buffer = allocateDirect(length.intValue())
channel = open()
channel.configureBlocking(true)
def randoPort = current().nextInt(9999)
println randoPort
channel.bind(new InetSocketAddress("localhost", randoPort))
executor.submit({
while (channel.isOpen()) {
println 'listening'
SocketAddress address = channel.receive(buffer)
println "responded: ${buffer.remaining()}"
buffer.flip()
handlers.get(opcodes.apply(buffer)).accept(buffer, address)
buffer.clear()
}
return null
})
}
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)
}
}
void receive(Number opcode, BiConsumer<ByteBuffer, SocketAddress> handler) {
handlers.put(opcode, handler)
}
void request(Number opcode, SocketAddress address, Function<ByteBuffer, Consumer<ByteBuffer>> handler) {
send(address, opcode, { request ->
def callback = handler.apply(request)
receive(opcode, { response, ignored ->
callback.apply(response)
handlers.remove(opcode)
})
} as Consumer)
}
void respond(Number opcode, Function<ByteBuffer, Consumer<ByteBuffer>> handler) {
respond(opcode, { request, address -> handler.apply(request) } as BiFunction)
}
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)
channel.send(buffer, address)
} as BiConsumer)
}
@Override
void close() throws IOException {
channel.close()
executor.shutdown()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment