csharp/1ZouLTReX1/FP-MOG/top%20down%20shooter/Assets/Scripts/Server.cs

Server.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System.IO;
using System.Linq;
using System.Threading;

public clast User
{
    public Statistics statisticsModule;
    public Player player;
    public Socket sock = null;
    // Size of receive buffer.
    protected const int BufferSize = 4096;
    // Receive buffer.  
    public byte[] buffer = new byte[BufferSize];
    // Received data string.
    protected MemoryStream ms = new MemoryStream();
    byte[] data;

    int bytesRec = 0;
    int offset = 0;
    int len = 0;
    int cut;

    public User(Socket sock)
    {
        // Configure  the given socket for every client.
        this.sock = sock;
        // Disable the Nagle Algorithm for this tcp socket.
        this.sock.NoDelay = true;
        // Set the receive buffer size to 4k
        this.sock.ReceiveBufferSize = 4096;
        // Set the send buffer size to 4k.
        this.sock.SendBufferSize = 4096;

        statisticsModule = new Statistics();
    }
                            
    public bool ReceiveOnce()
    {
        /*
         * returns one message in a byte array which then get processed by the client
         * one message may require serveral calls to '.Receive' function.
         */

        // Receive the response from the remote device.
        if (offset >= bytesRec)
        {
            if (SafeReceive(ref buffer, ref bytesRec, ref offset))
                return false;
        }

        len = Globals.DeSerializeLenPrefix(buffer, offset);
        offset += sizeof(int);

        while (len > 0)
        {
            cut = Math.Min(len, bytesRec - offset);
            ms.Write(buffer, offset, cut);
            len -= cut;
            offset += cut;

            if (len > 0)
            {
                // The left over of the previous message.
                if (SafeReceive(ref buffer, ref bytesRec, ref offset))
                    return false;
            }
        }

        // Process one message from the stream.
        data = ms.ToArray();
        // Clear the buffer.
        ms.SetLength(0);

        // Process the new received message.
        ProcessMessage(data);
        return true;
    }

    private bool SafeReceive(ref byte[] buffer, ref int bytesRec, ref int offset)
    {
        try
        {
            bytesRec = sock.Receive(buffer);

            // Which means an empty packet
            if (bytesRec  (ushort)x).ToList();

        serverLoop = new ServerLoop(playerPrefab);
        StartServer();
    }

    private void Update()
    {
        // Network Tick.
        lock (instantiateJobs)
        {
            for (int i = 0; i < instantiateJobs.Count; i++)
            {
                (User user, ushort id) = instantiateJobs[i]; 

                var obj = Instantiate(playerPrefab, Vector3.zero, Quaternion.idensaty);
                var tmpPlayer = obj.GetComponent();
                tmpPlayer.SetPlayerID(id);

                user.player = tmpPlayer;

                // Attach the Lag compensation module to the new instantiated player.
                obj.AddComponent().Init(user.player);
            }

            instantiateJobs.Clear();
        }

        
        lock (disconnectedClients)
        {

            foreach (var clientSock in clients.Keys)
            {
                if (clients[clientSock].player.playerContainer == null)
                {
                    OnUserDisconnect(clientSock);
                }
            }

            foreach (var disconnectedSock in disconnectedClients)
            {
                var playerId = clients[disconnectedSock].player.playerId;
                if (clients[disconnectedSock].player.playerContainer != null)
                {
                    GameObject.Destroy(clients[disconnectedSock].player.playerContainer);
                }

                lock (clients)
                {
                    clients.Remove(disconnectedSock);
                }

                lock (playerIdList)
                {
                    playerIdList.Add(playerId);
                    // return the id number to the id pool for new players to join in.
                    Console.WriteLine("Client Disconnected, Returned Player ID: " + playerId);
                    Console.WriteLine("Player Count: " + (MaximumPlayers - playerIdList.Count) + " / " + MaximumPlayers);
                }
            }

            disconnectedClients.Clear();
        }

        // Every tick we call update function which will process all the user commands and apply them to the physics world.
        serverLoop.Update(clients.Values.Select(x => x.player).ToList());

        WorldState snapshot = serverLoop.GetSnapshot();

        lock (OutputsOG)
        {
            for (int i = OutputsOG.Count - 1; i >= 0; i--)
            {
                var sock = OutputsOG[i];

                try
                {
                    SendSnapshot(sock, snapshot);
                }
                catch
                {
                    OnUserDisconnect(sock);
                }
            }
        }
    }

    private void SendSnapshot(Socket sock, WorldState snapshot)
    {
        var usr = clients[sock];
        var statisticsModule = usr.statisticsModule;
        snapshot.UpdateStatistics(statisticsModule.tickAck, statisticsModule.GetTimeSpentIdleInTicks());

        var message = ServerPktSerializer.Serialize(snapshot);
        //Console.WriteLine("Update Send Reply " + message.Length);
        BeginSend(usr, message);
    }

    void BeginSend(User user, byte[] msgArray)
    {
        byte[] wrapped = Globals.SerializeLenPrefix(msgArray);
        user.sock.BeginSend(wrapped, 0, wrapped.Length, SocketFlags.None, EndSend, user);
    }

    void EndSend(IAsyncResult iar)
    {
        User user = (iar.AsyncState as User);
        user.statisticsModule.RecordSentPacket();
        int BytesSent = user.sock.EndSend(iar);
        //Console.WriteLine("Bytes Sent: " + BytesSent);
    }

    private void StartServer()
    {
        // Establish the local endpoint for the socket. 
        IPAddress ipAddress = ServerInfo.ipAddress;
        IPEndPoint localEndPoint = ServerInfo.localEP;

        Console.WriteLine("The server is running at: " + localEndPoint.Address.ToString() + " : " + localEndPoint.Port.ToString());
        Console.WriteLine("Is loopback: " + IPAddress.IsLoopback(localEndPoint.Address));

        // Create a TCP/IP socket.  
        listenerSocket = new Socket(ipAddress.AddressFamily,
            SocketType.Stream, ProtocolType.Tcp);

        // Bind the socket to the local endpoint and listen for incoming connections.  
        try
        {
            listenerSocket.Bind(localEndPoint);
            listenerSocket.Listen(MaximumPlayers);

            Thread selectThr = new Thread(StartListening);
            selectThr.Start();
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            Application.Quit();
        }
    }

    public void StartListening()
    {
        List Inputs;
        List Errors;

        Console.WriteLine("Main Loop Listening");
        InputsOG.Add(listenerSocket);

        isRunning = true;
        while (isRunning)
        {
            Inputs = InputsOG.ToList();
            Errors = InputsOG.ToList();

            Socket.Select(Inputs, null, Errors, -1);

            foreach (Socket sock in Inputs)
            {
                if (sock == listenerSocket)
                {
                    // If the sock is the server socket then we got a new player.
                    // So we need to Accept the socket and astign the socket an available new player ID and ensaty.
                    Socket newConnection = sock.Accept();

                    if (playerIdList.Count > 0)
                    {
                        OnUserConnect(newConnection);
                    } 
                    else
                    {
                        Debug.Log($"{newConnection.RemoteEndPoint} failed to connect: Server full!");
                        newConnection.Close();
                    }
                }
                else if (sock != null)
                {
                    if (!isRunning)
                        return;

                    try
                    {
                        User usr = clients[sock];
                        // Receive and process one message at a time.
                        bool result = usr.ReceiveOnce();

                        if (result == false)
                        {
                            OnUserDisconnect(sock);
                        }
                    }
                    catch { }
                }
            }

            foreach (Socket sock in Errors)
            {
                Console.WriteLine("Client Disconnected: Cause - Error");
                OnUserDisconnect(sock);
            }

            Errors.Clear();
        }

        Debug.Log("Stop Listening");
    }

    private void OnUserConnect(Socket newConnection)
    {
        User usr = new User(newConnection);

        ushort newPlayerID = GetPlayerId();
        // Instantiate at the main thread, not here.
        lock (instantiateJobs)
        {
            instantiateJobs.Add(Tuple.Create(usr, newPlayerID));
        }

        Console.WriteLine("Client Connected, Given Player ID: " + newPlayerID);
        Console.WriteLine("Player Count: " + (MaximumPlayers - playerIdList.Count) + " / " + MaximumPlayers);

        // Send to the connected client his new astigned ID.
        BeginSend(usr, WelcomeMessage.Serialize(newPlayerID));

        clients.Add(newConnection, usr);

        InputsOG.Add(newConnection);
        lock (OutputsOG)
        {
            OutputsOG.Add(newConnection);
        }
    }

    void OnApplicationQuit()
    {
        Debug.Log("Application Quit\nClosing Socket");
        CloseServer();
    }

    static void CloseServer()
    {
        if (listenerSocket == null)
            return;

        if (isRunning) {
            isRunning = false;

            if (listenerSocket.Connected)
                listenerSocket.Shutdown(SocketShutdown.Both);

            listenerSocket.Close();
            listenerSocket = null;
            Debug.Log("s: " + listenerSocket == null);
        }
    }

    static void OnUserDisconnect(Socket sock)
    {
        try
        {
            sock.Close();

            lock (disconnectedClients)
            {
                if (clients.ContainsKey(sock))
                    disconnectedClients.Add(sock);
            }

            InputsOG.Remove(sock);
            OutputsOG.Remove(sock);

            //Console.WriteLine("Client Disconnected");
        }
        catch (Exception e) 
        {
            Debug.Log("OnUserDisconnect: " + e);
        }
    }
}

/*
     
    Dictionary m_TickStats = new Dictionary();
    void UpdateActiveState()
    {
        int tickCount = 0;
        while (Game.frameTime > m_nextTickTime)
        {
            tickCount++;

            m_serverGameWorld.ServerTickUpdate();
            m_NetworkServer.GenerateSnapshot(m_serverGameWorld, m_LastSimTime);
            m_nextTickTime += m_serverGameWorld.TickInterval;

            m_performLateUpdate = true;
        }

        //
        // If running as headless we nudge the Application.targetFramerate back and forth
        // around the actual framerate -- always trying to have a remaining time of half a frame
        // The goal is to have the while loop above tick exactly 1 time
        //
        // The reason for using targetFramerate is to allow Unity to sleep between frames
        // reducing cpu usage on server.
        //

        if (Game.IsHeadless())
        {
            float remainTime = (float)(m_nextTickTime - Game.frameTime);

            int rate = m_serverGameWorld.TickRate;
            if (remainTime > 0.75f * m_serverGameWorld.TickInterval)
                rate -= 2;
            else if (remainTime < 0.25f * m_serverGameWorld.TickInterval)
                rate += 2;

            Application.targetFrameRate = rate;

            //
            // Show some stats about how many world ticks per unity update we have been running
            //

            if (debugServerTickStats.IntValue > 0)
            {
                if (Time.frameCount % 10 == 0)
                    GameDebug.Log(remainTime + ":" + rate);

                if (!m_TickStats.ContainsKey(tickCount))
                    m_TickStats[tickCount] = 0;
                m_TickStats[tickCount] = m_TickStats[tickCount] + 1;
                if (Time.frameCount % 100 == 0)
                {
                    foreach (var p in m_TickStats)
                    {
                        GameDebug.Log(p.Key + ":" + p.Value);
                    }
                }
            }
        }
    }

    */