Files
MSCO/SiteStatus.cs
T

135 lines
5.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace MSCO
{
/// <summary>Eine Ankündigung der MSCO-Admins (Keymaster → Wartung &amp; News, Ziel "Spiel").</summary>
public class Announcement
{
public string Id, Title, Text, Level; // Level: info, warning, critical
}
/// <summary>
/// Status vom Keymaster (/api/status?for=game): Wartungsmodus, Ankündigungen und die Mindest-Sicherheitsstufe der
/// Server. Wird im Hintergrund geladen (WinHTTP, blockiert das Spiel nicht) und alle paar Minuten aufgefrischt.
/// Ohne Verbindung gilt das Eingebaute (MSCO.MinServerSecurity), Ankündigungen fehlen dann einfach.
/// </summary>
public static class SiteStatus
{
const float RefreshSeconds = 300f;
static readonly object gate = new object();
static List<Announcement> announcements = new List<Announcement>();
static bool maintenance;
static string maintenanceMessage = "";
static int minServerLevel; // vom Keymaster (0 = unbekannt)
static float nextFetch;
static bool fetching;
/// <summary>Aktuelle Ankündigungen (wichtigste zuerst).</summary>
public static List<Announcement> Announcements { get { lock (gate) return new List<Announcement>(announcements); } }
public static bool Maintenance { get { lock (gate) return maintenance; } }
public static string MaintenanceMessage { get { lock (gate) return maintenanceMessage; } }
/// <summary>
/// Sicherheitsstufe, die Server und P2P-Hosts mindestens haben müssen: das Eingebaute oder - wenn höher - was die
/// MSCO-Admins im Keymaster eingestellt haben.
/// </summary>
public static int RequiredServerSecurity { get { lock (gate) return Math.Max(MSCO.MinServerSecurity, minServerLevel); } }
/// <summary>Neue Ankündigung seit dem letzten Abruf (für einen Hinweis im Spiel).</summary>
public static event Action<Announcement> NewAnnouncement;
static readonly HashSet<string> seen = new HashSet<string>();
static readonly List<Announcement> fresh = new List<Announcement>(); // neu, noch nicht gemeldet (Hauptthread)
static bool firstLoad = true;
/// <summary>Jedes Frame aufrufen (MultiplayerManager): lädt beim Start und dann alle 5 Minuten neu.</summary>
public static void Tick()
{
Action<Announcement> handler = NewAnnouncement;
List<Announcement> report = null;
lock (gate)
if (fresh.Count > 0) { report = new List<Announcement>(fresh); fresh.Clear(); }
if (report != null && handler != null)
foreach (Announcement a in report)
try { handler(a); } catch { }
if (fetching || Time.realtimeSinceStartup < nextFetch) return;
Refresh();
}
/// <summary>Sofort neu laden (z.B. beim Öffnen des Server-Browsers).</summary>
public static void Refresh()
{
if (fetching) return;
fetching = true;
nextFetch = Time.realtimeSinceStartup + RefreshSeconds;
var thread = new Thread(() =>
{
try
{
string error;
string text = WinHttp.Get(ServerBrowser.MasterUrl + "/api/status?for=game", 8000, out error);
if (text != null) Apply(text);
}
catch { }
finally { fetching = false; }
});
thread.IsBackground = true;
thread.Start();
}
static void Apply(string text)
{
var root = MiniJson.Parse(text) as Dictionary<string, object>;
if (root == null) return;
object v;
var m = root.TryGetValue("maintenance", out v) ? v as Dictionary<string, object> : null;
var sec = root.TryGetValue("security", out v) ? v as Dictionary<string, object> : null;
var list = new List<Announcement>();
var items = root.TryGetValue("announcements", out v) ? v as List<object> : null;
if (items != null)
foreach (object item in items)
{
var a = item as Dictionary<string, object>;
if (a == null) continue;
list.Add(new Announcement
{
Id = MiniJson.Str(a, "id", ""),
Title = Clip(MiniJson.Str(a, "title", ""), 120),
Text = Clip(MiniJson.Str(a, "text", ""), 600),
Level = MiniJson.Str(a, "level", "info"),
});
if (list.Count >= 5) break;
}
lock (gate)
{
announcements = list;
maintenance = m != null && MiniJson.Bool(m, "active");
maintenanceMessage = m != null ? Clip(MiniJson.Str(m, "message", ""), 500) : "";
if (sec != null) minServerLevel = Math.Max(0, Math.Min(1000, MiniJson.Int(sec, "minServerLevel")));
foreach (Announcement a in list)
if (seen.Add(a.Id) && !firstLoad) fresh.Add(a);
firstLoad = false;
}
}
/// <summary>Kein Steuerzeichen/Rich-Text aus dem Netz in die Oberfläche (Unity-GUI wertet &lt;color&gt; usw. aus).</summary>
static string Clip(string s, int max)
{
if (string.IsNullOrEmpty(s)) return "";
var sb = new System.Text.StringBuilder(Math.Min(s.Length, max));
foreach (char c in s)
{
if (sb.Length >= max) break;
if (c == '<') sb.Append('‹');
else if (c == '>') sb.Append('›');
else if (c == '\n' || !char.IsControl(c)) sb.Append(c);
}
return sb.ToString();
}
}
}