using System.Collections.Generic; namespace Luneraria.Core { /// Roles of a session (lune_session.gd Role). public enum LuneRole { Offline, Host, Client } /// One lobby row: peer 0 is a bot, peer 1 the host. public sealed class LobbyEntry { public int Peer; public int Slot; public string Name = ""; public bool Ready; public bool Bot; public LobbyEntry Clone() { return (LobbyEntry)MemberwiseClone(); } } /// Result of a session intent (Godot returns a Dictionary with ok, message, pending). public struct SessionResult { public bool Ok; public string Message; public bool Pending; public SessionResult(bool ok, string message, bool pending = false) { Ok = ok; Message = message; Pending = pending; } } /// One player row of the public state; Draft is set only for the receiving seat. public sealed class NetPlayerRow { public int Slot; public string Name = ""; public bool IsHuman; public int Gold; public bool Ready; public int Earned; public int Spent; public int NextSeq; public List Roster = new List(); public HeroState Hero; public List Bookings = new List(); /// Private draft of the receiving seat only; null for every other seat. public Draft Draft; /// Undo stack size of the receiving seat (0 for others). public int Undo; /// S1: permanent champion income (public, like the champion bookings). public int IncomeBonus; } /// Public state of the match for one seat (rpc_state). public sealed class NetState { public Phase Phase; public int Round; public int Winner; public string EndNote = ""; public Dictionary BuildingHp = new Dictionary(); public BossState Boss = new BossState(); public Settlement LastSettlement; public List DestroyedCores = new List(); public List Players = new List(); } /// A mobile unit as it crosses the wire (rpc_combat_begin / rpc_units_spawned). public sealed class NetUnitRecord { public int Id; public string Kind = ""; public int Team; public int TeamIndex; public int OwnerSlot = -1; public string Uid = ""; public string Lane = ""; public string Zone = ""; public UnitDef Def = new UnitDef(); public double X; public double Y; public double Facing; public double Hp; public double MaxHp; public bool Alive; } /// /// Outbound session messages (the Godot RPCs). A transport or a test recorder implements this; /// without a link the session still runs offline and for in-process host tests. /// public interface ILuneNetLink { /// Reliable message to one peer (1 = host). void Send(int peer, string kind, object payload); /// Unreliable ordered combat frame to one peer. void SendFrame(int peer, int serial, double elapsed, float[] units, float[] buildingHp); } /// Frames carry 13 floats per mobile unit (lune_session.gd FRAME_STRIDE). public static class LuneNet { public const int DefaultPort = 24571; public const int MaxClients = 5; public const int Protocol = 4; /// Seconds between combat snapshots the host sends. public const double FrameInterval = 0.05; public const int FrameStride = 13; public const string KHello = "hello"; public const string KSubmit = "submit"; public const string KReady = "ready"; public const string KRefused = "refused"; public const string KFeedback = "feedback"; public const string KSetup = "setup"; public const string KState = "state"; public const string KLobby = "lobby"; public const string KCombatBegin = "combat_begin"; public const string KUnitsSpawned = "units_spawned"; } }