Documentation

Findings

Reverse-engineering reference for Project A — engine internals, networking, assets, and the local server emulator.

Engine Offsets

RVAs recovered from the shipping client image. All values are relative to the module base unless noted as structure field offsets. Grouped by subsystem for SDK dumps, hooks, and runtime object traversal.

Static Find Object & Package Loading

Entry points for resolving UObject instances by path and loading packages. static_load_object_candidate shares an address with load_package — treat it as an internal load path until confirmed as the public StaticLoadObject export.

SymbolOffsetNotes
static_find_object0x1B92940Wide-path wrapper
static_find_object_thunk0x1B92C40Thunk layer
static_find_object_fast0x1BA2D30FName / object hash lookup
static_find_object_hash0x1BA2DD0Object hash walk
resolve_name0x1B8FB70Name resolution helper
load_package0x1B89C80Package load
load_package_wrapper0x1B89BB0Outer wrapper

Viewport & Rendering

UGameViewportClient VMT hook for post-render overlays. Slot index 96 → byte offset 768 (0x300, index × 8 on x64). Confirmed by the indirect call through the viewport vtable at +768 in viewport_draw.

SymbolImage RVAVMT IndexVMT OffsetNotes
game_viewport_client_vtable0x42B6C08——Primary vtable pointer
viewport_vtable_postrender—96768 (0x300)UGameViewportClient::PostRender
viewport_draw0x2A5A830——Caller — dispatches vtable slot 96
viewport_draw_vtable_entry0x42B6F68——Resolved vtable entry
viewport_draw_vtable_offset—108 (0x6C)864 (0x360)Separate draw slot
game_viewport_client_vtable_alt0x3CA20F8——Cooked duplicate
viewport_draw_entry_alt0x3CA2458——Cooked duplicate
PostRender vtable call — from viewport_draw
// Decompiler pattern from viewport_draw (0x2A5A830):
//   v152 - 48  →  UGameViewportClient* instance
//   + 768LL    →  vtable index 96  (96 × 8)

(*(void (__fastcall **)(__int64, __int64))
    (*(_QWORD *)(viewport - 48) + 768LL))(viewport - 48, canvas);

Skeleton & Bone Transforms

Bone query and component-space transform chain for mesh/skeleton inspection.

SymbolOffsetNotes
exec_get_bone_transform_by_name0x30DBF90Blueprint exec stub
get_bone_transform_by_name0x2941830Native bone lookup
calc_component_space_transforms0x29405B0Component-space pass
transform_to_matrix_world0x2946B20World matrix composition

Global Object Array (GObjects)

Standard UE4 chunked object array layout. Each FUObjectItem is 0x18 bytes.

SymbolOffsetNotes
gobjects_chunks0x5C4FA08Chunk pointer table
gobjects_count0x5C4FA1CLive object count
fuobject_item_size0x18Struct size
fuobject_item_object0x00Field: UObject*
fuobject_item_flags0x08Field: flags
fuobject_item_serial0x10Field: serial number

Name System (GNames / FName)

gname_state is an indirect pointer — dereference before walking chunks. Entry count lives at *(gname_state + 0x800). Chunks are 0x4000 entries wide.

SymbolOffsetNotes
gname_state0x5C4B390Read pointer first
fname_to_string0x19DB1D0FName → FString
fname_ctor_ansi0x19CED40ANSI constructor
fname_ctor_wide0x19CEDB0Wide constructor
fname_entry_to_string0x19D37E0Entry decode
fname_entry_ansi_decode0xEEF570ANSI entry path
fname_entry_wide_decode0xEF0790Wide entry path
name_chunk_size0x4000Entries per chunk
name_state_count0x800Offset of count field

UObject & FProperty Layout

Structure field offsets for manual serialization and property walking — not image RVAs.

FieldOffsetFieldOffset
UObject::Flags0x08UObject::InternalIndex0x0C
UObject::Class0x10UObject::Name (FName)0x18
UObject::Outer0x28UStruct::PropertyLink0x70
FProperty::PropertyFlags0x30FProperty::ArrayDim0x38
FProperty::ElementSize0x3CFProperty::Offset_Internal0x4C
FProperty::PropertyLinkNext0x60

World State & Allocator

Minimum globals needed for encrypted world pointer resolution and heap allocation hooks.

SymbolOffsetNotes
uworld_state0x5087140Encrypted UWorld state
uworld_key0x5087178Decryption key material
fmemory_malloc0x18FA960FMemory::Malloc
gmalloc0x5B48F48Global allocator pointer

ProcessEvent

Native Blueprint dispatch. process_event_wrapper is a derived override layer on top of the core implementation. Vtable slot index 72 (0x48) → offset 0x240.

SymbolOffset / RVAVMT IndexVMT OffsetNotes
process_event0x1B8CB70——Core implementation
process_event_wrapper0x280BB30——Override / wrapper
uobject_vtable0x3EA11C8——UObject vtable reference
process_event_vtable_slot—72 (0x48)0x240UObject::ProcessEvent — VMT hook slot
ufunction_function_flags0xA0——UFunction field
ufunction_parms_size0xA6——UFunction field
ufunction_return_offset0xA8——UFunction field
ufunction_native_func0xC8——Native function pointer
offsets.hpp — copy-paste header
// Image RVAs — Project A shipping build
namespace Offsets {
  // SFO / package loading
  inline constexpr uintptr_t StaticFindObject       = 0x1B92940;
  inline constexpr uintptr_t StaticFindObjectFast   = 0x1BA2D30;
  inline constexpr uintptr_t LoadPackage            = 0x1B89C80;

  // Globals
  inline constexpr uintptr_t GObjectsChunks         = 0x5C4FA08;
  inline constexpr uintptr_t GObjectsCount          = 0x5C4FA1C;
  inline constexpr uintptr_t GNameState             = 0x5C4B390;
  inline constexpr uintptr_t UWorldState            = 0x5087140;
  inline constexpr uintptr_t UWorldKey              = 0x5087178;

  // Dispatch
  inline constexpr uintptr_t ProcessEvent           = 0x1B8CB70;
  inline constexpr uintptr_t ProcessEventWrapper    = 0x280BB30;
  inline constexpr int       ProcessEventVmtIndex   = 72;    // 0x48
  inline constexpr uintptr_t ProcessEventVmtOffset  = 0x240; // index * 8

  // Viewport VMT — index 96, offset 768 (0x300)
  inline constexpr int       ViewportPostRenderIndex  = 96;
  inline constexpr uintptr_t ViewportPostRenderOffset = 768;   // 0x300
}

Ares Packet Path

Ares is the custom transport layer sitting between the UE net driver and the socket. Packets carry a CRC32 + length header, pass through optional obfuscation transforms, and are handed off to a secondary transport interface for the actual send. These offsets map the full handler pipeline recovered from the shipping binary.

Wire Format

Every Ares frame on the wire follows a fixed 6-byte header. The CRC covers the header plus payload.

FieldOffsetTypeDescription
CRC320x0uint32Checksum over bytes 0…end
Length0x4uint16Payload byte count
Payload0x6bytesApplication data

Handler Architecture

Two handler vtables exist — the primary Ares handler and a DDoS/tunnel variant. Incoming traffic enters through vtable slot +0x18; outgoing through +0x20.

SymbolOffsetRole
ares_handler_vtable0x422E900Primary handler vtable
ares_ddos_handler_vtable0x422E990DDoS / tunnel handler
ares_incoming_entry0x2766190Incoming dispatch (vtable +0x18)
ares_outgoing_entry0x2766A00Outgoing dispatch (vtable +0x20)
ares_packet_verify0x2768E80CRC + length verifier
ares_crc320x197EA90CRC32 implementation

Incoming Pipeline

Receive path: preprocess → verify CRC/length → reset or advance bitstream on failure/success.

StageOffsetStatus
Preprocessor A0x2765970Body dump pending
Preprocessor B0x27652A0Body dump pending
Verifier entry0x2768E80Read CRC at +0, len at +4
Verify fail / reset0x2768F9F / 0x276901ERecovery path
Verify success0x27690A8Advance to payload

Outgoing Pipeline

Send path builds the header, optionally obfuscates the payload, then hands off to the transport layer. A per-handler packet counter at handler+0x70 is copied into control frames and incremented.

StageOffsetNotes
Header build0x2766A00Writes CRC + len, copies payload
Payload transform0x2766410Outgoing obfuscation
Transport handoff0x2765D50Boundary to socket layer
Bitstream transform0x2766CE0Updates stream crypto state at +0xB8
Meta vfunc call0x2766000[transport_vtable+0x30]
Payload vfunc call0x2766033[secondary_vtable+0x38]

Bitstream Layout

Offsets within the Ares bitstream object used by the transform layer.

FieldOffset
Data buffer0x90
Bit end0xA0
Bit start0xA8
Crypto state0xB8

Control Frames & Tunnel Wrapper

Outgoing control blobs carry a packet counter and a mode magic dword. The DDoS handler prepends two byte-swapped IDs before the payload via the tunnel wrapper at 0x2764BE0.

Control blob layout
struct AresControlBlob {
    uint32_t packet_counter;  // copied from handler+0x70, then incremented
    uint32_t mode_magic;      // 0x35D8FB3D (mode 0) or 0xE6508848 (mode 1)
};

// Transport resolution at runtime:
// transport       = *(uintptr_t*)(handler + 0x28);
// meta_target     = *(uintptr_t*)(*(uintptr_t*)transport + 0x30);
// secondary_iface = transport + 0x8;
// payload_target  = *(uintptr_t*)(*(uintptr_t*)secondary_iface + 0x38);

Pak AES Key

The client ships encrypted .pak archives. This 256-bit AES key decrypts them for asset extraction with tools like FModel or umodel. Without it, pak index reads fail and content paths cannot be resolved.

Usage

Paste the key into your extractor's AES key field (FModel → Settings → AES). The key applies globally to all encrypted paks in the build — no per-pak variation was observed for this patchline.

AES-256 key (hex)
0x4BE71AF2459CF83899EC9DC2CB60E22AC4B3047E0211034BBABE9D174C069DD6

Matchmaking (Client)

Client-side matchmaking surface recovered from string analysis and SDK dumps. The game talks to backend services over HTTP during queue, party, pregame, and core-game phases. For the reconstructed local server endpoints, see the Server → Matchmaking API section.

Key Classes

ClassRole
UMatchmakingManagerTop-level manager — owns queue state and delegates to the service layer
UMatchmakingServiceService facade — bridges UI/party models to HTTP calls
UPartyMatchmakingModelParty-side model — tracks eligibility, queue ID, and member readiness
MatchmakingMetadataStruct carrying queue config, map pool, and provisioning flow hints

Client Functions

Blueprint-exposed and native entry points identified from exported strings:

FunctionPurpose
EnterMatchmakingQueue / LeaveMatchmakingQueueQueue lifecycle
JoinMatchmaking / LeaveMatchmakingParty-level join/leave
ConvertToMatchmakingPartyPromote a social party to matchmaking-ready
IsMatchmaking / IsLeavingMatchmakingState queries
OnMatchmakingChangedDelegate fired on queue state transitions
OnMatchmakingMatchFoundChangedDelegate fired when a match is found
GetMatchmakingMatchFoundDurationSecondsTimer helper for match-found UI

HTTP Surface (Client)

Paths the client calls against the RNet control plane:

PathPurpose
/matchmaking/v1/queues/configsFetch all queue definitions
/matchmaking/v1/queues/{queueId}Fetch a single queue (optionally version-suffixed)
/parties/v1/parties/{id}/matchmaking/join/{queueId}Join queue from party context
/parties/v1/parties/{id}/matchmaking/leaveLeave queue

Party States & Errors

SymbolMeaning
EAresPartyState::MATCHMAKINGParty is actively searching
EAresPartyState::STARTING_MATCHMAKINGTransition into queue
EAresPartyState::LEAVING_MATCHMAKINGTransition out of queue
ErrPartyStateCantEnterMatchmakingInvalid party state for queue entry
ErrPartyNotInMatchmakingLeave called while not queued
ErrMatchmakingErrorGeneric backend failure
EProvisioningFlowID::MatchmakingProvisioning flow tag for matchmade games

Debug cvar matchmaking.testqueue.enabled and boolean flags bMatchmaking, bLeavingMatchmaking, bMatchmakingMatchFound appear on the manager for local testing.

Pak Mount

Entry point for mounting encrypted pak archives into the engine's file system. The export at 0x1B93E30 forwards into sub_278B3B0, which registers a pak file with the platform file interface and handles chunked _P.pak naming.

Function Signature

sub_278B3B0 — decompiler signature
// pak mount = 0x1B93E30 → calls into:
bool __fastcall MountPak(FPakPlatformFile* platform, const wchar_t* pak_path, int mount_order, const wchar_t* mount_point);

Behaviour Summary

StepWhat happens
1. Resolve pathLooks up the pak file through the platform file interface vtable (+184)
2. Create readerAllocates a 480-byte pak reader via sub_277BBE0 and validates the header
3. Chunk detectionIf the path ends with _P.pak, parses the chunk index from the filename prefix (e.g. pakchunkN_P.pak → order 100 * N + mount_order)
4. Mount pointOptional fourth argument overrides the mount point string on the reader object
5. RegisterOn success, appends {order, reader} to the platform's mounted-pak array and returns true
6. Failure pathOn header validation failure, queues a deferred mount entry instead of registering immediately

Chunk Naming Logic

The _P.pak branch strips the suffix, splits on underscores, and parses the numeric chunk prefix to compute mount priority. This ensures patch chunks mount in the correct order relative to the base pak.

Chunk order — simplified from sub_278B3B0
if (path.ends_with(L"_P.pak")) {
    // strip "_P.pak", parse leading chunk number from "pakchunkN_..."
    int chunk_index = parse_chunk_prefix(path);  // via wcstol on prefix segment
    mount_order = 100 * chunk_index + original_mount_order;
}
// mount_order + reader pointer pushed to platform->mounted_paks[]

Game Mode State Machine

UGameModeStateMachineComponent drives the in-match state flow (round start, buy phase, combat, round end) by transitioning between UStateComponent children. Hooking ScriptSetStartingState allows observing or overriding which state the game mode enters on round init.

DetailValue
ModuleShooterGame
ParentUStateMachineComponent
Size0x01E0
Key methodScriptSetStartingState(UStateComponent*)
SDK — GameModeStateMachineComponent
// Class ShooterGame.GameModeStateMachineComponent
// 0x0000 (0x01E0 - 0x01E0)
class UGameModeStateMachineComponent final : public UStateMachineComponent
{
public:
    void ScriptSetStartingState(class UStateComponent* State);

public:
    static class UClass* StaticClass()
    {
        STATIC_CLASS_IMPL("GameModeStateMachineComponent")
    }
    static const class FName& StaticName()
    {
        STATIC_NAME_IMPL(L"GameModeStateMachineComponent")
    }
    static class UGameModeStateMachineComponent* GetDefaultObj()
    {
        return GetDefaultObjImpl<UGameModeStateMachineComponent>();
    }
};

Server Overview

Python control-plane reconstruction for the Project A client. HTTP/HTTPS RNet-compatible endpoints on 127.0.0.1:39001 by default; UDP/TCP gameplay observer on port 7777. Auth uses Basic riot:{account_key}.

Architecture
Server/project_a_server.py          entry point
Server/projecta/control_plane/
  server.py                         CLI bootstrap (--port 39001, --game-port 7777)
  routes/registry.py                ordered route-family dispatch
  routes/*.py                       per-domain HTTP handlers
  payloads/*.py                     JSON response builders
Server/projecta/gameplay/
  ares_server.py                    UDP game-port observer/responder
  handshake.py                      Ares handshake state machine

Game state phases: menus → custom → pregame → core.

Setup & Launch

Zero-config local run via start_server.bat — creates a Python venv, installs dependencies, and listens on 127.0.0.1:39001. Use start_client.bat to patch client certificates and launch ShooterClient.exe.

Ports & Protocols

PortProtocolPurpose
39001HTTP + HTTPSControl plane (same socket — TLS detected via first-byte peek)
39001WebSocket (WAMP)Live backend-state push over RMS
7777UDPGameplay — Ares / UE4 stateless connect handshake
5432PostgreSQLOptional durable account/state storage

Authentication

HTTP requests use Authorization: Basic riot:{account_key}. Access-token and entitlements-token endpoints also accept Bearer tokens issued locally. Default account from launcher: Riot ID as GameName#TAG → normalized account key GameName-TAG.

CLI Flags

FlagDefaultDescription
--host127.0.0.1Control-plane bind address
--port39001HTTP/HTTPS/WebSocket port
--game-host127.0.0.1Host returned in ConnectionDetails
--game-port7777UDP gameplay port
--allow-memory-dboffIn-memory accounts — auto-enabled by start_server.bat when no DB URL
--database-urlenvPostgreSQL URL for persistent storage
--reset-stateoffIgnore persisted party/match state on boot
--resume-stateoffResume last persisted transient state
--matchmaking-delay-ms0Delay before queue advances to pregame (0 = instant)
--riot-name / --tag-lineDevPlayer / LOCALDefault local account Riot ID

Environment Variables

VariablePurpose
PROJECTA_DATABASE_URLPostgreSQL connection string
PROJECT_A_RIOT_IDRiot ID for client launcher (Name#TAG)
PROJECT_A_CLIENT_EXEOverride path to ShooterClient.exe
PROJECT_A_MATCHMAKING_DELAY_MSMatchmaking delay override
PROJECT_A_AUTO_ACCEPT_FRIEND_REQUESTSAuto-accept friend requests (1)

Certificates

On first boot the server generates a local TLS certificate (reverse-logs/rnet_probe.crt). The client launcher patches cacert.pem bundles inside the game folder so the client trusts this CA. Existing bundles are backed up as *.bak-local-ca.

Manual start
python Server\project_a_server.py --host 127.0.0.1 --port 39001 --allow-memory-db --reset-state
python Server\project_a_server.py --game-port 7777 --matchmaking-delay-ms 0

Bootstrap & Auth

Client startup sequence — process control, local account, tokens, and region config.

GET/process-control/v1/process

Health check — returns isRunning, pid.

GET/local/v1/account

Current local account — Subject, partyId, Riot ID fields.

POST/local/v1/accounts

Create additional local accounts. Body: account_key, game_name, tag_line.

GET/rso-auth/v1/authorization/access-token

RSO access token for authenticated requests.

GET/entitlements/v1/token

Entitlement token payload.

GET/v1/config/{region}

Collapsed client config (e.g. /v1/config/na).

GET/metadata/v1/

Patchline metadata — ClientVersion.

POST/riotclient/region-locale

Set region/locale. Body: {"region": "na", "locale": "en_US"}.

Matchmaking API

Queue configuration endpoints. Handled by routes/matchmaking.py.

GET/matchmaking/v1/queues/configs

All queue configs. Known queue IDs:

Queue IDs
custom          Local Custom (default, max party 10)
ShootingRange   Shooting Range (solo)
v               Local Test Queue
unrated         Local Unrated
competitive     Local Competitive (ranked)
spikerush       Local Spike Rush

GET/matchmaking/v1/queues/{queueId}

Single queue config. Also accepts client-version suffix:

GET/matchmaking/v1/queues/unrated/release-0.45-shipping-13-404591

Each queue payload includes QueueID, Mode, Maps, TeamSize, ProvisioningFlow, and wait-time fields.

Party & Queue

Party management and matchmaking entry. Handled by routes/party.py.

GET/parties/v1/players/{subject}

Player party state — CurrentPartyID, eligibility.

GET/parties/v1/parties/{partyId}

Full party payload — members, State, EligibleQueues.

POST/parties/v1/parties/{partyId}/queue

Enter matchmaking. Body: {"QueueID": "unrated"}. Sets party state to MATCHMADE_GAME_STARTING and advances to pregame.

POST/parties/v1/parties/{partyId}/matchmaking/join/{queueId}

Alternate join path — queue ID in URL or body. Calls enter_matchmaking() with immediate_pregame=True.

POST/parties/v1/parties/{partyId}/matchmaking/leave

Leave matchmaking — resets party state to DEFAULT.

POST/parties/v1/parties/{partyId}/makecustomgame

Start custom game setup — optional map/mode in body.

POST/parties/v1/parties/{partyId}/startcustomgame

Launch custom game into pregame character select.

POST/parties/v1/parties/{partyId}/startsoloexperience

Solo experience — skips queue, goes directly to pregame with locked characters.

Pregame

Character select and match provisioning. Handled by routes/pregame.py.

GET/pregame/v1/players/{subject}

Player pregame state — MatchID, CharacterID, CharacterSelectionState.

GET/pregame/v1/matches/{matchId}

Match lobby — teams, PregameState, map/mode.

POST/pregame/v1/matches/{matchId}/select/{characterId}

Hover/select agent.

POST/pregame/v1/matches/{matchId}/lock/{characterId}

Lock agent. When all party members lock, auto-transitions to core phase.

POST/pregame/v1/matches/{matchId}/start

Force start — returns ConnectionDetails with game server host/port.

ConnectionDetails
{
  "ConnectionDetails": {
    "GameServerHost": "127.0.0.1",
    "GameServerPort": 7777,
    "GamePodID": "...",
    "PlayerKey": "..."
  }
}

GET/pregame/v1/matches/{matchId}/chattoken

GET/pregame/v1/matches/{matchId}/voicetoken

Team chat/voice tokens for pregame lobby.

Core Game

In-match state and game server connection. Handled by routes/core_game.py.

GET/core-game/v1/players/{subject}

Active match player state — MatchID when phase is core.

GET/core-game/v1/matches/{matchId}

Match connection details, teams, provisioning state.

POST/core-game/v1/players/{subject}/fixsession

Recover session — forces phase to core, re-primes backend state.

POST/core-game/v1/players/{subject}/disassociate/{matchId}

Disconnect from match.

GET/core-game/v1/matches/{matchId}/allchatmuctoken

GET/core-game/v1/matches/{matchId}/teamchatmuctoken

GET/core-game/v1/matches/{matchId}/teamvoicetoken

In-match chat and voice tokens.

Typical flow tested in test_endpoint_matrix.py:

Match Flow
POST /parties/v1/parties/{partyId}/queue          {"QueueID": "v"}
GET  /pregame/v1/players/{subject}                → MatchID
GET  /pregame/v1/matches/{matchId}
POST /pregame/v1/matches/{matchId}/lock/{charId}
POST /pregame/v1/matches/{matchId}/start          → ConnectionDetails (port 7777)
GET  /core-game/v1/players/{subject}
GET  /core-game/v1/matches/{matchId}

Session

Client heartbeat and session keepalive. Handled by routes/session.py.

POST/session/v1/sessions/{subject}/heartbeat

Client loop-state heartbeat. Body may include LoopState, LoopStateMetadata. Triggers presence broadcast.

GET/session/v1/sessions/{subject}

Current session payload for the authenticated player.

WebSocket & RMS

The control plane accepts WebSocket upgrades on the same port as HTTP/HTTPS (runtime/http.py). After the WAMP handshake, the server pushes party and match state changes through Riot Messaging Service (RMS) notifications — the client does not need to poll every transition.

Connection

Client sends Upgrade: websocket to the control-plane port. Server responds with WAMP session ID and registers the client in ws_clients. TLS and plain HTTP share the same listener — first byte 0x16 triggers SSL wrap.

Backend State Push

When party or match state changes (queue join, character lock, phase transition), routes call broadcast_backend_state_update(). This builds RMS messages via rms_party_messages() and rms_match_messages() and sends them to all connected WebSocket clients for the affected profile.

TriggerRMS sourceWhen
Party changerms_party_messages()Invite, join, queue, custom game, accessibility
Match changerms_match_messages()Pregame select/lock, start, core transition
Presencebroadcast_presence_update()Heartbeat, chat/v2/me update
Socialbroadcast_social_roster_update()Friend add/remove, alias change

HTTP RMS Endpoints

MethodPathPurpose
GET/riot-messaging-service/v1/sessionRMS session state
GET/riot-messaging-service/v1/out-of-syncSync check — returns empty message list
GET/riot-messaging-service/v1/message/{topic}Fetch pending RMS messages (fallback poll)
RMS notification payload
{
  "Service": "ares",
  "Resource": "/parties/v1/parties/{partyId}",
  "EventType": "Update",
  "Payload": "…JSON string of party/match state…"
}

Gameplay UDP

After pregame returns ConnectionDetails, the client connects to the game port (default 7777) over UDP. The server implements a minimal Ares/UE4 observer — not a full dedicated server. See also Ares Packet Path for the client-side wire format.

Modules

FileRole
gameplay/ares_server.pyStandalone UDP server — challenge/ack handshake + JSONL logging
gameplay/handshake.pyStateful Ares handshake observer with UE control channel
gameplay/ares.pyFrame build/parse, CRC32, bitstream transforms
gameplay/ue_control_channel.pyUE4 control channel — Welcome, LoginComplete
ue_game_server.pyAlternate UE-style gameplay responder entry point

Stateless Connect Handshake

UE4.22 stateless connect flow implemented in AresUdpServer:

StepDirectionAction
1Client → ServerProbe packet (timestamp = 0 or non-handshake)
2Server → ClientChallenge — random 20-byte cookie + server timestamp
3Client → ServerResponse — echoes cookie with client timestamp
4Server → ClientAck — secret_id=true, confirms connection

Wire Format

Packets use the Ares 6-byte header (CRC32 + u16 length + payload). Handshake payloads are encoded as UE4 StatelessConnectHandlerComponent bitstreams (~195 bits). Cookie size: 20 bytes.

Standalone UDP server
python -m Server.projecta.gameplay.ares_server --host 0.0.0.0 --port 7777 --log reverse-logs/ares_udp_server.jsonl

Limitation: Gameplay simulation is incomplete. The UDP layer handles handshake and packet logging; in-match game state is not fully simulated.

Chat, Store & Content

Supporting services the client hits during bootstrap and between matches. All endpoints are covered by test_full_api_surface.py.

Chat & Social

MethodPathPurpose
GET/chat/v1/sessionChat session bootstrap
PUT/chat/v2/meUpdate own presence
GET/chat/v3/friendsFriend list
GET/POST/chat/v4/friendrequestsList / send friend requests
GET/PUT/chat/v4/presencesFriend presence list / update
GET/POST/chat/v5/conversationsList / join conversations (party MUC)
GET/chat/v5/conversations/{cid}/messagesConversation history
POST/chat/v5/messagesSend message
POST/chat/v5/conversations/readMark conversation read

Voice

MethodPathPurpose
GET/POST/DELETE/voice-chat/v2/sessionsVoice session lifecycle
GET/voice-chat/v2/settingsInput mode, volume settings
GET/voice-chat/v2/devices/captureCapture devices
GET/voice-chat/v2/devices/renderRender devices
PUT/voice-chat/v1/push-to-talkPush-to-talk toggle

Store & Wallet

MethodPathPurpose
GET/store/v1/offers/Storefront offers
GET/store/v1/wallet/{subject}Player wallet balances
GET/cap/v1/walletsCAP wallet alias
GET/cap/v1/entitlementsAll owned entitlements
POST/cap/v1/ordersPurchase item — returns OrderID
POST/payments/v1/initialize-purchaseInitialize purchase flow
GET/entitlements/{typeId}/{subject}Entitlements by item type

Content, Contracts & Loadout

MethodPathPurpose
GET/content-service/v2/contentCharacters, maps, modes catalog
GET/contract-definitions/v2/definitionsContract templates
GET/contracts/v1/contracts/{subject}Player contract progress
POST/contracts/v1/item-upgrades/{id}/{subject}Item upgrade order
GET/personalization/v1/players/{subject}/playerloadoutWeapon loadout
PUT/ares-personalization/personalization/v1/players/{subject}/playerloadoutSave loadout
GET/agg-stats/v1/stats/v/0Aggregate player stats
GET/mmr/v1/players/{subject}/competitiveupdatesCompetitive tier updates

Account & Policy

MethodPathPurpose
GET/PUT/player-account/aliases/v1/activeCurrent / update Riot ID
POST/player-account/aliases/v1/validityCheck name availability
PUT/name-service/v2/playersResolve display names by subject
GET/anti-addiction/v1/products/ares/policies/*/anti-addiction-statePlaytime / shutdown policies
GET/eula/v1/agreement/contentEULA text
GET/ares-match-details/match-details/v1/matches/{matchId}Post-match details

Route Families & Path Normalization

All HTTP handlers are registered in routes/registry.py and dispatched in order. Before dispatch, routes/normalization.py rewrites service-prefixed and legacy paths onto the local namespace.

Path Aliases

Client path prefixNormalized to
/ares-core-game/…/core-game/…
/ares-personalization/…/personalization/…
/ares-parties/…/parties/…
/ares-pregame/…/pregame/…
/ares-contracts/…/contracts/…
/v1/parties/…/parties/v1/parties/…
/v1/players/…/parties/v1/players/…

Dispatch Order

Route Families
client           bootstrap, auth, config, RMS
chat             chat sessions, friends, presences, messages
social           friend requests, roster
account          local accounts, aliases, validity
policy           anti-addiction, EULA
voice            voice-chat sessions, devices, push-to-talk
session          heartbeats
party            parties, invites, custom games, queue join
matchmaking      queue configs
pregame          character select, match lobby
core_game        in-match state, connection details
personalization  loadouts, entitlements
content          content-service, contract definitions
contracts        player contracts, item upgrades
store            offers, wallet, CAP orders, payments
reporting        player reports
name_service     display name resolution
mmr              competitive tier data
misc             catch-all / auxiliary endpoints