Skip to main content

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)
buf
ByteBufWrapper
required
The buffer to write packet data to

read()

Deserializes packet data from a buffer.
public void read(ByteBufWrapper buf)
buf
ByteBufWrapper
required
The buffer to read packet data from

handle()

Processes the packet after it has been received and deserialized.
public void handle(Player player)
player
Player
required
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)
verificationBytes
byte[]
required
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
publicKey
X509EncodedKeySpec
The client’s public key (only present if status is SUCCESS)

read()

@Override
public void read(ByteBufWrapper buf)
  1. Reads the status enum
  2. 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

status
AttestationSignResult
The result of the signing attempt
signedData
byte[]
The signed data (only present if status is SUCCESS)

read()

@Override
public void read(ByteBufWrapper buf)
  1. Reads the status enum
  2. 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
SUCCESS
enum
Registration completed successfully, public key is available
SIGNING_NOT_ALLOWED
enum
The client’s environment does not support attestation signing
USER_CANCELLED
enum
The user cancelled the registration process
UNKNOWN_ERROR
enum
An unexpected error occurred during registration

AttestationSignResult

Possible outcomes of an attestation signing request.
public enum AttestationSignResult
SUCCESS
enum
Signing completed successfully, signed data is available
SIGNING_NOT_ALLOWED
enum
The client’s environment does not support attestation signing
SIGN_DATA_INVALID
enum
The provided data to sign was invalid
USER_CANCELLED
enum
The user cancelled the signing process
KEY_DOES_NOT_EXIST
enum
No attestation key has been registered yet
UNKNOWN_ERROR
enum
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

MAX_VAR_INT_LENGTH
int
Maximum length of a VarInt: 5 bytes
MAX_VAR_LONG_LENGTH
int
Maximum length of a VarLong: 10 bytes
DEFAULT_MAX_STRING_LENGTH
int
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();
            }
        }
    }
}

Build docs developers (and LLMs) love