2026-02-21 16:51:07 -05:00
|
|
|
"""Pydantic packet and entity models shared across server message handling."""
|
|
|
|
|
|
2026-02-20 08:16:43 -05:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import Literal
|
|
|
|
|
|
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BasePacket(BaseModel):
|
|
|
|
|
model_config = ConfigDict(extra="ignore")
|
|
|
|
|
type: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SignalPacket(BasePacket):
|
|
|
|
|
type: Literal["signal"]
|
|
|
|
|
targetId: str
|
|
|
|
|
sdp: dict | None = None
|
|
|
|
|
ice: dict | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdatePositionPacket(BasePacket):
|
|
|
|
|
type: Literal["update_position"]
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateNicknamePacket(BasePacket):
|
|
|
|
|
type: Literal["update_nickname"]
|
|
|
|
|
nickname: str = Field(min_length=1, max_length=32)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatMessagePacket(BasePacket):
|
|
|
|
|
type: Literal["chat_message"]
|
|
|
|
|
message: str = Field(min_length=1, max_length=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PingPacket(BasePacket):
|
|
|
|
|
type: Literal["ping"]
|
|
|
|
|
clientSentAt: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemAddPacket(BasePacket):
|
|
|
|
|
type: Literal["item_add"]
|
2026-02-24 18:48:08 -05:00
|
|
|
itemType: str = Field(min_length=1)
|
2026-02-20 08:16:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemPickupPacket(BasePacket):
|
|
|
|
|
type: Literal["item_pickup"]
|
|
|
|
|
itemId: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemDropPacket(BasePacket):
|
|
|
|
|
type: Literal["item_drop"]
|
|
|
|
|
itemId: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemDeletePacket(BasePacket):
|
|
|
|
|
type: Literal["item_delete"]
|
|
|
|
|
itemId: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemUsePacket(BasePacket):
|
|
|
|
|
type: Literal["item_use"]
|
|
|
|
|
itemId: str
|
|
|
|
|
|
|
|
|
|
|
2026-02-22 23:42:17 -05:00
|
|
|
class ItemPianoNotePacket(BasePacket):
|
|
|
|
|
type: Literal["item_piano_note"]
|
|
|
|
|
itemId: str
|
|
|
|
|
keyId: str = Field(min_length=1, max_length=32)
|
|
|
|
|
midi: int = Field(ge=0, le=127)
|
|
|
|
|
on: bool
|
|
|
|
|
|
|
|
|
|
|
2026-02-23 00:36:36 -05:00
|
|
|
class ItemPianoRecordingPacket(BasePacket):
|
|
|
|
|
type: Literal["item_piano_recording"]
|
|
|
|
|
itemId: str
|
2026-02-23 02:00:01 -05:00
|
|
|
action: Literal["toggle_record", "playback", "stop_playback", "stop_record"]
|
2026-02-23 00:36:36 -05:00
|
|
|
|
|
|
|
|
|
2026-02-20 08:16:43 -05:00
|
|
|
class ItemUpdatePacket(BasePacket):
|
|
|
|
|
type: Literal["item_update"]
|
|
|
|
|
itemId: str
|
|
|
|
|
title: str | None = Field(default=None, max_length=80)
|
|
|
|
|
params: dict | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ClientPacket = (
|
|
|
|
|
SignalPacket
|
|
|
|
|
| UpdatePositionPacket
|
|
|
|
|
| UpdateNicknamePacket
|
|
|
|
|
| ChatMessagePacket
|
|
|
|
|
| PingPacket
|
|
|
|
|
| ItemAddPacket
|
|
|
|
|
| ItemPickupPacket
|
|
|
|
|
| ItemDropPacket
|
|
|
|
|
| ItemDeletePacket
|
|
|
|
|
| ItemUsePacket
|
2026-02-22 23:42:17 -05:00
|
|
|
| ItemPianoNotePacket
|
2026-02-23 00:36:36 -05:00
|
|
|
| ItemPianoRecordingPacket
|
2026-02-20 08:16:43 -05:00
|
|
|
| ItemUpdatePacket
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RemoteUser(BaseModel):
|
|
|
|
|
id: str
|
|
|
|
|
nickname: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WelcomePacket(BasePacket):
|
|
|
|
|
type: Literal["welcome"]
|
|
|
|
|
id: str
|
|
|
|
|
users: list[RemoteUser]
|
|
|
|
|
items: list[dict] | None = None
|
2026-02-21 19:12:58 -05:00
|
|
|
worldConfig: dict | None = None
|
|
|
|
|
uiDefinitions: dict | None = None
|
2026-02-22 18:20:13 -05:00
|
|
|
serverInfo: dict | None = None
|
2026-02-20 08:16:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserLeftPacket(BasePacket):
|
|
|
|
|
type: Literal["user_left"]
|
|
|
|
|
id: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BroadcastPositionPacket(BasePacket):
|
|
|
|
|
type: Literal["update_position"]
|
|
|
|
|
id: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BroadcastNicknamePacket(BasePacket):
|
|
|
|
|
type: Literal["update_nickname"]
|
|
|
|
|
id: str
|
|
|
|
|
nickname: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForwardSignalPacket(BasePacket):
|
|
|
|
|
type: Literal["signal"]
|
|
|
|
|
senderId: str
|
|
|
|
|
senderNickname: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
sdp: dict | None = None
|
|
|
|
|
ice: dict | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BroadcastChatMessagePacket(BasePacket):
|
|
|
|
|
type: Literal["chat_message"]
|
|
|
|
|
message: str
|
|
|
|
|
senderId: str | None = None
|
|
|
|
|
senderNickname: str | None = None
|
|
|
|
|
system: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PongPacket(BasePacket):
|
|
|
|
|
type: Literal["pong"]
|
|
|
|
|
clientSentAt: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NicknameResultPacket(BasePacket):
|
|
|
|
|
type: Literal["nickname_result"]
|
|
|
|
|
accepted: bool
|
|
|
|
|
requestedNickname: str
|
|
|
|
|
effectiveNickname: str
|
|
|
|
|
reason: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WorldItem(BaseModel):
|
|
|
|
|
id: str
|
2026-02-24 18:48:08 -05:00
|
|
|
type: str = Field(min_length=1)
|
2026-02-20 08:16:43 -05:00
|
|
|
title: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
createdBy: str
|
|
|
|
|
createdAt: int
|
|
|
|
|
updatedAt: int
|
|
|
|
|
version: int
|
|
|
|
|
capabilities: list[str]
|
2026-02-21 16:13:48 -05:00
|
|
|
useSound: str | None = None
|
2026-02-21 16:01:40 -05:00
|
|
|
emitSound: str | None = None
|
2026-02-20 08:16:43 -05:00
|
|
|
params: dict
|
|
|
|
|
carrierId: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PersistedWorldItem(BaseModel):
|
|
|
|
|
model_config = ConfigDict(extra="ignore")
|
|
|
|
|
id: str
|
2026-02-24 18:48:08 -05:00
|
|
|
type: str = Field(min_length=1)
|
2026-02-20 08:16:43 -05:00
|
|
|
title: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
createdBy: str
|
|
|
|
|
createdAt: int
|
|
|
|
|
updatedAt: int
|
|
|
|
|
version: int
|
|
|
|
|
params: dict
|
|
|
|
|
carrierId: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemUpsertPacket(BasePacket):
|
|
|
|
|
type: Literal["item_upsert"]
|
|
|
|
|
item: WorldItem
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemRemovePacket(BasePacket):
|
|
|
|
|
type: Literal["item_remove"]
|
|
|
|
|
itemId: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemActionResultPacket(BasePacket):
|
|
|
|
|
type: Literal["item_action_result"]
|
|
|
|
|
ok: bool
|
|
|
|
|
action: Literal["add", "pickup", "drop", "delete", "use", "update"]
|
|
|
|
|
message: str
|
|
|
|
|
itemId: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemUseSoundPacket(BasePacket):
|
|
|
|
|
type: Literal["item_use_sound"]
|
|
|
|
|
itemId: str
|
|
|
|
|
sound: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
2026-02-22 23:42:17 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemPianoNoteBroadcastPacket(BasePacket):
|
|
|
|
|
type: Literal["item_piano_note"]
|
|
|
|
|
itemId: str
|
|
|
|
|
senderId: str
|
|
|
|
|
keyId: str
|
|
|
|
|
midi: int
|
|
|
|
|
on: bool
|
|
|
|
|
instrument: str
|
2026-02-23 00:22:36 -05:00
|
|
|
voiceMode: str
|
|
|
|
|
octave: int
|
2026-02-22 23:42:17 -05:00
|
|
|
attack: int
|
|
|
|
|
decay: int
|
2026-02-23 00:05:01 -05:00
|
|
|
release: int
|
|
|
|
|
brightness: int
|
2026-02-22 23:42:17 -05:00
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
emitRange: int
|