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
|
|
|
|
|
|
|
|
|
|
|
2026-02-24 20:55:02 -05:00
|
|
|
class TeleportCompletePacket(BasePacket):
|
|
|
|
|
type: Literal["teleport_complete"]
|
2026-02-24 21:01:21 -05:00
|
|
|
x: int
|
|
|
|
|
y: int
|
2026-02-24 20:55:02 -05:00
|
|
|
|
|
|
|
|
|
2026-02-20 08:16:43 -05:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2026-02-24 22:03:10 -05:00
|
|
|
class AuthRegisterPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_register"]
|
|
|
|
|
username: str = Field(min_length=1, max_length=128)
|
|
|
|
|
password: str = Field(min_length=1, max_length=256)
|
|
|
|
|
email: str | None = Field(default=None, max_length=320)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuthLoginPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_login"]
|
|
|
|
|
username: str = Field(min_length=1, max_length=128)
|
|
|
|
|
password: str = Field(min_length=1, max_length=256)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuthResumePacket(BasePacket):
|
|
|
|
|
type: Literal["auth_resume"]
|
|
|
|
|
sessionToken: str = Field(min_length=1, max_length=512)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuthLogoutPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_logout"]
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 03:37:20 -05:00
|
|
|
class AdminRolesListPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_roles_list"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRoleCreatePacket(BasePacket):
|
|
|
|
|
type: Literal["admin_role_create"]
|
|
|
|
|
name: str = Field(min_length=1, max_length=32)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRoleUpdatePermissionsPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_role_update_permissions"]
|
|
|
|
|
role: str = Field(min_length=1, max_length=32)
|
|
|
|
|
permissions: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRoleDeletePacket(BasePacket):
|
|
|
|
|
type: Literal["admin_role_delete"]
|
|
|
|
|
role: str = Field(min_length=1, max_length=32)
|
|
|
|
|
replacementRole: str = Field(min_length=1, max_length=32)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUsersListPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_users_list"]
|
2026-02-27 03:49:28 -05:00
|
|
|
action: Literal["set_role", "ban", "unban"] | None = None
|
2026-02-27 03:37:20 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUserSetRolePacket(BasePacket):
|
|
|
|
|
type: Literal["admin_user_set_role"]
|
|
|
|
|
username: str = Field(min_length=1, max_length=128)
|
|
|
|
|
role: str = Field(min_length=1, max_length=32)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUserBanPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_user_ban"]
|
|
|
|
|
username: str = Field(min_length=1, max_length=128)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUserUnbanPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_user_unban"]
|
|
|
|
|
username: str = Field(min_length=1, max_length=128)
|
|
|
|
|
|
|
|
|
|
|
2026-02-20 08:16:43 -05:00
|
|
|
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-25 01:11:47 -05:00
|
|
|
class ItemSecondaryUsePacket(BasePacket):
|
|
|
|
|
type: Literal["item_secondary_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
|
2026-02-24 20:55:02 -05:00
|
|
|
| TeleportCompletePacket
|
2026-02-20 08:16:43 -05:00
|
|
|
| UpdateNicknamePacket
|
|
|
|
|
| ChatMessagePacket
|
2026-02-24 22:03:10 -05:00
|
|
|
| AuthRegisterPacket
|
|
|
|
|
| AuthLoginPacket
|
|
|
|
|
| AuthResumePacket
|
|
|
|
|
| AuthLogoutPacket
|
2026-02-27 03:37:20 -05:00
|
|
|
| AdminRolesListPacket
|
|
|
|
|
| AdminRoleCreatePacket
|
|
|
|
|
| AdminRoleUpdatePermissionsPacket
|
|
|
|
|
| AdminRoleDeletePacket
|
|
|
|
|
| AdminUsersListPacket
|
|
|
|
|
| AdminUserSetRolePacket
|
|
|
|
|
| AdminUserBanPacket
|
|
|
|
|
| AdminUserUnbanPacket
|
2026-02-20 08:16:43 -05:00
|
|
|
| PingPacket
|
|
|
|
|
| ItemAddPacket
|
|
|
|
|
| ItemPickupPacket
|
|
|
|
|
| ItemDropPacket
|
|
|
|
|
| ItemDeletePacket
|
|
|
|
|
| ItemUsePacket
|
2026-02-25 01:11:47 -05:00
|
|
|
| ItemSecondaryUsePacket
|
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
|
2026-02-24 19:52:38 -05:00
|
|
|
player: RemoteUser
|
2026-02-20 08:16:43 -05:00
|
|
|
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-24 22:03:10 -05:00
|
|
|
auth: dict | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuthRequiredPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_required"]
|
|
|
|
|
message: str
|
2026-02-24 22:35:29 -05:00
|
|
|
authPolicy: dict | None = None
|
2026-02-24 22:03:10 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuthResultPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_result"]
|
|
|
|
|
ok: bool
|
|
|
|
|
message: str
|
|
|
|
|
sessionToken: str | None = None
|
|
|
|
|
username: str | None = None
|
|
|
|
|
role: str | None = None
|
2026-02-27 03:37:20 -05:00
|
|
|
permissions: list[str] | None = None
|
2026-02-27 03:49:28 -05:00
|
|
|
adminMenuActions: list[dict[str, str]] | None = None
|
2026-02-24 22:03:10 -05:00
|
|
|
nickname: str | None = None
|
2026-02-24 22:35:29 -05:00
|
|
|
authPolicy: dict | None = None
|
2026-02-20 08:16:43 -05:00
|
|
|
|
|
|
|
|
|
2026-02-27 03:37:20 -05:00
|
|
|
class AuthPermissionsPacket(BasePacket):
|
|
|
|
|
type: Literal["auth_permissions"]
|
|
|
|
|
role: str
|
|
|
|
|
permissions: list[str]
|
2026-02-27 03:49:28 -05:00
|
|
|
adminMenuActions: list[dict[str, str]] | None = None
|
2026-02-27 03:37:20 -05:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2026-02-24 20:55:02 -05:00
|
|
|
class BroadcastTeleportCompletePacket(BasePacket):
|
|
|
|
|
type: Literal["teleport_complete"]
|
|
|
|
|
id: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
|
|
|
|
|
|
|
|
|
|
2026-02-20 08:16:43 -05:00
|
|
|
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
|
2026-02-27 02:34:58 -05:00
|
|
|
createdByName: str
|
2026-02-27 02:27:16 -05:00
|
|
|
updatedBy: str
|
2026-02-27 02:34:58 -05:00
|
|
|
updatedByName: str
|
2026-02-20 08:16:43 -05:00
|
|
|
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
|
2026-02-27 01:32:25 -05:00
|
|
|
display: dict[str, str] | None = None
|
2026-02-20 08:16:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2026-02-27 02:34:58 -05:00
|
|
|
createdByName: str | None = None
|
2026-02-27 02:27:16 -05:00
|
|
|
updatedBy: str | None = None
|
2026-02-27 02:34:58 -05:00
|
|
|
updatedByName: str | None = None
|
2026-02-20 08:16:43 -05:00
|
|
|
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
|
2026-02-25 01:11:47 -05:00
|
|
|
action: Literal["add", "pickup", "drop", "delete", "use", "secondary_use", "update"]
|
2026-02-20 08:16:43 -05:00
|
|
|
message: str
|
|
|
|
|
itemId: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemUseSoundPacket(BasePacket):
|
|
|
|
|
type: Literal["item_use_sound"]
|
|
|
|
|
itemId: str
|
|
|
|
|
sound: str
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
2026-02-27 01:10:32 -05:00
|
|
|
range: int | None = None
|
2026-02-22 23:42:17 -05:00
|
|
|
|
|
|
|
|
|
2026-02-27 01:05:23 -05:00
|
|
|
class ItemClockAnnouncePacket(BasePacket):
|
|
|
|
|
type: Literal["item_clock_announce"]
|
|
|
|
|
itemId: str
|
|
|
|
|
sounds: list[str]
|
|
|
|
|
x: int
|
|
|
|
|
y: int
|
2026-02-27 01:12:24 -05:00
|
|
|
range: int | None = None
|
2026-02-27 01:05:23 -05:00
|
|
|
|
|
|
|
|
|
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
|
2026-02-24 19:56:44 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ItemPianoStatusPacket(BasePacket):
|
|
|
|
|
type: Literal["item_piano_status"]
|
|
|
|
|
itemId: str
|
|
|
|
|
event: Literal[
|
|
|
|
|
"use_mode_entered",
|
|
|
|
|
"record_started",
|
|
|
|
|
"record_paused",
|
|
|
|
|
"record_resumed",
|
|
|
|
|
"record_stopped",
|
|
|
|
|
"playback_started",
|
|
|
|
|
"playback_stopped",
|
|
|
|
|
]
|
|
|
|
|
recordingState: Literal["idle", "recording", "paused", "playback"] | None = None
|
2026-02-27 03:37:20 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRoleSummary(BaseModel):
|
|
|
|
|
id: int
|
|
|
|
|
name: str
|
|
|
|
|
isSystem: bool
|
|
|
|
|
userCount: int
|
|
|
|
|
permissions: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminRolesListResultPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_roles_list"]
|
|
|
|
|
roles: list[AdminRoleSummary]
|
|
|
|
|
permissionKeys: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUserSummary(BaseModel):
|
|
|
|
|
id: str
|
|
|
|
|
username: str
|
|
|
|
|
role: str
|
|
|
|
|
status: Literal["active", "disabled"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminUsersListResultPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_users_list"]
|
|
|
|
|
users: list[AdminUserSummary]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminActionResultPacket(BasePacket):
|
|
|
|
|
type: Literal["admin_action_result"]
|
|
|
|
|
ok: bool
|
|
|
|
|
action: Literal[
|
|
|
|
|
"role_create",
|
|
|
|
|
"role_update_permissions",
|
|
|
|
|
"role_delete",
|
|
|
|
|
"user_set_role",
|
|
|
|
|
"user_ban",
|
|
|
|
|
"user_unban",
|
|
|
|
|
]
|
|
|
|
|
message: str
|