using System.Diagnostics; using GlobalClassLib; using LiteNetLib; using LiteNetLib.Utils; using PacketLib; namespace ONDServer.Net; public static class Server { public static ServerPlayer P1; public static ServerPlayer P2; public static readonly Dictionary Players = new(); public static ServerPlayer OtherPlayer(ServerPlayer player) => player.State.Pid == P1.State.Pid ? P2 : P1; public static bool AutoStop{ get; set; } = true; private static EventBasedNetListener listener; private static NetManager server; private const int TARGET_TICK_RATE = 30; private const double TARGET_DELTA_TIME = 1.0 / TARGET_TICK_RATE; private const long TARGET_TICK_TIME_MS = 1000 / TARGET_TICK_RATE; private static NetDataWriter writer; private static NetPacketProcessor processor; private static bool isRunning; // AI generated public static void Start(int port) { writer = new NetDataWriter(); processor = new NetPacketProcessor(); NestedTypeManager.AutoRegister(processor); processor.SubscribeReusable(OnJoinReceived); processor.SubscribeReusable(OnCommandReceived); listener = new EventBasedNetListener(); // ← Initialize the listener! server = new NetManager(listener){ AutoRecycle = true }; Console.WriteLine($"Starting server on {port}"); listener.ConnectionRequestEvent += request => { Console.WriteLine($"Connection Request from {request.RemoteEndPoint}"); if (Players.Count >= 2){ Console.WriteLine($"{request.RemoteEndPoint} denied, server full"); return; } request.Accept(); }; listener.NetworkReceiveEvent += (peer, reader, channel, method) => { OnNetworkReceive(peer, reader, method); }; listener.PeerDisconnectedEvent += OnPeerDisconnected; server.Start(port); Run(); } public static void SendPacket(T packet, NetPeer peer, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { if (peer != null) { writer.Reset(); processor.Write(writer, packet); peer.Send(writer, deliveryMethod); } } public static void SendPacket(T packet, int pid, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { SendPacket(packet, Players[pid].Peer, deliveryMethod); } public static void SendPacketToAll(T packet, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableOrdered) where T : class, new() { foreach (var player in Players.Values){ SendPacket(packet, player.Peer, deliveryMethod); } } public static void SendUpdate(GameEvent[] gevents, int pid) { SendPacket(new UpdatePlayerPacket{Events = gevents}, pid); } public static void SendUpdateToAll(GameEvent[] gevents) { SendPacketToAll(new UpdatePlayerPacket{Events = gevents}); } public static void OnJoinReceived(JoinPacket packet, NetPeer peer) { Console.WriteLine($"Received join from {packet.Username} (pid: {(uint)peer.Id})"); ServerPlayer newPlayer = (Players[peer.Id] = new ServerPlayer { Peer = peer, State = new PlayerState(peer.Id, GameLogic.START_CAMERA, [false, false, false], Power.MAX_POWER_VALUE, false), Username = packet.Username }); if (Players.Count == 1){ newPlayer.State.OfficeTileId = 10; newPlayer.State.NeighbouringTiles = [5, 11, 15]; SendPacket(new JoinAcceptPacket { State = newPlayer.State }, peer); P1 = newPlayer; } else{ newPlayer.State.OfficeTileId = 14; newPlayer.State.NeighbouringTiles = [19, 13, 9]; SendPacket(new JoinAcceptPacket { State = newPlayer.State }, peer); P2 = newPlayer; SendPacket(new OpponentInitPacket{State = newPlayer.State, Username = newPlayer.Username}, P1.Peer); SendPacket(new OpponentInitPacket{State = P1.State, Username = P1.Username}, P2.Peer); GameLogic.Init(); } } public static void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { processor.ReadAllPackets(reader, peer); } public static void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { if (Players.Count == 2){ GameLogic.DeclareWinner(OtherPlayer(Players.Values.First(p => Equals(p.Peer, peer)))); } Players.Remove(peer.Id); if (Players.Count == 0){ Stop(); } } public static void OnCommandReceived(PlayerCommandPacket packet, NetPeer peer) { CommandProcessor.Evaluate(packet.Commands, peer.Id); } public static void Update() { server.PollEvents(); GameLogic.Update(); } // AI generated public static void Run(CancellationToken cancellationToken = default) { isRunning = true; var stopwatch = Stopwatch.StartNew(); long previousTicks = 0; while (isRunning && !cancellationToken.IsCancellationRequested) { long currentTicks = stopwatch.ElapsedMilliseconds; long elapsed = currentTicks - previousTicks; if (elapsed >= TARGET_TICK_TIME_MS) { Update(); previousTicks = currentTicks; if (elapsed > TARGET_TICK_TIME_MS * 2) { Console.WriteLine($"Warning: Tick took {elapsed}ms (target: {TARGET_TICK_TIME_MS}ms)"); } } else { int sleepTime = (int)(TARGET_TICK_TIME_MS - elapsed - 2); if (sleepTime > 0) { Thread.Sleep(sleepTime); } while (stopwatch.ElapsedMilliseconds - previousTicks < TARGET_TICK_TIME_MS){ Thread.SpinWait(1); } } } } public static void Stop() { isRunning = false; server.Stop(); } }