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:
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BSO
|
||||
{
|
||||
/// <summary>
|
||||
/// Modelldatei einer Entity finden (z.B. Models\Vehicles\Citaro\Citaro12m\...\.model). Die .pak-Dateien des Spiels sind
|
||||
/// verschluesselt und VisBaseEntity_cl::GetMesh ist nicht exportiert - also: Entity -> Mesh (Zeiger in der Entity) ->
|
||||
/// Dateiname (char* im Mesh) durchsuchen. Jeder Zeiger wird vorher geprueft (Mem).
|
||||
/// </summary>
|
||||
public static class Models
|
||||
{
|
||||
/// <summary>Modell des eigenen Busses / der eigenen Figur - werden fuer die Mitspieler benutzt.</summary>
|
||||
public static string Bus { get; private set; }
|
||||
public static string Character { get; private set; }
|
||||
|
||||
static readonly Dictionary<nint, string> byEntity = new();
|
||||
|
||||
/// <summary>Solange man noch nicht im eigenen Bus sass: irgendein Fahrzeug aus der Szene.</summary>
|
||||
static string fallbackVehicle;
|
||||
public static string Vehicle => Bus ?? fallbackVehicle;
|
||||
|
||||
public static void RememberBus(nint entity) => Bus ??= Remember(entity, "Bus");
|
||||
public static void RememberCharacter(nint entity) => Character ??= Remember(entity, "Spielfigur");
|
||||
public static void RememberFallbackVehicle(nint entity) => fallbackVehicle ??= Remember(entity, "Fahrzeug (Ersatz, bis du im Bus sitzt)");
|
||||
|
||||
static string Remember(nint entity, string what)
|
||||
{
|
||||
string file = Find(entity);
|
||||
Bootstrap.Log(file != null ? $"Modell {what}: {file}" : $"Modell {what}: nicht gefunden");
|
||||
return file;
|
||||
}
|
||||
|
||||
/// <summary>Modelldatei der Entity oder null.</summary>
|
||||
public static string Find(nint entity)
|
||||
{
|
||||
if (byEntity.TryGetValue(entity, out string known)) return known;
|
||||
string found = null;
|
||||
for (int off = 8; off < 0x400 && found == null; off += 8)
|
||||
{
|
||||
if (!Mem.TryReadPtr(entity + off, out nint p) || p == 0 || Mem.IsImage(p)) continue;
|
||||
found = ModelString(p); // Dateiname direkt in der Entity
|
||||
for (int off2 = 0; off2 < 0x100 && found == null; off2 += 8)
|
||||
{
|
||||
// ... oder im Mesh, auf das die Entity zeigt
|
||||
if (!Mem.TryReadPtr(p + off2, out nint q) || q == 0 || Mem.IsImage(q)) continue;
|
||||
found = ModelString(q);
|
||||
}
|
||||
}
|
||||
byEntity[entity] = found;
|
||||
return found;
|
||||
}
|
||||
|
||||
static string ModelString(nint p)
|
||||
{
|
||||
if (!Mem.IsReadable(p, 8)) return null;
|
||||
string s = Mem.ReadAscii(p, 260);
|
||||
if (s.Length < 8 || !s.EndsWith(".model", StringComparison.OrdinalIgnoreCase)) return null;
|
||||
foreach (char c in s) if (c < 0x20 || c > 0x7e) return null;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user