Skip to content

Commit ac57f36

Browse files
committed
feat: reject equip-slot ground item registration
1 parent 86ae8e8 commit ac57f36

4 files changed

Lines changed: 19 additions & 2 deletions

File tree

docs/qa/manual-client-checklist.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ Expected result:
294294
- valid pickup removes the ground actor, refreshes the carried inventory slot or compatible stack according to the authored stack metadata, and shows the normal pickup notice
295295
- a non-zero gold/elk field follows the gold-drop path regardless of the packed item position: gold decreases, the carried inventory remains unchanged, and a gold ground marker appears
296296
- loaded drop template metadata whose carried stack already exceeds the authored `max_count` fails closed: no ground actor, no carried-slot deletion/update, and no quickslot mutation is visible
297+
- if a corrupt/disposable fixture reaches the shared-world ground-handle seam with stale equipment-slot metadata on an otherwise unequipped ground snapshot, registration fails closed and no temporary ground actor becomes available
297298
- if a corrupt/disposable fixture has duplicate live items in the same carried cell, `ITEM_DROP` / `ITEM_DROP2` fails closed with no ground actor, no carried-slot deletion/update, no quickslot mutation, and no persisted-state mutation
298299
- missing, malformed, mismatched, or ground-count-over-template-`max_count` authored pickup template metadata fails closed: no item pickup notice, no inventory mutation, and the ground handle remains available for a later valid retry
299300
- loaded pickup template metadata marked `anti_give` or restricted by the selected character's job/sex/min-level metadata also fails closed with the bootstrap inventory-full info message and leaves the ground handle available for a later valid retry

internal/minimal/shared_world.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ func (r *sharedWorldRegistry) removeOwnedGroundItemsLocked(ownerID uint64, visib
916916

917917
func (r *sharedWorldRegistry) RegisterGroundItem(ownerID uint64, ownerLogin string, character loginticket.Character, vid uint32, item inventory.ItemInstance) bool {
918918
const maxItemGetCountCarrier = uint16(^uint8(0))
919-
if item.Vnum == 0 || item.Count == 0 || item.Count > maxItemGetCountCarrier || item.Locked || item.Equipped {
919+
if item.Vnum == 0 || item.Count == 0 || item.Count > maxItemGetCountCarrier || item.Locked || item.Equipped || item.EquipSlot != inventory.EquipmentSlotNone {
920920
return false
921921
}
922922
return r.registerGroundItem(ownerID, ownerLogin, character, vid, item, 0)

internal/minimal/shared_world_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,22 @@ func TestSharedWorldRegistryRegisterGroundItemRejectsEquippedItem(t *testing.T)
348348
}
349349
}
350350

351+
func TestSharedWorldRegistryRegisterGroundItemRejectsUnequippedItemWithEquipSlot(t *testing.T) {
352+
registry := newSharedWorldRegistry()
353+
owner := peerVisibilityCharacter("EquipSlotGroundOwner", 0x0103019d, 0x0204019d, 1200, 2200, 0, 101, 201)
354+
ownerID, _ := registry.Join(owner, newPendingServerFrames(), nil)
355+
if ownerID == 0 {
356+
t.Fatal("expected equip-slot ground-item owner join to allocate a shared-world entity id")
357+
}
358+
359+
if registry.RegisterGroundItem(ownerID, "equip-slot-ground-owner", owner, 0x07000014, inventory.ItemInstance{Vnum: 3001, Count: 1, EquipSlot: inventory.EquipmentSlotWeapon}) {
360+
t.Fatal("expected unequipped ground item with equip slot metadata to fail closed")
361+
}
362+
if registry.GroundItemExists(0x07000014) {
363+
t.Fatal("expected rejected equip-slot ground item to stay absent")
364+
}
365+
}
366+
351367
func TestSharedWorldRegistryRegisterGroundItemRejectsZeroVID(t *testing.T) {
352368
registry := newSharedWorldRegistry()
353369
owner := peerVisibilityCharacter("ZeroVIDDropOwner", 0x0103019a, 0x0204019a, 1200, 2200, 0, 101, 201)

spec/protocol/item-drop-pickup-bootstrap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ The first gold-drop runtime slice owns the gold amount fields on `CG::ITEM_DROP`
139139

140140
For the first visible-peer pickup runtime slice, accepted pickup is visible-world scoped:
141141

142-
1. Accepted drops are registered as temporary bootstrap ground handles at the dropper's current effective map/position after the selected character mutation is persisted; zero-HP/dead owners, locked item snapshots, and snapshots still marked equipped are rejected at the shared-world ground-handle seam so death-state races or stale lock/equipment-state snapshots cannot create new visible handles.
142+
1. Accepted drops are registered as temporary bootstrap ground handles at the dropper's current effective map/position after the selected character mutation is persisted; zero-HP/dead owners, locked item snapshots, snapshots still marked equipped, and unequipped snapshots that still carry authored equipment-slot metadata are rejected at the shared-world ground-handle seam so death-state races or stale lock/equipment-state snapshots cannot create new visible handles.
143143
2. The dropper receives the same direct `GC::ITEM_GROUND_ADD` + `GC::ITEM_OWNERSHIP` already owned by the first drop slice, currently visible living peers receive one queued add/ownership pair for the same handle, and later visible sessions that enter `GAME` while the handle remains pending receive that same add/ownership pair after self bootstrap, peer-player bootstrap, and visible static-actor bootstrap frames. Zero-HP/dead peers stay registered for their own restart/teardown flow, but they are skipped as recipients for new ground-handle visibility fanout.
144144
3. `ITEM_PICKUP` is accepted when its `vid` matches a still-pending bootstrap ground handle in the collector's visible world and the collector is within the first owned pickup reach (`300` coordinate units) of the ground handle. Visible-but-out-of-reach attempts fail closed with no frames, no inventory mutation, and the temporary handle left pending.
145145
4. The picked ground item snapshot itself must validate as a well-formed unequipped carried item before any compatible-stack merge or fresh-slot placement is attempted. Malformed ground snapshots, including stale snapshots that still carry an authored equipment slot while not marked equipped or snapshots still marked `equipped`, fail closed before recipient inventory mutation, pickup notice, persistence, or temporary ground-handle removal.

0 commit comments

Comments
 (0)