chore: Remove migration script from codebase

This commit is contained in:
bash 2026-05-09 16:03:47 +02:00
parent 82f5939561
commit 238c6b2c04

View File

@ -1,348 +0,0 @@
#!/usr/bin/env python3
"""
Import missing travel-node graph data from cmangos-playerbots into AC mod-playerbots.
Strategy:
1. Match cmangos nodes to ours by (name, mapId) closest by position wins
when there are duplicates.
2. Identify missing nodes (cmangos has, we don't).
3. Assign new IDs starting at max(existing) + 1.
4. Build cmangosId -> ourId remap covering matched + new.
5. For links: insert only those involving at least one NEW node preserves
all our existing existing-to-existing relationships.
6. For paths: same rule.
Output: SQL update files in data/sql/playerbots/updates/.
"""
import os
import re
import sys
from pathlib import Path
# Inputs
CMANGOS_SQL = Path(r"C:/Users/Admin/git/scratch/cmangos-playerbots/sql/world/wotlk/ai_playerbot_travel_nodes.sql")
MOD_DIR = Path(r"C:/Users/Admin/git/main/azerothcore-wotlk/modules/mod-playerbots")
OUR_NODE_SQL = MOD_DIR / "data/sql/playerbots/base/playerbots_travelnode.sql"
OUR_LINK_SQL = MOD_DIR / "data/sql/playerbots/base/playerbots_travelnode_link.sql"
# Output
UPDATES_DIR = MOD_DIR / "data/sql/playerbots/updates"
NODE_OUT = UPDATES_DIR / "2026_05_09_01_travelnode_cmangos_import.sql"
LINK_OUT = UPDATES_DIR / "2026_05_09_02_travelnode_link_cmangos_import.sql"
PATH_OUT = UPDATES_DIR / "2026_05_09_03_travelnode_path_cmangos_import.sql"
NODE_RE = re.compile(
r"\((\d+),\s*'([^']*)',\s*(\d+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+),\s*(\d+)\)"
)
LINK_RE = re.compile(
r"\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+),\s*(\d+),\s*(-?\d+),\s*(-?\d+),\s*(-?\d+)\)"
)
PATH_RE = re.compile(
r"\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+),\s*(-?[\d.eE+-]+)\)"
)
def parse_nodes(path: Path):
"""Parse all rows from a `*_travelnode` table-style SQL file. Returns dict id -> (name, mapId, x, y, z, linked)."""
out = {}
text = path.read_text(encoding="utf-8", errors="replace")
# Skip CREATE TABLE blocks: only count rows after the INSERT line in the
# node table. The link/path table rows have a different shape so they
# won't match NODE_RE anyway, but constraining via `INSERT INTO ... travelnode`
# boundary makes intent explicit.
in_node_block = False
for line in text.splitlines():
if "INSERT INTO" in line and "travelnode" in line.lower() and "_link" not in line.lower() and "_path" not in line.lower():
in_node_block = True
continue
if "INSERT INTO" in line and ("_link" in line.lower() or "_path" in line.lower()):
in_node_block = False
continue
if not in_node_block:
continue
for m in NODE_RE.finditer(line):
nid, name, mapId, x, y, z, linked = m.groups()
out[int(nid)] = (name, int(mapId), float(x), float(y), float(z), int(linked))
return out
def parse_links(path: Path):
"""Parse links into list[(node_id, to_node_id, type, object, distance, swim, extra, calc, mc0, mc1, mc2)]."""
out = []
text = path.read_text(encoding="utf-8", errors="replace")
in_link_block = False
for line in text.splitlines():
if "INSERT INTO" in line and "travelnode_link" in line.lower():
in_link_block = True
continue
if "INSERT INTO" in line and "travelnode_link" not in line.lower():
in_link_block = False
continue
if "CREATE TABLE" in line:
in_link_block = False
continue
if not in_link_block:
continue
for m in LINK_RE.finditer(line):
g = m.groups()
out.append((int(g[0]), int(g[1]), int(g[2]), int(g[3]),
float(g[4]), float(g[5]), float(g[6]),
int(g[7]), int(g[8]), int(g[9]), int(g[10])))
return out
def parse_paths(path: Path):
"""Parse paths into list[(node_id, to_node_id, nr, map_id, x, y, z)]."""
out = []
text = path.read_text(encoding="utf-8", errors="replace")
in_path_block = False
for line in text.splitlines():
if "INSERT INTO" in line and "travelnode_path" in line.lower():
in_path_block = True
continue
if "INSERT INTO" in line and "travelnode_path" not in line.lower():
in_path_block = False
continue
if "CREATE TABLE" in line:
in_path_block = False
continue
if not in_path_block:
continue
for m in PATH_RE.finditer(line):
g = m.groups()
out.append((int(g[0]), int(g[1]), int(g[2]), int(g[3]),
float(g[4]), float(g[5]), float(g[6])))
return out
def sq2(a, b):
return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2
def build_remap(cmangos_nodes, our_nodes):
"""Build cmangosId -> ourId map.
Returns (remap, missing_cmangos_ids, new_id_assignments).
new_id_assignments is dict cmangosId -> assigned ourId for the missing ones.
"""
# Bucket our nodes by (name, mapId) for fast lookup; multiple ours may
# share the same name (different positions), so we keep a list.
our_by_key = {}
for oid, (name, mid, x, y, z, lk) in our_nodes.items():
our_by_key.setdefault((name, mid), []).append((oid, x, y, z))
remap = {}
missing = []
for cid, (name, mid, x, y, z, lk) in cmangos_nodes.items():
candidates = our_by_key.get((name, mid))
if candidates:
# Closest by position
best = min(candidates, key=lambda c: sq2((c[1], c[2], c[3]), (x, y, z)))
remap[cid] = best[0]
else:
missing.append(cid)
# Assign new IDs to missing
max_our = max(our_nodes.keys()) if our_nodes else 0
new_assign = {}
next_id = max_our + 1
for cid in missing:
new_assign[cid] = next_id
remap[cid] = next_id
next_id += 1
return remap, missing, new_assign
def write_node_sql(out_path, cmangos_nodes, missing_ids, new_assign):
rows = []
for cid in missing_ids:
ourId = new_assign[cid]
name, mid, x, y, z, lk = cmangos_nodes[cid]
# Escape single quotes in name
ename = name.replace("'", "''")
rows.append(f"({ourId}, '{ename}', {mid}, {x:.4f}, {y:.4f}, {z:.4f}, {lk})")
with open(out_path, "w", encoding="utf-8") as f:
f.write("-- Imported from cmangos-playerbots ai_playerbot_travelnode (wotlk)\n")
f.write(f"-- {len(rows)} new nodes (cmangos has them, we didn't)\n")
f.write("-- Matched on (name, mapId) + closest position; remaining are unique to cmangos.\n\n")
if not rows:
f.write("-- (no new nodes to insert)\n")
return
f.write("INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`) VALUES\n")
# Batch in chunks of 500 rows per INSERT for readability
BATCH = 500
i = 0
while i < len(rows):
chunk = rows[i:i + BATCH]
f.write(",\n".join(chunk))
if i + BATCH < len(rows):
f.write(";\nINSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`) VALUES\n")
else:
f.write(";\n")
i += BATCH
def write_link_sql(out_path, cmangos_links, remap, new_ids_set, our_link_pairs):
"""Insert cmangos links involving at least one NEW node, mapped to our IDs.
Skip links where both endpoints are existing-to-existing (we keep our own).
"""
rows = []
skipped_existing = 0
skipped_unmapped = 0
skipped_dup_with_ours = 0
for c_from, c_to, t, obj, dist, swim, extra, calc, mc0, mc1, mc2 in cmangos_links:
if c_from not in remap or c_to not in remap:
skipped_unmapped += 1
continue
o_from = remap[c_from]
o_to = remap[c_to]
# Skip if both endpoints are existing — preserve our own link data.
if o_from not in new_ids_set and o_to not in new_ids_set:
skipped_existing += 1
continue
# If we already have this exact link (shouldn't happen since both new
# IDs are fresh, but defensive), skip.
if (o_from, o_to) in our_link_pairs:
skipped_dup_with_ours += 1
continue
rows.append(
f"({o_from}, {o_to}, {t}, {obj}, {dist:.4f}, {swim:.4f}, {extra:.4f}, {calc}, {mc0}, {mc1}, {mc2})"
)
with open(out_path, "w", encoding="utf-8") as f:
f.write("-- Imported from cmangos-playerbots ai_playerbot_travelnode_link (wotlk)\n")
f.write(f"-- {len(rows)} new links involving new nodes (mapped cmangos IDs to our IDs)\n")
f.write(f"-- skipped: {skipped_existing} existing-to-existing, "
f"{skipped_unmapped} unmapped, {skipped_dup_with_ours} dup-with-ours\n\n")
if not rows:
f.write("-- (no new links to insert)\n")
return
f.write(
"INSERT INTO `playerbots_travelnode_link` "
"(`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, "
"`extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`) VALUES\n"
)
BATCH = 500
i = 0
while i < len(rows):
chunk = rows[i:i + BATCH]
f.write(",\n".join(chunk))
if i + BATCH < len(rows):
f.write(
";\nINSERT INTO `playerbots_travelnode_link` "
"(`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, "
"`extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`) VALUES\n"
)
else:
f.write(";\n")
i += BATCH
def write_path_sql(out_path, cmangos_paths, remap, new_ids_set, our_link_pairs):
"""Insert cmangos paths whose link involves at least one NEW node."""
rows = []
skipped_existing = 0
skipped_unmapped = 0
skipped_dup_with_ours = 0
# Stream rows; the path list is millions of entries
for c_from, c_to, nr, mid, x, y, z in cmangos_paths:
if c_from not in remap or c_to not in remap:
skipped_unmapped += 1
continue
o_from = remap[c_from]
o_to = remap[c_to]
if o_from not in new_ids_set and o_to not in new_ids_set:
skipped_existing += 1
continue
if (o_from, o_to) in our_link_pairs:
skipped_dup_with_ours += 1
continue
rows.append((o_from, o_to, nr, mid, x, y, z))
with open(out_path, "w", encoding="utf-8") as f:
f.write("-- Imported from cmangos-playerbots ai_playerbot_travelnode_path (wotlk)\n")
f.write(f"-- {len(rows)} new path waypoints belonging to links involving new nodes\n")
f.write(f"-- skipped: {skipped_existing} existing-to-existing, "
f"{skipped_unmapped} unmapped, {skipped_dup_with_ours} dup-with-ours\n\n")
if not rows:
f.write("-- (no new path rows to insert)\n")
return
f.write(
"INSERT INTO `playerbots_travelnode_path` "
"(`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`) VALUES\n"
)
BATCH = 1000
i = 0
while i < len(rows):
chunk_strs = []
for o_from, o_to, nr, mid, x, y, z in rows[i:i + BATCH]:
chunk_strs.append(f"({o_from}, {o_to}, {nr}, {mid}, {x:.4f}, {y:.4f}, {z:.4f})")
f.write(",\n".join(chunk_strs))
if i + BATCH < len(rows):
f.write(
";\nINSERT INTO `playerbots_travelnode_path` "
"(`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`) VALUES\n"
)
else:
f.write(";\n")
i += BATCH
def parse_our_link_pairs(path: Path):
"""Return set of (node_id, to_node_id) pairs we already have."""
pairs = set()
text = path.read_text(encoding="utf-8", errors="replace")
for m in LINK_RE.finditer(text):
pairs.add((int(m.group(1)), int(m.group(2))))
return pairs
def main():
print("Parsing cmangos nodes...", flush=True)
cmangos_nodes = parse_nodes(CMANGOS_SQL)
print(f" {len(cmangos_nodes)} nodes")
print("Parsing our nodes...", flush=True)
our_nodes = parse_nodes(OUR_NODE_SQL)
print(f" {len(our_nodes)} nodes")
print("Building remap...", flush=True)
remap, missing, new_assign = build_remap(cmangos_nodes, our_nodes)
print(f" matched: {len(remap) - len(missing)}, missing (new): {len(missing)}")
new_ids_set = set(new_assign.values())
print("Parsing cmangos links...", flush=True)
cm_links = parse_links(CMANGOS_SQL)
print(f" {len(cm_links)} links")
print("Parsing our existing link pairs...", flush=True)
our_link_pairs = parse_our_link_pairs(OUR_LINK_SQL)
print(f" {len(our_link_pairs)} existing pairs")
print("Parsing cmangos paths...", flush=True)
cm_paths = parse_paths(CMANGOS_SQL)
print(f" {len(cm_paths)} path rows")
UPDATES_DIR.mkdir(parents=True, exist_ok=True)
print(f"Writing {NODE_OUT.name}...", flush=True)
write_node_sql(NODE_OUT, cmangos_nodes, missing, new_assign)
print(f"Writing {LINK_OUT.name}...", flush=True)
write_link_sql(LINK_OUT, cm_links, remap, new_ids_set, our_link_pairs)
print(f"Writing {PATH_OUT.name}...", flush=True)
write_path_sql(PATH_OUT, cm_paths, remap, new_ids_set, our_link_pairs)
print("Done.")
if __name__ == "__main__":
main()