0.3.0: Freunde und Lobby, Server-Browser entfernt
- Seite Freunde: hinzufuegen, annehmen, online-Anzeige, Lobby erstellen/einladen/beitreten, Server starten - SPIELEN in der Lobby: Ticket fuer die Lobby + Relay-Adresse in BSO.json - Knopf 'Mod herunterladen' holt jederzeit die neueste Mod Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+48
-40
@@ -19,9 +19,9 @@ public sealed class MainForm : Form
|
||||
readonly Settings settings = Settings.Load();
|
||||
|
||||
/// <summary>Befehle, die nur einmal gleichzeitig laufen duerfen (Downloads/Installationen/Start).</summary>
|
||||
static readonly HashSet<string> Exclusive = new() { "installMod", "launch", "join", "installLauncherUpdate", "uninstall" };
|
||||
static readonly HashSet<string> Exclusive = new() { "installMod", "launch", "playLobby", "installLauncherUpdate", "uninstall" };
|
||||
readonly HashSet<string> running = new();
|
||||
static readonly HashSet<string> QuietCommands = new() { "window", "status", "me", "init", "releases", "servers" };
|
||||
static readonly HashSet<string> QuietCommands = new() { "window", "status", "me", "init", "releases", "friends", "lobby" };
|
||||
bool updateInstalled;
|
||||
static readonly JsonSerializerOptions Json = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
|
||||
@@ -288,18 +288,23 @@ public sealed class MainForm : Form
|
||||
return await InstallMod();
|
||||
case "launch":
|
||||
return await Launch();
|
||||
case "servers":
|
||||
return await ServerList();
|
||||
case "join":
|
||||
{
|
||||
// Beitreten: Server merken, beim Start automatisch verbinden, Spiel starten
|
||||
string address = Arg(args, "address").Trim();
|
||||
if (address.Length == 0 || address.Length > 260) throw new Exception("Ungueltige Serveradresse");
|
||||
settings.Server = address;
|
||||
settings.AutoConnect = true;
|
||||
settings.Save();
|
||||
return await Launch();
|
||||
}
|
||||
case "playLobby":
|
||||
return await Launch(lobby: true);
|
||||
|
||||
// --- Freunde und Lobby (Website, mit dem Konto-Token)
|
||||
case "friends": return await Site("GET", "/api/friends");
|
||||
case "friendRequest": return await Site("POST", "/api/friends/request", new { username = Arg(args, "username").Trim() });
|
||||
case "friendAccept": return await Site("POST", "/api/friends/accept", new { id = Arg(args, "id") });
|
||||
case "friendRemove": return await Site("POST", "/api/friends/remove", new { id = Arg(args, "id") });
|
||||
case "lobby": return await Site("GET", "/api/lobby");
|
||||
case "lobbyCreate": return await Site("POST", "/api/lobby/create");
|
||||
case "lobbyInvite": return await Site("POST", "/api/lobby/invite", new { accountId = Arg(args, "accountId") });
|
||||
case "lobbyJoin": return await Site("POST", "/api/lobby/join", new { lobbyId = Arg(args, "lobbyId") });
|
||||
case "lobbyDecline": return await Site("POST", "/api/lobby/decline", new { lobbyId = Arg(args, "lobbyId") });
|
||||
case "lobbyLeave": return await Site("POST", "/api/lobby/leave");
|
||||
case "lobbyKick": return await Site("POST", "/api/lobby/kick", new { accountId = Arg(args, "accountId") });
|
||||
case "lobbyStart": return await Site("POST", "/api/lobby/start");
|
||||
case "lobbyStop": return await Site("POST", "/api/lobby/stop");
|
||||
case "releases":
|
||||
{
|
||||
var r = await Api.Releases();
|
||||
@@ -375,19 +380,21 @@ public sealed class MainForm : Form
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Serverliste der Website, jeder Server direkt angefragt (Ping, aktuelle Spielerzahl). Nicht erreichbar = ohne Ping.</summary>
|
||||
static async Task<object> ServerList()
|
||||
/// <summary>Anfrage an die Website mit dem Konto-Token. Abgelaufene Anmeldung -> zurueck zum Anmelden.</summary>
|
||||
async Task<JsonElement> Site(string method, string path, object? body = null)
|
||||
{
|
||||
var servers = await Api.Servers();
|
||||
var pings = await Task.WhenAll(servers.Select(s => ServerPing.Query(s.host, s.port)));
|
||||
return servers.Select((s, i) => new
|
||||
{
|
||||
s.name, s.description, s.address, s.version,
|
||||
players = pings[i]?.Players ?? s.players,
|
||||
maxPlayers = pings[i]?.MaxPlayers ?? s.maxPlayers,
|
||||
ping = pings[i]?.Ping,
|
||||
compatible = s.protocol == 0 || s.protocol == 2 // Protokoll der Mod, die der Launcher installiert
|
||||
}).OrderBy(s => s.ping == null).ThenByDescending(s => s.players).ToList();
|
||||
string token = settings.Token ?? throw new Exception("Bitte anmelden");
|
||||
var r = method == "GET" ? await Api.Get(path, token) : await Api.Post(path, token, body);
|
||||
if (r.Status == 401) { LoggedOut(); throw new Exception("Anmeldung abgelaufen - bitte neu anmelden"); }
|
||||
if (!r.Ok) throw new Exception(r.Error ?? "Fehlgeschlagen");
|
||||
return r.Json;
|
||||
}
|
||||
|
||||
void LoggedOut()
|
||||
{
|
||||
settings.Token = null;
|
||||
settings.Save();
|
||||
Emit(new { @event = "loggedOut" });
|
||||
}
|
||||
|
||||
async Task<object> InstallMod()
|
||||
@@ -400,7 +407,8 @@ public sealed class MainForm : Form
|
||||
return new { version = latest.version };
|
||||
}
|
||||
|
||||
async Task<object> Launch()
|
||||
/// <summary>Spiel starten. lobby: auf den Server der eigenen Lobby (laeuft im BSO-Relay), sonst auf settings.Server.</summary>
|
||||
async Task<object> Launch(bool lobby = false)
|
||||
{
|
||||
string folder = Game.FindFolder(settings) ?? throw new Exception("Bus-Simulator 2012 nicht gefunden - in den Einstellungen den Spielordner waehlen");
|
||||
if (!Game.DotnetInstalled()) throw new Exception("Fuer BSO fehlt die .NET 10 Runtime (x64) - unter \"Status\" herunterladen");
|
||||
@@ -415,22 +423,22 @@ public sealed class MainForm : Form
|
||||
else if (installed == null) throw new Exception("BSO ist nicht installiert und der BSO-Server ist nicht erreichbar");
|
||||
}
|
||||
|
||||
// Spiel-Ticket: ohne gueltiges Konto kein Multiplayer (Mod und Server pruefen es)
|
||||
string token = settings.Token ?? throw new Exception("Bitte anmelden");
|
||||
var t = await Api.Ticket(token);
|
||||
if (t.Status == 401)
|
||||
// Spiel-Ticket: ohne gueltiges Konto kein Multiplayer (Mod und Relay pruefen es). Fuer die Lobby gilt es fuer deren Server.
|
||||
var t = await Site("POST", "/api/auth/ticket", lobby ? new { lobby = true } : null);
|
||||
string ticket = t.GetProperty("ticket").GetString()!;
|
||||
if (lobby)
|
||||
{
|
||||
settings.Token = null;
|
||||
settings.Save();
|
||||
Emit(new { @event = "loggedOut" });
|
||||
throw new Exception("Anmeldung abgelaufen - bitte neu anmelden");
|
||||
string lobbyId = t.GetProperty("lobby").GetString()!, label = t.GetProperty("lobbyName").GetString() ?? "Lobby";
|
||||
string relay = (await Site("GET", "/api/config")).GetProperty("relay").GetString()!;
|
||||
Game.WriteModConfig(folder, settings.Username, "relay:" + lobbyId, true, ticket, label, relay);
|
||||
LauncherLog.Info($"Spiel gestartet ({label} ueber {relay})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Game.WriteModConfig(folder, settings.Username, settings.Server, settings.AutoConnect, ticket);
|
||||
LauncherLog.Info($"Spiel gestartet (Server {settings.Server}, automatisch verbinden {settings.AutoConnect})");
|
||||
}
|
||||
if (!t.Ok) throw new Exception("Konto konnte nicht geprueft werden: " + t.Error);
|
||||
string ticket = t.Json.GetProperty("ticket").GetString()!;
|
||||
|
||||
Game.WriteModConfig(folder, settings.Username, settings.Server, settings.AutoConnect, ticket);
|
||||
Game.Launch();
|
||||
LauncherLog.Info($"Spiel gestartet (Server {settings.Server}, automatisch verbinden {settings.AutoConnect})");
|
||||
if (!settings.KeepOpen)
|
||||
{
|
||||
await Task.Delay(2500);
|
||||
|
||||
Reference in New Issue
Block a user