Usage Examples

This section provides practical examples of implementing the Reborn protocol in various programming languages.

Basic Client Connection

Python Example

import socket
import struct
import zlib

class RebornClient:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = None
        self.encryption_key = 123  # Set during login
        self.iterator = 0x4A80B38   # GEN_5 initial value
        
    def connect(self):
        """Connect to Reborn server"""
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect((self.host, self.port))
        
    def send_packet(self, packet_id, data=b''):
        """Send a packet with GEN_5 encryption"""
        # Build packet
        packet = bytes([packet_id + 32]) + data + b'\n'
        
        # Compress (use zlib for packets > 55 bytes)
        if len(packet) > 55:
            compression_type = 0x04  # ZLIB
            compressed = zlib.compress(packet)
            encrypt_limit = 4096
        else:
            compression_type = 0x02  # UNCOMPRESSED
            compressed = packet
            encrypt_limit = 40
            
        # Encrypt first N bytes
        encrypted = self.encrypt(compressed, encrypt_limit)
        
        # Send with length prefix
        bundle = bytes([compression_type]) + encrypted
        length_data = struct.pack('<H', len(bundle))
        self.socket.send(length_data + bundle)
        
    def encrypt(self, data, limit):
        """GEN_5 encryption"""
        result = bytearray(data)
        bytes_to_encrypt = min(len(data), limit)
        
        for i in range(bytes_to_encrypt):
            result[i] ^= (self.encryption_key + self.iterator) & 0xFF
            self.iterator = (self.iterator + 1) & 0xFF
            
        return bytes(result)
        
    def send_login(self, username, password):
        """Send login packet"""
        login_data = f"{username}\n{password}\n"
        self.send_packet(0, login_data.encode('latin-1'))  # PLI_LEVELWARP for login
        
    def send_chat(self, message):
        """Send chat message"""
        self.send_packet(6, message.encode('latin-1'))  # PLI_TOALL
        
    def send_movement(self, x, y):
        """Send player movement"""
        # Build player props packet with position
        props = bytearray()
        props.append(15 + 32)  # PLPROP_X
        props.append(int(x/2) + 32)  # Half-pixel coordinates
        props.append(16 + 32)  # PLPROP_Y  
        props.append(int(y/2) + 32)
        
        self.send_packet(2, bytes(props))  # PLI_PLAYERPROPS

# Usage
client = RebornClient("localhost", 14900)
client.connect()
client.send_login("username", "password")
client.send_chat("Hello, world!")
client.send_movement(32, 48)  # Move to tile (2, 3)

JavaScript Example

class RebornClient {
    constructor(host, port) {
        this.host = host;
        this.port = port;
        this.ws = null;
        this.encryptionKey = 123;
        this.iterator = 0x4A80B38;
    }
    
    connect() {
        // WebSocket connection for browser usage
        this.ws = new WebSocket(`ws://${this.host}:${this.port}`);
        this.ws.binaryType = 'arraybuffer';
        
        this.ws.onmessage = (event) => {
            this.handlePacket(new Uint8Array(event.data));
        };
    }
    
    sendPacket(packetId, data = new Uint8Array()) {
        // Build packet
        const packet = new Uint8Array(1 + data.length + 1);
        packet[0] = packetId + 32;
        packet.set(data, 1);
        packet[packet.length - 1] = 10; // newline
        
        // Compress and encrypt
        let bundle;
        if (packet.length > 55) {
            // Use zlib compression (would need pako library)
            const compressed = pako.deflate(packet);
            const encrypted = this.encrypt(compressed, 4096);
            bundle = new Uint8Array(1 + encrypted.length);
            bundle[0] = 0x04; // ZLIB
            bundle.set(encrypted, 1);
        } else {
            const encrypted = this.encrypt(packet, 40);
            bundle = new Uint8Array(1 + encrypted.length);
            bundle[0] = 0x02; // UNCOMPRESSED
            bundle.set(encrypted, 1);
        }
        
        // Send with length prefix
        const lengthData = new Uint8Array(2);
        new DataView(lengthData.buffer).setUint16(0, bundle.length, true);
        
        const fullPacket = new Uint8Array(lengthData.length + bundle.length);
        fullPacket.set(lengthData);
        fullPacket.set(bundle, lengthData.length);
        
        this.ws.send(fullPacket);
    }
    
    encrypt(data, limit) {
        const result = new Uint8Array(data);
        const bytesToEncrypt = Math.min(data.length, limit);
        
        for (let i = 0; i < bytesToEncrypt; i++) {
            result[i] ^= (this.encryptionKey + this.iterator) & 0xFF;
            this.iterator = (this.iterator + 1) & 0xFF;
        }
        
        return result;
    }
    
    sendChat(message) {
        const data = new TextEncoder().encode(message);
        this.sendPacket(6, data); // PLI_TOALL
    }
}

C++ Example

#include <iostream>
#include <vector>
#include <string>
#include <cstdint>

class RebornClient {
private:
    uint8_t encryptionKey = 123;
    uint32_t iterator = 0x4A80B38;
    
public:
    void sendPacket(uint8_t packetId, const std::vector<uint8_t>& data = {}) {
        // Build packet
        std::vector<uint8_t> packet;
        packet.push_back(packetId + 32);
        packet.insert(packet.end(), data.begin(), data.end());
        packet.push_back('\n');
        
        // Determine compression
        uint8_t compressionType;
        int encryptLimit;
        std::vector<uint8_t> compressed;
        
        if (packet.size() > 55) {
            compressionType = 0x04; // ZLIB
            encryptLimit = 4096;
            // Would use zlib here: compressed = zlibCompress(packet);
            compressed = packet; // Simplified
        } else {
            compressionType = 0x02; // UNCOMPRESSED
            encryptLimit = 40;
            compressed = packet;
        }
        
        // Encrypt
        encrypt(compressed.data(), compressed.size(), encryptLimit);
        
        // Send bundle
        std::vector<uint8_t> bundle;
        bundle.push_back(compressionType);
        bundle.insert(bundle.end(), compressed.begin(), compressed.end());
        
        // Add length prefix and send
        uint16_t length = bundle.size();
        // Send length (little-endian) + bundle
        sendToSocket(reinterpret_cast<uint8_t*>(&length), 2);
        sendToSocket(bundle.data(), bundle.size());
    }
    
    void encrypt(uint8_t* data, size_t length, int limit) {
        size_t bytesToEncrypt = std::min(length, static_cast<size_t>(limit));
        
        for (size_t i = 0; i < bytesToEncrypt; i++) {
            data[i] ^= (encryptionKey + iterator) & 0xFF;
            iterator = (iterator + 1) & 0xFF;
        }
    }
    
    void sendChat(const std::string& message) {
        std::vector<uint8_t> data(message.begin(), message.end());
        sendPacket(6, data); // PLI_TOALL
    }
    
    void sendMovement(uint16_t x, uint16_t y) {
        std::vector<uint8_t> props;
        props.push_back(15 + 32); // PLPROP_X
        props.push_back((x/2) + 32);
        props.push_back(16 + 32); // PLPROP_Y
        props.push_back((y/2) + 32);
        
        sendPacket(2, props); // PLI_PLAYERPROPS
    }
    
private:
    void sendToSocket(const uint8_t* data, size_t length) {
        // Platform-specific socket send implementation
    }
};

G-Type Encoding Examples

GSHORT Encoding

def encode_gshort(value):
    """Encode 16-bit value as GSHORT (7-bit shift, max 28767)"""
    t = min(value, 28767)  # GServer max value
    val0 = t >> 7
    if val0 > 223:
        val0 = 223
    val1 = t - (val0 << 7)
    return bytes([val0 + 32, val1 + 32])

def decode_gshort(data):
    """Decode GSHORT to 16-bit value"""
    return (data[0] << 7) + data[1] - 0x1020  # 0x1020 = (32 << 7) + 32

# Examples (corrected for 7-bit encoding)
assert encode_gshort(0) == b'\x20\x20'      # 0 -> 32,32
assert encode_gshort(1000) == b'\x27\x88'   # 1000 -> 39,136  
assert encode_gshort(28767) == b'\xff\x7f'  # 28767 -> 255,127 (max)
assert decode_gshort(b'\x20\x20') == 0
assert decode_gshort(b'\x27\x88') == 1000

GSTRING Encoding

def encode_gstring(text):
    """Encode string with length prefix"""
    data = text.encode('latin-1')
    length = min(len(data), 223)  # Max GCHAR value
    return bytes([length + 32]) + data[:length]

def decode_gstring(data):
    """Decode GSTRING"""
    if len(data) < 1:
        return ""
    length = data[0] - 32
    if length < 0 or length > len(data) - 1:
        return ""
    return data[1:1+length].decode('latin-1', errors='replace')

# Examples
assert encode_gstring("hello") == b'\x25hello'  # 5+32=37=0x25
assert decode_gstring(b'\x25hello') == "hello"

Player Property Handling

class PlayerProperties:
    def __init__(self):
        self.properties = {}
        
    def set_property(self, prop_id, value):
        """Set a player property"""
        self.properties[prop_id] = value
        
    def build_props_packet(self):
        """Build PLI_PLAYERPROPS packet data"""
        data = bytearray()
        
        for prop_id, value in self.properties.items():
            data.append(prop_id + 32)  # Property ID
            
            if prop_id in [0, 10, 11, 12, 20, 21, 34, 35]:  # String properties
                # GSTRING encoding
                str_data = str(value).encode('latin-1')
                data.append(min(len(str_data), 223) + 32)
                data.extend(str_data[:223])
                
            elif prop_id in [1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19]:  # Byte properties
                # GCHAR encoding
                data.append(int(value) + 32)
                
            elif prop_id in [14, 27, 28, 29]:  # Short properties
                # GSHORT encoding (7-bit shift)
                val = min(int(value), 28767)
                val0 = val >> 7
                if val0 > 223:
                    val0 = 223
                val1 = val - (val0 << 7)
                data.append(val0 + 32)
                data.append(val1 + 32)
                
        return bytes(data)

# Usage
props = PlayerProperties()
props.set_property(0, "PlayerName")      # NICKNAME
props.set_property(15, 64)               # X position
props.set_property(16, 96)               # Y position
props.set_property(12, "Hello!")         # CHAT

packet_data = props.build_props_packet()

File Transfer Example

def request_file(client, filename):
    """Request a file from server"""
    data = filename.encode('latin-1')
    client.send_packet(23, data)  # PLI_WANTFILE
    
def handle_file_packet(packet_data):
    """Handle incoming PLO_FILE packet"""
    if len(packet_data) < 6:
        return None
        
    # Read file type and size
    file_type = packet_data[0] - 32
    size_bytes = packet_data[1:6]
    
    # Decode GINT5 size
    file_size = 0
    for i in range(5):
        file_size = (file_size << 7) | ((size_bytes[i] - 32) & 0x7F)
    
    # Extract file data
    file_data = packet_data[6:6+file_size]
    
    return {
        'type': file_type,
        'size': file_size,
        'data': file_data
    }

These examples demonstrate the core concepts needed to implement a compatible Reborn protocol client or server in any programming language.