using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Npgsql; using PhoenixLib.Logging; using WingsAPI.Data.GameData; using WingsEmu.DTOs.Maps; namespace Plugin.ResourceLoader.Loaders { public class MapResourceFileLoader : IResourceLoader { private readonly ResourceLoadingConfiguration _config; public MapResourceFileLoader(ResourceLoadingConfiguration config) => _config = config; public async Task> LoadAsync() { string filePath = Path.Combine(_config.GameDataPath, "MapIDData.dat"); if (!File.Exists(filePath)) { bool dbFirst = string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("RESOURCE_DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase); if (dbFirst) { TryHydrateDatFileFromDatabase("MapIDData.dat", filePath); TryHydrateMapFilesFromDatabase(); } } if (!File.Exists(filePath)) { throw new FileNotFoundException($"{filePath} should be present"); } var maps = new List(); var dictionaryId = new Dictionary(); int i = 0; using (var mapIdStream = new StreamReader(filePath, Encoding.GetEncoding(1252))) { string line; while ((line = await mapIdStream.ReadLineAsync()) != null) { string[] values = line.Split(' '); if (values.Length <= 1) { continue; } if (!int.TryParse(values[0], out int mapId)) { continue; } if (!dictionaryId.ContainsKey(mapId)) { dictionaryId.Add(mapId, values[4]); } } } if (!Directory.Exists(_config.GameMapsPath) || !new DirectoryInfo(_config.GameMapsPath).GetFiles().Any()) { bool dbFirst = string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("RESOURCE_DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase); if (dbFirst) { TryHydrateMapFilesFromDatabase(); } } foreach (FileInfo file in new DirectoryInfo(_config.GameMapsPath).GetFiles()) { string name = string.Empty; if (dictionaryId.TryGetValue(int.Parse(file.Name), out string value)) { name = value; } byte[] data = await File.ReadAllBytesAsync(file.FullName); short width = BitConverter.ToInt16(data, 0); short height = BitConverter.ToInt16(data, 2); maps.Add(new MapDataDTO { Id = short.Parse(file.Name), Name = name, Width = width, Height = height, Grid = data.Skip(4).ToArray() }); i++; } Log.Info($"[RESOURCE_LOADER] {maps.Count} Maps loaded"); return maps; } private void TryHydrateDatFileFromDatabase(string datFileName, string targetPath) { try { using var conn = OpenConnection(); using var cmd = new NpgsqlCommand("SELECT content FROM resource_files WHERE category='dat' AND relative_path=@path LIMIT 1;", conn); cmd.Parameters.AddWithValue("path", $"dat/{datFileName}"); object result = cmd.ExecuteScalar(); if (result is byte[] bytes && bytes.Length > 0) { Directory.CreateDirectory(Path.GetDirectoryName(targetPath) ?? _config.GameDataPath); File.WriteAllBytes(targetPath, bytes); Log.Info($"[DB_FIRST] Hydrated {datFileName} from resource_files"); } } catch (Exception ex) { Log.Error($"[DB_FIRST] Could not hydrate {datFileName} from database", ex); } } private void TryHydrateMapFilesFromDatabase() { try { Directory.CreateDirectory(_config.GameMapsPath); using var conn = OpenConnection(); using var cmd = new NpgsqlCommand("SELECT relative_path, content FROM resource_files WHERE category='maps';", conn); using var reader = cmd.ExecuteReader(); int count = 0; while (reader.Read()) { string relative = reader.GetString(0).Replace('/', Path.DirectorySeparatorChar); byte[] content = (byte[])reader[1]; string path = Path.Combine(_config.ResourcePaths, relative); Directory.CreateDirectory(Path.GetDirectoryName(path) ?? _config.GameMapsPath); File.WriteAllBytes(path, content); count++; } if (count > 0) { Log.Info($"[DB_FIRST] Hydrated {count} map files from resource_files"); } } catch (Exception ex) { Log.Error("[DB_FIRST] Could not hydrate map files from database", ex); } } private static NpgsqlConnection OpenConnection() { 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"; var conn = new NpgsqlConnection($"Host={host};Port={port};Database={db};Username={user};Password={pass}"); conn.Open(); return conn; } } }