using System; using System.Collections; using System.Diagnostics; using System.Threading; using FNAF_Server.Map; using LiteNetLib; using LiteNetLib.Utils; using PacketLib; namespace FNAF_Server; public 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; private static EventBasedNetListener listener; private static NetManager server; private const int TargetTickRate = 30; private const double TargetDeltaTime = 1.0 / TargetTickRate; private const long TargetTickTimeMs = 1000 / TargetTickRate; private static NetDataWriter writer; private static NetPacketProcessor processor; private static bool isRunning; 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 += (peer, info) => { OnPeerDisconnected(peer, info); }; 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{eventQueue = new EventQueue{Events = events}}, pid); SendPacket(new UpdatePlayerPacket{events = gevents}, pid); } public static void SendUpdateToAll(GameEvent[] gevents) { // SendPacketToAll(new UpdatePlayerPacket{eventQueue = new EventQueue{Events = events}}); 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 { pid = peer.Id, camera = GameLogic.START_CAMERA, doorStates = [false, false, 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 (peer.Tag != null) { Players.Remove(peer.Id); } } public static void OnCommandReceived(PlayerCommandPacket packet, NetPeer peer) { CommandProcessor.Evaluate(packet.commands, peer.Id); } public static void Update() { server.PollEvents(); // Console.WriteLine("update"); GameLogic.Update(); } 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 >= TargetTickTimeMs) { Update(); previousTicks = currentTicks; if (elapsed > TargetTickTimeMs * 2) { Console.WriteLine($"Warning: Tick took {elapsed}ms (target: {TargetTickTimeMs}ms)"); } } else { int sleepTime = (int)(TargetTickTimeMs - elapsed - 2); if (sleepTime > 0) { Thread.Sleep(sleepTime); } while (stopwatch.ElapsedMilliseconds - previousTicks < TargetTickTimeMs){ Thread.SpinWait(1); } } } } public static void Stop() { isRunning = false; } }