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(() => HtmlBuilder.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 { 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(); } var formDict = new Dictionary(); 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] ?? ""; formDict[key] = value; Debug.Log($" {key} = {formDict[key]}"); if (key.Contains("%")) { string decodedKey = Uri.UnescapeDataString(key); Debug.Log($"РУЧНОЙ ДЕКОД: {decodedKey}"); } } } } catch (Exception parseEx) { Debug.LogWarning($"ParseQueryString ошибка: {parseEx.Message}"); } SettingsApplier.ApplySettings(formDict); //SettingsManager.OnSettingsChanged?.Invoke(); UnityMainThreadDispatcher.Instance.Enqueue(() => SettingsManager.OnSettingsChanged?.Invoke()); UnityMainThreadDispatcher.Instance.Enqueue(() => GameManager.instance.AdminPanel.GetComponent().RefreshViews()); // Отправляем ответ обратно string responseHtml = @" Обновлено

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

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

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

    "; // Добавляем список полученных данных в ответ foreach (var kvp in formDict) { 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); } } 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(); } }