Warning
This repository is now deprecated.
Please use Godot Rollback Netcode Mono, a C# wrapper for SnopekGame's Godot Rollback Netcode addon instead!
This is a C# implementation of rollback netcode in Godot. This is based off of SnopekGames' Godot Rollback Netcode addon.
public class Game : Node2D { public enum Scenes { Player } NetcodeManager _netcodeManager; DIContainer _diContainer; PackedScene _playerPrefab; Player _player; public void _Ready() { InitDIContainer(); StartGame(); } private void InitDIContainer() { _netcodeManager = new NetcodeManager(); _diContainer = new DIContainer(); AddChild(_diContainer); _diContainer.Bind<NetcodeManager>().To(_netcodeManager); } private void StartGame() { // Spawn automatically registers the instance to the // sync manager if it implements any of the INetwork // interfaces. _player = _netcodeManager.Spawn(this, (int)Scenes.Player, _playerPrefab); } } public class Explosion : Node2D, INetworkSpawnable, IInjectable { public class SpawnData { public Vector2 position; } private NetworkTimer _despawnTimer; private NetcodeManager _netcodeManager; public void Construct(DIContainer container) { _netcodeManager = container.Resolve<NetcodeManager>(); } public void _Ready() { _despawnTimer = GetNode<NetworkTimer>("ExplosionTimer"); _despawnTimer.Timeout += OnDespawnTimerTimeout; } // ----- INetworkSpawnable ----- // public void NetworkSpawn(object data) { var typedData = data as SpawnData; GlobalPosition = typedData.position; _despawnTimer.Start(); } public void NetworkDespawn() {} // ----- INetworkSpawnable ----- // public void OnDespawnTimerTimeout() { _netcodeManager.Despawn(this); } } public class Bomb : Node2D, INetworkSpawnable, IInjectable { public enum Scenes { Explosion } public class SpawnData { public Vector2 position; } private NetworkTimer _explosionTimer; private NetcodeManager _netcodeManager; public void Construct(DIContainer container) { _netcodeManager = container.Resolve<NetcodeManager>(); } public void _Ready() { _explosionTimer = GetNode<NetworkTimer>("ExplosionTimer"); _explosionTimer.Timeout += OnExplosionTimerTimeout; } // ----- INetworkSpawnable ----- // public void NetworkSpawn(object data) { var typedData = data as SpawnData; GlobalPosition = typedData.position; _explosionTimer.Start(); } public void NetworkDespawn() { } // ----- INetworkSpawnable ----- // public void OnExplosionTimerTimeout() { _netcodeManager.Spawn(this, (int)Scenes.Explosion, new Explosion.SpawnData() { position = GlobalPosition }); _netcodeManager.Despawn(this); } } public class Player : Node2D, INetworkSerializable, INetworkInput, INetworkProcess, INetworkSpawner { public enum Scenes { Bomb } public class Data { public Vector2 position; public Vector2 attackCooldown; } public class Input { public bool bombDropped; public Vector2 inputDirection; } public Data data; private NetcodeManager _netcodeManager; public void Construct(DIContainer container) { _netcodeManager = container.Resolve<NetcodeManager>(); } // ----- INetworkSpawner ----- // public void SpawnwerSpawned(int id, Node scene) { if (id == (int) Scenes.Bomb) { scene.Exploded += OnBombExploded; } } public void SpawnwerDespawned(int id, Node scene) { if (id == (int) Scenes.Bomb) { scene.Exploded -= OnBombExploded; } } // ----- INetworkSpawner ----- // // ----- INetworkProcess ----- // private void NetworkProcess(object input) { if (input is Input typedInput) { if (typedInput.bombDropped) _netcodeManager.Spawn(this, (int)Scenes.Bomb, new Bomb.SpawnData() { position = GlobalPosition }); } } // ----- INetworkProcess ----- // // ----- INetworkSerializable ----- // public object Save() { return Object.MemberwiseClone(data); } public void Load(object data) { data = Object.MemberwiseClone(data) as Data; } // ----- INetworkSerializable ----- // // ----- INetworkInput ----- // public object GetLocalInput() { return new Input() { inputDirection = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); bombDropped = Input.IsActionJustPressed("ui_accept") }; } public object PredictLocalInput(object input, int ticksSinceRealInput) { var input = Object.MemberwiseClone(input); if (input.bombDropped) input.bombDropped = false; return input; } // ----- INetworkInput ----- // private void OnBombExploded() { } }