Files
MrMahoandClaude Opus 5.5 51c1ebc938 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>
2026-10-28 18:10:48 +01:00

64 lines
2.8 KiB
C#

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;
}
}
}