fix(headless): FA6 — name-match the Recruit bot instead of nearest-any-player
The second live gate run exposed a real environmental hazard: this shared ACE dev instance has a THIRD player character online (+Je, guid 0x50000001), and after @teleallto it ended up nearer to the Leader bot than the actual Recruit bot (+Horan, 0x5000000B). RuntimeFriendlyTargetQuery. FindClosestOtherPlayer — "nearest ANY other player" — picked +Je, and the fellowship recruit sent to it obviously never completed (confirmed live: WaitRecruited/WaitForRecruit both timed out, both bots quarantined and gracefully logged out cleanly). RuntimeFriendlyTargetQuery.FindPlayerByName resolves the nearest player whose streamed name matches exactly, with 3 new conformance tests (preferring the named player over a closer stranger, returning null when absent, and case-sensitivity/hidden/no-draw/self rejection). FellowshipAllegianceGateCoordinator (AcDream.Headless.Policies) is a small same-process, no-locking (single update thread) carrier for the Recruit bot's own discovered character name — set by its own HeadlessSessionHost the instant CharacterList selection resolves it, which IS D8's "discover it live" mechanism, not a hard-coded value. Constructed once per HeadlessProcessHost and threaded through HeadlessBotPolicyFactory.Create into the Leader policy, which now name-matches instead of taking whichever player entity happens to be closest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
11641597db
commit
ab79b91f1b
5 changed files with 280 additions and 33 deletions
|
|
@ -77,6 +77,62 @@ public static class RuntimeFriendlyTargetQuery
|
|||
return closest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The nearest OTHER player entity whose streamed name matches
|
||||
/// <paramref name="name"/> exactly (ordinal), or <see langword="null"/>
|
||||
/// when none is currently resolvable. Live-run finding (FA6, second
|
||||
/// gate attempt): a shared ACE dev instance can have a THIRD player
|
||||
/// character online and nearer than the actual counterpart bot after
|
||||
/// an <c>@teleallto</c> — <see cref="FindClosestOtherPlayer"/> alone
|
||||
/// picked the wrong one in that case. Callers that know the exact
|
||||
/// expected name should prefer this method over the ambiguous
|
||||
/// "nearest ANY player" query.
|
||||
/// </summary>
|
||||
public static uint? FindPlayerByName(GameRuntime runtime, string name)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
ArgumentException.ThrowIfNullOrEmpty(name);
|
||||
uint playerGuid = runtime.PlayerIdentity.ServerGuid;
|
||||
if (playerGuid == 0u
|
||||
|| !runtime.EntityObjects.Entities.TryGetActive(
|
||||
playerGuid,
|
||||
out RuntimeEntityRecord playerRecord)
|
||||
|| playerRecord.Snapshot.Position is not { } playerPosition)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector3 playerWorld = AbsolutePosition(playerPosition);
|
||||
uint? closest = null;
|
||||
float closestDistanceSquared = float.PositiveInfinity;
|
||||
foreach (RuntimeEntityRecord record
|
||||
in runtime.EntityObjects.Entities.ActiveRecords)
|
||||
{
|
||||
if (record.ServerGuid == playerGuid
|
||||
|| record.Snapshot.Position is not { } position
|
||||
|| (record.FinalPhysicsState
|
||||
& (PhysicsStateFlags.Hidden
|
||||
| PhysicsStateFlags.NoDraw)) != 0
|
||||
|| !IsPlayer(record)
|
||||
|| !string.Equals(
|
||||
record.Snapshot.Name,
|
||||
name,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float distanceSquared = Vector3.DistanceSquared(
|
||||
playerWorld,
|
||||
AbsolutePosition(position));
|
||||
if (distanceSquared >= closestDistanceSquared)
|
||||
continue;
|
||||
closestDistanceSquared = distanceSquared;
|
||||
closest = record.ServerGuid;
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The streamed-in <c>WeenieHeader</c> name for <paramref name="guid"/>,
|
||||
/// or <see langword="null"/> when the entity is not currently resolvable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue