Skip to content

Instantly share code, notes, and snippets.

@aadnk
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aadnk/e5906a3af254b88924cb to your computer and use it in GitHub Desktop.
Save aadnk/e5906a3af254b88924cb to your computer and use it in GitHub Desktop.
Recieve custom data through ping packets. See https://gist.github.com/aadnk/59756145619d076ccd04 in order to test this.
package com.comphenix.example;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.google.common.collect.MapMaker;
import com.google.common.primitives.Bytes;
public class ExampleMod extends JavaPlugin {
private static final long MAGIC_START = 0x1C5FEEC587C1C8C5L;
@Override
public void onEnable() {
ProtocolLibrary.getProtocolManager().addPacketListener(
new PacketAdapter(this, PacketType.Status.Client.IN_PING) {
private ConcurrentMap<Player, CustomDataState> buffers = new MapMaker().weakKeys().makeMap();
@Override
public void onPacketReceiving(PacketEvent event) {
CustomDataState current = buffers.get(event.getPlayer());
long data = event.getPacket().getLongs().read(0);
if (current != null) {
if (current.receieve(data)) {
byte[] remote = current.getData();
// Clean up
buffers.remove(event.getPlayer());
System.out.println("Recieved data: " + Bytes.asList(remote));
}
} else if (data == MAGIC_START) {
// Start capturing. Theoretically, a normal client could trigger this, but it wouldn't normally send
// a second PING packet.
buffers.put(event.getPlayer(), new CustomDataState());
} else {
// Let Minecraft handle the rest
return;
}
// This is our special "data channel" - don't let Minecraft interfer
event.setCancelled(true);
}
});
}
private static class CustomDataState {
private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private DataOutputStream writer = new DataOutputStream(buffer);
// Number of bytes to expect - maximum 2GB
private int payload;
private int written;
/**
* Recieve the given data.
* @param data - the data.
* @return TRUE if we are done recieving data, FALSE otherwise.
* @throws IOException
*/
public boolean receieve(long data) {
try {
if (written == 0) {
// Get the payload size
payload = (int)data;
writer.writeInt((int) (data >> 32));
written += 4;
} else {
writer.writeLong(data);
written += 8;
}
// See if we have enough data to produce an output
return hasData();
} catch (IOException e) {
throw new RuntimeException("Cannot recieve data.", e);
}
}
public boolean hasData() {
return written >= payload;
}
public byte[] getData() {
if (!hasData())
throw new IllegalStateException("Insufficient data.");
return Arrays.copyOf(buffer.toByteArray(), payload);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment