0.4.0: Mitspieler in der Welt, BSO-Konto (Spiel-Ticket)
- Mitspieler als Bus/Spielfigur mit Namensschild, weich bewegt (RemotePlayers, Models) - Ohne gueltiges Spiel-Ticket vom Launcher kein Multiplayer; Name kommt aus dem Konto - Protokoll 2 (Hello mit Ticket), IPv6-Adressen - Nach Szenenwechsel erst nach 3 s neu anlegen (Absturz im Test 0.3.0) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+80
-4
@@ -21,8 +21,49 @@ namespace BSO
|
||||
{
|
||||
public int Id;
|
||||
public string Name;
|
||||
public PlayerState State;
|
||||
public PlayerState State; // zuletzt empfangen
|
||||
public bool HasState;
|
||||
|
||||
// Weiche Bewegung: vom angezeigten Stand zum neuen Paket gleiten, so lange wie seit dem letzten Paket vergangen ist
|
||||
PlayerState from, to;
|
||||
long startTicks, durationTicks = 1;
|
||||
|
||||
public void Receive(PlayerState s)
|
||||
{
|
||||
long now = Stopwatch.GetTimestamp();
|
||||
from = HasState ? Display : s;
|
||||
to = s;
|
||||
long since = now - startTicks;
|
||||
durationTicks = Math.Clamp(since, Stopwatch.Frequency / 20, Stopwatch.Frequency / 4); // 50..250 ms
|
||||
startTicks = now;
|
||||
State = s;
|
||||
HasState = true;
|
||||
}
|
||||
|
||||
/// <summary>Stand fuer dieses Bild (zwischen den 10 Paketen pro Sekunde weich weiterbewegt).</summary>
|
||||
public PlayerState Display
|
||||
{
|
||||
get
|
||||
{
|
||||
if (from.Mode != to.Mode) return to; // Ein-/Aussteigen: nicht gleiten
|
||||
float t = Math.Clamp((Stopwatch.GetTimestamp() - startTicks) / (float)durationTicks, 0, 1);
|
||||
PlayerState s = to;
|
||||
s.X = from.X + (to.X - from.X) * t;
|
||||
s.Y = from.Y + (to.Y - from.Y) * t;
|
||||
s.Z = from.Z + (to.Z - from.Z) * t;
|
||||
s.Yaw = Angle(from.Yaw, to.Yaw, t);
|
||||
s.Pitch = Angle(from.Pitch, to.Pitch, t);
|
||||
s.Roll = Angle(from.Roll, to.Roll, t);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Winkel (Grad) auf dem kuerzeren Weg mischen - sonst dreht sich der Bus bei 359 -> 1 einmal ganz herum.</summary>
|
||||
static float Angle(float a, float b, float t)
|
||||
{
|
||||
float d = ((b - a) % 360 + 540) % 360 - 180;
|
||||
return a + d * t;
|
||||
}
|
||||
}
|
||||
|
||||
const int ConnectTimeoutMs = 5000;
|
||||
@@ -85,8 +126,32 @@ namespace BSO
|
||||
|
||||
public static string ServerAddress => Config.Current.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Gueltiges Spiel-Ticket vom Launcher? Ohne gibt es keinen Multiplayer - der Server prueft es zusaetzlich (die Mod
|
||||
/// allein liesse sich austricksen), hier gibt es nur die verstaendliche Meldung vorab.
|
||||
/// </summary>
|
||||
public static string AccountProblem
|
||||
{
|
||||
get
|
||||
{
|
||||
var t = Ticket.Read(Config.Current.Ticket);
|
||||
if (t == null) return "Nicht ueber den BSO Launcher gestartet - Multiplayer gesperrt";
|
||||
if (t.Expired) return "Spiel-Ticket abgelaufen - im BSO Launcher neu auf SPIELEN druecken";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Name im Spiel: aus dem Konto (Ticket), sonst aus BSO.json.</summary>
|
||||
public static string PlayerName => Ticket.Read(Config.Current.Ticket)?.Username ?? Config.Current.Name;
|
||||
|
||||
static void Connect()
|
||||
{
|
||||
if (AccountProblem is string problem)
|
||||
{
|
||||
LastError = problem;
|
||||
Bootstrap.Log("Nicht verbunden: " + problem);
|
||||
return;
|
||||
}
|
||||
(string host, int port) = ParseAddress(ServerAddress);
|
||||
Bootstrap.Log($"Verbinde mit {host}:{port} ...");
|
||||
LastError = "";
|
||||
@@ -114,7 +179,8 @@ namespace BSO
|
||||
{
|
||||
w.Write(Protocol.Version);
|
||||
w.Write(Bootstrap.ModVersion);
|
||||
w.Write(Config.Current.Name ?? "");
|
||||
w.Write(PlayerName ?? "");
|
||||
w.Write(Config.Current.Ticket ?? "");
|
||||
}));
|
||||
// Erst nach Welcome gilt man als verbunden - bis dahin bleibt der Status "Verbinde"
|
||||
Status = NetStatus.Connected;
|
||||
@@ -167,8 +233,7 @@ namespace BSO
|
||||
case MsgType.PlayerState:
|
||||
int id = r.ReadInt32();
|
||||
if (!Players.TryGetValue(id, out Remote p2)) return; // PlayerJoined kommt immer vorher
|
||||
p2.State = PlayerState.Read(r);
|
||||
p2.HasState = true;
|
||||
p2.Receive(PlayerState.Read(r));
|
||||
break;
|
||||
|
||||
case MsgType.Pong:
|
||||
@@ -196,6 +261,17 @@ namespace BSO
|
||||
static (string, int) ParseAddress(string address)
|
||||
{
|
||||
address = (address ?? "").Trim();
|
||||
// IPv6: [::1]:7777 oder [::1]
|
||||
if (address.StartsWith("["))
|
||||
{
|
||||
int end = address.IndexOf(']');
|
||||
if (end > 1)
|
||||
{
|
||||
string host6 = address.Substring(1, end - 1);
|
||||
return address.Length > end + 2 && address[end + 1] == ':' && int.TryParse(address.Substring(end + 2), out int p6)
|
||||
? (host6, p6) : (host6, Protocol.DefaultPort);
|
||||
}
|
||||
}
|
||||
int colon = address.LastIndexOf(':');
|
||||
if (colon > 0 && int.TryParse(address.Substring(colon + 1), out int port)) return (address.Substring(0, colon), port);
|
||||
return (address.Length > 0 ? address : "127.0.0.1", Protocol.DefaultPort);
|
||||
|
||||
Reference in New Issue
Block a user