2.10.11: security levels (no joining servers/hosts below the required level, OUTDATED in the browser), announcements and maintenance notice from the keymaster in the MSCO menu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+134
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>Eine Ankündigung der MSCO-Admins (Keymaster → Wartung & 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 <color> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user