Overview
Packets are the fundamental units of communication between the server and Ember client. Each packet extends the base Packet class and implements serialization and handling logic.
Base Packet Class
public abstract class Packet
All packet classes extend this base class and can override its methods.
Methods
write()
Serializes packet data to a buffer for transmission.
public void write(ByteBufWrapper buf)
The buffer to write packet data to
read()
Deserializes packet data from a buffer.
public void read(ByteBufWrapper buf)
The buffer to read packet data from
handle()
Processes the packet after it has been received and deserialized.
public void handle(Player player)
The player associated with this packet
Outgoing Packets
Packets sent from server to client.
OutAttestationRegister
Requests the client to register an attestation key pair.
Packet ID: 1
public class OutAttestationRegister extends Packet
Usage:
ECServerAPI api = ECServerAPI.getInstance();
api.sendPacket(player, new OutAttestationRegister());
This packet has no fields. It simply signals the client to begin the attestation registration process.
OutAttestationSign
Requests the client to sign data using their registered attestation key.
Packet ID: 2
public class OutAttestationSign extends Packet
Constructor
public OutAttestationSign(byte[] verificationBytes)
The data to be signed by the client’s attestation key
write()
@Override
public void write(ByteBufWrapper buf)
Serializes the verification bytes as a Base64-encoded string.
Usage:
ECServerAPI api = ECServerAPI.getInstance();
byte[] dataToSign = "challenge_data".getBytes(StandardCharsets.UTF_8);
api.sendPacket(player, new OutAttestationSign(dataToSign));
Incoming Packets
Packets received from client to server.
InAttestationRegister
Response from the client after attempting to register an attestation key.
Packet ID: 1001
public class InAttestationRegister extends Packet
Fields
status
AttestationRegisterResult
The result of the registration attempt
The client’s public key (only present if status is SUCCESS)
read()
@Override
public void read(ByteBufWrapper buf)
- Reads the status enum
- If status is SUCCESS, reads the Base64-encoded public key string and decodes it into an X509EncodedKeySpec
handle()
@Override
public void handle(Player player)
Fires an EmberAttestationRegisterEvent with the registration result.
InAttestationSign
Response from the client after attempting to sign data.
Packet ID: 1002
public class InAttestationSign extends Packet
Fields
The result of the signing attempt
The signed data (only present if status is SUCCESS)
read()
@Override
public void read(ByteBufWrapper buf)
- Reads the status enum
- If status is SUCCESS, reads the Base64-encoded signed data string and decodes it into a byte array
handle()
@Override
public void handle(Player player)
Fires an EmberAttestationSignEvent with the signing result.
Result Enums
AttestationRegisterResult
Possible outcomes of an attestation registration request.
public enum AttestationRegisterResult
Registration completed successfully, public key is available
The client’s environment does not support attestation signing
The user cancelled the registration process
An unexpected error occurred during registration
AttestationSignResult
Possible outcomes of an attestation signing request.
public enum AttestationSignResult
Signing completed successfully, signed data is available
The client’s environment does not support attestation signing
The provided data to sign was invalid
The user cancelled the signing process
No attestation key has been registered yet
An unexpected error occurred during signing
ByteBufWrapper
A wrapper around Netty’s ByteBuf with convenience methods for reading and writing common data types.
Common Methods
Variable-Length Integers
public void writeVarInt(int value)
public int readVarInt()
public void writeVarLong(long value)
public long readVarLong()
Strings
public void writeString(String s)
public String readString()
public void writeString(String s, int allowedLength)
public String readString(int expectedLength)
Enums
public void writeEnum(Enum<?> e)
public <T extends Enum<T>> T readEnum(Class<T> clazz)
Byte Arrays
public void writeByteArray(byte[] array)
public byte[] readByteArray()
public byte[] readByteArray(int limit)
UUID
public void writeUuid(UUID uuid)
public UUID readUuid()
Collections
public <T> void writeCollection(Collection<T> collection, BiConsumer<ByteBuf, T> entrySerializer)
public <T, C extends Collection<T>> C readCollection(IntFunction<C> collectionFactory, Function<ByteBuf, T> entryParser)
Maps
public <K, V> void writeMap(Map<K, V> map, BiConsumer<ByteBuf, K> keySerializer, BiConsumer<ByteBuf, V> valueSerializer)
public <K, V, M extends Map<K, V>> M readMap(IntFunction<M> mapFactory, Function<ByteBuf, K> keyParser, Function<ByteBuf, V> valueParser)
Optional Values
public <T> void writeOptional(@Nullable T object, BiConsumer<ByteBuf, T> entrySerializer)
public <T> T readOptional(Function<ByteBuf, T> entryParser)
Constants
Maximum length of a VarInt: 5 bytes
Maximum length of a VarLong: 10 bytes
DEFAULT_MAX_STRING_LENGTH
Default maximum string length: 32767 characters
Complete Example
import com.emberclient.serverapi.ECServerAPI;
import com.emberclient.serverapi.event.EmberAttestationRegisterEvent;
import com.emberclient.serverapi.event.EmberAttestationSignEvent;
import com.emberclient.serverapi.packet.impl.attestation.register.AttestationRegisterResult;
import com.emberclient.serverapi.packet.impl.attestation.register.OutAttestationRegister;
import com.emberclient.serverapi.packet.impl.attestation.sign.AttestationSignResult;
import com.emberclient.serverapi.packet.impl.attestation.sign.OutAttestationSign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
public class AttestationExample implements Listener {
public void requestAttestation(Player player) {
ECServerAPI api = ECServerAPI.getInstance();
// Request registration
api.sendPacket(player, new OutAttestationRegister());
}
@EventHandler
public void onAttestationRegister(EmberAttestationRegisterEvent event) {
if (event.getStatus() == AttestationRegisterResult.SUCCESS) {
Player player = event.getPlayer();
X509EncodedKeySpec publicKeySpec = event.getPublicKey();
// Store the public key for later verification
// ...
// Request signing of challenge data
byte[] challenge = "verify_player".getBytes(StandardCharsets.UTF_8);
ECServerAPI.getInstance().sendPacket(player, new OutAttestationSign(challenge));
}
}
@EventHandler
public void onAttestationSign(EmberAttestationSignEvent event) {
if (event.getStatus() == AttestationSignResult.SUCCESS) {
Player player = event.getPlayer();
byte[] signedData = event.getSignedData();
// Verify the signature with the stored public key
try {
X509EncodedKeySpec publicKeySpec = // ... retrieve stored key
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initVerify(publicKey);
signature.update("verify_player".getBytes(StandardCharsets.UTF_8));
if (signature.verify(signedData)) {
player.sendMessage("Attestation verified!");
} else {
player.sendMessage("Attestation verification failed!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}