server-master/srcs/_plugins/Plugin.ResourceLoader/Services/DbFirstResourceHydratorService.cs

117 lines
5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Npgsql;
using PhoenixLib.Logging;
namespace Plugin.ResourceLoader.Services
{
public class DbFirstResourceHydratorService : IHostedService
{
private readonly ResourceLoadingConfiguration _configuration;
public DbFirstResourceHydratorService(ResourceLoadingConfiguration configuration)
{
_configuration = configuration;
}
public Task StartAsync(CancellationToken cancellationToken)
{
bool dbFirst = string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase)
|| string.Equals(Environment.GetEnvironmentVariable("RESOURCE_DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase);
if (!dbFirst)
{
return Task.CompletedTask;
}
bool strictDbOnly = string.Equals(Environment.GetEnvironmentVariable("STRICT_DB_ONLY"), "true", StringComparison.OrdinalIgnoreCase);
try
{
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";
using var conn = new NpgsqlConnection($"Host={host};Port={port};Database={db};Username={user};Password={pass}");
conn.Open();
var counters = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["dat"] = 0,
["lang"] = 0,
["maps"] = 0,
["config_scripts"] = 0
};
using var cmd = new NpgsqlCommand("SELECT category, relative_path, content FROM resource_files;", conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
string category = reader.GetString(0);
string relativePath = reader.GetString(1).Replace('/', Path.DirectorySeparatorChar);
byte[] content = (byte[])reader[2];
string? destination = category switch
{
"dat" => Path.Combine(_configuration.ResourcePaths, relativePath),
"lang" => Path.Combine(_configuration.ResourcePaths, relativePath),
"maps" => Path.Combine(_configuration.ResourcePaths, relativePath),
"config_scripts" => Path.Combine(Directory.GetCurrentDirectory(), relativePath),
_ => null
};
if (destination == null)
{
continue;
}
string? dir = Path.GetDirectoryName(destination);
if (!string.IsNullOrWhiteSpace(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllBytes(destination, content);
if (counters.ContainsKey(category))
{
counters[category]++;
}
}
if (strictDbOnly && (counters["dat"] == 0 || counters["lang"] == 0 || counters["maps"] == 0))
{
throw new InvalidOperationException("STRICT_DB_ONLY enabled but one or more required resource categories are missing in resource_files.");
}
Log.Info($"[DB_FIRST] Hydrated files from DB: dat={counters["dat"]} lang={counters["lang"]} maps={counters["maps"]} scripts={counters["config_scripts"]}");
}
catch (Exception ex)
{
Log.Error("[DB_FIRST] Failed to hydrate resources from database", ex);
if (strictDbOnly)
{
throw;
}
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}