using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Threading.Tasks; using UnityEngine; //Зависит от SettingsManager [System.Serializable] public class ServerResponse { public string message; public string timestamp; } public class WebServer : MonoBehaviour { private HttpListener listener; private bool isRunning = true; private static byte[] css; async void Start() { SettingsManager.Initialize();//надо будет убирать LoadCss(); await StartWebServerAsync(); } async Task StartWebServerAsync() { listener = new HttpListener(); listener.Prefixes.Add("http://localhost:8080/"); //тест listener.Prefixes.Add("http://fishka:8080/"); // 4. ВСЕ сетевые интерфейсы (самый важный!) listener.Prefixes.Add("http://*:8080/"); listener.Start(); Debug.Log("Async Web сервер запущен"); while (isRunning) { try { var context = await listener.GetContextAsync(); _ = Task.Run(() => ProcessRequestAsync(context)); } catch (System.Exception e) { Debug.LogError($"Ошибка: {e.Message}"); } } } async Task ProcessRequestAsync(HttpListenerContext context) { try { string path = context.Request.Url.LocalPath; string method = context.Request.HttpMethod; Debug.Log($"Запрос: {method} {path}"); if (method == "GET" && path == "/") { await HandleGetSettingsAsync(context); } else if (method == "GET" && path == "/style.css") { await HandleCssRequestAsync(context); } else if (method == "POST" && path == "/upd") { await HandlePostUpdateAsync(context); } else { SendHtmlResponse(context, "

404 Not Found

", 404); } } catch (Exception ex) { Debug.LogError($"Error processing request: {ex.Message}"); SendHtmlResponse(context, $"

Error: {ex.Message}

", 500); } } async Task HandleGetSettingsAsync(HttpListenerContext context) { await Task.Run(() => { try { Debug.Log("Starting SettingsToHtml..."); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); var htmlTask = Task.Run(() => SettingsToHtml.GetSettingsPage(SettingsManager.Container.Settings)); //SettingsToHtml.GetSettingsString(SettingsManager.Container.Settings)); if (htmlTask.Wait(TimeSpan.FromSeconds(5))) { string htmlContent = htmlTask.Result; stopwatch.Stop(); Debug.Log($"GetSettingsString выполнился за {stopwatch.ElapsedMilliseconds}ms"); if (string.IsNullOrEmpty(htmlContent)) { htmlContent = "

Error: Method returned empty string

"; } SendHtmlResponse(context, htmlContent); } else { Debug.LogError("GetSettingsString TIMEOUT (5 seconds)!"); SendHtmlResponse(context, "

Error: Settings generation timeout

", 500); } } catch (Exception ex) { Debug.LogError($"Error in GetSettingsString: {ex.Message}"); SendHtmlResponse(context, $"

Error: {ex.Message}

", 500); } }); } async Task HandleCssRequestAsync(HttpListenerContext context) { context.Response.ContentType = "text/css"; context.Response.ContentLength64 = css.Length; await context.Response.OutputStream.WriteAsync(css, 0, css.Length); context.Response.OutputStream.Close(); } private static void LoadCss() { TextAsset cssAsset = Resources.Load("style"); try { print(cssAsset.text); } catch (Exception) { throw; } if (cssAsset != null) { css= Encoding.UTF8.GetBytes(cssAsset.text); Debug.Log("CSS OK"); } else { Debug.LogWarning("CSS NOT OK"); css = new byte[0]; } } async Task HandlePostUpdateAsync(HttpListenerContext context) { try { // Определяем кодировку из Content-Type или используем UTF-8 по умолчанию Encoding encoding = Encoding.UTF8; string contentType = context.Request.ContentType?.ToLower() ?? ""; if (contentType.Contains("charset=")) { try { string charset = contentType.Split("charset=")[1].Trim().Split(';')[0]; encoding = Encoding.GetEncoding(charset); Debug.Log($"Определена кодировка из Content-Type: {charset}"); } catch { encoding = Encoding.UTF8; Debug.Log($"Не удалось определить кодировку из Content-Type, используем UTF-8"); } } // Читаем тело POST-запроса string requestBody; using (var reader = new StreamReader(context.Request.InputStream, encoding)) { requestBody = await reader.ReadToEndAsync(); } Debug.Log($"Получен POST запрос на /upd"); Debug.Log($"Content-Type: {contentType}"); Debug.Log($"Используемая кодировка: {encoding.WebName}"); Debug.Log($"Тело запроса (сырое): {requestBody}"); // Ручное декодирование данных формы Debug.Log("\n=== Декодированные данные формы ==="); var decodedValues = new Dictionary(); // Также можно использовать ParseQueryString для сравнения try { var formData = System.Web.HttpUtility.ParseQueryString(requestBody, encoding); Debug.Log("\n=== Данные через ParseQueryString ==="); foreach (string key in formData.AllKeys) { if (key != null) { string value = formData[key] ?? ""; Debug.Log($" {key} = {value}"); // Если ParseQueryString не декодировал ключ, делаем это вручную if (key.Contains("%")) { string decodedKey = Uri.UnescapeDataString(key); Debug.Log($" Декодированный ключ: {decodedKey}"); } } } } catch (Exception parseEx) { Debug.LogWarning($"ParseQueryString ошибка: {parseEx.Message}"); } // Получаем action из декодированных данных string action = decodedValues.ContainsKey("action") ? decodedValues["action"] : (decodedValues.ContainsKey("action") ? decodedValues["action"] : null); Debug.Log($"\nAction: {action}"); // Обработка разных действий if (!string.IsNullOrEmpty(action)) { if (action == "save") { await HandleSaveAction(decodedValues); } else if (action == "reset") { await HandleResetAction(decodedValues); } else { Debug.Log($"Неизвестное действие: {action}"); } } else { Debug.Log("Action не указан, проверяем наличие отдельных кнопок..."); // Проверяем наличие других кнопок if (decodedValues.ContainsKey("save") && !string.IsNullOrEmpty(decodedValues["save"])) { await HandleSaveAction(decodedValues); } else if (decodedValues.ContainsKey("reset") && !string.IsNullOrEmpty(decodedValues["reset"])) { await HandleResetAction(decodedValues); } } // Отправляем ответ обратно string responseHtml = @" Обновлено

✓ Данные успешно получены!

Сервер обработал ваш запрос.

Полученные данные:

    "; // Добавляем список полученных данных в ответ foreach (var kvp in decodedValues) { responseHtml += $"
  • {kvp.Key}: {kvp.Value}
  • "; } responseHtml += @"
Вернуться к настройкам "; SendHtmlResponse(context, responseHtml); } catch (Exception ex) { Debug.LogError($"Ошибка обработки POST /upd: {ex.Message}"); Debug.LogError($"StackTrace: {ex.StackTrace}"); SendHtmlResponse(context, $"Ошибка" + $"

Ошибка: {ex.Message}

" + $"
{ex.StackTrace}
" + $"Вернуться назад", 500); } } // Обновленный HandleSaveAction для работы с Dictionary async Task HandleSaveAction(Dictionary formData) { Debug.Log("\n=== Обработка сохранения настроек ==="); foreach (var kvp in formData) { string key = kvp.Key; string value = kvp.Value; Debug.Log($"Поле формы: '{key}' = '{value}'"); // Пропускаем служебные поля if (key == "action" || key == "_charset_" || key == "submit") continue; // TODO: Здесь логика обновления SettingsManager // Например: // var setting = SettingsManager.Container.Settings.FirstOrDefault(s => s.Id == key); // if (setting != null) { ... } } Debug.Log("Сохранение настроек завершено"); await Task.CompletedTask; } async Task HandleResetAction(Dictionary formData) { Debug.Log("\n=== Обработка сброса настроек ==="); // TODO: Реализовать сброс настроек await Task.CompletedTask; } async Task HandleSaveAction(System.Collections.Specialized.NameValueCollection formData) { // Здесь обрабатываем сохранение настроек Debug.Log("Обработка сохранения настроек..."); // Пример: получаем значения из формы и обновляем настройки foreach (string key in formData.AllKeys) { string value = formData[key]; Debug.Log($"Поле формы: {key} = {value}"); // TODO: Обновить SettingsManager.Container.Settings на основе данных формы } // TODO: Сохранить настройки // SettingsManager.Save(); await Task.CompletedTask; } async Task HandleResetAction(System.Collections.Specialized.NameValueCollection formData) { // Здесь обрабатываем сброс настроек Debug.Log("Обработка сброса настроек..."); // TODO: Сбросить настройки к значениям по умолчанию await Task.CompletedTask; } void SendHtmlResponse(HttpListenerContext context, string html, int statusCode = 200) { try { byte[] buffer = Encoding.UTF8.GetBytes(html); context.Response.StatusCode = statusCode; context.Response.ContentType = "text/html; charset=utf-8"; context.Response.ContentLength64 = buffer.Length; context.Response.OutputStream.Write(buffer, 0, buffer.Length); context.Response.OutputStream.Close(); } catch (Exception ex) { Debug.LogError($"Error sending response: {ex.Message}"); } } void OnDestroy() { isRunning = false; listener?.Stop(); listener?.Close(); } private void OnApplicationQuit() { isRunning = false; listener?.Stop(); listener?.Close(); } }