server-master/srcs/_plugins/WingsEmu.Plugins.BasicImplementation/ServerConfigs/Persistence/ParserDataPostgresSync.cs

555 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using Npgsql;
using PhoenixLib.Logging;
using WingsAPI.Data.Drops;
using WingsAPI.Data.Shops;
using WingsEmu.DTOs.Maps;
using WingsEmu.DTOs.Recipes;
using WingsEmu.DTOs.ServerDatas;
using WingsEmu.DTOs.Shops;
namespace WingsEmu.Plugins.BasicImplementations.ServerConfigs.Persistence;
public static class ParserDataPostgresSync
{
private static bool Enabled => !string.Equals(Environment.GetEnvironmentVariable("PARSER_DB_SYNC"), "false", StringComparison.OrdinalIgnoreCase);
private static string BuildConnectionString()
{
string host = Environment.GetEnvironmentVariable("DATABASE_IP")
?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP")
?? "127.0.0.1";
string port = Environment.GetEnvironmentVariable("DATABASE_PORT")
?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PORT")
?? "5432";
string db = Environment.GetEnvironmentVariable("DATABASE_NAME")
?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_NAME")
?? "game";
string user = Environment.GetEnvironmentVariable("DATABASE_USER")
?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER")
?? "postgres";
string pass = Environment.GetEnvironmentVariable("DATABASE_PASSWORD")
?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD")
?? "postgres";
return $"Host={host};Port={port};Database={db};Username={user};Password={pass}";
}
public static void SyncMapsAndPortals(IReadOnlyList<ServerMapDto> maps, IReadOnlyList<PortalDTO> portals)
{
if (!Enabled)
{
return;
}
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"
CREATE TABLE IF NOT EXISTS server_maps (
map_id INT PRIMARY KEY,
map_vnum INT,
map_name_id INT,
map_music_id INT
);
CREATE TABLE IF NOT EXISTS server_map_flags (
map_id INT NOT NULL,
flag TEXT NOT NULL,
PRIMARY KEY(map_id, flag)
);
CREATE TABLE IF NOT EXISTS 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
);", conn, tx))
{
cmd.ExecuteNonQuery();
}
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE server_map_flags, server_maps, map_portals RESTART IDENTITY;", conn, tx))
{
cmd.ExecuteNonQuery();
}
foreach (ServerMapDto map in maps)
{
using var cmd = new NpgsqlCommand("INSERT INTO server_maps(map_id,map_vnum,map_name_id,map_music_id) VALUES (@id,@vnum,@name,@music);", conn, tx);
cmd.Parameters.AddWithValue("id", map.Id);
cmd.Parameters.AddWithValue("vnum", map.MapVnum);
cmd.Parameters.AddWithValue("name", map.NameId);
cmd.Parameters.AddWithValue("music", map.MusicId);
cmd.ExecuteNonQuery();
if (map.Flags == null)
{
continue;
}
foreach (MapFlags flag in map.Flags)
{
using var fcmd = new NpgsqlCommand("INSERT INTO server_map_flags(map_id,flag) VALUES (@id,@flag);", conn, tx);
fcmd.Parameters.AddWithValue("id", map.Id);
fcmd.Parameters.AddWithValue("flag", flag.ToString());
fcmd.ExecuteNonQuery();
}
}
foreach (PortalDTO portal in portals)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO map_portals(destination_map_id,destination_map_x,destination_map_y,source_map_id,source_map_x,source_map_y,type)
VALUES (@dmid,@dmx,@dmy,@smid,@smx,@smy,@type);", conn, tx);
cmd.Parameters.AddWithValue("dmid", portal.DestinationMapId);
cmd.Parameters.AddWithValue("dmx", portal.DestinationX);
cmd.Parameters.AddWithValue("dmy", portal.DestinationY);
cmd.Parameters.AddWithValue("smid", portal.SourceMapId);
cmd.Parameters.AddWithValue("smx", portal.SourceX);
cmd.Parameters.AddWithValue("smy", portal.SourceY);
cmd.Parameters.AddWithValue("type", portal.Type);
cmd.ExecuteNonQuery();
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced maps={maps.Count} portals={portals.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync maps/portals", ex);
}
}
public static void SyncMapNpcs(IReadOnlyList<MapNpcDTO> npcs)
{
if (!Enabled)
{
return;
}
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"
CREATE TABLE IF NOT EXISTS 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
);", conn, tx))
{
cmd.ExecuteNonQuery();
}
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE map_npcs RESTART IDENTITY;", conn, tx))
{
cmd.ExecuteNonQuery();
}
foreach (MapNpcDTO npc in npcs)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO map_npcs(map_npc_id,map_id,vnum,pos_x,pos_y,effect_vnum,effect_delay,dialog_id,direction_facing)
VALUES (@id,@map,@vnum,@x,@y,@ev,@ed,@dialog,@dir);", conn, tx);
cmd.Parameters.AddWithValue("id", npc.Id);
cmd.Parameters.AddWithValue("map", npc.MapId);
cmd.Parameters.AddWithValue("vnum", npc.NpcVNum);
cmd.Parameters.AddWithValue("x", npc.MapX);
cmd.Parameters.AddWithValue("y", npc.MapY);
cmd.Parameters.AddWithValue("ev", npc.Effect);
cmd.Parameters.AddWithValue("ed", npc.EffectDelay);
cmd.Parameters.AddWithValue("dialog", npc.Dialog);
cmd.Parameters.AddWithValue("dir", npc.Direction);
cmd.ExecuteNonQuery();
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced map_npcs={npcs.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync map_npcs", ex);
}
}
public static void SyncMapMonsters(IReadOnlyList<MapMonsterDTO> monsters)
{
if (!Enabled)
{
return;
}
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"
CREATE TABLE IF NOT EXISTS map_monsters (
id BIGSERIAL PRIMARY KEY,
map_monster_id INT,
map_id INT,
vnum INT,
map_x INT,
map_y INT,
direction INT,
can_move BOOLEAN
);
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS direction INT;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS can_move BOOLEAN;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS map_monster_id INT;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS map_id INT;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS vnum INT;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS map_x INT;
ALTER TABLE map_monsters ADD COLUMN IF NOT EXISTS map_y INT;", conn, tx))
{
cmd.ExecuteNonQuery();
}
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE map_monsters RESTART IDENTITY;", conn, tx))
{
cmd.ExecuteNonQuery();
}
foreach (MapMonsterDTO monster in monsters)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO map_monsters(map_monster_id,map_id,vnum,map_x,map_y,direction,can_move)
VALUES (@id,@map,@vnum,@x,@y,@dir,@move);", conn, tx);
cmd.Parameters.AddWithValue("id", monster.Id);
cmd.Parameters.AddWithValue("map", monster.MapId);
cmd.Parameters.AddWithValue("vnum", monster.MonsterVNum);
cmd.Parameters.AddWithValue("x", monster.MapX);
cmd.Parameters.AddWithValue("y", monster.MapY);
cmd.Parameters.AddWithValue("dir", monster.Direction);
cmd.Parameters.AddWithValue("move", monster.IsMoving);
cmd.ExecuteNonQuery();
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced map_monsters={monsters.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync map_monsters", ex);
}
}
public static void SyncTeleporters(IReadOnlyList<TeleporterDTO> teleporters)
{
if (!Enabled) return;
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"CREATE TABLE IF NOT EXISTS map_teleporters (
id BIGSERIAL PRIMARY KEY,
teleporter_id INT,
idx INT,
type INT,
map_id INT,
map_npc_id INT,
map_x INT,
map_y INT
);", conn, tx)) cmd.ExecuteNonQuery();
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE map_teleporters RESTART IDENTITY;", conn, tx)) cmd.ExecuteNonQuery();
foreach (var t in teleporters)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO map_teleporters(teleporter_id,idx,type,map_id,map_npc_id,map_x,map_y)
VALUES (@id,@idx,@type,@map,@npc,@x,@y);", conn, tx);
cmd.Parameters.AddWithValue("id", t.Id);
cmd.Parameters.AddWithValue("idx", t.Index);
cmd.Parameters.AddWithValue("type", (int)t.Type);
cmd.Parameters.AddWithValue("map", t.MapId);
cmd.Parameters.AddWithValue("npc", t.MapNpcId);
cmd.Parameters.AddWithValue("x", t.MapX);
cmd.Parameters.AddWithValue("y", t.MapY);
cmd.ExecuteNonQuery();
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced map_teleporters={teleporters.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync map_teleporters", ex);
}
}
public static void SyncShops(IReadOnlyList<ShopDTO> shops)
{
if (!Enabled) return;
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"CREATE TABLE IF NOT EXISTS shops (
id BIGSERIAL PRIMARY KEY,
map_npc_id INT,
menu_type INT,
shop_type INT,
name TEXT
);
CREATE TABLE IF NOT EXISTS shop_items (
id BIGSERIAL PRIMARY KEY,
map_npc_id INT,
slot INT,
color INT,
item_vnum INT,
rare INT,
type INT,
upgrade INT,
price INT
);
CREATE TABLE IF NOT EXISTS shop_skills (
id BIGSERIAL PRIMARY KEY,
map_npc_id INT,
skill_vnum INT,
slot INT,
type INT
);", conn, tx)) cmd.ExecuteNonQuery();
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE shops, shop_items, shop_skills RESTART IDENTITY;", conn, tx)) cmd.ExecuteNonQuery();
foreach (var s in shops)
{
using (var cmd = new NpgsqlCommand("INSERT INTO shops(map_npc_id,menu_type,shop_type,name) VALUES (@npc,@menu,@type,@name);", conn, tx))
{
cmd.Parameters.AddWithValue("npc", s.MapNpcId);
cmd.Parameters.AddWithValue("menu", s.MenuType);
cmd.Parameters.AddWithValue("type", s.ShopType);
cmd.Parameters.AddWithValue("name", (object?)s.Name ?? DBNull.Value);
cmd.ExecuteNonQuery();
}
if (s.Items != null)
{
foreach (ShopItemDTO it in s.Items)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO shop_items(map_npc_id,slot,color,item_vnum,rare,type,upgrade,price)
VALUES (@npc,@slot,@color,@vnum,@rare,@type,@upg,@price);", conn, tx);
cmd.Parameters.AddWithValue("npc", s.MapNpcId);
cmd.Parameters.AddWithValue("slot", it.Slot);
cmd.Parameters.AddWithValue("color", it.Color);
cmd.Parameters.AddWithValue("vnum", it.ItemVNum);
cmd.Parameters.AddWithValue("rare", it.Rare);
cmd.Parameters.AddWithValue("type", it.Type);
cmd.Parameters.AddWithValue("upg", it.Upgrade);
cmd.Parameters.AddWithValue("price", (object?)it.Price ?? DBNull.Value);
cmd.ExecuteNonQuery();
}
}
if (s.Skills != null)
{
foreach (ShopSkillDTO sk in s.Skills)
{
using var cmd = new NpgsqlCommand("INSERT INTO shop_skills(map_npc_id,skill_vnum,slot,type) VALUES (@npc,@skill,@slot,@type);", conn, tx);
cmd.Parameters.AddWithValue("npc", s.MapNpcId);
cmd.Parameters.AddWithValue("skill", sk.SkillVNum);
cmd.Parameters.AddWithValue("slot", sk.Slot);
cmd.Parameters.AddWithValue("type", sk.Type);
cmd.ExecuteNonQuery();
}
}
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced shops={shops.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync shops", ex);
}
}
public static void SyncItemBoxes(IReadOnlyList<ItemBoxDto> itemBoxes)
{
if (!Enabled) return;
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"CREATE TABLE IF NOT EXISTS item_boxes (
id BIGSERIAL PRIMARY KEY,
item_vnum INT,
box_type INT,
min_rewards INT,
max_rewards INT,
shows_raid_panel BOOLEAN
);
CREATE TABLE IF NOT EXISTS item_box_items (
id BIGSERIAL PRIMARY KEY,
item_vnum INT,
probability INT,
min_original_rare INT,
max_original_rare INT,
generated_amount INT,
generated_vnum INT,
generated_random_rarity BOOLEAN,
generated_upgrade INT
);", conn, tx)) cmd.ExecuteNonQuery();
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE item_boxes, item_box_items RESTART IDENTITY;", conn, tx)) cmd.ExecuteNonQuery();
foreach (var b in itemBoxes)
{
using (var cmd = new NpgsqlCommand(@"INSERT INTO item_boxes(item_vnum,box_type,min_rewards,max_rewards,shows_raid_panel)
VALUES (@vnum,@type,@min,@max,@raid);", conn, tx))
{
cmd.Parameters.AddWithValue("vnum", b.Id);
cmd.Parameters.AddWithValue("type", (int)b.ItemBoxType);
cmd.Parameters.AddWithValue("min", (object?)b.MinimumRewards ?? DBNull.Value);
cmd.Parameters.AddWithValue("max", (object?)b.MaximumRewards ?? DBNull.Value);
cmd.Parameters.AddWithValue("raid", b.ShowsRaidBoxPanelOnOpen);
cmd.ExecuteNonQuery();
}
if (b.Items == null) continue;
foreach (var it in b.Items)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO item_box_items(item_vnum,probability,min_original_rare,max_original_rare,generated_amount,generated_vnum,generated_random_rarity,generated_upgrade)
VALUES (@vnum,@prob,@minr,@maxr,@amt,@gv,@rr,@upg);", conn, tx);
cmd.Parameters.AddWithValue("vnum", b.Id);
cmd.Parameters.AddWithValue("prob", it.Probability);
cmd.Parameters.AddWithValue("minr", it.MinimumOriginalItemRare);
cmd.Parameters.AddWithValue("maxr", it.MaximumOriginalItemRare);
cmd.Parameters.AddWithValue("amt", it.ItemGeneratedAmount);
cmd.Parameters.AddWithValue("gv", it.ItemGeneratedVNum);
cmd.Parameters.AddWithValue("rr", it.ItemGeneratedRandomRarity);
cmd.Parameters.AddWithValue("upg", it.ItemGeneratedUpgrade);
cmd.ExecuteNonQuery();
}
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced item_boxes={itemBoxes.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync item_boxes", ex);
}
}
public static void SyncDrops(IReadOnlyList<DropDTO> drops)
{
if (!Enabled) return;
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"CREATE TABLE IF NOT EXISTS drops (
id BIGSERIAL PRIMARY KEY,
drop_id INT,
amount INT,
drop_chance INT,
item_vnum INT,
map_id INT,
monster_vnum INT,
race_type INT,
race_sub_type INT
);", conn, tx)) cmd.ExecuteNonQuery();
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE drops RESTART IDENTITY;", conn, tx)) cmd.ExecuteNonQuery();
foreach (var d in drops)
{
using var cmd = new NpgsqlCommand(@"INSERT INTO drops(drop_id,amount,drop_chance,item_vnum,map_id,monster_vnum,race_type,race_sub_type)
VALUES (@id,@amount,@chance,@item,@map,@mon,@race,@subrace);", conn, tx);
cmd.Parameters.AddWithValue("id", d.Id);
cmd.Parameters.AddWithValue("amount", d.Amount);
cmd.Parameters.AddWithValue("chance", d.DropChance);
cmd.Parameters.AddWithValue("item", d.ItemVNum);
cmd.Parameters.AddWithValue("map", (object?)d.MapId ?? DBNull.Value);
cmd.Parameters.AddWithValue("mon", (object?)d.MonsterVNum ?? DBNull.Value);
cmd.Parameters.AddWithValue("race", (object?)d.RaceType ?? DBNull.Value);
cmd.Parameters.AddWithValue("subrace", (object?)d.RaceSubType ?? DBNull.Value);
cmd.ExecuteNonQuery();
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced drops={drops.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync drops", ex);
}
}
public static void SyncRecipes(IReadOnlyList<RecipeDTO> recipes)
{
if (!Enabled) return;
try
{
using var conn = new NpgsqlConnection(BuildConnectionString());
conn.Open();
using var tx = conn.BeginTransaction();
using (var cmd = new NpgsqlCommand(@"CREATE TABLE IF NOT EXISTS recipes (
id BIGSERIAL PRIMARY KEY,
recipe_id INT,
amount INT,
producer_map_npc_id INT,
produced_item_vnum INT,
producer_item_vnum INT,
producer_npc_vnum INT
);
CREATE TABLE IF NOT EXISTS recipe_items (
id BIGSERIAL PRIMARY KEY,
recipe_id INT,
slot INT,
item_vnum INT,
amount INT
);", conn, tx)) cmd.ExecuteNonQuery();
using (var cmd = new NpgsqlCommand("TRUNCATE TABLE recipes, recipe_items RESTART IDENTITY;", conn, tx)) cmd.ExecuteNonQuery();
foreach (var r in recipes)
{
using (var cmd = new NpgsqlCommand(@"INSERT INTO recipes(recipe_id,amount,producer_map_npc_id,produced_item_vnum,producer_item_vnum,producer_npc_vnum)
VALUES (@id,@amount,@mapNpc,@produced,@prodItem,@prodNpc);", conn, tx))
{
cmd.Parameters.AddWithValue("id", r.Id);
cmd.Parameters.AddWithValue("amount", r.Amount);
cmd.Parameters.AddWithValue("mapNpc", (object?)r.ProducerMapNpcId ?? DBNull.Value);
cmd.Parameters.AddWithValue("produced", r.ProducedItemVnum);
cmd.Parameters.AddWithValue("prodItem", (object?)r.ProducerItemVnum ?? DBNull.Value);
cmd.Parameters.AddWithValue("prodNpc", (object?)r.ProducerNpcVnum ?? DBNull.Value);
cmd.ExecuteNonQuery();
}
if (r.Items == null) continue;
foreach (RecipeItemDTO it in r.Items)
{
using var cmd = new NpgsqlCommand("INSERT INTO recipe_items(recipe_id,slot,item_vnum,amount) VALUES (@id,@slot,@item,@amount);", conn, tx);
cmd.Parameters.AddWithValue("id", r.Id);
cmd.Parameters.AddWithValue("slot", it.Slot);
cmd.Parameters.AddWithValue("item", it.ItemVNum);
cmd.Parameters.AddWithValue("amount", it.Amount);
cmd.ExecuteNonQuery();
}
}
tx.Commit();
Log.Info($"[PARSER_DB_SYNC] Synced recipes={recipes.Count}");
}
catch (Exception ex)
{
Log.Error("[PARSER_DB_SYNC] Failed to sync recipes", ex);
}
}
}