diff --git a/client/src/network/protocol.ts b/client/src/network/protocol.ts index 9afbbe2..d1abcae 100644 --- a/client/src/network/protocol.ts +++ b/client/src/network/protocol.ts @@ -387,7 +387,7 @@ export type OutgoingMessage = | { type: 'item_drop'; itemId: string; x: number; y: number } | { type: 'item_delete'; itemId: string } | { type: 'item_transfer_targets'; itemId: string } - | { type: 'item_transfer'; itemId: string; targetId?: string; targetUserId?: string } + | { type: 'item_transfer'; itemId: string; targetUserId: string } | { type: 'item_use'; itemId: string } | { type: 'item_secondary_use'; itemId: string } | { type: 'item_piano_note'; itemId: string; keyId: string; midi: number; on: boolean } diff --git a/docs/protocol-notes.md b/docs/protocol-notes.md index 6308503..e3cb9fe 100644 --- a/docs/protocol-notes.md +++ b/docs/protocol-notes.md @@ -29,7 +29,7 @@ This is a behavior guide for packet semantics beyond raw schemas. - `ping`: latency measurement. - `item_add`, `item_pickup`, `item_drop`, `item_delete`, `item_use`, `item_update`: item actions. - `item_transfer_targets`: request transfer target accounts for one item (includes online + offline active users, excluding current owner). -- `item_transfer`: transfer item ownership to another account (supports `targetUserId`; `targetId` remains accepted for compatibility). +- `item_transfer`: transfer item ownership to another account (`targetUserId` required). - `item_secondary_use`: trigger type-specific secondary action when implemented. - `item_piano_note`: realtime piano note on/off for active piano use mode. - `item_piano_recording`: piano record/playback control (`toggle_record`, `playback`, `stop_playback`). diff --git a/server/app/models.py b/server/app/models.py index bc7f860..0aa5c5e 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -140,8 +140,7 @@ class ItemDeletePacket(BasePacket): class ItemTransferPacket(BasePacket): type: Literal["item_transfer"] itemId: str - targetId: str | None = None - targetUserId: str | None = None + targetUserId: str class ItemTransferTargetsPacket(BasePacket): diff --git a/server/app/server.py b/server/app/server.py index a90f459..eb02d1d 100644 --- a/server/app/server.py +++ b/server/app/server.py @@ -2545,11 +2545,7 @@ class SignalingServer: if not can_transfer_any and not can_transfer_own: await self._send_item_result(client, False, "transfer", "Not authorized to transfer this item.", item.id) return - target_user_id = str(packet.targetUserId or "").strip() - if not target_user_id and packet.targetId: - target = self._get_client_by_id(packet.targetId) - if target and target.authenticated and target.user_id: - target_user_id = target.user_id + target_user_id = str(packet.targetUserId).strip() if not target_user_id: await self._send_item_result(client, False, "transfer", "Target user is not available.", item.id) return diff --git a/server/tests/test_server_message_handling.py b/server/tests/test_server_message_handling.py index c73ebe6..92bfff4 100644 --- a/server/tests/test_server_message_handling.py +++ b/server/tests/test_server_message_handling.py @@ -377,7 +377,10 @@ async def test_item_transfer_updates_item_owner(monkeypatch: pytest.MonkeyPatch) monkeypatch.setattr(server, "_broadcast_item", fake_broadcast_item) monkeypatch.setattr(server, "_broadcast", fake_broadcast) - await server._handle_message(owner, json.dumps({"type": "item_transfer", "itemId": item.id, "targetId": target.id})) + await server._handle_message( + owner, + json.dumps({"type": "item_transfer", "itemId": item.id, "targetUserId": target.user_id}), + ) assert item.createdBy == target.user_id assert item.createdByName == target.username @@ -439,7 +442,10 @@ async def test_item_transfer_allows_self_target_for_transfer_any(monkeypatch: py monkeypatch.setattr(server, "_send", fake_send) monkeypatch.setattr(server, "_broadcast_item", fake_broadcast_item) - await server._handle_message(actor, json.dumps({"type": "item_transfer", "itemId": item.id, "targetId": actor.id})) + await server._handle_message( + actor, + json.dumps({"type": "item_transfer", "itemId": item.id, "targetUserId": actor.user_id}), + ) assert item.createdBy == actor.user_id assert item.createdByName == actor.username @@ -677,7 +683,10 @@ async def test_item_transfer_rejects_when_not_authorized(monkeypatch: pytest.Mon monkeypatch.setattr(server, "_send", fake_send) - await server._handle_message(owner, json.dumps({"type": "item_transfer", "itemId": item.id, "targetId": target.id})) + await server._handle_message( + owner, + json.dumps({"type": "item_transfer", "itemId": item.id, "targetUserId": target.user_id}), + ) assert item.createdBy == owner.user_id assert send_payloads