0.5.0: Lobby ueber das BSO-Relay (ohne Portfreigabe)
- Server relay:<Lobby> verbindet per WebSocket ueber eine lokale Bruecke (RelayClient) - Ticket mit Lobby, Anzeige 'Lobby von ...' - Nach Szenenwechsel Mitspieler-Modelle erst nach 3 s neu anlegen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<RootNamespace>BSO</RootNamespace>
|
<RootNamespace>BSO</RootNamespace>
|
||||||
<AssemblyName>BSO</AssemblyName>
|
<AssemblyName>BSO</AssemblyName>
|
||||||
<Version>0.4.0</Version>
|
<Version>0.5.0</Version>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ namespace BSO
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static unsafe class Bootstrap
|
public static unsafe class Bootstrap
|
||||||
{
|
{
|
||||||
public const string ModVersion = "0.4.0";
|
public const string ModVersion = "0.5.0";
|
||||||
|
|
||||||
static HostApi* host;
|
static HostApi* host;
|
||||||
static int frameErrors;
|
static int frameErrors;
|
||||||
|
|||||||
+5
-1
@@ -3,10 +3,14 @@
|
|||||||
Neue Version: hier einen Abschnitt `## <Version>` anlegen, `<Version>` in `BSO.csproj` und `Bootstrap.ModVersion`
|
Neue Version: hier einen Abschnitt `## <Version>` anlegen, `<Version>` in `BSO.csproj` und `Bootstrap.ModVersion`
|
||||||
erhöhen, dann `.\build.ps1 -Release`.
|
erhöhen, dann `.\build.ps1 -Release`.
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
- Lobby: mit Freunden auf dem Lobby-Server spielen - ueber die BSO-Website, ohne Portfreigabe
|
||||||
|
- Anzeige "Lobby von ..." statt der Adresse
|
||||||
|
|
||||||
## 0.4.0
|
## 0.4.0
|
||||||
- Mitspieler in der Welt: Bus bzw. Spielfigur mit Namensschild und Entfernung, weich bewegt
|
- Mitspieler in der Welt: Bus bzw. Spielfigur mit Namensschild und Entfernung, weich bewegt
|
||||||
- BSO-Konto: ohne Start über den Launcher kein Multiplayer, der Name im Spiel kommt aus dem Konto
|
- BSO-Konto: ohne Start über den Launcher kein Multiplayer, der Name im Spiel kommt aus dem Konto
|
||||||
- Beitreten über den Server-Browser im Launcher (verbindet automatisch)
|
- Beitreten über den Launcher (verbindet automatisch)
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
- Mitspieler-Liste: Name, zu Fuß/Bus, Entfernung, Tempo
|
- Mitspieler-Liste: Name, zu Fuß/Bus, Entfernung, Tempo
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ namespace BSO
|
|||||||
{
|
{
|
||||||
/// <summary>Name, den die anderen sehen.</summary>
|
/// <summary>Name, den die anderen sehen.</summary>
|
||||||
public string Name { get; set; } = Environment.UserName;
|
public string Name { get; set; } = Environment.UserName;
|
||||||
/// <summary>Server als host oder host:port.</summary>
|
/// <summary>Server als host oder host:port - oder relay:<Lobby-ID> (Server der Lobby im BSO-Relay).</summary>
|
||||||
public string Server { get; set; } = "127.0.0.1:7777";
|
public string Server { get; set; } = "127.0.0.1:7777";
|
||||||
|
/// <summary>Anzeige fuer den Server, z.B. "Lobby von MrMaho" (schreibt der Launcher).</summary>
|
||||||
|
public string ServerLabel { get; set; }
|
||||||
|
/// <summary>BSO-Relay (Lobby-Server) - schreibt der Launcher.</summary>
|
||||||
|
public string Relay { get; set; } = "https://bso-relay.mbm.wtf";
|
||||||
/// <summary>Beim Spielstart automatisch verbinden (sonst F8).</summary>
|
/// <summary>Beim Spielstart automatisch verbinden (sonst F8).</summary>
|
||||||
public bool AutoConnect { get; set; }
|
public bool AutoConnect { get; set; }
|
||||||
/// <summary>Spiel-Ticket der BSO-Website - schreibt der Launcher bei SPIELEN hinein (24 h gueltig).</summary>
|
/// <summary>Spiel-Ticket der BSO-Website - schreibt der Launcher bei SPIELEN hinein (24 h gueltig).</summary>
|
||||||
|
|||||||
+22
-4
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BSO.Net;
|
using BSO.Net;
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ namespace BSO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int ConnectTimeoutMs = 5000;
|
const int ConnectTimeoutMs = 5000, RelayTimeoutMs = 20000; // Relay: Website + Antwort des Lobby-Servers
|
||||||
const int SendIntervalMs = 100, PingIntervalMs = 2000;
|
const int SendIntervalMs = 100, PingIntervalMs = 2000;
|
||||||
|
|
||||||
public static NetStatus Status { get; private set; }
|
public static NetStatus Status { get; private set; }
|
||||||
@@ -125,6 +126,8 @@ namespace BSO
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static string ServerAddress => Config.Current.Server;
|
public static string ServerAddress => Config.Current.Server;
|
||||||
|
/// <summary>Fuer die Anzeige: "Lobby von ..." statt relay:<id>.</summary>
|
||||||
|
public static string ServerDisplay => Config.Current.ServerLabel ?? (ServerAddress.StartsWith("relay:") ? "Lobby" : ServerAddress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gueltiges Spiel-Ticket vom Launcher? Ohne gibt es keinen Multiplayer - der Server prueft es zusaetzlich (die Mod
|
/// Gueltiges Spiel-Ticket vom Launcher? Ohne gibt es keinen Multiplayer - der Server prueft es zusaetzlich (die Mod
|
||||||
@@ -152,11 +155,26 @@ namespace BSO
|
|||||||
Bootstrap.Log("Nicht verbunden: " + problem);
|
Bootstrap.Log("Nicht verbunden: " + problem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(string host, int port) = ParseAddress(ServerAddress);
|
|
||||||
Bootstrap.Log($"Verbinde mit {host}:{port} ...");
|
|
||||||
LastError = "";
|
LastError = "";
|
||||||
tcp = new TcpClient();
|
tcp = new TcpClient();
|
||||||
|
if (ServerAddress.StartsWith("relay:"))
|
||||||
|
{
|
||||||
|
// Server der Lobby: laeuft im BSO-Relay, verbunden ueber eine lokale Bruecke - keine Portfreigabe noetig
|
||||||
|
string lobby = ServerAddress.Substring(6), relay = Config.Current.Relay, ticket = Config.Current.Ticket;
|
||||||
|
Bootstrap.Log($"Verbinde ueber {relay} mit Lobby {lobby} ...");
|
||||||
|
TcpClient client = tcp;
|
||||||
|
connectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
int port = await RelayClient.JoinAsync(relay, lobby, ticket, CancellationToken.None);
|
||||||
|
await client.ConnectAsync("127.0.0.1", port);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(string host, int port) = ParseAddress(ServerAddress);
|
||||||
|
Bootstrap.Log($"Verbinde mit {host}:{port} ...");
|
||||||
connectTask = tcp.ConnectAsync(host, port);
|
connectTask = tcp.ConnectAsync(host, port);
|
||||||
|
}
|
||||||
connectClock.Restart();
|
connectClock.Restart();
|
||||||
Status = NetStatus.Connecting;
|
Status = NetStatus.Connecting;
|
||||||
}
|
}
|
||||||
@@ -165,7 +183,7 @@ namespace BSO
|
|||||||
{
|
{
|
||||||
if (!connectTask.IsCompleted)
|
if (!connectTask.IsCompleted)
|
||||||
{
|
{
|
||||||
if (connectClock.ElapsedMilliseconds > ConnectTimeoutMs) Fail("Server antwortet nicht");
|
if (connectClock.ElapsedMilliseconds > (ServerAddress.StartsWith("relay:") ? RelayTimeoutMs : ConnectTimeoutMs)) Fail("Server antwortet nicht");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (connectTask.IsFaulted || !tcp.Connected)
|
if (connectTask.IsFaulted || !tcp.Connected)
|
||||||
|
|||||||
+3
-3
@@ -63,16 +63,16 @@ namespace BSO
|
|||||||
text.Add(Multiplayer.AccountProblem);
|
text.Add(Multiplayer.AccountProblem);
|
||||||
return;
|
return;
|
||||||
case Multiplayer.NetStatus.Offline:
|
case Multiplayer.NetStatus.Offline:
|
||||||
text.Add($"Offline - [F8] verbinden mit {Multiplayer.ServerAddress} als {Multiplayer.PlayerName}");
|
text.Add($"Offline - [F8] verbinden mit {Multiplayer.ServerDisplay} als {Multiplayer.PlayerName}");
|
||||||
if (Multiplayer.LastError.Length > 0) text.Add(" " + Multiplayer.LastError);
|
if (Multiplayer.LastError.Length > 0) text.Add(" " + Multiplayer.LastError);
|
||||||
return;
|
return;
|
||||||
case Multiplayer.NetStatus.Connecting:
|
case Multiplayer.NetStatus.Connecting:
|
||||||
text.Add($"Verbinde mit {Multiplayer.ServerAddress} ...");
|
text.Add($"Verbinde mit {Multiplayer.ServerDisplay} ...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Multiplayer.Welcomed)
|
if (!Multiplayer.Welcomed)
|
||||||
{
|
{
|
||||||
text.Add($"Verbinde mit {Multiplayer.ServerAddress} ... warte auf Server");
|
text.Add($"Verbinde mit {Multiplayer.ServerDisplay} ... warte auf Server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ Multiplayer-Mod für **Bus-Simulator 2012** (European Bus Simulator 2012, TML-St
|
|||||||
eigenständiger Loader im Spielordner, die Mod selbst in C# (.NET 10).
|
eigenständiger Loader im Spielordner, die Mod selbst in C# (.NET 10).
|
||||||
|
|
||||||
- Mod: dieses Repository
|
- Mod: dieses Repository
|
||||||
- Dedizierter Server: [BSO-Server](https://git.mbm.wtf/MrMaho/BSO-Server)
|
- Lobby-Server (ohne Portfreigabe): [BSO-Relay](https://git.mbm.wtf/MrMaho/BSO-Relay)
|
||||||
- Launcher (installiert und aktualisiert die Mod): [BSO-Launcher](https://git.mbm.wtf/MrMaho/BSO-Launcher)
|
- Launcher (installiert und aktualisiert die Mod): [BSO-Launcher](https://git.mbm.wtf/MrMaho/BSO-Launcher)
|
||||||
- Website, Konten und Releases: [BSO-Website](https://git.mbm.wtf/MrMaho/BSO-Website)
|
- Website, Konten, Freunde, Lobbys, Releases: [BSO-Website](https://git.mbm.wtf/MrMaho/BSO-Website)
|
||||||
|
|
||||||
**Stand:** Spieler verbinden sich per IP mit einem Server und sehen die anderen als Liste (Name, zu Fuß/Bus,
|
**Stand:** Freunde spielen zusammen in einer Lobby – der Server läuft im BSO-Relay, die Mod verbindet sich per WebSocket,
|
||||||
Entfernung, Tempo). Als Nächstes kommen sichtbare Busse der Mitspieler in der Welt.
|
niemand braucht eine Portfreigabe. Mitspieler erscheinen als Bus bzw. Spielfigur mit Namensschild in der Welt.
|
||||||
|
|
||||||
## Wie es funktioniert
|
## Wie es funktioniert
|
||||||
|
|
||||||
@@ -24,7 +24,8 @@ Entfernung, Tempo). Als Nächstes kommen sichtbare Busse der Mitspieler in der W
|
|||||||
| `Config.cs` | `BSO.json` (Name, Server, AutoConnect) | – |
|
| `Config.cs` | `BSO.json` (Name, Server, AutoConnect) | – |
|
||||||
| `PositionHud.cs` | Anzeige im Spiel | `Overlays.cs` |
|
| `PositionHud.cs` | Anzeige im Spiel | `Overlays.cs` |
|
||||||
| `BorderlessWindow.cs` | Vollbild im Fenster (F11) | – |
|
| `BorderlessWindow.cs` | Vollbild im Fenster (F11) | – |
|
||||||
| `Shared\*.cs` | Protokoll, `PacketReader`, `Connection`/`FrameParser` – **gleiche Dateien wie in BSO-Server** | `NetSession.cs` |
|
| `RemotePlayers.cs`, `Models.cs` | Mitspieler als Bus/Spielfigur in der Welt, Namensschild | `RemotePlayer.cs` |
|
||||||
|
| `Shared\*.cs` | Protokoll, `Connection`, `Ticket`, `RelayClient` (WebSocket-Brücke) – **gleiche Dateien wie in BSO-Relay** | `NetSession.cs` |
|
||||||
|
|
||||||
- **Bild-Takt:** Das Spiel fragt die Tastatur mehrmals pro Bild über DirectInput ab. Der Loader hängt sich dort ein
|
- **Bild-Takt:** Das Spiel fragt die Tastatur mehrmals pro Bild über DirectInput ab. Der Loader hängt sich dort ein
|
||||||
und ruft `Bootstrap.OnFrame` im Hauptthread auf. Gerechnet wird, wenn die Engine-Uhr weiterläuft; Text wird bei
|
und ruft `Bootstrap.OnFrame` im Hauptthread auf. Gerechnet wird, wenn die Engine-Uhr weiterläuft; Text wird bei
|
||||||
@@ -82,5 +83,6 @@ Protokoll: `Bin_High_Win64\BSO\BSO_Loader.txt`
|
|||||||
|
|
||||||
1. ~~Mod lädt, Busposition anzeigen~~
|
1. ~~Mod lädt, Busposition anzeigen~~
|
||||||
2. ~~Netzwerk: Server + Positionen austauschen, Mitspieler als Text~~
|
2. ~~Netzwerk: Server + Positionen austauschen, Mitspieler als Text~~
|
||||||
3. Mitspieler-Busse in der Welt (`VisGame_cl::CreateEntity`), Namensschild mit `VisMessage_cl::DrawMessage3D`
|
3. ~~Mitspieler-Busse in der Welt, Namensschild~~
|
||||||
4. Chat, Konsole, Steam-P2P, Serverliste
|
4. ~~Konten, Freunde, Lobby ohne Portfreigabe (BSO-Relay)~~
|
||||||
|
5. Direkt ins Spiel laden, Chat, Türen/Lichter
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BSO.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// BSO-Relay (Lobby-Server ohne Portfreigabe): eine TCP-Verbindung wird ueber einen WebSocket zum Relay
|
||||||
|
/// durchgereicht. Die Mod spricht weiter ganz normal TCP (Connection) - nur eben mit 127.0.0.1, die Bruecke
|
||||||
|
/// schickt alles ueber das Relay. Angemeldet wird mit dem Spiel-Ticket der Lobby (Kopfzeile X-BSO-Ticket).
|
||||||
|
/// </summary>
|
||||||
|
public static class RelayClient
|
||||||
|
{
|
||||||
|
/// <summary>https://site/relay/... -> wss://site/relay/...</summary>
|
||||||
|
public static Uri WsUri(string master, string pathAndQuery)
|
||||||
|
{
|
||||||
|
string b = master.TrimEnd('/');
|
||||||
|
if (b.StartsWith("https://")) b = "wss://" + b.Substring(8);
|
||||||
|
else if (b.StartsWith("http://")) b = "ws://" + b.Substring(7);
|
||||||
|
return new Uri(b + pathAndQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ClientWebSocket> ConnectAsync(string master, string pathAndQuery, string ticket, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var ws = new ClientWebSocket();
|
||||||
|
ws.Options.SetRequestHeader("X-BSO-Ticket", ticket ?? "");
|
||||||
|
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(20);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ws.ConnectAsync(WsUri(master, pathAndQuery), ct);
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
catch (WebSocketException e)
|
||||||
|
{
|
||||||
|
ws.Dispose();
|
||||||
|
string msg = e.Message ?? "";
|
||||||
|
if (msg.Contains("404")) throw new Exception("Der Server der Lobby laeuft gerade nicht");
|
||||||
|
if (msg.Contains("403")) throw new Exception("Kein Zugang zu dieser Lobby - im Launcher neu auf SPIELEN druecken");
|
||||||
|
throw new Exception("BSO-Relay nicht erreichbar: " + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mitspieler: mit dem Lobby-Server verbinden und eine lokale Bruecke oeffnen. Gibt den Port auf 127.0.0.1 zurueck,
|
||||||
|
/// mit dem man sich dann normal per TCP verbindet. Die Bruecke nimmt genau eine Verbindung an.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<int> JoinAsync(string master, string lobbyId, string ticket, CancellationToken ct)
|
||||||
|
{
|
||||||
|
ClientWebSocket ws = await ConnectAsync(master, "/join?lobby=" + Uri.EscapeDataString(lobbyId), ticket, ct);
|
||||||
|
var listener = new TcpListener(IPAddress.Loopback, 0);
|
||||||
|
listener.Start();
|
||||||
|
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var accept = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||||
|
using TcpClient tcp = await listener.AcceptTcpClientAsync(accept.Token);
|
||||||
|
listener.Stop();
|
||||||
|
await BridgeAsync(tcp, ws);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try { listener.Stop(); } catch { }
|
||||||
|
ws.Dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Daten zwischen TCP und WebSocket in beide Richtungen, bis eine Seite trennt.</summary>
|
||||||
|
public static async Task BridgeAsync(TcpClient tcp, WebSocket ws)
|
||||||
|
{
|
||||||
|
tcp.NoDelay = true;
|
||||||
|
NetworkStream stream = tcp.GetStream();
|
||||||
|
using var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
async Task TcpToWs()
|
||||||
|
{
|
||||||
|
var buffer = new byte[64 * 1024];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
while ((n = await stream.ReadAsync(buffer, cts.Token)) > 0)
|
||||||
|
await ws.SendAsync(new ArraySegment<byte>(buffer, 0, n), WebSocketMessageType.Binary, true, cts.Token);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task WsToTcp()
|
||||||
|
{
|
||||||
|
var buffer = new byte[64 * 1024];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cts.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var r = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);
|
||||||
|
if (r.MessageType == WebSocketMessageType.Close) break;
|
||||||
|
await stream.WriteAsync(buffer, 0, r.Count, cts.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(TcpToWs(), WsToTcp());
|
||||||
|
try { tcp.Close(); } catch { }
|
||||||
|
try { if (ws.State == WebSocketState.Open) await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-2
@@ -6,7 +6,7 @@ using System.Text.Json;
|
|||||||
namespace BSO.Net
|
namespace BSO.Net
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Spiel-Ticket der BSO-Website: base64url(JSON {u, id, iat, exp}) + "." + base64url(RSA-SHA256-Signatur). Der Launcher
|
/// Spiel-Ticket der BSO-Website: base64url(JSON {u, id, iat, exp, lb?, ln?}) + "." + base64url(RSA-SHA256-Signatur). Der Launcher
|
||||||
/// holt es beim Spielen und schreibt es in BSO.json, die Mod schickt es im Hello mit. Die Mod liest es nur (Name, Ablauf),
|
/// holt es beim Spielen und schreibt es in BSO.json, die Mod schickt es im Hello mit. Die Mod liest es nur (Name, Ablauf),
|
||||||
/// pruefen kann es nur der Server mit dem oeffentlichen Schluessel der Website (Verify) - wer die Mod veraendert, kommt so
|
/// pruefen kann es nur der Server mit dem oeffentlichen Schluessel der Website (Verify) - wer die Mod veraendert, kommt so
|
||||||
/// trotzdem nicht ohne gueltiges Konto auf einen Server.
|
/// trotzdem nicht ohne gueltiges Konto auf einen Server.
|
||||||
@@ -16,6 +16,9 @@ namespace BSO.Net
|
|||||||
public string Username { get; private set; }
|
public string Username { get; private set; }
|
||||||
public string AccountId { get; private set; }
|
public string AccountId { get; private set; }
|
||||||
public DateTime Expires { get; private set; }
|
public DateTime Expires { get; private set; }
|
||||||
|
/// <summary>Lobby, fuer die das Ticket gilt (null = keine) und ihr Name ("Lobby von ...").</summary>
|
||||||
|
public string Lobby { get; private set; }
|
||||||
|
public string LobbyName { get; private set; }
|
||||||
public bool Expired => DateTime.UtcNow >= Expires;
|
public bool Expired => DateTime.UtcNow >= Expires;
|
||||||
|
|
||||||
/// <summary>Inhalt lesen, ohne die Signatur zu pruefen. null = kein Ticket / kaputt.</summary>
|
/// <summary>Inhalt lesen, ohne die Signatur zu pruefen. null = kein Ticket / kaputt.</summary>
|
||||||
@@ -32,7 +35,9 @@ namespace BSO.Net
|
|||||||
{
|
{
|
||||||
Username = r.GetProperty("u").GetString(),
|
Username = r.GetProperty("u").GetString(),
|
||||||
AccountId = r.GetProperty("id").GetString(),
|
AccountId = r.GetProperty("id").GetString(),
|
||||||
Expires = DateTimeOffset.FromUnixTimeSeconds(r.GetProperty("exp").GetInt64()).UtcDateTime
|
Expires = DateTimeOffset.FromUnixTimeSeconds(r.GetProperty("exp").GetInt64()).UtcDateTime,
|
||||||
|
Lobby = r.TryGetProperty("lb", out var lb) ? lb.GetString() : null,
|
||||||
|
LobbyName = r.TryGetProperty("ln", out var ln) ? ln.GetString() : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch { return null; }
|
catch { return null; }
|
||||||
|
|||||||
Reference in New Issue
Block a user