Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RakNet-php

A pure PHP implementation of the RakNet protocol (the UDP transport used by Minecraft Bedrock/PE). Includes both server and client with built-in UDP sockets — it's not just a codec, it handles the full connection lifecycle.

Requirements

  • PHP >= 8.1
  • sockets extension enabled (php -m | grep sockets)

On Termux/proot-Ubuntu:

apt update && apt install -y php php-cli
php -m | grep sockets   # if it doesn't show up, you may need php-sockets or a build with --enable-sockets

If php-sockets doesn't exist as a separate package on your distro, check php -i | grep sockets — on many builds it's already compiled into the base binary.

Setup

cd RakNet-php
composer dump-autoload   # no external dependencies, this just generates the PSR-4 autoloader

If you don't have composer, you can use a minimal manual autoloader (see tests/loopback_test.php for reference on which classes to import).

Running the end-to-end test (loopback)

php tests/loopback_test.php

This spins up a server and a client in the same process on 127.0.0.1:39132, runs the full handshake, and tests:

  1. Full handshake (Open Connection 1/2 + Connection Request/Accepted + New Incoming Connection)
  2. Small reliable-ordered message with roundtrip
  3. Large message (5000+ bytes) that forces fragmentation and reassembly
  4. Delivery order with 10 messages fired quickly on an order channel

If something fails, the output tells you exactly which assertion failed — send it over as-is.

Basic usage

The snippets below are illustrative. For code you can run as-is, check examples/:

  • examples/server_echo.php — standalone server that logs connect/disconnect/message events and echoes back any message it receives.
  • examples/client_connect.php — client that first sends an Unconnected Ping (to read the MOTD), then does the full handshake, then sends a Connected Ping every 2s. Use it against server_echo.php or against a real RakNet server (e.g. a Bedrock server).

Try them in two terminals:

# Terminal A
php examples/server_echo.php 19132

# Terminal B
php examples/client_connect.php 127.0.0.1 19132

Client

<?php
require 'vendor/autoload.php';

use RakNet\Socket\RakNetClient;
use RakNet\Session\SessionState;
use RakNet\FrameSet\Reliability;

$client = new RakNetClient('127.0.0.1', 19132, protocolVersion: 11);
$client->connect();

while ($client->getState() !== SessionState::CONNECTED) {
    $client->tick();
    usleep(10_000);
}

// 0xfe = Game Packet id (see wiki) - everything after that is your application protocol
$client->send("\xfe" . "my payload", Reliability::RELIABLE_ORDERED);

while (true) {
    $client->tick();
    foreach ($client->receive() as $message) {
        // handle $message (raw bytes, starts with your own protocol's id)
    }
    usleep(10_000);
}

Server

<?php
require 'vendor/autoload.php';

use RakNet\Socket\RakNetServer;
use RakNet\Socket\ServerEventType;
use RakNet\FrameSet\Reliability;

$server = new RakNetServer('0.0.0.0', 19132, protocolVersion: 11, serverIdStringProvider: function () {
    // MOTD format: see the "Unconnected Pong" table on the wiki
    return "MCPE;My Server;11;1.0.0;0;20;{$guid};World;Survival;1;19132;19132;";
});
$server->start();

while (true) {
    $server->tick();

    foreach ($server->pollEvents() as $event) {
        switch ($event->type) {
            case ServerEventType::CONNECT:
                echo "Client connected: {$event->sessionKey} (guid={$event->guid})\n";
                break;
            case ServerEventType::MESSAGE:
                // $event->message is the raw Game Packet bytes
                $server->send($event->sessionKey, "\xfe" . "response", Reliability::RELIABLE_ORDERED);
                break;
            case ServerEventType::DISCONNECT:
                echo "Client disconnected: {$event->sessionKey}\n";
                break;
        }
    }

    usleep(10_000);
}

Architecture

src/
  Utils/BinaryStream.php         - byte reading/writing, all data types from the wiki
  Protocol/
    DataType/InternetAddress.php - the "address" type (reversed IPv4 octets / IPv6)
    MessageIdentifiers.php       - packet ID constants
    OfflinePackets.php           - ping/pong, open connection request/reply 1 and 2, incompatible protocol
    ConnectedPackets.php         - connected ping/pong, connection request/accepted, new incoming connection
    PacketDispatcher.php         - identifies an incoming buffer's packet type from its first byte
  FrameSet/
    Reliability.php              - constants + helpers for the 8 reliability types
    Frame.php                    - a single Frame (encode/decode)
    FrameSetPacket.php           - the 0x80-0x8d datagram that wraps Frames
    AcknowledgePacket.php        - ACK (0xc0) / NACK (0xa0) with range compression
    FragmentAssembler.php        - fragment reassembly by compound id
    OrderingChannel.php          - in-order delivery buffer (reliable ordered) and sequenced
  Session/
    Session.php                  - CORE: reliability tracking, resend, fragmentation, ordering, handshake state
    SessionState.php             - connection lifecycle enum
  Socket/
    RakNetClient.php             - real client over UDP
    RakNetServer.php             - real server over UDP (multi-session)
    ServerEvent.php              - events emitted by the server (connect/disconnect/message)
examples/
  server_echo.php                - standalone server that logs events and echoes messages
  client_connect.php             - client that does ping + handshake + connected ping against any RakNet server

Session is transport-agnostic (it never touches sockets directly) — it only produces/consumes raw bytes. This keeps the reliability logic (testable without real networking) separate from the I/O layer.

Protocol version

Not hardcoded to 11. It's passed as a parameter (protocolVersion) to both RakNetClient and RakNetServer. If the server receives an Open Connection Request 1 with a version different from its own, it responds with Incompatible Protocol Version (0x19) as specified by the wiki.

Known limitations / TODO

  • Doesn't implement the full "security cookie" challenge (only echoes it back) — the wiki itself notes it's optional and rarely used in real implementations.
  • IPv6 in InternetAddress is implemented but hasn't been tested against a real peer (only IPv4 was exercised in the loopback test).
  • No Game Packet compression/encryption — that's a Bedrock protocol layer, outside the scope of pure RakNet.
  • Session::$receivedReliableIndexes grows indefinitely in very long sessions (no pruning of old, already-acknowledged indexes). Doesn't affect short-term correctness, but it's memory that never gets freed.

About

RakNet project PHP librería

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages