Files
WV-Networking/NETWORKING_DESIGN.md

730 lines
23 KiB
Markdown

# WVNet - Networking Architecture Design
## Overview
WVNet is a networking library for WV-Core that implements Unreal Engine-style server-authoritative networking and replication. This document outlines the architecture, design decisions, and implementation strategy.
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ WV-Core App │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ App::Run() Game Loop │ │
│ │ Start() -> Update() -> NetworkTick() -> Render() │ │
│ └─────────────────────┬────────────────────────────────┘ │
└────────────────────────┼──────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ WVNet Library │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ NetworkManager │ │
│ │ • Initialize/Shutdown │ │
│ │ • Tick (process packets, replicate actors) │ │
│ │ • Owns NetDriver │ │
│ └────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ NetDriver │ │
│ │ • Socket management │ │
│ │ • Accept/connect clients │ │
│ │ • Send/receive packets │ │
│ │ • Owns multiple NetConnections │ │
│ └────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ NetConnection (per client) │ │
│ │ • Packet buffering & reliability │ │
│ │ • Sequence numbers │ │
│ │ • Connection state (connecting/open/closed) │ │
│ │ • Per-connection replication state │ │
│ └────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ReplicationManager │ │
│ │ • Track replicated actors │ │
│ │ • Compute property deltas │ │
│ │ • Relevancy/interest management │ │
│ │ • Serialize/deserialize actor state │ │
│ └────────┬─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ RPCManager │ │
│ │ • Register RPC functions │ │
│ │ • Serialize/deserialize RPC calls │ │
│ │ • Dispatch RPCs (Server/Client/Multicast) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Platform Socket Abstraction │ │
│ │ • WVSocket, WVSocketAddress │ │
│ │ • Windows: WinSock2 │ │
│ │ • macOS/Linux: BSD sockets │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
```
## Core Components
### 1. NetworkManager (Singleton)
**Responsibilities:**
- Central networking coordinator
- Initialize/shutdown networking subsystem
- Main tick function called from game loop
- Configuration management
- Access point for networking functionality
**API:**
```cpp
class NetworkManager {
public:
static NetworkManager& Get();
void Initialize(const NetworkConfig& config);
void Shutdown();
void Tick(float deltaTime);
bool IsServer() const;
bool IsClient() const;
bool IsNetworked() const;
NetDriver* GetNetDriver();
ReplicationManager* GetReplicationManager();
RPCManager* GetRPCManager();
};
```
**Configuration:**
```cpp
struct NetworkConfig {
NetworkMode mode; // Server, Client, or Standalone
std::string serverAddress; // For clients
uint16_t serverPort; // Default: 7777
uint32_t maxConnections; // For servers, default: 64
float tickRate; // Network update rate (Hz), default: 30
bool enableRelevancy; // Enable actor relevancy checks
float relevancyDistance; // Default relevancy distance
};
```
---
### 2. NetDriver
**Responsibilities:**
- Low-level socket management
- Accept incoming connections (server)
- Connect to server (client)
- Send/receive raw packets
- Manage NetConnection objects
- Packet routing
**API:**
```cpp
class NetDriver {
public:
bool InitAsServer(uint16_t port, uint32_t maxConnections);
bool InitAsClient(const std::string& address, uint16_t port);
void Shutdown();
void Tick(float deltaTime);
void SendPacket(NetConnection* connection, const Packet& packet);
const std::vector<NetConnection*>& GetConnections() const;
NetConnection* GetServerConnection(); // For clients
private:
WVSocket* m_socket;
std::vector<std::unique_ptr<NetConnection>> m_connections;
NetConnection* m_serverConnection; // Client only
NetworkMode m_mode;
};
```
---
### 3. NetConnection
**Responsibilities:**
- Represents a single network connection
- Packet reliability and ordering
- Sequence number management
- Connection state tracking
- Per-connection replication state
**API:**
```cpp
enum class ConnectionState {
Connecting,
Connected,
Disconnecting,
Disconnected
};
class NetConnection {
public:
NetConnection(const WVSocketAddress& address);
void SendPacket(const Packet& packet, bool reliable = true);
void ReceivePacket(const Packet& packet);
void Tick(float deltaTime);
ConnectionState GetState() const;
const WVSocketAddress& GetAddress() const;
float GetRoundTripTime() const;
// Replication state per actor per connection
ActorReplicationState* GetActorReplicationState(uint32_t actorNetId);
private:
WVSocketAddress m_address;
ConnectionState m_state;
// Reliability
uint32_t m_outgoingSequence;
uint32_t m_incomingSequence;
std::map<uint32_t, Packet> m_reliableBuffer; // Awaiting ack
std::queue<Packet> m_outgoingQueue;
// Timing
float m_roundTripTime;
float m_lastReceiveTime;
// Replication state
std::unordered_map<uint32_t, std::unique_ptr<ActorReplicationState>> m_replicationState;
};
```
---
### 4. ReplicationManager
**Responsibilities:**
- Track all replicated actors in the world
- Compute property deltas (what changed since last replication)
- Manage actor relevancy (which clients see which actors)
- Serialize/deserialize actor state
- Handle actor spawn/destroy replication
**API:**
```cpp
class ReplicationManager {
public:
void Tick(float deltaTime);
// Actor registration
void RegisterActor(Actor* actor);
void UnregisterActor(Actor* actor);
// Replication
void ReplicateActors(NetConnection* connection);
void ProcessActorReplication(const Packet& packet);
// Relevancy
bool IsActorRelevantForConnection(Actor* actor, NetConnection* connection);
void SetRelevancyDistance(float distance);
private:
std::vector<Actor*> m_replicatedActors;
float m_replicationInterval; // 1.0 / tickRate
float m_timeSinceLastReplication;
float m_relevancyDistance;
};
```
**Actor Replication State (per actor, per connection):**
```cpp
struct ActorReplicationState {
uint32_t actorNetId;
std::unordered_map<std::string, ReplicatedProperty> properties;
float lastReplicationTime;
bool spawned; // Has this actor been spawned on the client?
};
struct ReplicatedProperty {
std::string name;
PropertyType type;
std::vector<uint8_t> lastValue; // Last replicated value
bool dirty; // Has it changed?
};
```
---
### 5. RPCManager
**Responsibilities:**
- Register RPC functions with metadata
- Serialize RPC calls with parameters
- Deserialize and dispatch RPC calls
- Handle different RPC types (Server, Client, Multicast)
**API:**
```cpp
enum class RPCType {
Server, // Called on client, executed on server
Client, // Called on server, executed on specific client
Multicast // Called on server, executed on all clients
};
class RPCManager {
public:
// Registration
void RegisterRPC(const std::string& functionName, RPCType type,
std::function<void(Actor*, BitStream&)> handler);
// Invocation
void CallServerRPC(Actor* actor, const std::string& functionName, BitStream& params);
void CallClientRPC(Actor* actor, NetConnection* client, const std::string& functionName, BitStream& params);
void CallMulticastRPC(Actor* actor, const std::string& functionName, BitStream& params);
// Processing
void ProcessRPC(const Packet& packet, NetConnection* sender);
private:
struct RPCMetadata {
std::string name;
RPCType type;
std::function<void(Actor*, BitStream&)> handler;
};
std::unordered_map<std::string, RPCMetadata> m_rpcRegistry;
};
```
---
### 6. Actor System (New for WV-Core)
Since WV-Core lacks an entity/actor system, we'll introduce a lightweight one:
**Actor Base Class:**
```cpp
class Actor {
public:
Actor();
virtual ~Actor();
virtual void Tick(float deltaTime) {}
virtual void OnSpawn() {}
virtual void OnDestroy() {}
// Networking
void SetReplicates(bool replicates);
bool GetReplicates() const;
uint32_t GetNetId() const;
bool IsNetworked() const;
// Transform (basic, can be extended)
void SetPosition(const glm::vec3& pos);
glm::vec3 GetPosition() const;
void SetRotation(const glm::quat& rot);
glm::quat GetRotation() const;
// Replication hooks
virtual void GetReplicatedProperties(std::vector<ReplicatedProperty*>& outProps);
virtual void OnReplicated() {}
protected:
// Helpers for derived classes
void RegisterReplicatedProperty(const std::string& name, void* ptr, PropertyType type);
private:
uint32_t m_netId;
bool m_replicates;
glm::vec3 m_position;
glm::quat m_rotation;
std::unordered_map<std::string, ReplicatedProperty> m_replicatedProperties;
};
```
**World/Scene Manager:**
```cpp
class World {
public:
static World& Get();
void Tick(float deltaTime);
Actor* SpawnActor(std::unique_ptr<Actor> actor);
void DestroyActor(Actor* actor);
Actor* GetActorByNetId(uint32_t netId);
const std::vector<Actor*>& GetActors() const;
private:
std::vector<std::unique_ptr<Actor>> m_actors;
std::unordered_map<uint32_t, Actor*> m_actorsByNetId;
uint32_t m_nextNetId;
};
```
---
### 7. Platform Socket Abstraction
**Cross-platform socket wrapper:**
```cpp
// Socket address abstraction
class WVSocketAddress {
public:
WVSocketAddress();
WVSocketAddress(const std::string& ip, uint16_t port);
std::string GetIP() const;
uint16_t GetPort() const;
bool IsValid() const;
// Platform-specific accessors
#ifdef PLATFORM_WINDOWS
const sockaddr_in& GetNative() const;
#else
const sockaddr_in& GetNative() const;
#endif
private:
sockaddr_in m_address;
};
// Socket abstraction
class WVSocket {
public:
WVSocket();
~WVSocket();
// Initialization
bool CreateUDP();
bool Bind(uint16_t port);
void Close();
// I/O (non-blocking)
bool SetNonBlocking(bool nonBlocking);
int32_t SendTo(const void* data, size_t size, const WVSocketAddress& dest);
int32_t ReceiveFrom(void* buffer, size_t size, WVSocketAddress& source);
// State
bool IsValid() const;
int32_t GetLastError() const;
private:
#ifdef PLATFORM_WINDOWS
SOCKET m_socket;
bool m_wsaInitialized;
#else
int m_socket;
#endif
};
// Socket initialization helper
class SocketSystem {
public:
static bool Initialize();
static void Shutdown();
};
```
---
## Packet Structure
### Base Packet Format
```
┌────────────────────────────────────────────────────┐
│ Header (12 bytes) │
├────────────────────────────────────────────────────┤
│ uint32_t magic (4 bytes) - 0x57564E45 ('WVNE') │
│ uint32_t sequence (4 bytes) │
│ uint16_t packetType (2 bytes) │
│ uint16_t payloadSize (2 bytes) │
├────────────────────────────────────────────────────┤
│ Payload (variable size) │
└────────────────────────────────────────────────────┘
```
### Packet Types
```cpp
enum class PacketType : uint16_t {
// Connection
ConnectionRequest = 0,
ConnectionAccept = 1,
ConnectionDenied = 2,
Disconnect = 3,
// Reliability
Acknowledgement = 10,
// Replication
ActorSpawn = 20,
ActorDestroy = 21,
ActorReplication = 22,
// RPC
RPCServer = 30,
RPCClient = 31,
RPCMulticast = 32,
// Control
Heartbeat = 100,
};
```
---
## Replication Strategy
### Property Replication
1. **Registration**: Actors register properties for replication using macros or manual registration
2. **Delta Calculation**: Before sending, compare current value with last replicated value
3. **Serialization**: Only serialize changed properties (delta compression)
4. **Per-Connection State**: Track what each client has received independently
### Example Usage
```cpp
class PlayerActor : public Actor {
public:
PlayerActor() {
SetReplicates(true);
RegisterReplicatedProperty("Health", &m_health, PropertyType::Int32);
RegisterReplicatedProperty("Position", &m_position, PropertyType::Vector3);
}
WV_RPC_SERVER
void TakeDamage(int32_t damage) {
if (NetworkManager::Get().IsServer()) {
m_health -= damage;
if (m_health <= 0) {
OnDeath();
}
}
}
WV_RPC_MULTICAST
void OnDeath() {
// Play death animation on all clients
}
private:
int32_t m_health = 100;
glm::vec3 m_position;
};
```
---
## RPC System
### RPC Macros
```cpp
// Macro definitions
#define WV_RPC_SERVER // Marks function as Server RPC
#define WV_RPC_CLIENT // Marks function as Client RPC
#define WV_RPC_MULTICAST // Marks function as Multicast RPC
// Implementation uses template metaprogramming to:
// 1. Automatically serialize parameters
// 2. Generate network call wrapper
// 3. Register with RPCManager
```
### RPC Serialization
- Automatic serialization for primitive types (int, float, bool, etc.)
- Manual serialization for custom types via `BitStream& operator<<`
- Variadic templates for flexible parameter lists
---
## Relevancy & Interest Management
### Initial Implementation (Distance-Based)
```cpp
bool ReplicationManager::IsActorRelevantForConnection(Actor* actor, NetConnection* connection) {
// Get the player's pawn/controlled actor for this connection
Actor* playerActor = connection->GetPlayerActor();
if (!playerActor) return false;
// Always relevant if within distance
float distance = glm::distance(actor->GetPosition(), playerActor->GetPosition());
return distance <= m_relevancyDistance;
}
```
### Future Extensions
- **Always Relevant**: Certain actors (e.g., game state) always replicate
- **Owner Relevant**: Actor always replicates to its owner
- **Team Relevant**: Replicate based on team membership
- **Custom Relevancy**: User-defined relevancy functions
---
## Integration with WV-Core
### Minimal Changes to WV-Core
1. **Add network tick to game loop** (`src/app/App.cpp`):
```cpp
void App::Run() {
// ... existing code ...
while(!window.ShouldClose()) {
// ... existing code ...
Update();
// Network tick (if initialized)
if (NetworkManager::Get().IsNetworked()) {
NetworkManager::Get().Tick(m_deltaTime);
}
Render();
// ... existing code ...
}
}
```
2. **Optional: Add World::Tick()** to update all actors if actor system is adopted
### Application Usage
```cpp
class MyGame : public WillowVox::App {
public:
void Start() override {
// Initialize networking
WVNet::NetworkConfig config;
config.mode = WVNet::NetworkMode::Server;
config.serverPort = 7777;
config.maxConnections = 32;
config.tickRate = 30.0f;
WVNet::NetworkManager::Get().Initialize(config);
// Spawn some actors
auto player = std::make_unique<PlayerActor>();
WVNet::World::Get().SpawnActor(std::move(player));
}
void Update() override {
// Game logic
WVNet::World::Get().Tick(m_deltaTime);
}
};
```
---
## Build System Integration
### CMakeLists.txt Addition
```cmake
# Add WVNet library
add_subdirectory(WVNet)
# Link to WVCore
target_link_libraries(WVCore PUBLIC WVNet)
# Platform-specific networking libraries
if(WIN32)
target_link_libraries(WVNet PRIVATE ws2_32)
elseif(UNIX)
# BSD sockets are part of libc on Unix
endif()
```
---
## Design Principles
1. **Minimal Coupling**: WVNet should work with WV-Core without heavy modifications
2. **Opt-in**: Networking is optional; non-networked games shouldn't include overhead
3. **Extensible**: Easy to add custom replication logic, relevancy rules, or packet types
4. **Performance**: Delta compression, relevancy culling, and efficient serialization
5. **Cross-platform**: Same API on Windows, macOS, and Linux
6. **Developer-Friendly**: Clear APIs, good defaults, easy debugging
---
## Unreal Engine Concept Mapping
| Unreal Engine | WVNet | Notes |
|---------------|-------|-------|
| `UNetDriver` | `NetDriver` | Socket management and connection handling |
| `UNetConnection` | `NetConnection` | Per-client connection state |
| `UChannel` | (Implicit in PacketType) | Simplified; may add explicit channels later |
| `AActor` | `Actor` | Replicated game object base class |
| `UWorld` | `World` | Scene/level container for actors |
| `UPROPERTY(Replicated)` | `RegisterReplicatedProperty()` | Property replication registration |
| `UFUNCTION(Server)` | `WV_RPC_SERVER` | Server RPC macro |
| `UFUNCTION(Client)` | `WV_RPC_CLIENT` | Client RPC macro |
| `UFUNCTION(NetMulticast)` | `WV_RPC_MULTICAST` | Multicast RPC macro |
| `GetLifetimeReplicatedProps()` | `GetReplicatedProperties()` | Property registration hook |
| `IsNetRelevantFor()` | `IsActorRelevantForConnection()` | Relevancy check |
| `FRepLayout` | `ActorReplicationState` | Per-connection replication state |
---
## File Structure
```
WVNet/
├── include/
│ └── wvnet/
│ ├── Core.h # Core definitions and macros
│ ├── NetworkManager.h
│ ├── NetDriver.h
│ ├── NetConnection.h
│ ├── ReplicationManager.h
│ ├── RPCManager.h
│ ├── Actor.h
│ ├── World.h
│ ├── Packet.h
│ ├── BitStream.h # Serialization helper
│ └── platform/
│ ├── Socket.h # Platform-agnostic API
│ ├── SocketWindows.h # Windows implementation
│ └── SocketUnix.h # Unix/macOS implementation
├── src/
│ ├── NetworkManager.cpp
│ ├── NetDriver.cpp
│ ├── NetConnection.cpp
│ ├── ReplicationManager.cpp
│ ├── RPCManager.cpp
│ ├── Actor.cpp
│ ├── World.cpp
│ ├── Packet.cpp
│ ├── BitStream.cpp
│ └── platform/
│ ├── SocketWindows.cpp
│ └── SocketUnix.cpp
├── samples/
│ ├── SimpleServer/
│ └── SimpleClient/
├── tests/
│ ├── SocketTests.cpp
│ ├── SerializationTests.cpp
│ └── ReplicationTests.cpp
├── CMakeLists.txt
└── README.md
```
---
**End of Design Document**