Newer
Older
Simple-Multiplayer-Unity3D / Assets / Scripts / GameServer.cs
using Multiplayer.Network;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

public class GameServer : MonoBehaviour
{
    private TcpListener listener;
    private List<TcpClient> clients = new List<TcpClient>();
    private Dictionary<ushort, PlayerData> connectedPlayers = new Dictionary<ushort, PlayerData>();
    private Dictionary<TcpClient, ushort> clientToPlayerId = new Dictionary<TcpClient, ushort>();
    private Thread serverThread;
    private bool isRunning;
    private ushort nextPlayerId = 1;

    void Start()
    {
        isRunning = true;
        serverThread = new Thread(RunServer);
        serverThread.Start();
    }

    //Runs the server
    void RunServer()
    {
        listener = new TcpListener(IPAddress.Any, 7777);
        listener.Start();
        Debug.Log("Server started on port 7777");

        while (isRunning)
        {
            if (listener.Pending())
            {
                TcpClient client = listener.AcceptTcpClient();
                clients.Add(client);
                Debug.Log("Client connected!");

                Thread clientThread = new Thread(() => HandleClient(client));
                clientThread.Start();
            }

            Thread.Sleep(10);
        }
    }

    void HandleClient(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];

        try
        {
            // Wait for initial player data (as serialized NetworkMessage)
            int bytesRead = stream.Read(buffer, 0, buffer.Length);
            byte[] receivedData = new byte[bytesRead];
            Array.Copy(buffer, receivedData, bytesRead);

            NetworkMessage joinMsg = NetworkSerializer.FromBytes<NetworkMessage>(receivedData);
            if (joinMsg.command != NetworkCommand.PlayerJoin || !(joinMsg.data is PlayerData))
            {
                Debug.LogError("Invalid initial message received from client.");
                return;
            }

            PlayerData playerData = (PlayerData)joinMsg.data;
            playerData.playerId = nextPlayerId++;

            // Store player info
            connectedPlayers[playerData.playerId] = playerData;
            clientToPlayerId[client] = playerData.playerId;

            Debug.Log($"Player '{playerData.username}' connected with ID {playerData.playerId}");

            //Send PlayerJoinConfirmed message to this client
            NetworkMessage confirmMsg = new NetworkMessage
            {
                command = NetworkCommand.PlayerJoinConfirmed,
                data = playerData
            };

            byte[] confirmBytes = NetworkSerializer.ToBytes(confirmMsg);
            stream.Write(confirmBytes, 0, confirmBytes.Length);

            // Send existing players to the new client
            SendExistingPlayers(client, playerData.playerId);

            // Notify all clients of the new player
            BroadcastPlayerJoin(playerData);

            while (isRunning && client.Connected)
            {
                if (stream.DataAvailable)
                {
                    bytesRead = stream.Read(buffer, 0, buffer.Length);
                    receivedData = new byte[bytesRead];
                    Array.Copy(buffer, receivedData, bytesRead);

                    NetworkMessage msg = NetworkSerializer.FromBytes<NetworkMessage>(receivedData);

                    switch (msg.command)
                    {
                        case NetworkCommand.PlayerMove:
                            PlayerData moveData = (PlayerData)msg.data;
                            moveData.playerId = playerData.playerId; // ensure correct ID
                            connectedPlayers[moveData.playerId] = moveData;
                            BroadcastPlayerMove(moveData);
                            break;

                            // Add more cases here if needed (e.g. chat, item pickup, etc.)
                    }
                }

                Thread.Sleep(10);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Exception while handling client: {ex}");
        }
        finally
        {
            // Handle disconnect
            if (clientToPlayerId.TryGetValue(client, out ushort id))
            {
                BroadcastPlayerLeave(id);
                connectedPlayers.Remove(id);
                clientToPlayerId.Remove(client);
            }

            clients.Remove(client);
            client.Close();
        }
    }

    void SendExistingPlayers(TcpClient newClient, ushort newPlayerId)
    {
        NetworkStream stream = newClient.GetStream();

        foreach (KeyValuePair<ushort, PlayerData> kvp in connectedPlayers)
        {
            if (kvp.Key != newPlayerId)
            {
                NetworkMessage msg = new NetworkMessage
                {
                    command = NetworkCommand.PlayerJoin,
                    data = kvp.Value
                };

                byte[] data = NetworkSerializer.ToBytes(msg);
                stream.Write(data, 0, data.Length);
            }
        }
    }

    void BroadcastPlayerJoin(PlayerData playerData)
    {
        NetworkMessage msg = new NetworkMessage
        {
            command = NetworkCommand.PlayerJoin,
            data = playerData
        };
        BroadcastSerialized(msg);
    }

    void BroadcastPlayerMove(PlayerData playerData)
    {
        NetworkMessage msg = new NetworkMessage
        {
            command = NetworkCommand.PlayerMove,
            data = playerData
        };
        BroadcastSerialized(msg);
    }

    void BroadcastPlayerLeave(ushort playerId)
    {
        PlayerData leaveData = new PlayerData { playerId = playerId };
        NetworkMessage msg = new NetworkMessage
        {
            command = NetworkCommand.PlayerLeave,
            data = leaveData
        };
        BroadcastSerialized(msg);
    }

    void BroadcastSerialized(NetworkMessage msg, TcpClient excludeClient = null)
    {
        byte[] bytes = NetworkSerializer.ToBytes(msg);
        foreach (TcpClient client in clients)
        {
            if (client == excludeClient || !client.Connected) continue;

            try
            {
                client.GetStream().Write(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Debug.LogError("Failed to send message to client: " + e);
            }
        }
    }


    void OnDestroy()
    {
        isRunning = false;
        listener?.Stop();
        foreach (TcpClient client in clients)
        {
            client.Close();
        }
    }
}