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