// ReSharper restore FunctionNeverReturns private static void ListenForNewConnectionThread() { try { _tcpListener.Start(); } catch (SocketException ex) { //todo: this will cause hard crash on the client, need a nicer way for the client to handle errors here if (ex.ErrorCode == 10048) throw new Exception("Only one server allowed at a time."); } while (true) { TcpClient client; System.Net.EndPoint endPoint; //blocks until a client has connected to the server try { client = _tcpListener.AcceptTcpClient(); client.GetStream().WriteTimeout = 30000; endPoint = client.Client.RemoteEndPoint; } catch { WriteToServerConsoleLog("Failed to accept connection"); continue; } try { WriteToServerConsoleLog(string.Format("Accepting connection from {0}", endPoint)); var connect = new Connect(); connect.AcceptNewConnection(client); //backdoor constructor because a player does not exist yet var player = new NetworkPlayer(_nextPlayerId, connect.UserName, client) {Coords = new Coords(WorldData.SizeInBlocksX / 2f, 0, WorldData.SizeInBlocksZ / 2f)}; player.Coords.Yf = WorldData.Chunks[player.Coords].HeightMap[player.Coords.Xblock % Chunk.CHUNK_SIZE, player.Coords.Zblock % Chunk.CHUNK_SIZE] + 1; //start player on block above the surface new Connect(player.Id, player.UserName, player.Coords) { ConnectedPlayer = player, Immediate = true }.Send(); _nextPlayerId++; WriteToServerConsoleLog(String.Format("{0} (id {1}, ip {2}) Connected", player.UserName, player.Id, player.IpAddress)); var tcpThread = new Thread(() => PlayerThread(player)) { IsBackground = true, Name = "PlayerThread" }; tcpThread.Start(); } catch (Exception ex) { WriteToServerConsoleLog(string.Format("Failed to accept connection from {0}: {1}", endPoint, ex.Message)); } } // ReSharper disable FunctionNeverReturns }
internal static void Connect(object sender, DoWorkEventArgs e) { var args = (object[])e.Argument; ServerIp = (IPAddress)args[0]; ServerPort = (ushort)args[1]; Players.Clear(); //when running client and server it takes a second for the server to start listening so make multiple connection attempts //when joining a server only try once because the attempt can take a long time ~22secs var connectionAttempts = (Config.IsSinglePlayer ? 4 : 1); for (var tries = 0; tries < connectionAttempts; tries++) { Settings.Launcher.UpdateProgressInvokable(string.Format("Connecting ({0} of {1})", tries + 1, connectionAttempts), tries + 1, connectionAttempts); try { TcpClient = new TcpClient(); TcpClient.Connect(ServerIp, ServerPort); break; } catch (SocketException) { if (tries == connectionAttempts - 1) { throw new ServerConnectException(); } Thread.Sleep(1000); //wait one second before trying again } } _tcpStream = TcpClient.GetStream(); _tcpStream.ReadTimeout = 15000; //15s timeout during connect Settings.Launcher.UpdateProgressInvokable("Connected...", 0, 0); var connect = new Connect(-1, Config.UserName, new Coords()); try { connect.Send(); //server will immediately reply to tell us where we are and our Id, or disconnect us var actionTypebytes = new byte[sizeof(ushort)]; var bytesRead = 0; while (bytesRead < actionTypebytes.Length) { bytesRead += _tcpStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead); } var actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0); if (actionType == ActionType.Connect) { connect.Receive(); } else if (actionType == ActionType.Disconnect) { var disconnect = new Disconnect(); disconnect.Receive(); } else { throw new Exception(string.Format("Received {0} packet out of order during connect sequence.", actionType)); } Game.Player = Players[connect.PlayerId]; //then a list of the players connected, followed by the world Settings.Launcher.UpdateProgressInvokable("Waiting for World...", 0, 0); while (!WorldData.IsLoaded) { bytesRead = 0; while (bytesRead < actionTypebytes.Length) { bytesRead += _tcpStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead); } actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0); switch (actionType) { case ActionType.Connect: var recvPlayerList = new Connect(); recvPlayerList.Receive(); break; case ActionType.GetWorld: var getWorld = new GetWorld(); getWorld.Receive(); break; default: throw new Exception(string.Format("Received {0} packet out of order during connect sequence.", actionType)); } } } catch (Exception ex) { //HandleNetworkError(ex); throw new ServerConnectException(ex); } _tcpStream.ReadTimeout = -1; TcpClient.NoDelay = true; var listenerThread = new Thread(ListenForServerMessageThread) { IsBackground = true, Name = "ListenForServerMessageThread" }; listenerThread.Start(); //send misc player information to server periodically (ie fps, memory). _playerInfoTimer = new System.Timers.Timer(PlayerInfo.PLAYER_INFO_SEND_INTERVAL); _playerInfoTimer.Start(); _playerInfoTimer.Elapsed += _playerInfoTimer_Elapsed; //wire elapsed event handler }
internal static void Connect(object sender, DoWorkEventArgs e) { var args = (object[])e.Argument; ServerIp = (IPAddress)args[0]; ServerPort = (ushort)args[1]; Players.Clear(); //when running client and server it takes a second for the server to start listening so make multiple connection attempts //when joining a server only try once because the attempt can take a long time ~22secs var connectionAttempts = (Config.IsSinglePlayer ? 4 : 1); for (var tries = 0; tries < connectionAttempts; tries++) { Settings.Launcher.UpdateProgressInvokable(string.Format("Connecting ({0} of {1})", tries + 1, connectionAttempts), tries + 1, connectionAttempts); try { TcpClient = new TcpClient(); TcpClient.Connect(ServerIp, ServerPort); break; } catch (SocketException) { if (tries == connectionAttempts - 1) throw new ServerConnectException(); Thread.Sleep(1000); //wait one second before trying again } } _tcpStream = TcpClient.GetStream(); _tcpStream.ReadTimeout = 15000; //15s timeout during connect Settings.Launcher.UpdateProgressInvokable("Connected...", 0, 0); var connect = new Connect(-1, Config.UserName, new Coords()); try { connect.Send(); //server will immediately reply to tell us where we are and our Id, or disconnect us var actionTypebytes = new byte[sizeof(ushort)]; var bytesRead = 0; while (bytesRead < actionTypebytes.Length) bytesRead += _tcpStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead); var actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0); if (actionType == ActionType.Connect) { connect.Receive(); } else if(actionType == ActionType.Disconnect) { var disconnect = new Disconnect(); disconnect.Receive(); } else { throw new Exception(string.Format("Received {0} packet out of order during connect sequence.", actionType)); } Game.Player = Players[connect.PlayerId]; //then a list of the players connected, followed by the world Settings.Launcher.UpdateProgressInvokable("Waiting for World...", 0, 0); while (!WorldData.IsLoaded) { bytesRead = 0; while (bytesRead < actionTypebytes.Length) bytesRead += _tcpStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead); actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0); switch (actionType) { case ActionType.Connect: var recvPlayerList = new Connect(); recvPlayerList.Receive(); break; case ActionType.GetWorld: var getWorld = new GetWorld(); getWorld.Receive(); break; default: throw new Exception(string.Format("Received {0} packet out of order during connect sequence.", actionType)); } } } catch (Exception ex) { //HandleNetworkError(ex); throw new ServerConnectException(ex); } _tcpStream.ReadTimeout = -1; TcpClient.NoDelay = true; var listenerThread = new Thread(ListenForServerMessageThread) { IsBackground = true, Name = "ListenForServerMessageThread" }; listenerThread.Start(); //send misc player information to server periodically (ie fps, memory). _playerInfoTimer = new System.Timers.Timer(PlayerInfo.PLAYER_INFO_SEND_INTERVAL); _playerInfoTimer.Start(); _playerInfoTimer.Elapsed += _playerInfoTimer_Elapsed; //wire elapsed event handler }