using Multiplayer.Network; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEditor.PackageManager; using UnityEngine; public class GameClient : MonoBehaviour { private TcpClient client; private NetworkStream stream; private bool isConnected; private static GameClient instance; private ushort localPlayerId; private string username; //Player isntance [SerializeField] private PlayerController playerController; //Players [SerializeField]private GameObject playerPrefab; private Dictionary<ushort, LobbyPlayer> lobbyPlayers = new Dictionary<ushort, LobbyPlayer>(); private readonly Queue<Action> mainThreadActions = new Queue<Action>(); public static GameClient Instance => instance; public void ConnectToServer(string ip, string playerName) { client = new TcpClient(); client.Connect(ip, 7777); stream = client.GetStream(); isConnected = true; // Set username and create PlayerData username = playerName; // Send player join message to server SendNetworkMessage(NetworkCommand.PlayerJoin, playerController.playerData); // Start listening thread new Thread(ListenForMessages).Start(); } private void Awake() { if (instance != null && instance != this) { Destroy(gameObject); // Ensures there's only one instance } else { instance = this; } } private void Start() { ConnectToServer("localhost", playerController.name); } //Listens messages from the game server void ListenForMessages() { byte[] buffer = new byte[1024]; while (isConnected) { if (stream.DataAvailable) { int bytesRead = stream.Read(buffer, 0, buffer.Length); byte[] receivedData = new byte[bytesRead]; Array.Copy(buffer, receivedData, bytesRead); NetworkMessage msg = NetworkSerializer.FromBytes<NetworkMessage>(receivedData); switch (msg.command) { case NetworkCommand.PlayerJoinConfirmed: PlayerData myData = (PlayerData)msg.data; localPlayerId = myData.playerId; Debug.Log("Received PlayerJoinConfirmed. My player ID: " + localPlayerId); break; case NetworkCommand.PlayerJoin: PlayerData joinData = (PlayerData)msg.data; if (joinData.playerId == localPlayerId) break; // Don't spawn yourself mainThreadActions.Enqueue(() => SpawnPlayer(joinData.playerId, joinData)); break; case NetworkCommand.PlayerMove: PlayerData moveData = (PlayerData)msg.data; mainThreadActions.Enqueue(() => UpdatePlayerPosition(moveData.playerId, moveData.position, moveData.rotation)); break; case NetworkCommand.PlayerLeave: PlayerData leaveData = (PlayerData)msg.data; mainThreadActions.Enqueue(() => DestroyPlayer(leaveData.playerId)); break; } } } } public void SendNetworkMessage(NetworkCommand command, INetworkSerializable data) { if (isConnected) { NetworkMessage msg = new NetworkMessage { command = command, data = data }; byte[] serialized = NetworkSerializer.ToBytes(msg); SendData(serialized); } else { Debug.Log("The client is not connected to a server!"); } } //Sends data to the server public void SendData(byte[] data) { if (isConnected) { stream.Write(data, 0, data.Length); } } //Spawns other player that connected to the server void SpawnPlayer(ushort playerId, PlayerData playerData) { GameObject player = Instantiate(playerPrefab, Vector3.zero, Quaternion.identity); player.GetComponent<LobbyPlayer>().SetPlayerData(playerData); lobbyPlayers.Add(playerId, player.GetComponent<LobbyPlayer>()); } //Removes the player from the server void DestroyPlayer(ushort playerId) { if (lobbyPlayers.TryGetValue(playerId, out LobbyPlayer player)) //Player search by ID { Destroy(player); //Destroys the player GameObject lobbyPlayers.Remove(playerId); // Removes it from the Dictionary } } //Updates the player position void UpdatePlayerPosition(ushort playerId, Vector3 pos, Quaternion rotation) { if (playerId == playerController.playerData.playerId) return; // Don't update our own player! if (lobbyPlayers.TryGetValue(playerId, out LobbyPlayer player)) { if (IsPositionValid(pos)) { player.NextPosition = pos; } player.NextRotation = rotation; } } private bool IsPositionValid(Vector3 pos) { return !(float.IsNaN(pos.x) || float.IsInfinity(pos.x) || float.IsNaN(pos.y) || float.IsInfinity(pos.y) || float.IsNaN(pos.z) || float.IsInfinity(pos.z)); } private void Update() { while (mainThreadActions.Count > 0) { mainThreadActions.Dequeue().Invoke(); } } //Invoked when the client disconnects void OnDestroy() { isConnected = false; client?.Close(); } }