param( [string]$DbContainer = "wingsemu-postgres", [string]$DbName = "game", [string]$DbUser = "postgres", [string]$ConfigRoot = "config" ) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)) Set-Location $root $configPath = Join-Path $root $ConfigRoot if (-not (Test-Path $configPath)) { throw "Config path not found: $configPath" } $maps = @() $mapFlags = @() $portals = @() $npcs = @() $monsters = @() function To-IntOrNull($v) { if ([string]::IsNullOrWhiteSpace($v)) { return $null } $v = ($v -split '#')[0].Trim() if ($v -match '^-?\d+$') { return [int]$v } return $null } function SqlEsc([string]$s) { if ($null -eq $s) { return 'NULL' } return "'" + ($s -replace "'","''") + "'" } function SqlNum($n) { if ($null -eq $n) { return 'NULL' } return [string]$n } function Read-NonCommentLines([string]$file) { Get-Content $file | ForEach-Object { $_.TrimEnd() } | Where-Object { $_ -notmatch '^\s*#' } } # -------- maps -------- Get-ChildItem (Join-Path $configPath 'maps') -File -ErrorAction SilentlyContinue | ForEach-Object { $lines = Read-NonCommentLines $_.FullName $current = $null $inFlags = $false foreach ($line in $lines) { if ($line -match '^\s*-\s*map_id\s*:\s*(.+)$') { if ($current) { $maps += $current } $current = [ordered]@{ map_id = To-IntOrNull $matches[1]; map_vnum = $null; map_name_id = $null; map_music_id = $null; flags = @() } $inFlags = $false continue } if (-not $current) { continue } if ($line -match '^\s*flags\s*:') { $inFlags = $true; continue } if ($inFlags -and $line -match '^\s*-\s*([A-Z0-9_]+)\s*$') { $current.flags += $matches[1] continue } if ($line -match '^\s*map_vnum\s*:\s*(.+)$') { $current.map_vnum = To-IntOrNull $matches[1]; $inFlags=$false; continue } if ($line -match '^\s*map_name_id\s*:\s*(.+)$') { $current.map_name_id = To-IntOrNull $matches[1]; $inFlags=$false; continue } if ($line -match '^\s*map_music_id\s*:\s*(.+)$') { $current.map_music_id = To-IntOrNull $matches[1]; $inFlags=$false; continue } } if ($current) { $maps += $current } } foreach ($m in $maps) { foreach ($f in $m.flags) { $mapFlags += [ordered]@{ map_id = $m.map_id; flag = $f } } } # -------- portals -------- Get-ChildItem (Join-Path $configPath 'map_portals') -File -ErrorAction SilentlyContinue | ForEach-Object { $lines = Read-NonCommentLines $_.FullName $current = $null foreach ($line in $lines) { if ($line -match '^\s*-\s*destination_map_id\s*:\s*(.+)$') { if ($current) { $portals += $current } $current = [ordered]@{ destination_map_id = To-IntOrNull $matches[1] destination_map_x = $null destination_map_y = $null source_map_id = $null source_map_x = $null source_map_y = $null type = $null } continue } if (-not $current) { continue } if ($line -match '^\s*destination_map_x\s*:\s*(.+)$') { $current.destination_map_x = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*destination_map_y\s*:\s*(.+)$') { $current.destination_map_y = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*source_map_id\s*:\s*(.+)$') { $current.source_map_id = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*source_map_x\s*:\s*(.+)$') { $current.source_map_x = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*source_map_y\s*:\s*(.+)$') { $current.source_map_y = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*type\s*:\s*(.+)$') { $current.type = To-IntOrNull $matches[1]; continue } } if ($current) { $portals += $current } } # -------- npcs -------- Get-ChildItem (Join-Path $configPath 'map_npc_placement') -File -ErrorAction SilentlyContinue | ForEach-Object { $lines = Read-NonCommentLines $_.FullName $mapId = $null $current = $null foreach ($line in $lines) { if ($line -match '^\s*map_id\s*:\s*(.+)$') { $mapId = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*-\s*map_npc_id\s*:\s*(.+)$') { if ($current) { $npcs += $current } $current = [ordered]@{ map_id=$mapId; map_npc_id=To-IntOrNull $matches[1]; vnum=$null; pos_x=$null; pos_y=$null; effect_vnum=$null; effect_delay=$null; dialog_id=$null; direction_facing=$null } continue } if (-not $current) { continue } if ($line -match '^\s*vnum\s*:\s*(.+)$') { $current.vnum = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*pos_x\s*:\s*(.+)$') { $current.pos_x = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*pos_y\s*:\s*(.+)$') { $current.pos_y = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*effect_vnum\s*:\s*(.+)$') { $current.effect_vnum = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*effect_delay\s*:\s*(.+)$') { $current.effect_delay = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*dialog_id\s*:\s*(.+)$') { $current.dialog_id = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*direction_facing\s*:\s*(.+)$') { $current.direction_facing = To-IntOrNull $matches[1]; continue } } if ($current) { $npcs += $current } } # -------- monsters -------- Get-ChildItem (Join-Path $configPath 'map_monster_placement') -File -ErrorAction SilentlyContinue | ForEach-Object { $lines = Read-NonCommentLines $_.FullName $mapId = $null $current = $null foreach ($line in $lines) { if ($line -match '^\s*map_id\s*:\s*(.+)$') { $mapId = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*-\s*map_monster_id\s*:\s*(.+)$') { if ($current) { $monsters += $current } $current = [ordered]@{ map_id=$mapId; map_monster_id=To-IntOrNull $matches[1]; vnum=$null; map_x=$null; map_y=$null; position=$null; can_move=$null } continue } if (-not $current) { continue } if ($line -match '^\s*vnum\s*:\s*(.+)$') { $current.vnum = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*map_x\s*:\s*(.+)$') { $current.map_x = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*map_y\s*:\s*(.+)$') { $current.map_y = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*position\s*:\s*(.+)$') { $current.position = To-IntOrNull $matches[1]; continue } if ($line -match '^\s*can_move\s*:\s*(.+)$') { $current.can_move = ($matches[1].Trim().ToLower() -eq 'true'); continue } } if ($current) { $monsters += $current } } $sql = New-Object System.Text.StringBuilder [void]$sql.AppendLine("BEGIN;") [void]$sql.AppendLine(@" DROP TABLE IF EXISTS server_map_flags; DROP TABLE IF EXISTS server_maps; DROP TABLE IF EXISTS map_portals; DROP TABLE IF EXISTS map_npcs; DROP TABLE IF EXISTS map_monsters; CREATE TABLE server_maps ( map_id INT PRIMARY KEY, map_vnum INT, map_name_id INT, map_music_id INT ); CREATE TABLE server_map_flags ( map_id INT NOT NULL, flag TEXT NOT NULL, PRIMARY KEY (map_id, flag) ); CREATE TABLE map_portals ( id BIGSERIAL PRIMARY KEY, destination_map_id INT, destination_map_x INT, destination_map_y INT, source_map_id INT, source_map_x INT, source_map_y INT, type INT ); CREATE TABLE map_npcs ( id BIGSERIAL PRIMARY KEY, map_npc_id INT, map_id INT, vnum INT, pos_x INT, pos_y INT, effect_vnum INT, effect_delay INT, dialog_id INT, direction_facing INT ); CREATE TABLE map_monsters ( id BIGSERIAL PRIMARY KEY, map_monster_id INT, map_id INT, vnum INT, map_x INT, map_y INT, position INT, can_move BOOLEAN ); "@) foreach ($m in $maps | Where-Object { $null -ne $_.map_id }) { [void]$sql.AppendLine("INSERT INTO server_maps(map_id,map_vnum,map_name_id,map_music_id) VALUES ($(SqlNum $m.map_id),$(SqlNum $m.map_vnum),$(SqlNum $m.map_name_id),$(SqlNum $m.map_music_id));") } foreach ($f in $mapFlags | Where-Object { $null -ne $_.map_id -and -not [string]::IsNullOrWhiteSpace($_.flag) }) { [void]$sql.AppendLine("INSERT INTO server_map_flags(map_id,flag) VALUES ($(SqlNum $f.map_id),$(SqlEsc $f.flag));") } foreach ($p in $portals) { [void]$sql.AppendLine("INSERT INTO map_portals(destination_map_id,destination_map_x,destination_map_y,source_map_id,source_map_x,source_map_y,type) VALUES ($(SqlNum $p.destination_map_id),$(SqlNum $p.destination_map_x),$(SqlNum $p.destination_map_y),$(SqlNum $p.source_map_id),$(SqlNum $p.source_map_x),$(SqlNum $p.source_map_y),$(SqlNum $p.type));") } foreach ($n in $npcs | Where-Object { $null -ne $_.map_npc_id }) { [void]$sql.AppendLine("INSERT INTO map_npcs(map_npc_id,map_id,vnum,pos_x,pos_y,effect_vnum,effect_delay,dialog_id,direction_facing) VALUES ($(SqlNum $n.map_npc_id),$(SqlNum $n.map_id),$(SqlNum $n.vnum),$(SqlNum $n.pos_x),$(SqlNum $n.pos_y),$(SqlNum $n.effect_vnum),$(SqlNum $n.effect_delay),$(SqlNum $n.dialog_id),$(SqlNum $n.direction_facing));") } foreach ($m in $monsters | Where-Object { $null -ne $_.map_monster_id }) { $mv = if ($null -eq $m.can_move) { 'NULL' } elseif ($m.can_move) { 'TRUE' } else { 'FALSE' } [void]$sql.AppendLine("INSERT INTO map_monsters(map_monster_id,map_id,vnum,map_x,map_y,position,can_move) VALUES ($(SqlNum $m.map_monster_id),$(SqlNum $m.map_id),$(SqlNum $m.vnum),$(SqlNum $m.map_x),$(SqlNum $m.map_y),$(SqlNum $m.position),$mv);") } [void]$sql.AppendLine("COMMIT;") $tmpSql = Join-Path $env:TEMP "parser_import.sql" [System.IO.File]::WriteAllText($tmpSql, $sql.ToString()) Get-Content -Raw $tmpSql | docker exec -i $DbContainer psql -v ON_ERROR_STOP=1 -U $DbUser -d $DbName | Out-Host Write-Host "Imported maps=$($maps.Count) flags=$($mapFlags.Count) portals=$($portals.Count) npcs=$($npcs.Count) monsters=$($monsters.Count)" -ForegroundColor Green