Network Modules
As your multiplayer game grows, you'll quickly find yourself wanting to reuse networking logic across different objects. Maybe multiple enemy types all need a health system, or several different objects share the same sync pattern. Putting all of that into MonoBehaviours can get messy, especially when you want to share logic between objects that don't share a component hierarchy.
NetworkModule is a base class that solves this. It allows you to create modular, network-aware components that are not MonoBehaviours. These modules can be attached to any NetworkIdentity, enabling code reuse and clean separation of concerns in your networked game.
Key Features
- Modularity: Encapsulate specific functionality into reusable modules.
- Network Integration: Access networking properties like isServer, isClient, and send RPCs.
- Lifecycle Hooks: Override OnSpawn and OnDespawned for initialization and cleanup and many others.
Example: HealthModule with State Synchronization
Here's a concise example of a HealthModule that synchronizes a health value across the network, ensuring proper state synchronization even if clients reconnect.
using PurrNet;
using PurrNet.Transports;
using System;
public class HealthModule : NetworkModule
{
private float _health;
public event Action<float> OnHealthChanged;
public float Health
{
get => _health;
set
{
if (!isServer) return;
// Synchronize health with all clients
RpcUpdateHealth(value);
}
}
public HealthModule(float initialHealth)
{
_health = initialHealth;
}
public override void OnSpawn()
{
base.OnSpawn();
if (isServer)
{
// Ensure new or reconnecting clients receive the current health
RpcUpdateHealth(_health);
}
}
[ObserversRPC(Channel.Reliable, bufferLast: true, runLocally: true)]
private void RpcUpdateHealth(float healthValue)
{
_health = healthValue;
OnHealthChanged?.Invoke(_health);
}
}
Explanation
- Buffered RPC: The RpcUpdateHealth method uses bufferLast: true to buffer the last health value so new or reconnecting clients receive the correct state.
- Server Authority: Only the server can modify the Health property to maintain authoritative game state.
- Event Handling: Clients can subscribe to OnHealthChanged to react to health updates.
Attaching HealthModule to a NetworkIdentity
using UnityEngine;
using PurrNet;
public class Player : NetworkIdentity
{
private HealthModule healthModule = new HealthModule(100); //Default to 100hp
public override void OnSpawn()
{
healthModule.OnHealthChanged += OnHealthChanged;
}
public override void OnDespawned()
{
healthModule.OnHealthChanged -= OnHealthChanged;
}
private void OnHealthChanged(float newHealth)
{
// Update UI or other client-side logic
Debug.Log($"Player health is now {newHealth}");
}
public void TakeDamage(float damage)
{
if (!isServer) return;
healthModule.Health -= damage;
if (healthModule.Health <= 0)
{
// Handle player death
Debug.Log("Player has died.");
}
}
}