diag(app): full CreateObject spawn logging + drop reason breakdown
Phase 4.7g visual verification turned up several issues and I needed
better visibility into what's actually streaming from the server.
Removes the "suppress after 10" limit on the spawn log and adds
drop-reason counters so I can distinguish "inventory item with no
position" (expected, ~16 per login) from "setup dat missing" and
"zero mesh refs" (unexpected failures).
Findings from a full live run with the new logging:
live: summary recv=60 hydrated=44 drops: noPos=16 noSetup=0
setupMissing=0 noMesh=0
Meaning: every positioned CreateObject is hydrating cleanly into
IGameState. The only drops are the 16 inventory/equipped items that
the server sends without a position (they inherit from their wearer).
The foundry statue is NOT being silently dropped at the codec layer —
it's in our render list somewhere, probably indistinguishable from
generic naked humanoids because we don't decode ObjectDesc yet.
User observations from the visual verification:
* NPCs + +Acdream visible, but naked (no clothing/armor)
* Doors now exist (Phase 4 win over offline-only)
* Portals render as black squares
* Foundry statue not identifiable (most likely a generic-looking
spawn due to missing ObjectDesc)
* Holtburg town crier sign half underground (small Z offset)
All of the "wrong appearance" findings trace back to the same root
cause: CreateObject.TryParse skips past ModelData without extracting
the palette swaps, texture changes, and animpart changes that define
each entity's unique visual presentation. Base setup mesh renders
as-is. Phase 5 work.
Next step in this session: port the ModelData parser (primarily the
AnimPartChanges list — replacing body parts with armored/statue
versions is the single biggest visual improvement for a character model).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
713bec256b
commit
fc6c9dc240
1 changed files with 38 additions and 10 deletions
|
|
@ -37,6 +37,10 @@ public sealed class GameWindow : IDisposable
|
|||
private uint _liveEntityIdCounter = 1_000_000u; // well above any dat-hydrated id
|
||||
private int _liveSpawnReceived; // diagnostics
|
||||
private int _liveSpawnHydrated;
|
||||
private int _liveDropReasonNoPos;
|
||||
private int _liveDropReasonNoSetup;
|
||||
private int _liveDropReasonSetupDatMissing;
|
||||
private int _liveDropReasonNoMeshRefs;
|
||||
|
||||
public GameWindow(string datDir, WorldGameState worldGameState, WorldEvents worldEvents)
|
||||
{
|
||||
|
|
@ -513,12 +517,25 @@ public sealed class GameWindow : IDisposable
|
|||
private void OnLiveEntitySpawned(AcDream.Core.Net.WorldSession.EntitySpawn spawn)
|
||||
{
|
||||
_liveSpawnReceived++;
|
||||
|
||||
// Log every spawn that arrives so we can inventory what the server
|
||||
// sends (including the ones we can't render yet). The foundry statue
|
||||
// hunt in Phase 2c / 4.7 is the main reason for this — we want to
|
||||
// see EVERY guid+setup to find it in the list.
|
||||
string posStr = spawn.Position is { } sp
|
||||
? $"({sp.PositionX:F1},{sp.PositionY:F1},{sp.PositionZ:F1})@0x{sp.LandblockId:X8}"
|
||||
: "no-pos";
|
||||
string setupStr = spawn.SetupTableId is { } su ? $"0x{su:X8}" : "no-setup";
|
||||
Console.WriteLine($"live: spawn guid=0x{spawn.Guid:X8} setup={setupStr} pos={posStr}");
|
||||
|
||||
if (_dats is null || _staticMesh is null) return;
|
||||
if (spawn.Position is null || spawn.SetupTableId is null)
|
||||
{
|
||||
// Can't place a mesh without both. Most of these are inventory
|
||||
// items anyway (no position because they're held), which have no
|
||||
// visible world presence.
|
||||
if (spawn.Position is null) _liveDropReasonNoPos++;
|
||||
else _liveDropReasonNoSetup++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -541,7 +558,13 @@ public sealed class GameWindow : IDisposable
|
|||
// Hydrate mesh refs from the Setup dat. This is the same code path
|
||||
// used by the static scenery pipeline (see the Setup hydration above).
|
||||
var setup = _dats.Get<DatReaderWriter.DBObjs.Setup>(spawn.SetupTableId.Value);
|
||||
if (setup is null) return;
|
||||
if (setup is null)
|
||||
{
|
||||
_liveDropReasonSetupDatMissing++;
|
||||
Console.WriteLine($"live: DROP setup dat 0x{spawn.SetupTableId.Value:X8} missing " +
|
||||
$"(guid=0x{spawn.Guid:X8})");
|
||||
return;
|
||||
}
|
||||
|
||||
var flat = AcDream.Core.Meshing.SetupMesh.Flatten(setup);
|
||||
var meshRefs = new List<AcDream.Core.World.MeshRef>();
|
||||
|
|
@ -553,7 +576,13 @@ public sealed class GameWindow : IDisposable
|
|||
_staticMesh.EnsureUploaded(mr.GfxObjId, subMeshes);
|
||||
meshRefs.Add(new AcDream.Core.World.MeshRef(mr.GfxObjId, mr.PartTransform));
|
||||
}
|
||||
if (meshRefs.Count == 0) return;
|
||||
if (meshRefs.Count == 0)
|
||||
{
|
||||
_liveDropReasonNoMeshRefs++;
|
||||
Console.WriteLine($"live: DROP no mesh refs from setup 0x{spawn.SetupTableId.Value:X8} " +
|
||||
$"(guid=0x{spawn.Guid:X8})");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = new AcDream.Core.World.WorldEntity
|
||||
{
|
||||
|
|
@ -578,15 +607,14 @@ public sealed class GameWindow : IDisposable
|
|||
_entities = extended;
|
||||
_liveSpawnHydrated++;
|
||||
|
||||
// Log the first few so we can confirm position translation is sane.
|
||||
if (_liveSpawnHydrated <= 10)
|
||||
// Dump a summary periodically so we can see drop breakdowns without
|
||||
// waiting for a graceful shutdown.
|
||||
if (_liveSpawnReceived % 20 == 0)
|
||||
{
|
||||
Console.WriteLine($"live: spawned guid=0x{spawn.Guid:X8} setup=0x{spawn.SetupTableId:X8} " +
|
||||
$"world=({worldPos.X:F1},{worldPos.Y:F1},{worldPos.Z:F1})");
|
||||
}
|
||||
if (_liveSpawnHydrated == 10)
|
||||
{
|
||||
Console.WriteLine("live: (suppressing further spawn logs)");
|
||||
Console.WriteLine(
|
||||
$"live: summary recv={_liveSpawnReceived} hydrated={_liveSpawnHydrated} " +
|
||||
$"drops: noPos={_liveDropReasonNoPos} noSetup={_liveDropReasonNoSetup} " +
|
||||
$"setupMissing={_liveDropReasonSetupDatMissing} noMesh={_liveDropReasonNoMeshRefs}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue