/// <summary>
        /// Обрабатываем приватное сообщение чата. Либо личное, либо в чат канал
        /// </summary>
        void HandlePrivmsgCommand(TcpPortHandler handler, string[] values)
        {
            // PRIVMSG #GPG!1 :dfg
            var channelName = values[1];

            // Определяем, отправлено ли сообщение в глобальный чат
            if (channelName == "#GPG!1")
            {
                _emulationAdapter.SendChatMessage(values[2]);
            }
            else
            {
                if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
                {
                    //var roomHash = channelName.Split('!')[2];

                    // Костыль для работы личных сообщений в игре. Удаляем префикс
                    if (values[1].EndsWith("-thq"))
                    {
                        values[1] = values[1].Substring(0, values[1].Length - 4);
                    }

                    _emulationAdapter.SendLobbyBroadcast(string.Join(" ", values));
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Обрабатывает новое сообщение, полученное по TCP чата
        /// </summary>
        unsafe void OnChatReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count)
        {
            // Дешифруем строку, если чат шифрованный
            if (_chatEncoded)
            {
                byte *bytesPtr = stackalloc byte[count];

                for (int i = 0; i < count; i++)
                {
                    bytesPtr[i] = buffer[i];
                }

                ChatCrypt.GSEncodeDecode(_chatClientKey, bytesPtr, count);

                for (int i = 0; i < count; i++)
                {
                    buffer[i] = bytesPtr[i];
                }
            }

            var str = buffer.ToUtf8(count);

            LogTrace(">>>>> " + str);

            //
            var lines = str.Split(_chatSplitChars, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < lines.Length; i++)
            {
                HandleChatLine(handler, node, lines[i]);
            }
        }
        /// <summary>
        /// Обратываем выход пользователя из комнаты чата
        /// </summary>
        void HandlePartCommand(TcpPortHandler handler, string[] values)
        {
            //CHATLINE PART #GSP!whamdowfr!Ml39ll1K9M :
            var channelName = values[1];

            // Определяем, глобальный чат или комната автоматча
            if (channelName == "#GPG!1")
            {
                // Main chat - ignore
            }
            else
            {
                _enteredLobbyHash = null;
                _localServerHash  = null;

                _emulationAdapter.LeaveFromCurrentLobby();
            }

            if (!_emulationAdapter.HasLocalUserActiveInGameProfile)
            {
                return;
            }

            LogForUser($"Part sended {channelName}");

            SendToClientChat($":{_user} PART {channelName} :Leaving\r\n");
        }
        /// <summary>
        /// Обрабатывает команду логина в чате. В этот момент пользователь уже авторизован, просто сообщаем ему его ID профиля.
        /// </summary>
        void HandleLoginCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            var nick = values[2];
            var id   = _emulationAdapter.GetUserInGameProfileId(nick);

            SendToClientChat(node, $":s 707 {nick} 12345678 {id}\r\n");
            SendToClientChat(node, $":s 687ru: Your languages have been set\r\n");
        }
Exemple #5
0
        /// <summary>
        /// Обрабатывает входящее сообщение сервера LOGIN (Client)
        /// </summary>
        void OnClientManagerReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count)
        {
            // Несколько сообщения может быть за раз - сплитим
            var messages = buffer.ToUtf8(count).Split(new string[] { @"\final\" }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < messages.Length; i++)
            {
                HandleClientManagerMessage(handler, node, messages[i]);
            }
        }
        /// <summary>
        /// Обрабатывает сохранение пары ключ-значение глобально в указанной комнате чата.
        /// Все другие пользователи должны после этого получить сообщение Broadcast.
        /// </summary>
        void HandleSetckeyCommand(TcpPortHandler handler, TcpClientNode node, string line, string[] values)
        {
            var channelName = values[1];

            if (channelName == "#GPG!1")
            {
                var keyValues = values[3];

                var pairs = keyValues.Split(':', '\\');

                if (pairs[1] == "username")
                {
                    SendToClientChat(node, $":s 702 #GPG!1 #GPG!1 {values[2]} BCAST :\\{pairs[1]}\\{pairs[2]}\r\n");
                    return;
                }

                SendToClientChat(node, $":s 702 #GPG!1 #GPG!1 {values[2]} BCAST :\\{pairs[1]}\\{pairs[2]}\r\n");

                var dictionary = new Dictionary <string, string>();

                for (int i = 1; i < pairs.Length; i += 2)
                {
                    dictionary[pairs[i]] = pairs[i + 1];
                }

                _emulationAdapter.SetGlobalKeyValues(dictionary);

                /* for (int i = 1; i < pairs.Length; i += 2)
                 *   SendToClientChat($":s 702 #GPG!1 #GPG!1 {values[2]} BCAST :\\{pairs[i]}\\{pairs[i + 1]}\r\n");*/
            }
            else
            {
                if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
                {
                    //var roomHash = channelName.Split('!')[2];

                    var keyValues = values[3];

                    var pairs = keyValues.Split(':', '\\');

                    // Skip first empty entry
                    for (int i = 1; i < pairs.Length; i += 2)
                    {
                        _emulationAdapter.SetLobbyKeyValue(pairs[i], pairs[i + 1]);
                    }

                    _emulationAdapter.SendLobbyBroadcast(line);
                    HandleRemoteSetckeyCommand(values);
                }
            }
        }
        public GameSpyServer(IEmulationAdapter handler)
        {
            _emulationAdapter     = handler;
            handler.GameSpyServer = this;

            _serverReport   = new UdpPortHandler(27900, OnServerReportReceived, OnError);
            _serverRetrieve = new TcpPortHandler(28910, new RetrieveTcpSetting(), OnServerRetrieveReceived, OnServerRetrieveError);

            _clientManager = new TcpPortHandler(29900, new LoginTcpSetting(), OnClientManagerReceived, OnError, OnClientAccept, KillNode);
            _searchManager = new TcpPortHandler(29901, new LoginTcpSetting(), OnSearchManagerReceived, OnError, null, KillNode);

            _chat  = new TcpPortHandler(6667, new ChatTcpSetting(), OnChatReceived, OnError, OnChatAccept, RestartServices);
            _stats = new TcpPortHandler(29920, new StatsTcpSetting(), OnStatsReceived, OnError, OnStatsAccept, RestartServices);
            _http  = new TcpPortHandler(80, new HttpTcpSetting(), OnHttpReceived, OnError, null, KillNode);

            LogTrace("Services inited");
        }
        /// <summary>
        /// Обратывает команду установки ника. Наследие IRC. Просто отвечаем по стандарту.
        /// </summary>
        void HandleNickCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            var users = _emulationAdapter.ActivePlayersCount;

            SendToClientChat(node, $":s 001 {_name} :Welcome to the Matrix {_name}\r\n");
            //SendToClientChat(node, $":s 002 {_name} :Your host is xs0, running version 1.0\r\n");
            // SendToClientChat(node, $":s 003 {_name} :This server was created Fri Oct 19 1979 at 21:50:00 PDT\r\n");
            //SendToClientChat(node, $":s 004 {_name} s 1.0 iq biklmnopqustvhe\r\n");
            // SendToClientChat(node, $":s 375 {_name} :- (M) Message of the day - \r\n");
            // SendToClientChat(node, $":s 372 {_name} :- Welcome to GameSpy\r\n");
            //SendToClientChat(node, $":s 251 :There are {users} users and 0 services on 1 servers\r\n");
            // SendToClientChat(node, $":s 252 0 :operator(s)online\r\n");
            // SendToClientChat(node, $":s 253 1 :unknown connection(s)\r\n");
            //  SendToClientChat(node, $":s 254 1 :channels formed\r\n");
            //  SendToClientChat(node, $":s 255 :I have {users} clients and 1 servers\r\n");

            SendToClientChat(node, $":{_user} NICK {_name}\r\n");
        }
        /// <summary>
        /// Обратывает установку заголовка комнаты.
        /// Для лобби это всегда имя хоста.
        /// </summary>
        void HandleTopicCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            // :Bambochuk2!Xu4FpqOa9X|[email protected] TOPIC #GSP!whamdowfr!76561198408785287 :Bambochuk2

            _emulationAdapter.SetLobbyTopic(values[2]);
            SendToClientChat(node, $":{_user} TOPIC #GSP!{_gameGSkey}!{_enteredLobbyHash} :{values[2]}\r\n");

            //TOPIC #GSP!whamdowfr!Ml39ll1K9M :elamaunt

            /*var channelName = values[1];
             *
             * if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
             * {
             *  var roomHash = channelName.Split('!')[2];
             *
             *  if (roomHash == _localServerHash)
             *  {
             *      SteamLobbyManager.SetLobbyTopic(values[2]);
             *  }
             * }*/
        }
 /// <summary>
 /// Сообщаем игре ее IP. Как правило, этот ответ ни на что не влияет. По крайней мере, я не обнаружил какой-либо зависимости.
 /// Особенность библиотеки GameSpy.
 /// </summary>
 void HandleUsripCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
 {
     SendToClientChat(node, $":s 302  :=+@{node.RemoteEndPoint?.Address}\r\n");
 }
        /// <summary>
        /// Обратываем команду редимов комнаты чата
        /// </summary>
        void HandleModeCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            var channelName = values[1];

            // Определяем, главный ли чат
            if (channelName.StartsWith("#GPG", StringComparison.OrdinalIgnoreCase))
            {
                // Просто захардкоженный ответ под Soulstorm, типа успех
                SendToClientChat(node, $":s 324 {_name} {channelName} +\r\n");
            }
            else
            {
                // В авот с автоматчем поинтереснее
                if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
                {
                    // Уникальный хэш комнаты
                    var roomHash = channelName.Split('!')[2];

                    // Извлекаем хоста по хэшу. Если не удастся, значит это локальный хост.
                    if (_lastLoadedLobbies.TryGetValue(roomHash, out GameServerDetails details))
                    {
                        // Отправляем ограничение на количество юзеров в комнате
                        var maxPLayers = _emulationAdapter.GetCurrentLobbyMaxPlayers();

                        if (maxPLayers == 2 || maxPLayers == 4 || maxPLayers == 6 || maxPLayers == 8)
                        {
                            SendToClientChat(node, $":s 324 {_name} {channelName} +l {maxPLayers}\r\n");
                        }
                        else
                        {
                            SendToClientChat(node, $":s 324 {_name} {channelName} +\r\n");
                        }
                    }
                    else
                    {
                        // На всякий случай проверяем на соответствие локальный хэш
                        if (roomHash == _localServerHash)
                        {
                            // Обрабатываем установку ограничения на количество юзеров в комнате
                            if (values.Length < 4)
                            {
                                // Это был запрос. Отвечаем
                                var max = _emulationAdapter.GetCurrentLobbyMaxPlayers();

                                if (max > 0 && max < 9)
                                {
                                    SendToClientChat(node, $":s 324 {_name} {channelName} +l {max}\r\n");
                                }
                                else
                                {
                                    SendToClientChat(node, $":s 324 {_name} {channelName} +\r\n");
                                }
                            }
                            else
                            {
                                // Это была установка. Задаем ограничение и отправляем успех
                                var maxPlayers = values[3];

                                if (int.TryParse(maxPlayers, out int value))
                                {
                                    _emulationAdapter.SetLocalLobbyMaxPlayers(value);
                                }

                                SendToClientChat(node, $":{_user} MODE #GSP!whamdowfr!{_enteredLobbyHash} +l {maxPlayers}\r\n");
                            }

                            // CHATLINE MODE #GSP!whamdowfr!Ml39ll1K9M +l 2
                            // CHATLINE MODE #GSP!whamdowfr!Ml39ll1K9M -i-p-s-m-n-t+l+e 2
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Обрабатывает команду перехода в шифрованный чат
        /// </summary>
        unsafe void HandleCryptCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            _chatEncoded    = true;
            _gameGSkeyBytes = null;

            // В зависимости от игры. Байты шифрования отличаются. Таблица есть в инете
            // https://gamerecon.net/support/topic/gamespy-supported-games-list/
            // https://github.com/luisj135/nintendo_dwc_emulator/blob/master/gslist.cfg

            // Dawn of War
            if (values.Contains("dow"))
            {
                _gameGSkey       = "dow";
                _gameGSNameBytes = "dow".ToAsciiBytes();
                _gameGSkeyBytes  = "VLxgwe".ToAsciiBytes();
            }

            // Dawn of War (более похоже на правду)
            if (values.Contains("whammer40000"))
            {
                _gameGSkey       = "whammer40000";
                _gameGSNameBytes = "whammer40000".ToAsciiBytes();
                _gameGSkeyBytes  = "uJ8d3N".ToAsciiBytes();
            }

            // Winter Assault
            // ключа почему-то нет в таблице. Возьмем ключ 1 дова.
            // TODO: Надо потестить WA
            if (values.Contains("dowwad"))
            {
                _gameGSkey       = "dowwad";
                _gameGSNameBytes = "dowwad".ToAsciiBytes();
                _gameGSkeyBytes  = "uJ8d3N".ToAsciiBytes();
            }

            // Dark Crusade
            if (values.Contains("whammer40kdc"))
            {
                _gameGSkey       = "whammer40kdc";
                _gameGSNameBytes = "whammer40kdc".ToAsciiBytes();
                _gameGSkeyBytes  = "Ue9v3H".ToAsciiBytes();
            }

            // Soulstorm
            if (values.Contains("whamdowfr"))
            {
                _gameGSkey       = "whamdowfr";
                _gameGSNameBytes = "whamdowfr".ToAsciiBytes();
                _gameGSkeyBytes  = "pXL838".ToAsciiBytes();
            }

            if (_gameGSkeyBytes == null)
            {
                Restart();
                return;
            }

            // Ключ шифрования. Для простоты просто нули. Не тестил, что будет, если не нули.
            var chall = "0000000000000000".ToAsciiBytes();

            var clientKey = new ChatCrypt.GDCryptKey();
            var serverKey = new ChatCrypt.GDCryptKey();

            // Инициализируем структуры-ключи для выполнения алгоритма спая
            fixed(byte *challPtr = chall)
            {
                fixed(byte *gamekeyPtr = _gameGSkeyBytes)
                {
                    ChatCrypt.GSCryptKeyInit(clientKey, challPtr, gamekeyPtr, _gameGSkeyBytes.Length);
                    ChatCrypt.GSCryptKeyInit(serverKey, challPtr, gamekeyPtr, _gameGSkeyBytes.Length);
                }
            }

            // Сохраняем структуры
            _chatClientKey = clientKey;
            _chatServerKey = serverKey;

            // Отправляем идентичные ключи для сервера и клиента. Шифрование в итоге будет полностью совпадать.
            // С этого момента чат шифрованный
            handler.SendAskii(node, ":s 705 * 0000000000000000 0000000000000000\r\n");
        }
 void HandleQuitCommand(TcpPortHandler handler, string[] values)
 {
     // Выход из чата не значит выход с сервера. Выход из чата обычно происходит при старте игры.
     // После игры игрок снова войдет в чат, но для других пользователей он всегда остается в чате
     //Restart();
 }
 void RestartServices(TcpPortHandler handler, TcpClientNode node)
 {
     // Получение 0 байт говорит о том, что клиент хочет прекратить взаимодействие.
     // Сбрасываем все соединения
     Restart();
 }
 /// <summary>
 /// Обратаываем UTM сообщение а чате. Уникальная команда GameSpy. Передаем ее всем участникам чата через Broadcast
 /// </summary>
 void HandleUtmCommand(TcpPortHandler handler, string line)
 {
     _emulationAdapter.SendLobbyBroadcast(line);
 }
Exemple #16
0
        void OnServerRetrieveReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count)
        {
            var str = buffer.ToASCII(count);

            LogTrace("RETRIEVE " + str);

            var endPoint = node.RemoteEndPoint;

            if (endPoint == null)
            {
                handler.KillClient(node);
                return;
            }

            string[] data = str.Split(new char[] { '\x00' }, StringSplitOptions.RemoveEmptyEntries);

            string validate = data[4];
            string filter   = null;

            bool isAutomatch = false;

            if (validate.Length > 8)
            {
                filter   = validate.Substring(8);
                validate = validate.Substring(0, 8);
            }
            else
            {
                //Log(Category, "ROOMS REQUEST - "+ data[2]);

                isAutomatch = data[2].EndsWith("am");

                if (!isAutomatch)
                {
                    SendChatRooms(handler, node, validate);
                    return;
                }
            }

            var lobbies = _emulationAdapter.GetOpenedLobbies();

            try
            {
                // var currentRating = ServerContext.ChatServer.CurrentRating;

                /*for (int i = 0; i < lobbies.Length; i++)
                 * {
                 *  var server = lobbies[i];
                 *
                 *  //server["score_"] = GetCurrentRating(server.MaxPlayers);
                 * }*/

                var fields = data[5].Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);

                var unencryptedBytes = ParseHelper.PackServerList(endPoint, lobbies, fields, isAutomatch);

                _lastLoadedLobbies.Clear();

                for (int i = 0; i < lobbies.Length; i++)
                {
                    var server      = lobbies[i];
                    var address     = server.HostIP ?? server.LocalIP;
                    var port        = ushort.Parse(server.HostPort ?? server.LocalPort);
                    var channelHash = ChatCrypt.PiStagingRoomHash(address, address, port);

                    Log($"HASHFOR {address}:{port}  {channelHash}");

                    server.RoomHash = channelHash;
                    _lastLoadedLobbies[channelHash] = server;
                }

                Log("SERVERS VALIDATE VALUE ~" + validate + "~");

                var encryptedBytes = GSEncoding.Encode(_gameGSkeyBytes, validate.ToAsciiBytes(), unencryptedBytes, unencryptedBytes.LongLength);

                Log("SERVERS bytes " + encryptedBytes.Length);

                int autoGames   = 0;
                int customGames = 0;
                // TODO вынести в отдельно
                for (int i = 0; i < lobbies.Length; i++)
                {
                    var server = lobbies[i];
                    if (server.Ranked)
                    {
                        autoGames++;
                    }
                    else
                    {
                        customGames++;
                    }
                }

                //CoreContext.ClientServer.SendAsServerMessage(
                //    "Received game list: " + customGames + " - custom; " + autoGames +
                //    " - auto; Mod: "+ CoreContext.ThunderHawkModManager.CurrentModName);

                handler.Send(node, encryptedBytes);
            }
            finally
            {
                handler.KillClient(node);
            }
        }
 /// <summary>
 /// Обрабатывает получение нового TPC соединения для получения статистики игроков
 /// </summary>
 void OnStatsAccept(TcpPortHandler handler, TcpClientNode node, CancellationToken token)
 {
     // Отправляем challenge сервера шифрованный через Xor. Надо отправить, но особо ни на что не влияет
     // Все сообщения также надо будет прогонять через Xor.
     handler.Send(GSUtils.XorBytes(@"\lc\1\challenge\KNDVKXFQWP\id\1\final\", "GameSpy3D", 7));
 }
Exemple #18
0
 /// <summary>
 /// Обрабатывает получение нового подключения по TCP порту IRC чата
 /// </summary>
 void OnChatAccept(TcpPortHandler handler, TcpClientNode node, CancellationToken token)
 {
     _inChat      = false;
     _chatEncoded = false;
 }
Exemple #19
0
        /// <summary>
        /// Отправляем список публичных комнат в чате.
        /// Сейчас фейкает только один чат рум GPG1 с названием "Room 1"
        /// Игра сама подставит красивое имя из HTTP данных о списке комнат
        /// </summary>
        /// <param name="validate">Строка валидации запроса. Должна быть получена от игры</param>
        void SendChatRooms(TcpPortHandler handler, TcpClientNode node, string validate)
        {
            var bytes = new List <byte>();

            //var remoteEndPoint = handler.RemoteEndPoint;
            //bytes.AddRange(remoteEndPoint.Address.GetAddressBytes());
            bytes.AddRange(IPAddress.Loopback.GetAddressBytes());

            byte[] value2 = BitConverter.GetBytes((ushort)6500);

            bytes.AddRange(BitConverter.IsLittleEndian ? value2.Reverse() : value2);

            bytes.Add(5); // fields count
            bytes.Add(0);


            // Забивает поля, которые нужны игре. В этом же порядке надо будет пихнуть значения дальше для каждой комнаты
            bytes.AddRange("hostname".ToAsciiBytes());
            bytes.Add(0);
            bytes.Add(0);
            bytes.AddRange("numwaiting".ToAsciiBytes());
            bytes.Add(0);
            bytes.Add(0);
            bytes.AddRange("maxwaiting".ToAsciiBytes());
            bytes.Add(0);
            bytes.Add(0);
            bytes.AddRange("numservers".ToAsciiBytes());
            bytes.Add(0);
            bytes.Add(0);
            bytes.AddRange("numplayersname".ToAsciiBytes());
            bytes.Add(0);
            bytes.Add(0);

            // Изначально было 10 комнат в игре, но мы сделаем только одну и весь код написан для синхронизации чата в игре с чатом из лаунчера

            // for (int i = 1; i <= 10; i++)
            // {

            // Странный байт в начале инфы о комнате
            bytes.Add(81);

            // инфа об IP комнаты, но игре на нее пофиг
            var b2 = BitConverter.GetBytes((long)1);

            bytes.Add(b2[3]);
            bytes.Add(b2[2]);
            bytes.Add(b2[1]);
            bytes.Add(b2[0]);

            // инфа о порте комнаты, но игре на нее пофиг
            bytes.Add(0);
            bytes.Add(0);

            // Скрытое название комнаты. Только такой формат принимает с цифрой в конце
            bytes.Add(255);
            bytes.AddRange("Room 1".ToAsciiBytes());
            bytes.Add(0);

            // Количество игроков в комнате
            bytes.Add(255);
            bytes.AddRange(_emulationAdapter.ActivePlayersCount.ToString().ToAsciiBytes());
            bytes.Add(0);

            bytes.Add(255);
            bytes.AddRange("1000".ToAsciiBytes());
            bytes.Add(0);

            bytes.Add(255);
            bytes.AddRange("1".ToAsciiBytes());
            bytes.Add(0);

            bytes.Add(255);
            bytes.AddRange("20".ToAsciiBytes());
            bytes.Add(0);
            // }

            // Непонятный набор байт в конце, но без него не сработает
            bytes.AddRange(new byte[] { 0, 255, 255, 255, 255 });

            var array = bytes.ToArray();

            // Шифруем алгоритмом спая. Участвует строка валидации и уникальный ключ игры
            byte[] enc = GSEncoding.Encode(_gameGSkeyBytes, validate.ToAsciiBytes(), array, array.LongLength);

            handler.Send(node, enc);

            handler.KillClient(node);
        }
Exemple #20
0
        /// <summary>
        /// Обратывает строку IRC чата и вызывает соответствующий метод обработчик команды
        /// </summary>
        void HandleChatLine(TcpPortHandler handler, TcpClientNode node, string line)
        {
            var values = GetIrcChatLineValues(line);

            if (line.StartsWith("LOGIN", StringComparison.OrdinalIgnoreCase))
            {
                HandleLoginCommand(handler, node, values); return;
            }
            if (line.StartsWith("USRIP", StringComparison.OrdinalIgnoreCase))
            {
                HandleUsripCommand(handler, node, values); return;
            }
            if (line.StartsWith("CRYPT", StringComparison.OrdinalIgnoreCase))
            {
                HandleCryptCommand(handler, node, values); return;
            }
            if (line.StartsWith("USER", StringComparison.OrdinalIgnoreCase))
            {
                HandleUserCommand(handler, node, values); return;
            }
            if (line.StartsWith("NICK", StringComparison.OrdinalIgnoreCase))
            {
                HandleNickCommand(handler, node, values); return;
            }
            if (line.StartsWith("CDKEY", StringComparison.OrdinalIgnoreCase))
            {
                HandleCdkeyCommand(handler, node, values); return;
            }
            if (line.StartsWith("JOIN", StringComparison.OrdinalIgnoreCase))
            {
                HandleJoinCommand(handler, node, line, values); return;
            }
            if (line.StartsWith("MODE", StringComparison.OrdinalIgnoreCase))
            {
                HandleModeCommand(handler, node, values); return;
            }
            if (line.StartsWith("QUIT", StringComparison.OrdinalIgnoreCase))
            {
                HandleQuitCommand(handler, values); return;
            }
            if (line.StartsWith("PRIVMSG", StringComparison.OrdinalIgnoreCase))
            {
                HandlePrivmsgCommand(handler, values); return;
            }
            if (line.StartsWith("SETCKEY", StringComparison.OrdinalIgnoreCase))
            {
                HandleSetckeyCommand(handler, node, line, values); return;
            }
            if (line.StartsWith("GETCKEY", StringComparison.OrdinalIgnoreCase))
            {
                HandleGetckeyCommand(handler, node, values); return;
            }
            if (line.StartsWith("TOPIC", StringComparison.OrdinalIgnoreCase))
            {
                HandleTopicCommand(handler, node, values); return;
            }
            if (line.StartsWith("PART", StringComparison.OrdinalIgnoreCase))
            {
                HandlePartCommand(handler, values); return;
            }
            if (line.StartsWith("UTM", StringComparison.OrdinalIgnoreCase))
            {
                HandleUtmCommand(handler, line); return;
            }
            if (line.StartsWith("PING", StringComparison.OrdinalIgnoreCase))
            {
                HandlePingCommand(handler, node, values); return;
            }

            Debugger.Break();
        }
Exemple #21
0
        /// <summary>
        /// Обрабатывает одно сообщение сервера
        /// </summary>
        private void HandleClientManagerMessage(TcpPortHandler handler, TcpClientNode node, string mes)
        {
            LogTrace("CLIENT " + mes);
            var pairs = ParseHelper.ParseMessage(mes, out string query);

            if (pairs == null || string.IsNullOrWhiteSpace(query))
            {
                return;
            }

            // Исправление бага, когда игра по какой-то причине соединяет логин и почту в одну строку. Разбиваем, иначе не будет работать алгоритм хэширования при логине
            if (pairs.ContainsKey("name") && !pairs.ContainsKey("email"))
            {
                var parts = pairs["name"].Split('@');

                if (parts.Length > 2)
                {
                    pairs["name"]  = parts[0];
                    pairs["email"] = parts[1] + "@" + parts[2];
                }
            }

            switch (query)
            {
            case "login":
                HandleLogin(node, pairs);
                RestartUserSessionTimer(node);
                break;

            case "logout":
                _emulationAdapter.LeaveFromCurrentLobby();
                _emulationAdapter.OnLogout();
                break;

            case "registernick":
                handler.SendAskii(node, string.Format(@"\rn\{0}\id\{1}\final\", pairs["uniquenick"], pairs["id"]));
                break;

            case "ka":
                handler.SendAskii(node, $@"\ka\\final\");
                break;

            case "status":
                HandleStatus(node, pairs);
                break;

            case "newuser":
            {
                var nick     = pairs["nick"];
                var email    = pairs["email"];
                var password = GSUtils.DecryptPassword(pairs["passwordenc"]);
                var passHash = password.ToMD5();

                _emulationAdapter.TryCreateProfile(nick, email, passHash);
            }
            break;

            case "getprofile":
                // TODO
                break;

            default:
                Debugger.Break();
                break;
            }
        }
        /// <summary>
        /// Обработка HTTP запроса от игры на порт 80.
        /// Обрабатывает новостное сообщение, наличие патча, запрос страницы статистики, настройки автоматча и список имен комнат чата.
        /// </summary>
        void OnHttpReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count)
        {
            try
            {
                var str = buffer.ToUtf8(count);

                LogTrace("HTTP CLIENT HASH " + node.GetHashCode());
                LogTrace("HTTP " + str);

                HttpRequest request;

                using (var ms = new MemoryStream(buffer, 0, count, false, true))
                    request = HttpHelper.GetRequest(ms);

                using (var ms = new MemoryStream())
                {
                    // Запрос страницы статистики по кнопке из игры
                    if (request.Url.StartsWith("/SS_StatsPage", StringComparison.OrdinalIgnoreCase))
                    {
                        HttpHelper.WriteResponse(ms, HttpResponceBuilder.DowstatsRedirect());
                        goto END;
                    }

                    // Запрос текста новостей
                    if (request.Url.EndsWith("news.txt", StringComparison.OrdinalIgnoreCase))
                    {
                        LogForUser($"News requested");

                        // Фикс для рускоязычных
                        if (request.Url.EndsWith("Russiandow_news.txt", StringComparison.OrdinalIgnoreCase))
                        {
                            HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(GameSpyHttpDataConstants.RusNews, Encoding.Unicode));
                        }
                        else
                        {
                            HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(GameSpyHttpDataConstants.EnNews, Encoding.Unicode));
                        }
                        goto END;
                    }

                    // Отправка сообщения дня. Вроде нигде не отображается
                    if (request.Url.StartsWith("/motd/motd", StringComparison.OrdinalIgnoreCase))
                    {
                        HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(GameSpyHttpDataConstants.RusNews, Encoding.Unicode));
                        goto END;
                    }

                    // Проверка на существование патча. Можно прокидывать свои патчи для игры
                    if (request.Url.StartsWith("/motd/vercheck", StringComparison.OrdinalIgnoreCase))
                    {
                        LogForUser($"Vercheck requested");

                        // Пример отправки ссылки на скачивания патча
                        //HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(@"\newver\1\newvername\1.4\dlurl\http://127.0.0.1/NewPatchHere.exe"));

                        // Отправка инфы о том, что патча сейчас нет
                        HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(@"\newver\0", Encoding.UTF8));
                        goto END;
                    }

                    // Запрос списка комнат с именами для отображения в интерфейсе
                    if (request.Url.EndsWith("LobbyRooms.lua", StringComparison.OrdinalIgnoreCase))
                    {
                        LogForUser($"LobbyRooms requested");
                        HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(GameSpyHttpDataConstants.RoomPairs, Encoding.ASCII));
                        goto END;
                    }

                    // Запрос дефолных настроек автоматча
                    if (request.Url.EndsWith("AutomatchDefaultsSS.lua", StringComparison.OrdinalIgnoreCase) || request.Url.EndsWith("AutomatchDefaultsDXP2Fixed.lua", StringComparison.OrdinalIgnoreCase))
                    {
                        LogForUser($"AutomatchDefaults requested");
                        //HttpHelper.WriteResponse(ms, HttpResponceBuilder.TextFileBytes(CoreContext.MasterServer.AutomatchDefaultsBytes));
                        HttpHelper.WriteResponse(ms, HttpResponceBuilder.Text(GameSpyHttpDataConstants.AutomatchDefaults, Encoding.ASCII));
                        goto END;
                    }

                    /*if (request.Url.EndsWith("homepage.php.htm", StringComparison.OrdinalIgnoreCase))
                     * {
                     *  if (StatsResponce == null || (DateTime.Now - _lastStatsUpdate).TotalMinutes > 5)
                     *      StatsResponce = BuildTop10StatsResponce();
                     *
                     *  HttpHelper.WriteResponse(ms, StatsResponce);
                     *  goto END;
                     * }*/

                    // Если дошли сюда - отправляет NotFound
                    HttpHelper.WriteResponse(ms, HttpResponceBuilder.NotFound());

END:
                    LogTrace("HTTP WANT TO SEND " + node.GetHashCode() + " " + ms.Length);
                    handler.Send(node, ms.ToArray());
                    handler.KillClient(node);
                }
            }
            catch (InvalidDataException ex)
            {
                //Log(ex);
            }
        }
 /// <summary>
 /// На Ping в чате отвечаем Pong. Поддерживает соединение с чат сервером.
 /// </summary>
 void HandlePingCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
 {
     SendToClientChat(node, $":s PONG :s\r\n");
 }
 /// <summary>
 /// Обратаываем установку полного имени пользователя.
 /// </summary>
 void HandleUserCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
 {
     // Просто сохраняем себе для дальнейшего использования
     _user      = $@"{_name}!{values[1]}@{node.RemoteEndPoint?.Address}";
     _shortUser = values[1];
 }
        /// <summary>
        /// Обрабатывает полученное сообщение по TCP соединению для запросов статистики
        /// </summary>
        void OnStatsReceived(TcpPortHandler handler, TcpClientNode node, byte[] buffer, int count)
        {
            var str = Encoding.UTF8.GetString(GSUtils.XorBytes(buffer, 0, count - 7, "GameSpy3D"), 0, count);

            LogTrace("STATS " + str);

            // Авторизация на сервере. Выдаем случайный ключ сессии (не обязательно)
            if (str.StartsWith(@"\auth\\gamename\", StringComparison.OrdinalIgnoreCase))
            {
                var sesskey = Interlocked.Increment(ref _sessionCounter).ToString("0000000000");

                handler.Send(node, GSUtils.XorBytes($@"\lc\2\sesskey\{sesskey}\proof\0\id\1\final\", "GameSpy3D", 7));
                return;
            }

            // Игра присылает серверу ID авторизованного профиля. Но мы и так его знаем, поэтому просто фейкаем успех
            if (str.StartsWith(@"\authp\\pid\", StringComparison.OrdinalIgnoreCase))
            {
                var pid       = GetPidFromInput(str, 12);
                var profileId = long.Parse(pid);

                handler.Send(node, GSUtils.XorBytes($@"\pauthr\{pid}\lid\1\final\", "GameSpy3D", 7));
                return;
            }

            // Запрос данных профиля. Надо отправить данные по списку запрошенных ключей.
            // Всегда запрашивается статистика
            if (str.StartsWith(@"\getpd\", StringComparison.OrdinalIgnoreCase))
            {
                // \\getpd\\\\pid\\87654321\\ptype\\3\\dindex\\0\\keys\\\u0001points\u0001points2\u0001points3\u0001stars\u0001games\u0001wins\u0001disconn\u0001a_durat\u0001m_streak\u0001f_race\u0001SM_wins\u0001Chaos_wins\u0001Ork_wins\u0001Tau_wins\u0001SoB_wins\u0001DE_wins\u0001Eldar_wins\u0001IG_wins\u0001Necron_wins\u0001lsw\u0001rnkd_vics\u0001con_rnkd_vics\u0001team_vics\u0001mdls1\u0001mdls2\u0001rg\u0001pw\\lid\\1\\final\\
                // \getpd\\pid\87654321\ptype\3\dindex\0\keys\pointspoints2points3starsgameswinsdisconna_duratm_streakf_raceSM_winsChaos_winsOrk_winsTau_winsSoB_winsDE_winsEldar_winsIG_winsNecron_winslswrnkd_vicscon_rnkd_vicsteam_vicsmdls1mdls2rgpw\lid\1\final\
                var profileId = GetPidFromInput(str, 12);

                var keysIndex = str.IndexOf("keys") + 5;
                var keys      = str.Substring(keysIndex);
                var keysList  = keys.Split(new string[] { "\u0001", "\\lid\\1\\final\\", "final", "\\", "lid" }, StringSplitOptions.RemoveEmptyEntries);

                var keysResult = new StringBuilder();
                var stats      = _emulationAdapter.GetUserStatsInfo(profileId.ParseToLongOrDefault());

                for (int i = 0; i < keysList.Length; i++)
                {
                    var key = keysList[i];

                    keysResult.Append("\\" + key + "\\");

                    switch (key)
                    {
                    case "points": keysResult.Append(stats.Score1v1); break;

                    case "points2": keysResult.Append(stats.Score2v2); break;

                    case "points3": keysResult.Append(stats.Score3v3_4v4); break;

                    case "stars": keysResult.Append(stats.StarsCount); break;

                    case "games": keysResult.Append(stats.GamesCount); break;

                    case "wins": keysResult.Append(stats.WinsCount); break;

                    case "disconn": keysResult.Append(stats.Disconnects); break;

                    case "a_durat": keysResult.Append(stats.AverageDuration); break;

                    case "m_streak": keysResult.Append(stats.Best1v1Winstreak); break;

                    case "f_race": keysResult.Append(stats.FavouriteRace); break;

                    // Ключи, которые не используюся игрой, но запрашиваются. Может на что-то и влияет, но я ничего не обнаружил

                    /* case "SM_wins": keysResult.Append("0"); break;
                     * case "Chaos_wins": keysResult.Append("0"); break;
                     * case "Ork_wins": keysResult.Append("0"); break;
                     * case "Tau_wins": keysResult.Append("0"); break;
                     * case "SoB_wins": keysResult.Append("0"); break;
                     * case "DE_wins": keysResult.Append("0"); break;
                     * case "Eldar_wins": keysResult.Append("0"); break;
                     * case "IG_wins": keysResult.Append("0"); break;
                     * case "Necron_wins": keysResult.Append("0"); break;
                     * case "lsw": keysResult.Append("0"); break;
                     * case "rnkd_vics": keysResult.Append("0"); break;
                     * case "con_rnkd_vics": keysResult.Append("0"); break;
                     * case "team_vics": keysResult.Append("0"); break;
                     * case "mdls1": keysResult.Append("0"); break;
                     * case "mdls2": keysResult.Append("0"); break;
                     * case "rg": keysResult.Append("0"); break;
                     * case "pw": keysResult.Append("0"); break;*/
                    default:
                        keysResult.Append("0");
                        break;
                    }
                }

                handler.Send(node, GSUtils.XorBytes($@"\getpdr\1\lid\1\pid\{profileId}\mod\{stats.ModifiedTimeTick}\length\{keys.Length}\data\{keysResult}\final\", "GameSpy3D", 7));

                return;
            }

            // Игра присылает обновление данных профиля по списку ключей.
            // Игнорируем, потому что статистика обновляется другим способов. Просто фейкаем успех
            if (str.StartsWith(@"\setpd\", StringComparison.OrdinalIgnoreCase))
            {
                var pid = GetPidFromInput(str, 12);

                var lidIndex = str.IndexOf("\\lid\\", StringComparison.OrdinalIgnoreCase);
                var lid      = str.Substring(lidIndex + 5, 1);

                var timeInSeconds = (ulong)((DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds);
                // \setpd\\pid\3\ptype\1\dindex\0\kv\1\lid\1\length\413\data\\ckey\5604 - 7796 - 6425 - 0127 - DA96\system\Nr.Proc:8, Type: 586, GenuineIntel, unknown: f = 6,m = 12, Fam: 6, Mdl: 12, St: 3, Fe: 7, OS: 7, Ch: 15\speed\CPUSpeed: 3.5Mhz\os\OS NT 6.2\lang\Language: Русский(Россия), Country: Россия, User Language:Русский(Россия), User Country:Россия\vid\Card: Dx9: Hardware TnL, NVIDIA GeForce GTX 1080, \vidmod\Mode: 1920 x 1080 x 32\mem\2048Mb phys. memory
                handler.Send(node, GSUtils.XorBytes($@"\setpdr\1\lid\{lid}\pid\{pid}\mod\{timeInSeconds}\final\", "GameSpy3D", 7));

                return;
            }

            // Игры присылает информацию о завершенной игре (камстока или автоматч)
            // Отсюда можно обновлять статистику игроков. Может быть прислано несколько раз от разных игроков в конце одного и того же матча,
            // поэтому необходима защита на сервере от двойного засчитывания статистики
            if (str.StartsWith(@"\updgame\", StringComparison.OrdinalIgnoreCase))
            {
                var gamedataIndex = str.IndexOf("gamedata");

                // Обрезаем конец сообщения
                var finalIndex     = str.IndexOf("final");
                var gameDataString = str.Substring(gamedataIndex + 9, finalIndex - gamedataIndex - 10);

                var valuesList = gameDataString.Split(new string[] { "\u0001", "\\lid\\1\\final\\", "\\" }, StringSplitOptions.None);

                // Преобразываем все пары ключ-значение в словарь для удобства
                var dictionary = new Dictionary <string, string>();

                for (int i = 0; i < valuesList.Length - 1; i += 2)
                {
                    if (i == valuesList.Length - 1)
                    {
                        continue;
                    }

                    dictionary[valuesList[i]] = valuesList[i + 1];
                }

                if (!dictionary.TryGetValue("Mod", out string v))
                {
                    dictionary.Clear();

                    for (int i = 1; i < valuesList.Length - 1; i += 2)
                    {
                        if (i == valuesList.Length - 1)
                        {
                            continue;
                        }

                        dictionary[valuesList[i]] = valuesList[i + 1];
                    }
                }

                // Использованный мод и его версия
                var mod        = dictionary["Mod"];
                var modVersion = dictionary["ModVer"];

                var playersCount = int.Parse(dictionary["Players"]);

                for (int i = 0; i < playersCount; i++)
                {
                    // Dont process games with AI
                    if (dictionary["PHuman_" + i] != "1")
                    {
                        LogTrace($"Stats socket: GAME WITH NONHUMAN PLAYER");
                        return;
                    }
                }

                var gameInternalSession = dictionary["SessionID"];
                var teamsCount          = int.Parse(dictionary["Teams"]);
                var version             = dictionary["Version"];

                // Строим уникальные идентификатор сессии игры, чтобы в дальнейшем не зачислить дважды одну и ту же игру
                var uniqueGameSessionBuilder = new StringBuilder(gameInternalSession);

                for (int i = 0; i < playersCount; i++)
                {
                    uniqueGameSessionBuilder.Append('<');
                    uniqueGameSessionBuilder.Append(dictionary["player_" + i]);
                    uniqueGameSessionBuilder.Append('>');
                }

                var uniqueSession = uniqueGameSessionBuilder.ToString();

                // Строим объекты с данным игроков
                var players = new PlayerData[playersCount];

                for (int i = 0; i < players.Length; i++)
                {
                    var player = new PlayerData();

                    player.Name       = dictionary["player_" + i];
                    player.Race       = dictionary["PRace_" + i];
                    player.Team       = int.Parse(dictionary["PTeam_" + i]);
                    player.FinalState = (PlayerFinalState)Enum.Parse(typeof(PlayerFinalState), dictionary["PFnlState_" + i]);

                    players[i] = player;
                }

                // Собираем окончательный объект с данными
                var gameFinishedMessage = new GameFinishedData
                {
                    Map        = dictionary["Scenario"],
                    SessionId  = uniqueSession,
                    Duration   = long.Parse(dictionary["Duration"]),
                    ModName    = dictionary["Mod"],
                    ModVersion = dictionary["ModVer"],
                    Players    = players,
                    IsRateGame = dictionary["Ladder"] == "1"
                };

                _emulationAdapter.SendGameFinishedData(gameFinishedMessage);

                //DowstatsReplaySender.SendReplay(gameFinishedMessage);

                return;
            }

            // Создание новой игры до регистрации статистики. Никакой полезной информации нет, поэтому игнорируем.
            // Вся логика произойдет в момент отправки данных об игре.
            if (str.StartsWith(@"\newgame\", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            // На случай, если есть еще какие-то команды
            Debugger.Break();
        }
 void KillNode(TcpPortHandler handler, TcpClientNode node)
 {
     // Получение 0 байт говорит о том, что клиент хочет прекратить взаимодействие.
     handler.KillClient(node);
 }
        /// <summary>
        /// Обрабатывает вход в чат.
        /// </summary>
        void HandleJoinCommand(TcpPortHandler handler, TcpClientNode node, string line, string[] values)
        {
            var channelName = values[1];

            // Определяем, главный ли чат
            if (channelName.StartsWith("#GPG", StringComparison.OrdinalIgnoreCase))
            {
                var users = _emulationAdapter.GetUsersInMainChat();

                var builder = new StringBuilder();

                builder.Append($":{_user} JOIN {channelName}\r\n");
                // SendToClientChat(node, $":{_user} JOIN {channelName}\r\n");
                builder.Append($":s 331 {channelName} :No topic is set\r\n");
                // SendToClientChat(node, $":s 331 {channelName} :No topic is set\r\n");

                _inChat = true;

                var playersList = new StringBuilder();

                for (int i = 0; i < users.Length; i++)
                {
                    var user = users[i];

                    playersList.Append(user + " ");
                }

                // Посылаем список юзеров в чате
                builder.Append($":s 353 {_name} = {channelName} :{playersList}\r\n");
                //SendToClientChat(node, $":s 353 {_name} = {channelName} :{playersList}\r\n");
                builder.Append($":s 366 {_name} {channelName} :End of NAMES list\r\n");
                //SendToClientChat(node, $":s 366 {_name} {channelName} :End of NAMES list\r\n");

                SendToClientChat(builder.ToString());
            }
            else
            {
                if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
                {
                    // Вход в комнату для автоматча
                    // Извлекаем уникальный хэш, чтобы определить сопоставить со списком выданных ранее хостов.
                    var roomHash = channelName.Split('!')[2];

                    LogForUser($"Try to get lobby [{roomHash}]");

                    // Берем хост, если хоста нет. Значит это попытка войти в комнате локального хоста.
                    if (_lastLoadedLobbies.TryGetValue(roomHash, out GameServerDetails details))
                    {
                        // Входим в чужой хост
                        LogForUser($"Try to enter lobby [{roomHash}]");

                        var hostId = details.HostId;

                        // Попытка войти в хост. Сервер решает успех
                        _emulationAdapter.TryEnterInLobby(hostId, _name,
                                                          new EnterInLobbySuccessDelegate((hostName, members) =>
                        {
                            _enteredLobbyHash = details.RoomHash;
                            Log($"Entered to lobby [{roomHash}]");

                            var playersList = new StringBuilder();

                            // Вошедший должен быть в этом списке
                            for (int i = 0; i < members.Length; i++)
                            {
                                var member = members[i];

                                Log($"Player {i} [{GetNickHash(member)}]");
                                playersList.Append(member + " ");
                            }

                            // Теперь все должны узнать, что юзер вошел. Делается через Broadcast
                            _emulationAdapter.SendLobbyBroadcast($"JOIN {_user}");

                            // Себе отправляем результат сразу.
                            SendToClientChat(node, $":{_user} JOIN {channelName}\r\n");

                            var topic = hostName;

                            // Присылаем список пользователей в комнате
                            SendToClientChat(node, $":s 331 {channelName} :{topic}\r\n");
                            SendToClientChat(node, $":s 353 {_name} = {channelName} :@{playersList}\r\n");
                            SendToClientChat(node, $":s 366 {_name} {channelName} :End of NAMES list\r\n");
                        }),
                                                          new EnterInLobbyFailedDelegate(() =>
                        {
                            // Не удалось войти в чате, причина не важна. После этого игра попробует создать хост самостоятельно
                            SendToClientChat(node, $":{_user} {channelName} :Bad Channel Mask\r\n");
                        }));
                    }
                    else
                    {
                        // Мы в своем хосте. Выполним требования IRC
                        LogForUser($"This lobby is local [{roomHash}]");
                        LogForUser($"Player 0 [{GetNickHash(_name)}]");

                        _localServerHash  = roomHash;
                        _enteredLobbyHash = roomHash;

                        var builder = new StringBuilder();

                        builder.Append($":{_user} JOIN {channelName}\r\n");
                        builder.Append($":s 331 {channelName} :No topic is set\r\n");

                        // Мы только одни будем в комнате в этот момент.
                        builder.Append($":s 353 {_name} = {channelName} :@{_name}\r\n");
                        builder.Append($":s 366 {_name} {channelName} :End of NAMES list\r\n");

                        SendToClientChat(node, builder.ToString());
                    }
                }
            }
        }
        /// <summary>
        /// Обрабатывает проверку CD ключа игры. Мы просто всегда возвращаем успех.
        /// Если ключа у юзера нет в реестре, то до этой команды не дойдет. Помогает разблокировка рас.
        /// </summary>
        void HandleCdkeyCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            LogForUser($"Cdkey check");

            SendToClientChat(node, $":s 706 {_name}: 1 :\"Authenticated\"\r\n");
        }
        /// <summary>
        /// Обрабатывает команду IRC на получение значения юзера в комнате чата по массиву ключей
        /// </summary>
        void HandleGetckeyCommand(TcpPortHandler handler, TcpClientNode node, string[] values)
        {
            var channelName = values[1];

            //GETCKEY #GPG!1 * 000 0 :\\username\\b_flags
            var id         = values[3];
            var keysString = values[5];

            // Извлекаем список ключей
            var keys    = keysString.Split(':', '\\');
            var builder = new StringBuilder();

            if (channelName.StartsWith("#GSP", StringComparison.OrdinalIgnoreCase))
            {
                //var roomHash = channelName.Split('!')[2];

                // Если мы по какой-то причине не в лобби - эмулируем ответ, будто мы в лобби. Защита от багов
                if (!_emulationAdapter.IsInLobbyNow)
                {
                    for (int k = 0; k < keys.Length; k++)
                    {
                        var key = keys[k];

                        if (string.IsNullOrWhiteSpace(key))
                        {
                            continue;
                        }

                        string value;

                        if (key == "username")
                        {
                            value = _shortUser;
                        }
                        else
                        {
                            value = _emulationAdapter.GetLobbyKeyValue(key);
                        }

                        builder.Append($@"\{value ?? string.Empty}");
                    }

                    SendToClientChat(node, $":s 702 {_name} {channelName} {id} :{builder}\r\n");
                }
                else
                {
                    // Если мы в лобби - отправляем нормальные данные по указанным ключам
                    var members = _emulationAdapter.GetLobbyMembers();

                    for (int i = 0; i < members.Length; i++)
                    {
                        builder.Clear();

                        var name = members[i];

                        for (int k = 0; k < keys.Length; k++)
                        {
                            var key = keys[k];

                            if (string.IsNullOrWhiteSpace(key))
                            {
                                continue;
                            }

                            var value = _emulationAdapter.GetLobbyMemberData(name, key);

                            builder.Append(@"\" + value);
                        }

                        SendToClientChat(node, $":s 702 {_name} {channelName} {name} {id} :{builder}\r\n");
                    }
                }

                SendToClientChat(node, $":s 703 {_name} {channelName} {id} :End of GETCKEY\r\n");
            }
            else
            {
                // Для главного чата эмулируем значения, если их по какой-то причине нет,
                // или даем нормальные данные, полученные от других клиентов
                if (channelName.StartsWith("#GPG", StringComparison.OrdinalIgnoreCase))
                {
                    var users = _emulationAdapter.GetUsersInMainChat();

                    for (int i = 0; i < users.Length; i++)
                    {
                        var user = users[i];

                        builder.Clear();

                        for (int k = 0; k < keys.Length; k++)
                        {
                            var key = keys[k];

                            if (string.IsNullOrWhiteSpace(key))
                            {
                                continue;
                            }

                            string value = string.Empty;

                            if (key == "username")
                            {
                                var localName = _emulationAdapter.LocalUserName;

                                if (string.Equals(localName, user, StringComparison.Ordinal))
                                {
                                    value = _shortUser;
                                }
                                else
                                {
                                    value = $"X{GetEncodedIp(user)}X|{_emulationAdapter.GetUserInGameProfileId(user)}";
                                }
                            }

                            if (key == "b_stats")
                            {
                                value = _emulationAdapter.GetUserGlobalKeyValue(user, key);

                                if (value == null)
                                {
                                    var stats = _emulationAdapter.GetUserStatsInfo(user);
                                    value = $"{_emulationAdapter.GetUserInGameProfileId(user)}|{stats.Score1v1}|{stats.StarsCount}|";
                                }
                            }

                            if (key == "b_flags")
                            {
                                value = _emulationAdapter.GetUserGlobalKeyValue(user, key);

                                if (value == null)
                                {
                                    value = string.Empty;
                                }
                            }

                            builder.Append(@"\" + value);
                        }

                        SendToClientChat(node, $":s 702 {_name} {channelName} {user} {id} :{builder}\r\n");
                    }

                    SendToClientChat(node, $":s 703 {_name} {channelName} {id} :End of GETCKEY\r\n");
                }
            }
        }
Exemple #30
0
 /// <summary>
 /// Обрабатывает входящие TCP соединения для сервера LOGIN (Client)
 /// </summary>
 void OnClientAccept(TcpPortHandler handler, TcpClientNode node, CancellationToken token)
 {
     //Обновляем челендж для нового соединения
     _serverChallenge = RandomHelper.GetString(10);
     handler.SendAskii(node, $@"\lc\1\challenge\{_serverChallenge}\id\1\final\");
 }