Пример #1
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);
        }
Пример #2
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);
            }
        }
 void KillNode(TcpPortHandler handler, TcpClientNode node)
 {
     // Получение 0 байт говорит о том, что клиент хочет прекратить взаимодействие.
     handler.KillClient(node);
 }
        /// <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);
            }
        }