Skip to content

Commit ddcf4dc

Browse files
committed
fix: prune stale static actor vids on update
1 parent 94a4b29 commit ddcf4dc

3 files changed

Lines changed: 30 additions & 6 deletions

File tree

internal/worldruntime/non_player_directory.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,13 @@ func (d *NonPlayerDirectory) Update(actor StaticEntity) bool {
6262
if d == nil || !validStaticEntity(actor) {
6363
return false
6464
}
65-
previous, ok := d.byEntityID[actor.Entity.ID]
66-
if !ok {
65+
if _, ok := d.byEntityID[actor.Entity.ID]; !ok {
6766
return false
6867
}
6968
if vid, ok := StaticActorVisibilityVID(actor); ok && conflictingEntityID(d.entityIDByVID, vid, actor.Entity.ID) {
7069
return false
7170
}
72-
if previousVID, ok := StaticActorVisibilityVID(previous); ok {
73-
delete(d.entityIDByVID, previousVID)
74-
}
71+
d.removeVisibilityVIDsForEntityID(actor.Entity.ID)
7572
d.byEntityID[actor.Entity.ID] = cloneStaticEntity(actor)
7673
if vid, ok := StaticActorVisibilityVID(actor); ok {
7774
d.entityIDByVID[vid] = actor.Entity.ID

internal/worldruntime/non_player_directory_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,33 @@ func TestNonPlayerDirectoryUpdateClearsVisibilityVIDLookupWhenActorStopsBeingEnc
132132
}
133133
}
134134

135+
func TestNonPlayerDirectoryUpdateClearsStaleVisibilityVIDsForSameEntityID(t *testing.T) {
136+
directory := NewNonPlayerDirectory()
137+
actor := StaticEntity{
138+
Entity: Entity{ID: 13, Kind: EntityKindStaticActor, Name: "VillageGuard"},
139+
Position: NewPosition(42, 1700, 2800),
140+
RaceNum: 20300,
141+
}
142+
if !directory.Register(actor) {
143+
t.Fatal("expected static actor registration to succeed")
144+
}
145+
directory.entityIDByVID[99] = actor.Entity.ID
146+
147+
updated := actor
148+
updated.Entity.Name = "Blacksmith"
149+
if !directory.Update(updated) {
150+
t.Fatal("expected static actor update to repair stale VID ownership")
151+
}
152+
153+
if _, ok := directory.ByVID(99); ok {
154+
t.Fatal("expected stale visibility VID lookup to be cleared after update")
155+
}
156+
lookup, ok := directory.ByVID(13)
157+
if !ok || lookup.Entity.ID != actor.Entity.ID || lookup.Entity.Name != "Blacksmith" {
158+
t.Fatalf("expected current visibility VID lookup to return Blacksmith, got actor=%+v ok=%v", lookup, ok)
159+
}
160+
}
161+
135162
func TestNonPlayerDirectoryPreservesCombatProfileOnRegisterAndUpdate(t *testing.T) {
136163
directory := NewNonPlayerDirectory()
137164
actor := StaticEntity{

spec/protocol/non-player-entity-bootstrap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ This slice does not yet freeze:
100100
- if a partially torn-down static actor is still present in the non-player directory but missing from the map index, a later in-place update must rebuild map presence while preserving the authored/bootstrap death reward instead of requiring delete-and-recreate
101101
- if the reverse partial-teardown shape leaves only map-index presence behind, in-place update must repair the entity index and move the actor to the requested effective map instead of failing closed; this keeps operator/static-spawn edit paths tolerant of index cleanup order without treating an unknown actor ID as creatable
102102
- if a bad partial-teardown or repair path leaves duplicate map-bucket presence for the same static actor entity ID, later register, in-place update, or remove must prune all stale map buckets before inserting the new effective-map presence or reporting final removal; map occupancy must never keep ghost static actors for the same entity ID on older maps
103-
- if a stale visibility-VID index entry survives for the same static actor entity ID, later registration must prune the stale VID before inserting the actor's current encodable visibility VID; visible-VID lookup must never keep an old alias for the same static actor after registration repair
103+
- if a stale visibility-VID index entry survives for the same static actor entity ID, later registration or in-place update must prune the stale VID before inserting the actor's current encodable visibility VID; visible-VID lookup must never keep an old alias for the same static actor after registration or update repair
104104
- if a stale visibility-VID index entry points at a missing static actor entity ID, a later `ByVID` lookup must prune that orphaned index entry before failing closed; visible-VID lookup must not keep returning through stale non-player directory aliases after partial teardown
105105
- inter-channel ownership
106106

0 commit comments

Comments
 (0)