Files
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

27 KiB
Raw Permalink Blame History

Network Layer

**Referenced Files in This Document** - [PacketHandler.cs](file://ClassicAssist.Shared/UO/Network/PacketHandler.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [OutgoingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketHandlers.cs) - [IncomingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketFilters.cs) - [OutgoingPacketFilters.cs](file://ClassicAssist.Shared/UO/Network/OutgoingPacketFilters.cs) - [ActionPacketQueue.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueue.cs) - [ActionPacketQueueItem.cs](file://ClassicAssist.Shared/UO/Network/ActionPacketQueueItem.cs) - [PacketReader.cs](file://ClassicAssist.Shared/UO/Data/PacketReader.cs) - [PacketWriter.cs](file://ClassicAssist.Shared/UO/Data/PacketWriter.cs) - [Compression.cs](file://ClassicAssist.Shared/UO/Compression.cs) - [BasePacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/BasePacket.cs) - [MobileUpdate.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileUpdate.cs) - [ContainerContentsPacket.cs](file://ClassicAssist.Shared/UO/Network/Packets/ContainerContentsPacket.cs) - [GumpButtonClick.cs](file://ClassicAssist.Shared/UO/Network/Packets/GumpButtonClick.cs) - [Entity.cs](file://ClassicAssist.Shared/UO/Objects/Entity.cs) - [Mobile.cs](file://ClassicAssist.Shared/UO/Objects/Mobile.cs) - [Item.cs](file://ClassicAssist.Shared/UO/Objects/Item.cs) - [EntityCollection.cs](file://ClassicAssist.Shared/UO/Objects/EntityCollection.cs)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion

Introduction

This document describes the network layer architecture for ClassicAssist.Avalonia, focusing on how raw network I/O is transformed into structured packets, routed to handlers, and applied as entity updates. It covers the PacketHandler architecture (incoming/outgoing routing, compression handling, protocol version detection), binary serialization via PacketReader/Writer, event-driven message distribution, connection management, error handling and retry strategies, performance considerations for high-frequency packet processing, and protocol compatibility/version negotiation with Ultima Online servers.

Project Structure

The network layer is primarily implemented under ClassicAssist.Shared/UO/Network and ClassicAssist.Shared/UO/Data. Key responsibilities:

  • Packet serialization/deserialization: PacketReader, PacketWriter
  • Protocol compression: Compression
  • Incoming/outgoing packet routing: PacketHandler, IncomingPacketHandlers, OutgoingPacketHandlers
  • Filtering: IncomingPacketFilters, OutgoingPacketFilters
  • Queuing and ordering: ActionPacketQueue, ActionPacketQueueItem
  • Packet models: BasePacket and specific packet types
  • Entity model and collections: Entity, Mobile, Item, EntityCollection
graph TB
subgraph "Serialization"
PR["PacketReader"]
PW["PacketWriter"]
COMP["Compression"]
end
subgraph "Routing"
PH["PacketHandler"]
IPH["IncomingPacketHandlers"]
OPH["OutgoingPacketHandlers"]
IF["IncomingPacketFilters"]
OF["OutgoingPacketFilters"]
end
subgraph "Queuing"
APQ["ActionPacketQueue"]
APQI["ActionPacketQueueItem"]
end
subgraph "Packets"
BP["BasePacket"]
MU["MobileUpdate"]
CCP["ContainerContentsPacket"]
GB["GumpButtonClick"]
end
subgraph "Entities"
E["Entity"]
M["Mobile"]
I["Item"]
EC["EntityCollection"]
end
PR --> PH
PW --> PH
COMP --> PH
PH --> IPH
PH --> OPH
PH --> IF
PH --> OF
PH --> APQ
APQ --> APQI
IPH --> BP
BP --> MU
BP --> CCP
BP --> GB
MU --> E
MU --> M
CCP --> I
EC --> E
EC --> M
EC --> I

Diagram sources

Section sources

Core Components

  • PacketReader and PacketWriter provide low-level binary serialization/deserialization primitives used by all packet implementations. They expose typed read/write methods for integers, strings, arrays, and variable-length fields required by UO protocol messages.
  • Compression encapsulates decompression/compression logic for compressed packets and streams, enabling efficient bandwidth usage when supported by the server.
  • PacketHandler orchestrates incoming and outgoing packet flows, including filtering, dispatching to registered handlers, and queuing actions for ordered execution.
  • IncomingPacketHandlers and OutgoingPacketHandlers register and route specific packet types to their respective handler methods.
  • IncomingPacketFilters and OutgoingPacketFilters allow selective inspection or modification of packets before they are processed or sent.
  • ActionPacketQueue and ActionPacketQueueItem implement a queue for serializing time-sensitive operations (e.g., movement, targeting) to preserve order and avoid race conditions.
  • BasePacket defines common metadata and lifecycle for packets; concrete packet classes (e.g., MobileUpdate, ContainerContentsPacket, GumpButtonClick) implement parsing and application logic.
  • Entities (Entity, Mobile, Item) and EntityCollection represent the game world state updated by incoming packets.

Section sources

Architecture Overview

The network pipeline transforms raw bytes into structured updates:

  • Raw I/O delivers compressed or uncompressed frames.
  • Compression layer decompresses frames if needed.
  • PacketReader parses frame payloads into typed packets.
  • PacketHandler applies filters, routes to registered handlers, and enqueues actions.
  • Handlers update entities and UI state via events or direct calls.
  • Outgoing packets are serialized via PacketWriter, optionally compressed, filtered, and queued for transmission.
sequenceDiagram
participant Net as "Network I/O"
participant Comp as "Compression"
participant Reader as "PacketReader"
participant Handler as "PacketHandler"
participant InHandlers as "IncomingPacketHandlers"
participant Queue as "ActionPacketQueue"
participant Entities as "EntityCollection"
Net->>Comp : "Receive frame"
Comp-->>Net : "Decompressed data"
Net->>Reader : "Parse payload"
Reader-->>Handler : "Typed packet instance"
Handler->>InHandlers : "Dispatch by type"
InHandlers->>Entities : "Apply updates"
InHandlers->>Queue : "Enqueue ordered action"
Queue-->>Entities : "Execute actions in order"

Diagram sources

Detailed Component Analysis

PacketHandler Architecture

PacketHandler centralizes incoming/outgoing routing, filter application, and action queuing. It maintains registries for incoming and outgoing handlers and provides methods to add/remove handlers and dispatch packets.

classDiagram
class PacketHandler {
+RegisterIncoming(type, handler)
+RegisterOutgoing(type, handler)
+HandleIncoming(packet)
+HandleOutgoing(packet)
+ApplyIncomingFilter(packet) bool
+ApplyOutgoingFilter(packet) bool
+EnqueueAction(item)
}
class IncomingPacketHandlers {
+Handle(packet)
}
class OutgoingPacketHandlers {
+Send(packet)
}
class IncomingPacketFilters {
+ShouldProcess(packet) bool
}
class OutgoingPacketFilters {
+ShouldProcess(packet) bool
}
class ActionPacketQueue {
+Add(item)
+Process()
}
PacketHandler --> IncomingPacketHandlers : "dispatch"
PacketHandler --> OutgoingPacketHandlers : "route"
PacketHandler --> IncomingPacketFilters : "filter"
PacketHandler --> OutgoingPacketFilters : "filter"
PacketHandler --> ActionPacketQueue : "enqueue"

Diagram sources

Section sources

Incoming Packet Processing Flow

Incoming packets follow a consistent flow from raw bytes to entity updates:

flowchart TD
Start(["Raw Frame Received"]) --> CheckCompressed{"Is Compressed?"}
CheckCompressed --> |Yes| Decompress["Decompress Payload"]
CheckCompressed --> |No| Parse["Parse with PacketReader"]
Decompress --> Parse
Parse --> CreatePacket["Create Typed Packet Instance"]
CreatePacket --> ApplyInFilters["Apply Incoming Filters"]
ApplyInFilters --> Filtered{"Allowed?"}
Filtered --> |No| Drop["Drop Packet"]
Filtered --> |Yes| Dispatch["Dispatch to IncomingPacketHandlers"]
Dispatch --> UpdateEntities["Update Entities"]
UpdateEntities --> EnqueueActions["Enqueue Ordered Actions"]
EnqueueActions --> End(["Done"])
Drop --> End

Diagram sources

Section sources

Outgoing Packet Routing and Compression

Outgoing packets are prepared, filtered, optionally compressed, and queued for transmission:

sequenceDiagram
participant App as "Application Logic"
participant Writer as "PacketWriter"
participant OutFilters as "OutgoingPacketFilters"
participant OutHandlers as "OutgoingPacketHandlers"
participant Comp as "Compression"
participant Net as "Network I/O"
App->>Writer : "Serialize packet"
Writer-->>App : "Serialized bytes"
App->>OutFilters : "Check should process"
OutFilters-->>App : "Allow/Deny"
App->>OutHandlers : "Route to handler"
OutHandlers->>Comp : "Compress if enabled"
Comp-->>OutHandlers : "Compressed bytes"
OutHandlers->>Net : "Transmit"

Diagram sources

Section sources

Packet Reader/Writer Implementation

PacketReader and PacketWriter provide typed accessors for binary fields. They support reading/writing integers, booleans, strings, arrays, and length-prefixed sequences. These primitives are consumed by each packet implementation to parse and serialize protocol-specific payloads.

Key characteristics:

  • Consistent endianness and encoding rules across all reads/writes.
  • Length-prefixed structures for variable-size payloads.
  • Efficient memory usage through minimal allocations per packet.

Section sources

Event-Driven Message Bus Pattern

While there is no explicit “message bus” class, the combination of PacketHandler, IncomingPacketHandlers, and OutgoingPacketHandlers implements an event-driven pattern:

  • Handlers register callbacks for specific packet types.
  • PacketHandler raises these callbacks upon receiving matching packets.
  • Handlers can publish side effects (entity updates, UI changes) via direct calls or shared state.
classDiagram
class PacketHandler {
+RegisterIncoming(type, callback)
+RaiseIncoming(packet)
}
class IncomingPacketHandlers {
+OnPacket(packet)
}
class OutgoingPacketHandlers {
+OnSend(packet)
}
PacketHandler --> IncomingPacketHandlers : "invokes callbacks"
PacketHandler --> OutgoingPacketHandlers : "invokes callbacks"

Diagram sources

Section sources

Connection Management, Error Handling, and Retry Mechanisms

Connection lifecycle and resilience are coordinated around the network stack:

  • Connection establishment triggers initialization of PacketHandler, registration of handlers, and configuration of compression and filters.
  • Errors during I/O or parsing are handled at the boundaries (compression, reader/writer, filters) and propagated up to the connection manager for recovery.
  • Retry mechanisms typically involve reconnection attempts with backoff and state reset, ensuring that entity state remains consistent after resync.

Operational notes:

  • Validate packet lengths and bounds to prevent malformed input.
  • Use timeouts and keep-alives to detect stale connections.
  • On failure, clear transient queues and reinitialize state as needed.

[No sources needed since this section provides general guidance]

Protocol Compatibility and Version Negotiation

Protocol compatibility is managed by:

  • Detecting server capabilities (e.g., compression support).
  • Selecting appropriate packet formats based on negotiated versions.
  • Using BasePacket and typed packet classes to abstract differences across versions.

Negotiation steps:

  • Initial handshake determines supported features.
  • PacketReader/Writer adapt field layouts according to negotiated version.
  • Handlers remain stable while underlying serialization adapts.

Section sources

Packet Models and Entity Updates

Concrete packet types encapsulate protocol semantics and apply updates to entities:

  • MobileUpdate modifies mobile positions, stats, and appearance.
  • ContainerContentsPacket updates item inventories and container states.
  • GumpButtonClick processes user interactions with gumps.
classDiagram
class BasePacket {
+Type
+Read(reader)
+Write(writer)
}
class MobileUpdate {
+Apply(entityCollection)
}
class ContainerContentsPacket {
+Apply(entityCollection)
}
class GumpButtonClick {
+Apply(gumpManager)
}
class EntityCollection {
+Get(id) Entity
+Update(entity)
}
BasePacket <|-- MobileUpdate
BasePacket <|-- ContainerContentsPacket
BasePacket <|-- GumpButtonClick
MobileUpdate --> EntityCollection : "updates"
ContainerContentsPacket --> EntityCollection : "updates"

Diagram sources

Section sources

Dependency Analysis

The network layer exhibits clear separation between serialization, routing, filtering, and application logic. Dependencies are directional:

  • PacketReader/Writer depend only on primitive types and shared utilities.
  • Compression depends on stream APIs and protocol flags.
  • PacketHandler depends on handler registries and filters.
  • Handlers depend on packet models and entity collections.
graph LR
PR["PacketReader"] --> PH["PacketHandler"]
PW["PacketWriter"] --> PH
COMP["Compression"] --> PH
PH --> IPH["IncomingPacketHandlers"]
PH --> OPH["OutgoingPacketHandlers"]
PH --> IF["IncomingPacketFilters"]
PH --> OF["OutgoingPacketFilters"]
IPH --> BP["BasePacket"]
BP --> MU["MobileUpdate"]
BP --> CCP["ContainerContentsPacket"]
BP --> GB["GumpButtonClick"]
MU --> EC["EntityCollection"]
CCP --> EC

Diagram sources

Section sources

Performance Considerations

High-frequency packet processing requires careful attention to CPU and memory:

  • Minimize allocations in hot paths: reuse buffers where possible, avoid string conversions inside tight loops.
  • Prefer value types and structs for small packet fields to reduce GC pressure.
  • Batch updates to entity collections to reduce contention and UI churn.
  • Use ActionPacketQueue to serialize time-sensitive operations and maintain deterministic order.
  • Enable compression selectively based on server capability and bandwidth constraints.
  • Profile critical sections (serialization, filtering, handler dispatch) to identify bottlenecks.

[No sources needed since this section provides general guidance]

Troubleshooting Guide

Common issues and strategies:

  • Malformed packets: validate lengths and bounds in PacketReader; log detailed context for diagnostics.
  • Compression errors: verify server capability flags and ensure correct decompression path selection.
  • Stale state after disconnect: reset entity collections and re-sync using resync packets.
  • Race conditions: rely on ActionPacketQueue to enforce ordering; avoid direct concurrent mutations.
  • High latency: inspect filter overhead and consider disabling non-essential filters in debug builds.

[No sources needed since this section provides general guidance]

Conclusion

ClassicAssist.Avalonia’s network layer separates concerns cleanly: serialization (PacketReader/Writer), compression, routing (PacketHandler with Incoming/Outgoing handlers), filtering, and queuing. This design supports robust protocol compatibility, efficient high-frequency processing, and reliable entity updates. By adhering to the patterns described—typed packet models, event-driven dispatch, and ordered action queues—the system remains maintainable and performant across varying server versions and network conditions.