Multiplayer netcode for Godot C#
Nebula is a tick-based, server-authoritative networking framework that makes building responsive online games in Godot straightforward. It handles the hard partsβclient-side prediction, rollback, interpolation, and state synchronizationβso you can focus on your game.
- Why Nebula?
- Quick Example
- Jump In
- Features
- Installation
- Core Concepts
- Comparison
- Documentation
- Roadmap
- Community
- Contributing
Building multiplayer games is hard. Building multiplayer games that feel good in Godot is even harder. Nebula solves the core challenges:
- Instant responsiveness β Client-side prediction means players see immediate feedback, not 100ms of input lag
- Cheat resistance β Server-authoritative model where the server is always the source of truth
- Smooth visuals β Automatic interpolation for non-owned entities eliminates jitter
- Simple API β Annotate properties with
[NetProperty]and let the source generator handle the rest - Zero-allocation hot paths β Carefully optimized to avoid GC pressure; no lag spikes from garbage collection during gameplay
using Godot;
using Nebula;
public partial class Player : NetNode3D
{
public Player()
{
Network.InitializeInput<PlayerInput>();
}
// Synced to all clients, with rollback and interpolation for smooth movement
[NetProperty(Interpolate = true, Predicted = true, NotifyOnChange = true)]
public Vector3 Position { get; set; }
// Defines misprediction threshold for automagic rollback
public float PositionPredictionTolerance { get; set; } = 2f;
// Interest management - only visible to specific players
[NetProperty(InterestMask = 0x02)]
public int SecretScore { get; set; }
// Called when Position changes on clients
protected virtual void OnNetChangePosition(int tick, Vector3 oldVal, Vector3 newVal)
{
GD.Print($"Moved to {newVal}");
}
public override void _NetworkProcess(int tick)
{
// Both server AND owning client run this for prediction
ref readonly var input = ref Network.GetInput<PlayerInput>();
// Normalized inputs for easy cheat/hack prevention!
Position += new Vector3(input.MoveX, 0, input.MoveY).Normalized() * Speed;
}
}If you're eager to get started quickly, you can follow the Big Chungus tutorial to build your first game with Nebula + Godot right away.
- Tick-based synchronization β Deterministic simulation at configurable tick rates (default 30Hz)
- Server-authoritative β Single source of truth prevents cheating and ensures consistency
- Input authority β Clients control specific entities; server validates and processes inputs
- Interest management β Fine-grained control over what data each player receives
- Instant feedback β Players see results immediately, not after round-trip latency
- Automatic rollback β When predictions are wrong, Nebula corrects seamlessly
- Configurable tolerance β Tune misprediction thresholds per-property
- Interpolation β Smooth movement for entities you don't control
- Prediction + Interpolation β Owned entities predict, others interpolateβsame property, automatic switching
- Source generators β No boilerplate; annotate properties and methods
- Compile-time validation β Errors like missing handlers caught at build time
- Typed inputs β Zero-allocation input structs with full type safety
- Virtual methods β Hook into network events with overridable virtual methods
- GC-friendly design β Hot paths avoid allocations; no lag spikes from .NET garbage collection
- Struct-based serialization β Inputs and state use unmanaged structs, not heap objects
- Pooled buffers β Network buffers are reused, not allocated per-packet
- No boxing β PropertyCache union type avoids boxing value types
- Multiple worlds β Run separate game instances (dungeons, lobbies, matches) in one server process
- Flexible serialization β Built-in support for primitives, vectors, quaternions, and custom types
- ENet transport β Reliable UDP with automatic connection management
- Godot 4.5+ with .NET support
- .NET 10.0+
-
Add Nebula to your project
Copy the
addons/Nebulafolder into your project'saddonsdirectory. -
Import in your .csproj
<Project Sdk="Godot.NET.Sdk/4.5.1"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> </PropertyGroup> <Import Project="addons\Nebula\Nebula.props" /> </Project>
-
Build your project
This compiles Nebula, allowing you to enable the plugin.
-
Enable the plugin
In Godot: Project β Project Settings β Plugins β Enable "Nebula"
Exports can fix the network role at compile time (NetRunner.IsServer / IsClient become const, so the
other role's code is compiled out), opt into BSON persistence support (off by default), and opt into
trimming the game assembly on NativeAOT platforms. All three are preset options set by Nebula's export
plugin; the editor build is unaffected. See
Build Configuration.
Inherit from NetNode, NetNode2D, or NetNode3D to create networked entities:
public partial class Enemy : NetNode3D
{
[NetProperty]
public int Health { get; set; } = 100;
}Mark properties for synchronization with [NetProperty]:
[NetProperty] // Basic sync
[NetProperty(NotifyOnChange = true)] // Triggers OnNetChange{Name}() on change
[NetProperty(Interpolate = true)] // Smooth interpolation for non-owners
[NetProperty(Predicted = true)] // Client-side prediction for owners
[NetProperty(InterestMask = 0x04)] // Only sync to players with this interest layerDefine input structs and process them on both server and client:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PlayerInput
{
public float MoveX;
public float MoveY;
public bool Jump;
}
// In your node:
public override void _PhysicsProcess(double delta)
{
Network.SetInput(new PlayerInput
{
MoveX = Input.GetAxis("left", "right"),
MoveY = Input.GetAxis("up", "down"),
Jump = Input.IsActionPressed("jump")
});
}// Server-side spawning with input authority
var player = playerScene.Instantiate<Player>();
worldRunner.Spawn(player, inputAuthority: peer);Nebula's version of an RPC.
[NetFunction(Source = NetFunction.NetworkSources.Client, ExecuteOnCaller = false)]
public void JoinGame()
{
var player = playerScene.Instantiate<Player>();
Network.CurrentWorld.Spawn(newPlayer, inputAuthority: Network.CurrentWorld.NetFunctionContext.Caller);
}| Feature | Nebula | Godot Built-in | Netfox |
|---|---|---|---|
| Language | C# only | GDScript & C# | GDScript & C# |
| Architecture | Server-authoritative | Flexible (RPC-based) | Server-authoritative |
| Client-side Prediction | β Built-in | β Manual | β Built-in |
| Rollback/Reconciliation | β Automatic | β Manual | β Automatic |
| Interpolation | β Per-property | β MultiplayerSynchronizer | β Built-in |
| Interest Management | β Fine-grained (property-level) | β Not built-in | |
| Multiple Game Worlds | β Built-in | β Manual | β Not built-in |
| Tick-based Simulation | β Yes | β Frame-based | β Yes |
| Source Generation | β Zero boilerplate | β N/A | β N/A |
| Compile-time Validation | β Yes | β No | β No |
| Transport | ENet (UDP) | ENet, WebSocket, WebRTC | Godot's built-in |
| Learning Curve | Moderate | Low | Moderate |
Choose Nebula if you:
- Are building in C#, want maximum type safety, and performance optimization
- Need fine-grained control over what data each player sees
- Want prediction and interpolation to "just work" together
- Are building a game with instances areas, lobbies, matches (multiple worlds)
Choose Godot's built-in multiplayer if you:
- Want the simplest possible setup
- Are building a casual/turn-based game where latency isn't critical
- Prefer GDScript and minimal dependencies
Choose Netfox if you:
- Prefer GDScript but still want prediction/rollback
- Want the noray integration for easier connectivity
- Are looking for a middle-ground solution
Comprehensive documentation is available at: https://nebula.heavenlode.com
For implementation details and architecture overview, see NEBULA_OVERVIEW.mdc. This file is particularly useful for AI/vibe coding as well.
- Client-side prediction and rollback
- Property interpolation
- Interest management
- Multiple world support
- Physics rollback (blocked by Godot #2821)
- Lag compensation for projectiles/hitscan
- Discord β Chat, questions, and support
- GitHub Issues β Bug reports and feature requests
Contributions are welcome! Whether it's bug reports, feature requests, documentation improvements, or code contributions.
- Questions or ideas? Drop by the Discord or open an issue
- Want to contribute code? Please open an issue first to discuss your proposal
- Found a bug? Bug reports with reproduction steps are incredibly helpful
Built with π for the Godot community
