/// <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); }
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); } }