Sync from GitHub main #1

Open
nomad wants to merge 145 commits from sync/from-github into main
Showing only changes of commit f24bd062eb - Show all commits
+237
View File
@@ -0,0 +1,237 @@
from __future__ import annotations
import math
from collections.abc import Callable
from typing import Any
MAX_CHARACTER_NAME_CHARS = 128
MAX_EQUIPPED_ITEMS = 32
MAX_EQUIPMENT_SLOT_CHARS = 64
MAX_HEAD_PARTS = 64
MAX_MORPHS = 128
MAX_MORPH_REGIONS = 128
MAX_FACIAL_BONE_MORPHS = 128
MAX_TINTS = 128
def _is_int(value: Any, minimum: int = 0, maximum: int = 0xFFFFFFFF) -> bool:
return isinstance(value, int) and not isinstance(value, bool) and minimum <= value <= maximum
def _is_finite_number(value: Any, minimum: float, maximum: float) -> bool:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
and minimum <= float(value) <= maximum
)
def _normalize_form_id(value: Any, *, allow_empty: bool = True) -> str | None:
if not isinstance(value, str):
return None
if allow_empty and value == "":
return ""
if not 1 <= len(value) <= 8:
return None
if any(character not in "0123456789abcdefABCDEF" for character in value):
return None
return value.upper().zfill(8)
def _normalize_vec3(value: Any, minimum: float, maximum: float) -> list[float] | None:
if not isinstance(value, list) or len(value) != 3:
return None
if not all(_is_finite_number(component, minimum, maximum) for component in value):
return None
return [float(component) for component in value]
def _normalize_appearance(value: Any) -> dict[str, Any] | None:
if not isinstance(value, dict):
return None
clean: dict[str, Any] = {}
version = value.get("version", 4)
if not _is_int(version, 1, 1000):
return None
clean["version"] = int(version)
for name in ("raceFormId", "hairColorFormId", "facialHairColorFormId", "complexionFormId"):
if name in value:
form_id = _normalize_form_id(value[name])
if form_id is None:
return None
clean[name] = form_id
if "height" in value:
if not _is_finite_number(value["height"], 0.25, 4.0):
return None
clean["height"] = float(value["height"])
if "isFemale" in value:
if not isinstance(value["isFemale"], bool):
return None
clean["isFemale"] = value["isFemale"]
if "morphWeight" in value:
weight = value["morphWeight"]
if not isinstance(weight, dict):
return None
clean_weight: dict[str, float] = {}
for name in ("thin", "muscular", "large"):
if not _is_finite_number(weight.get(name), -100.0, 100.0):
return None
clean_weight[name] = float(weight[name])
clean["morphWeight"] = clean_weight
if "bodyTintColor" in value:
color = value["bodyTintColor"]
if not isinstance(color, dict):
return None
clean_color: dict[str, int] = {}
for name in ("r", "g", "b", "a"):
if not _is_int(color.get(name), 0, 255):
return None
clean_color[name] = int(color[name])
clean["bodyTintColor"] = clean_color
if "headParts" in value:
head_parts = value["headParts"]
if not isinstance(head_parts, list) or len(head_parts) > MAX_HEAD_PARTS:
return None
clean_head_parts: list[str] = []
for form_id in head_parts:
normalized = _normalize_form_id(form_id, allow_empty=False)
if normalized is None:
return None
clean_head_parts.append(normalized)
clean["headParts"] = clean_head_parts
if "morphs" in value:
morphs = value["morphs"]
if not isinstance(morphs, list) or len(morphs) > MAX_MORPHS:
return None
clean_morphs: list[dict[str, Any]] = []
for morph in morphs:
if not isinstance(morph, dict):
return None
morph_id = _normalize_form_id(morph.get("id"), allow_empty=False)
if morph_id is None or not _is_finite_number(morph.get("value"), -1000.0, 1000.0):
return None
clean_morphs.append({"id": morph_id, "value": float(morph["value"])})
clean["morphs"] = clean_morphs
if "morphRegions" in value:
regions = value["morphRegions"]
if not isinstance(regions, list) or len(regions) > MAX_MORPH_REGIONS:
return None
if not all(_is_finite_number(region, -1000.0, 1000.0) for region in regions):
return None
clean["morphRegions"] = [float(region) for region in regions]
if "facialBoneMorphs" in value:
morphs = value["facialBoneMorphs"]
if not isinstance(morphs, list) or len(morphs) > MAX_FACIAL_BONE_MORPHS:
return None
clean_bones: list[dict[str, Any]] = []
for morph in morphs:
if not isinstance(morph, dict):
return None
morph_id = _normalize_form_id(morph.get("id"), allow_empty=False)
position = _normalize_vec3(morph.get("position"), -10000.0, 10000.0)
rotation = _normalize_vec3(morph.get("rotation"), -10000.0, 10000.0)
scale = _normalize_vec3(morph.get("scale"), -100.0, 100.0)
if morph_id is None or position is None or rotation is None or scale is None:
return None
clean_bones.append(
{
"id": morph_id,
"position": position,
"rotation": rotation,
"scale": scale,
}
)
clean["facialBoneMorphs"] = clean_bones
if "tints" in value:
tints = value["tints"]
if not isinstance(tints, list) or len(tints) > MAX_TINTS:
return None
clean_tints: list[dict[str, Any]] = []
for tint in tints:
if not isinstance(tint, dict):
return None
if not _is_int(tint.get("id"), 0, 0xFFFF):
return None
if not _is_int(tint.get("type"), 0, 0xFFFFFFFF):
return None
if not _is_int(tint.get("value"), 0, 255):
return None
clean_tint: dict[str, Any] = {
"id": int(tint["id"]),
"type": int(tint["type"]),
"value": int(tint["value"]),
}
if "color" in tint:
color = _normalize_form_id(tint["color"], allow_empty=False)
if color is None or not _is_int(tint.get("swatch", 0), 0, 0xFFFF):
return None
clean_tint["color"] = color
clean_tint["swatch"] = int(tint.get("swatch", 0))
clean_tints.append(clean_tint)
clean["tints"] = clean_tints
return clean
def normalize_player_state_packet(
packet: dict[str, Any],
normalize_action_events: Callable[[Any], list[dict[str, Any]]],
) -> dict[str, Any] | None:
clean: dict[str, Any] = {"type": "playerState"}
has_state = False
if "equippedItems" in packet:
items = packet["equippedItems"]
if not isinstance(items, list) or len(items) > MAX_EQUIPPED_ITEMS:
return None
clean_items: list[dict[str, str]] = []
for item in items:
if not isinstance(item, dict):
return None
slot = item.get("slot")
if not isinstance(slot, str) or not 1 <= len(slot) <= MAX_EQUIPMENT_SLOT_CHARS:
return None
form_id = _normalize_form_id(item.get("formId", ""))
if form_id is None:
return None
clean_items.append({"slot": slot, "formId": form_id})
clean["equippedItems"] = clean_items
has_state = True
if "appearance" in packet:
appearance = _normalize_appearance(packet["appearance"])
if appearance is None:
return None
clean["appearance"] = appearance
has_state = True
if "actionEvents" in packet:
action_events = packet["actionEvents"]
if not isinstance(action_events, list):
return None
normalized_actions = normalize_action_events(action_events)
if len(normalized_actions) != len(action_events):
return None
clean["actionEvents"] = normalized_actions
has_state = True
if "characterName" in packet:
name = packet["characterName"]
if not isinstance(name, str) or len(name) > MAX_CHARACTER_NAME_CHARS:
return None
clean["characterName"] = name
has_state = True
return clean if has_state else None