Exemplo n.º 1
0
        // ReSharper restore FunctionNeverReturns
        private static void PlayerThread(NetworkPlayer player)
        {
            NetworkStream clientStream;

            //make all the introductions. we do this before sending the world so the client doesn't see them as new connections
            foreach (var otherPlayer in Players.Values)
            {
                try
                {
                    new Connect(otherPlayer.Id, otherPlayer.UserName, otherPlayer.Coords) { ConnectedPlayer = player, Immediate = true }.Send();
                }
                catch (Exception ex)
                {
                    WriteToServerConsoleLog(string.Format("{0} {1} caused an exception and was removed: {2}", player.UserName, player.IpAddress, ex.Message));
            #if DEBUG
                    WriteToServerConsoleLog(ex.StackTrace);
            #endif
                }

                new Connect(player.Id, player.UserName, player.Coords) { ConnectedPlayer = otherPlayer }.Send();
            }

            try
            {
                Players.TryAdd(player.Id, player); //note: it is not possible for the add to fail on ConcurrentDictionary, see: http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections
                UpdateServerConsolePlayerList();

                var getWorld = new GetWorld { ConnectedPlayer = player };
                getWorld.Send();
                WriteToServerConsoleLog(String.Format("World send complete to {0} ({1} compressed, {2} uncompressed)", player.IpAddress, getWorld.DataLength, getWorld.UncompressedLength));

                //create a thread to handle communication with connected client
                player.TcpClient.NoDelay = true;
                clientStream = player.TcpClient.GetStream();
            }
            catch (Exception ex)
            {
                HandleNetworkError(player, ex);
                return;
            }

            var actionTypebytes = new byte[sizeof(ushort)];
            try
            {
                if (!string.IsNullOrWhiteSpace(Config.MOTD)) new ServerMsg(Config.MOTD, player).Send();

                while (true)
                {
                    Thread.Sleep(10); //bm: polling is expensive. don't remove this or the server will pin your machine when only a couple users are online
                    GameAction gameAction;
                    while (player.SendQueue.Count > 0 && player.SendQueue.TryDequeue(out gameAction))
                    {
                        gameAction.Immediate = true;
                        gameAction.Send();
                    }

                    if (!clientStream.DataAvailable) continue;
                    var bytesRead = 0;
                    while (bytesRead < actionTypebytes.Length) bytesRead += clientStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead);
                    var actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0);
                    switch (actionType)
                    {
                        case ActionType.AddBlock:
                            gameAction = new AddBlock();
                            break;
                        case ActionType.AddBlockItem:
                            gameAction = new AddBlockItem();
                            break;
                        case ActionType.AddBlockMulti:
                            gameAction = new AddBlockMulti();
                            break;
                        case ActionType.AddCuboid:
                            gameAction = new AddCuboid();
                            break;
                        case ActionType.AddProjectile:
                            gameAction = new AddProjectile();
                            break;
                        case ActionType.AddStaticItem:
                            gameAction = new AddStaticItem();
                            break;
                        case ActionType.AddStructure:
                            gameAction = new AddStructure();
                            break;
                        case ActionType.ChatMsg:
                            gameAction = new ChatMsg();
                            break;
                        case ActionType.Disconnect:
                            gameAction = new Disconnect();
                            break;
                        case ActionType.PickupBlockItem:
                            gameAction = new PickupBlockItem();
                            break;
                        case ActionType.PlayerInfo:
                            gameAction = new PlayerInfo();
                            break;
                        case ActionType.PlayerMove:
                            gameAction = new PlayerMove();
                            break;
                        case ActionType.PlayerOption:
                            gameAction = new PlayerOption();
                            break;
                        case ActionType.RemoveBlock:
                            gameAction = new RemoveBlock();
                            break;
                        case ActionType.RemoveBlockItem:
                            gameAction = new RemoveBlockItem();
                            break;
                        case ActionType.RemoveBlockMulti:
                            gameAction = new RemoveBlockMulti();
                            break;
                        case ActionType.ServerCommand:
                            gameAction = new ServerCommand();
                            break;
                        case ActionType.Connect:
                        case ActionType.ServerMsg:
                        case ActionType.ServerSync:
                        case ActionType.GetWorld:
                            throw new Exception(string.Format("Server should not receive action type: {0}", actionType));
                        case ActionType.Error:
                            var bytes = 0;
                            while (clientStream.ReadByte() != -1)
                            {
                                bytes++;
                            }
                            throw new Exception("GameAction 'Error' received. " + bytes + " byte(s) remained in the stream.");
                        default:
                            throw new Exception(string.Format("Unknown action type: {0}", actionType));
                    }
                    gameAction.ConnectedPlayer = player;
                    gameAction.Receive();
                    if (HasServerConsole && CaptureIncoming) //only stream messages if there is a console window and it has requested to display them
                    {
                        _serverConsole.UpdateStreamLogInvokable(gameAction, player, false);
                    }
                    if (actionType == ActionType.Disconnect) return;
                }
            }
            catch (Exception ex)
            {
                HandleNetworkError(player, ex);
            }
        }
Exemplo n.º 2
0
        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
        }
Exemplo n.º 3
0
        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
        }