AetherNet.Shared
0.1.0
dotnet add package AetherNet.Shared --version 0.1.0
NuGet\Install-Package AetherNet.Shared -Version 0.1.0
<PackageReference Include="AetherNet.Shared" Version="0.1.0" />
<PackageVersion Include="AetherNet.Shared" Version="0.1.0" />
<PackageReference Include="AetherNet.Shared" />
paket add AetherNet.Shared --version 0.1.0
#r "nuget: AetherNet.Shared, 0.1.0"
#:package AetherNet.Shared@0.1.0
#addin nuget:?package=AetherNet.Shared&version=0.1.0
#tool nuget:?package=AetherNet.Shared&version=0.1.0
<div align="center"> <h1>⚡ AetherNet</h1> <p><strong>GC-free, deterministic 2D physics for server-authoritative Unity games</strong></p>
<p> <img alt="Build" src="https://img.shields.io/github/actions/workflow/status/adielmag/aethernet/build.yml?style=flat-square&logo=github"> <img alt="NuGet" src="https://img.shields.io/nuget/v/AetherNet.Shared?style=flat-square&logo=nuget"> <img alt="License" src="https://img.shields.io/github/license/adielmag/aethernet?style=flat-square"> <img alt=".NET" src="https://img.shields.io/badge/.NET-8.0-512BD4?style=flat-square&logo=dotnet"> </p> </div>
What is AetherNet?
AetherNet completely decouples 2D physics simulation from Unity’s native engine. A single, deterministic physics loop powered by Aether.Physics2D (a pure C# Box2D port, actively maintained) runs identically on a headless .NET 8 server and a Unity client — with zero runtime heap allocation.
Unity is used strictly as a visual layer. There are no Rigidbody2D components, no Physics2D.Simulate calls, and no GC pressure from the physics tick. This makes AetherNet the right foundation for server-authoritative multiplayer games where client and server must agree on physics state down to the bit.
Features
- ⚡ Zero runtime GC allocation — flat memory profile during gameplay, pre-allocated parallel arrays throughout
- 🎯 100% deterministic fixed-timestep simulation — accumulator-based tick ensures identical results on server and client
- 🖥️ Headless .NET 8 server — no Unity license required on the game server; loads baked map files directly
- 💥 Unity-style collision callbacks —
OnCollisionEnter,OnCollisionExit,OnTriggerEnter,OnTriggerExitvia interface dispatch (no reflection) - 💪 Full force API —
AddForce,AddTorque,AddForceAtPositionwithForceMode(Force, Impulse, VelocityChange, Acceleration) - 🔍 Physics queries —
Raycast,OverlapCircle,OverlapBoxwith zero-alloc result buffers - 🔗 Transport-agnostic networking — bring your own transport; plug in Mirror, FishNet, LiteNetLib, or raw sockets
- 💤 Body sleep management — optional broad-phase deactivation for distant bodies
- 🔒 Rigidbody constraints —
FreezePositionX/Y,FreezeRotationapplied post-step, no drift
Install
dotnet add package AetherNet.Shared
Or add to your .csproj:
<PackageReference Include="AetherNet.Shared" Version="0.1.0" />
Architecture
graph TD
subgraph Server [".NET 8 Headless Server"]
SL[ServerTickLoop] -->|Advance| PM
ML[MapLoader] -->|CreateBody| PM
end
subgraph Shared ["AetherNet.Shared (.NET Standard 2.0 / .NET 8)"]
PM[PhysicsWorldManager]
CE[CollisionEventQueue]
CT[ContactTracker]
NS[Network Utilities\nStateSerializer · StateInterpolator\nSnapshotBuffer · TickAcknowledger]
PM --> CE
PM --> CT
end
Transport["Your Transport\nMirror · FishNet · LiteNetLib"] <-->|INetworkStateProvider| NS
Quick Start — Server
cd src/AetherNet.Server
dotnet run -- maps/level01.json
The server loads the baked level01.json, runs the physics loop at 60 Hz, and calls your INetworkStateProvider.OnTickComplete each tick.
var world = new PhysicsWorldManager(WorldConfig.Default);
var loader = new MapLoader();
loader.LoadInto(world, "maps/level01.json");
var loop = new ServerTickLoop(world);
loop.SetSnapshotCallback((states, count, tick) =>
{
int bytes = StateSerializer.Serialize(states, count, sendBuffer, 0);
myTransport.BroadcastUnreliable(sendBuffer, bytes);
});
loop.Run(CancellationToken.None);
Networking
AetherNet provides contracts and utilities, not a bundled transport:
| Type | Purpose |
|---|---|
INetworkStateProvider |
Hook into the tick loop — implement to broadcast state |
StateSerializer |
Zero-alloc binary write/read of EntityState[] arrays |
StateInterpolator |
Client-side snapshot lerp — smooths 20 Hz network updates to 144 Hz render |
SnapshotBuffer |
Circular buffer of authoritative snapshots |
TickAcknowledger |
Bitmask-based ack tracking for delta compression |
See examples/LiteNetLibExample/ for a complete working implementation using LiteNetLib.
Performance Guidelines
- No LINQ in hot paths. Use raw
forloops in physics callbacks. - No
GetComponentat runtime. Cache all references inAwake. - Never feed
Time.deltaTimeto physics. Use the accumulator exclusively viaAdvance(). - Pass large structs by
inorref.CollisionData,TriggerData,BodyDef— never copy by value in hot code. - Pre-allocate result buffers.
PhysicsQueryBufferis created once and reused indefinitely.
Contributing
- Fork the repo and create a branch:
feature/your-featureorfix/issue-description. - All changes to
AetherNet.Sharedmust passdotnet test. - PR checklist:
-
dotnet build AetherNet.slnwith no errors or warnings -
dotnet test— all tests green, determinism tests pass - No new GC.Alloc in hot paths
- New public API documented with XML summary comments
-
License
MIT — free to use in commercial and open-source projects.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Aether.Physics2D (>= 2.2.0)
- System.Memory (>= 4.5.5)
-
net8.0
- Aether.Physics2D (>= 2.2.0)
- System.Memory (>= 4.5.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.