From 0f8f22df29948e96dc9a9eb6c350a0bdfc48d412 Mon Sep 17 00:00:00 2001 From: NotSoToothless <67082114+FURRO404@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:05:51 -0700 Subject: [PATCH] update for spectra changes (#1363) --- BOT/lux_apis.py | 6 +++++ BOT/render_replay.py | 53 ++++++++++++++++++++++++++++++++------------ BOT/utils.py | 3 ++- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/BOT/lux_apis.py b/BOT/lux_apis.py index 5809f34..a5a881d 100644 --- a/BOT/lux_apis.py +++ b/BOT/lux_apis.py @@ -55,6 +55,12 @@ def normalize_ws_message(data: Any) -> Optional[List[Dict[str, Any]]]: if isinstance(data, dict) and 'completed' in data: return data['completed'] + if isinstance(data, dict) and isinstance(data.get('data'), dict): + return [data['data']] + + if isinstance(data, dict) and isinstance(data.get('data'), list): + return data['data'] + logger.warning(f"Unknown WS message format: {type(data)}") return None diff --git a/BOT/render_replay.py b/BOT/render_replay.py index 151575e..55af386 100644 --- a/BOT/render_replay.py +++ b/BOT/render_replay.py @@ -3185,6 +3185,10 @@ def _unit_to_model_name(unit_name: str) -> str: internal = (unit_name or "").strip() if not internal: return "tankModels/unknown" + if internal.startswith(("tankModels/", "airModels/")): + return internal + if internal.startswith("aircrafts/"): + return f"airModels/{internal.split('/', 1)[1]}" tags = _get_unit_tags(internal) or [] tag_set = set(tags) if "type_strike_ucav" in tag_set or "ucav" in internal.lower(): @@ -3245,6 +3249,15 @@ def _position_at_time(path: list[dict[str, float]], time_ms: float) -> dict[str, return prev +def _event_position_to_dict(pos: Any) -> dict[str, float] | None: + if not isinstance(pos, list) or len(pos) < 3: + return None + try: + return {"X": float(pos[0]), "Y": float(pos[1]), "Z": float(pos[2])} + except (TypeError, ValueError): + return None + + def _zone_geometry_from_raw_zones(zones_src: Any) -> dict[str, dict]: if not isinstance(zones_src, dict): return {} @@ -3307,7 +3320,7 @@ def _convert_ws_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, Any]: if not isinstance(ent, dict): continue uid = _to_int(ent.get("uid"), 0) - unit = str(ent.get("unit") or "") + unit = str(ent.get("model_path") or ent.get("unit") or "") path_raw = ent.get("path") or [] if not isinstance(path_raw, list): continue @@ -3349,16 +3362,22 @@ def _convert_ws_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, Any]: kill_time = float(kill.get("time") or 0.0) victim_path = entity_paths_by_uid.get(victim_id, []) killer_path = entity_paths_by_uid.get(killer_id, []) - victim_pos = _position_at_time(victim_path, kill_time) - killer_pos = _position_at_time(killer_path, kill_time) + victim_pos = ( + _position_at_time(victim_path, kill_time) + or _event_position_to_dict(kill.get("offended_pos")) + ) + killer_pos = ( + _position_at_time(killer_path, kill_time) + or _event_position_to_dict(kill.get("offender_pos")) + ) payload: dict[str, Any] = { "Time": kill_time, "VictimID": victim_id, "KillerID": killer_id, "VictimEntityIndex": uid_to_entity_index.get(victim_id, 0), "Weapon": str(kill.get("used_weapon") or kill.get("weapon") or ""), - "VictimModel": _unit_to_model_name(str(kill.get("offended_unit") or "")), - "KillerModel": _unit_to_model_name(str(kill.get("offender_unit") or "")), + "VictimModel": _unit_to_model_name(str(kill.get("offended_unit_model_path") or kill.get("offended_unit") or "")), + "KillerModel": _unit_to_model_name(str(kill.get("offender_unit_model_path") or kill.get("offender_unit") or "")), "crashed": bool(kill.get("crashed", False)), } if victim_pos: @@ -3383,8 +3402,8 @@ def _convert_ws_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, Any]: "Time": float(dmg.get("time") or 0.0), "OffenderID": _to_int(dmg.get("offender_uid"), 0), "OffendedID": _to_int(dmg.get("offended_uid"), 0), - "OffenderModel": _unit_to_model_name(str(dmg.get("offender_unit") or "")), - "OffendedModel": _unit_to_model_name(str(dmg.get("offended_unit") or "")), + "OffenderModel": _unit_to_model_name(str(dmg.get("offender_unit_model_path") or dmg.get("offender_unit") or "")), + "OffendedModel": _unit_to_model_name(str(dmg.get("offended_unit_model_path") or dmg.get("offended_unit") or "")), "Afire": bool(dmg.get("afire", False)), }) @@ -3468,7 +3487,7 @@ def _convert_local_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, An if not isinstance(ent, dict): continue uid = _to_int(ent.get("uid"), 0) - unit = str(ent.get("unit") or "") + unit = str(ent.get("model_path") or ent.get("unit") or "") path_raw = ent.get("path") or [] if not isinstance(path_raw, list): continue @@ -3510,16 +3529,22 @@ def _convert_local_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, An kill_time = float(kill.get("time") or 0.0) victim_path = entity_paths_by_uid.get(victim_id, []) killer_path = entity_paths_by_uid.get(killer_id, []) - victim_pos = _position_at_time(victim_path, kill_time) - killer_pos = _position_at_time(killer_path, kill_time) + victim_pos = ( + _position_at_time(victim_path, kill_time) + or _event_position_to_dict(kill.get("offended_pos")) + ) + killer_pos = ( + _position_at_time(killer_path, kill_time) + or _event_position_to_dict(kill.get("offender_pos")) + ) payload: dict[str, Any] = { "Time": kill_time, "VictimID": victim_id, "KillerID": killer_id, "VictimEntityIndex": uid_to_entity_index.get(victim_id, 0), "Weapon": str(kill.get("used_weapon") or kill.get("weapon") or ""), - "VictimModel": _unit_to_model_name(str(kill.get("offended_unit") or "")), - "KillerModel": _unit_to_model_name(str(kill.get("offender_unit") or "")), + "VictimModel": _unit_to_model_name(str(kill.get("offended_unit_model_path") or kill.get("offended_unit") or "")), + "KillerModel": _unit_to_model_name(str(kill.get("offender_unit_model_path") or kill.get("offender_unit") or "")), "crashed": bool(kill.get("crashed", False)), } if victim_pos: @@ -3544,8 +3569,8 @@ def _convert_local_replay_to_render_dict(replay: dict[str, Any]) -> dict[str, An "Time": float(dmg.get("time") or 0.0), "OffenderID": _to_int(dmg.get("offender_uid"), 0), "OffendedID": _to_int(dmg.get("offended_uid"), 0), - "OffenderModel": _unit_to_model_name(str(dmg.get("offender_unit") or "")), - "OffendedModel": _unit_to_model_name(str(dmg.get("offended_unit") or "")), + "OffenderModel": _unit_to_model_name(str(dmg.get("offender_unit_model_path") or dmg.get("offender_unit") or "")), + "OffendedModel": _unit_to_model_name(str(dmg.get("offended_unit_model_path") or dmg.get("offended_unit") or "")), "Afire": bool(dmg.get("afire", False)), }) diff --git a/BOT/utils.py b/BOT/utils.py index a4ae738..eee2b69 100644 --- a/BOT/utils.py +++ b/BOT/utils.py @@ -41,6 +41,7 @@ from data_parser import ( apply_vehicle_name_filters, normalize_name, ) +from spectra_replay_normalize import normalize_spectra_replay load_dotenv() @@ -1251,7 +1252,7 @@ def transform_to_local_format(api_data: Dict[str, Any]) -> Optional[Dict[str, An logging.error("Invalid API data structure") return None - replay = api_data["completed"][0] + replay = normalize_spectra_replay(api_data["completed"][0]) winner_winged = str(replay.get("winner") or "") loser_winged = str(replay.get("loser") or "")