using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Threading;
// (Unitys WWW wird nicht mehr benutzt - siehe WinHttp)
using UnityEngine;
namespace MSCO
{
/// Ein Eintrag der Serverliste (vom Master-Server, Spielerzahl/Ping direkt beim Server abgefragt).
public class ServerListEntry
{
public string Name;
public string Description;
public string Address;
public int Port;
public int Players;
public int MaxPlayers;
public string Version;
public string ServerVersion = ""; // Programmversion des Servers (z.B. 2.10.6), ältere melden sie nicht
public int Security; // Sicherheitsstufe des Servers (0 = alt, ohne Sicherheitsupdates)
/// Fehlen dem Server Sicherheitsupdates? Dann tritt die Mod nicht bei.
public bool Outdated { get { return Security < SiteStatus.RequiredServerSecurity; } }
public bool RequireSteam = true; // false = Non-Steam-Server
public string Owner; // Besitzer des Lizenzschlüssels
public bool Featured; // vom Keymaster-Admin hervorgehoben (steht in der Liste oben)
public List Tags = new List(); // Admin-Tags (z.B. "official") und eigene Tags des Servers (Web-Panel)
public List Mods = new List(); // Mods des Servers ("Name Version") - bekommt man beim Beitreten
public bool Modded { get { return Mods.Count > 0 || Tags.Contains("modded"); } }
public int UptimeSeconds = -1;
public List PlayerNames = new List();
public int Ping = -1; // -1 = noch nicht abgefragt
public bool Offline;
public bool IsLocal; // Server auf diesem PC (localhost)
}
///
/// Holt die Serverliste vom Master-Server (Keymaster) und fragt jeden Server im Hintergrund
/// nach aktuellen Infos und Ping (MsgType.Info).
///
public class ServerBrowser
{
/// Offizieller Master (Serverliste) - fest eingebaut.
public const string MasterUrl = "https://msco.mbm.wtf";
const int QueryTimeoutMs = 3000;
// Ein Server auf diesem PC erscheint automatisch oben in der Liste
static readonly int[] LocalPorts = { 7777, 7778, 7779, 7780 };
public const float AutoRefreshSeconds = 15f;
readonly object gate = new object();
List servers = new List();
readonly List localServers = new List();
public bool Loading { get; private set; }
public string Error { get; private set; }
/// Zeitpunkt der letzten Aktualisierung (Time.realtimeSinceStartup), für das Auto-Refresh.
public float LastRefresh { get; private set; }
/// Lokale Server zuerst, dann die öffentliche Liste.
public List Servers
{
get
{
lock (gate)
{
var all = new List(localServers);
all.AddRange(servers);
return all;
}
}
}
public void Refresh(MonoBehaviour host)
{
if (Loading) return;
host.StartCoroutine(Fetch());
}
IEnumerator Fetch()
{
Loading = true;
Error = null;
LastRefresh = Time.realtimeSinceStartup;
ProbeLocalServers(); // unabhängig vom Master: auch ohne Internet sichtbar
// Über WinHTTP (TLS 1.2/1.3) im Hintergrund - Unitys WWW kann nur TLS 1.0
string text = null, error = null;
bool done = false;
string url = MasterUrl + "/api/servers";
var thread = new Thread(() =>
{
text = WinHttp.Get(url, 8000, out error);
done = true;
});
thread.IsBackground = true;
thread.Start();
while (!done) yield return null;
Loading = false;
if (text == null)
{
Error = "Server list not reachable (" + error + ")";
yield break;
}
List list;
try { list = ParseList(text); }
catch (Exception e)
{
Error = "Invalid server list: " + e.Message;
yield break;
}
lock (gate) servers = list;
foreach (ServerListEntry entry in list) QueryAsync(entry);
}
/// Antwort von /api/servers lesen (eigener JSON-Leser, siehe MiniJson).
static List ParseList(string text)
{
var list = new List();
var items = MiniJson.Parse(text) as List