2020-05-08 01:44:21 -04:00
using System ;
2023-12-21 14:15:38 -05:00
using System.Collections.Generic ;
2020-05-08 01:44:21 -04:00
using System.IO ;
using System.IO.Compression ;
using System.Linq ;
2023-12-21 14:15:38 -05:00
using System.Text.Json ;
2020-05-08 01:44:21 -04:00
using System.Threading ;
using ACE.Common ;
namespace ACE.Server
{
partial class Program
{
private static void CheckForWorldDatabaseUpdate ( )
{
log . Info ( $"Automatic World Database Update started..." ) ;
2020-08-27 16:45:41 -04:00
try
{
var worldDb = new Database . WorldDatabase ( ) ;
var currentVersion = worldDb . GetVersion ( ) ;
log . Info ( $"Current World Database version: Base - {currentVersion.BaseVersion} | Patch - {currentVersion.PatchVersion}" ) ;
2020-05-08 01:44:21 -04:00
2022-05-07 11:30:42 -07:00
var url = "https://api.github.com/repos/ACEmulator/ACE-World-16PY-Patches/releases/latest" ;
2020-05-08 01:44:21 -04:00
2022-07-13 12:30:32 -04:00
using var client = new WebClient ( ) ;
var html = client . GetStringFromURL ( url ) . Result ;
2023-12-21 14:15:38 -05:00
var json = JsonSerializer . Deserialize < JsonElement > ( html ) ;
string tag = json . GetProperty ( "tag_name" ) . GetString ( ) ;
string dbURL = json . GetProperty ( "assets" ) [ 0 ] . GetProperty ( "browser_download_url" ) . GetString ( ) ;
string dbFileName = json . GetProperty ( "assets" ) [ 0 ] . GetProperty ( "name" ) . GetString ( ) ;
2020-05-08 01:44:21 -04:00
2020-08-27 16:45:41 -04:00
if ( currentVersion . PatchVersion ! = tag )
{
2020-08-31 13:06:15 -04:00
var patchVersionSplit = currentVersion . PatchVersion . Split ( "." ) ;
var tagSplit = tag . Split ( "." ) ;
int . TryParse ( patchVersionSplit [ 0 ] , out var patchMajor ) ;
int . TryParse ( patchVersionSplit [ 1 ] , out var patchMinor ) ;
int . TryParse ( patchVersionSplit [ 2 ] , out var patchBuild ) ;
int . TryParse ( tagSplit [ 0 ] , out var tagMajor ) ;
int . TryParse ( tagSplit [ 1 ] , out var tagMinor ) ;
int . TryParse ( tagSplit [ 2 ] , out var tagBuild ) ;
if ( tagMajor > patchMajor | | tagMinor > patchMinor | | ( tagBuild > patchBuild & & patchBuild ! = 0 ) )
{
log . Info ( $"Latest patch version is {tag} -- Update Required!" ) ;
UpdateToLatestWorldDatabase ( dbURL , dbFileName ) ;
var newVersion = worldDb . GetVersion ( ) ;
log . Info ( $"Updated World Database version: Base - {newVersion.BaseVersion} | Patch - {newVersion.PatchVersion}" ) ;
}
else
{
log . Info ( $"Latest patch version is {tag} -- No Update Required!" ) ;
}
2020-08-27 16:45:41 -04:00
}
else
{
log . Info ( $"Latest patch version is {tag} -- No Update Required!" ) ;
}
2020-05-08 01:44:21 -04:00
}
2020-08-27 16:45:41 -04:00
catch ( Exception ex )
2020-05-08 01:44:21 -04:00
{
2020-08-27 16:45:41 -04:00
log . Info ( $"Unable to continue with Automatic World Database Update due to the following error: {ex}" ) ;
2020-05-08 01:44:21 -04:00
}
log . Info ( $"Automatic World Database Update complete." ) ;
}
private static void UpdateToLatestWorldDatabase ( string dbURL , string dbFileName )
{
Console . WriteLine ( ) ;
if ( IsRunningInContainer )
{
Console . WriteLine ( " " ) ;
Console . WriteLine ( "This process will take a while, depending on many factors, and may look stuck while reading and importing the world database, please be patient! " ) ;
Console . WriteLine ( " " ) ;
}
Console . Write ( $"Downloading {dbFileName} .... " ) ;
2022-07-13 12:30:32 -04:00
using var client = new WebClient ( ) ;
try
2020-05-08 01:44:21 -04:00
{
2022-07-13 12:30:32 -04:00
var dlTask = client . DownloadFile ( dbURL , dbFileName ) ;
dlTask . Wait ( ) ;
}
catch
{
Console . Write ( $"Download for {dbFileName} failed!" ) ;
return ;
2020-05-08 01:44:21 -04:00
}
Console . WriteLine ( "download complete!" ) ;
Console . Write ( $"Extracting {dbFileName} .... " ) ;
ZipFile . ExtractToDirectory ( dbFileName , "." , true ) ;
Console . WriteLine ( "extraction complete!" ) ;
Console . Write ( $"Deleting {dbFileName} .... " ) ;
File . Delete ( dbFileName ) ;
Console . WriteLine ( "Deleted!" ) ;
var sqlFile = dbFileName . Substring ( 0 , dbFileName . Length - 4 ) ;
Console . Write ( $"Importing {sqlFile} into SQL server at {ConfigManager.Config.MySql.World.Host}:{ConfigManager.Config.MySql.World.Port} (This will take a while, please be patient) .... " ) ;
using ( var sr = File . OpenText ( sqlFile ) )
{
2023-07-20 02:05:18 -04:00
var sqlConnect = new MySqlConnector . MySqlConnection ( $"server={ConfigManager.Config.MySql.World.Host};port={ConfigManager.Config.MySql.World.Port};user={ConfigManager.Config.MySql.World.Username};password={ConfigManager.Config.MySql.World.Password};{ConfigManager.Config.MySql.World.ConnectionOptions}" ) ;
2020-05-08 01:44:21 -04:00
var line = string . Empty ;
var completeSQLline = string . Empty ;
2021-04-03 09:43:02 -07:00
var dbname = ConfigManager . Config . MySql . World . Database ;
2020-05-08 01:44:21 -04:00
while ( ( line = sr . ReadLine ( ) ) ! = null )
{
2021-04-03 09:43:02 -07:00
line = line . Replace ( "ace_world" , dbname ) ;
2020-05-08 01:44:21 -04:00
//do minimal amount of work here
if ( line . EndsWith ( ";" ) )
{
completeSQLline + = line + Environment . NewLine ;
2023-07-01 01:58:55 -06:00
var script = new MySqlConnector . MySqlCommand ( completeSQLline , sqlConnect ) ;
2020-05-08 01:44:21 -04:00
try
{
2023-07-01 01:58:55 -06:00
ExecuteScript ( script ) ;
2020-05-08 01:44:21 -04:00
}
2023-07-01 01:58:55 -06:00
catch ( MySqlConnector . MySqlException )
2020-05-08 01:44:21 -04:00
{
}
completeSQLline = string . Empty ;
}
else
completeSQLline + = line + Environment . NewLine ;
}
2023-07-01 01:58:55 -06:00
CleanupConnection ( sqlConnect ) ;
2020-05-08 01:44:21 -04:00
}
Console . WriteLine ( " complete!" ) ;
Console . Write ( $"Deleting {sqlFile} .... " ) ;
File . Delete ( sqlFile ) ;
Console . WriteLine ( "Deleted!" ) ;
}
private static string GetContentFolder ( )
{
2023-07-20 02:05:18 -04:00
var sqlConnect = new MySqlConnector . MySqlConnection ( $"server={ConfigManager.Config.MySql.Shard.Host};port={ConfigManager.Config.MySql.Shard.Port};user={ConfigManager.Config.MySql.Shard.Username};password={ConfigManager.Config.MySql.Shard.Password};database={ConfigManager.Config.MySql.Shard.Database};{ConfigManager.Config.MySql.Shard.ConnectionOptions}" ) ;
2020-05-08 01:44:21 -04:00
var sqlQuery = "SELECT `value` FROM config_properties_string WHERE `key` = 'content_folder';" ;
2023-07-01 01:58:55 -06:00
var sqlCommand = new MySqlConnector . MySqlCommand ( sqlQuery , sqlConnect ) ;
2020-05-08 01:44:21 -04:00
sqlConnect . Open ( ) ;
var sqlReader = sqlCommand . ExecuteReader ( ) ;
2020-05-08 12:23:03 -04:00
var content_folder = "" ;
if ( sqlReader . HasRows )
{
while ( sqlReader . Read ( ) )
{
content_folder = sqlReader . GetString ( 0 ) ;
break ;
}
}
else
content_folder = @".\Content" ;
2020-05-08 01:44:21 -04:00
sqlReader . Close ( ) ;
sqlCommand . Connection . Close ( ) ;
// handle relative path
if ( content_folder . StartsWith ( "." ) )
{
var cwd = Directory . GetCurrentDirectory ( ) + Path . DirectorySeparatorChar ;
content_folder = cwd + content_folder ;
}
return content_folder ;
}
private static void AutoApplyWorldCustomizations ( )
{
2020-05-11 20:51:51 -07:00
var content_folders_search_option = ConfigManager . Config . Offline . RecurseWorldCustomizationPaths ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly ;
var content_folders = new List < string > { GetContentFolder ( ) } ;
content_folders . AddRange ( ConfigManager . Config . Offline . WorldCustomizationAddedPaths ) ;
content_folders . Sort ( ) ;
2020-05-08 01:44:21 -04:00
Console . WriteLine ( $"Searching for World Customization SQL scripts .... " ) ;
2020-05-11 20:51:51 -07:00
content_folders . ForEach ( path = >
2020-05-08 01:44:21 -04:00
{
2020-05-11 20:51:51 -07:00
var contentDI = new DirectoryInfo ( path ) ;
if ( contentDI . Exists )
2020-05-08 01:44:21 -04:00
{
2023-07-28 12:23:25 -05:00
Console . WriteLine ( $"Searching for SQL files within {path} .... " ) ;
2020-05-08 01:44:21 -04:00
2023-07-20 02:05:18 -04:00
var sqlConnect = new MySqlConnector . MySqlConnection ( $"server={ConfigManager.Config.MySql.World.Host};port={ConfigManager.Config.MySql.World.Port};user={ConfigManager.Config.MySql.World.Username};password={ConfigManager.Config.MySql.World.Password};database={ConfigManager.Config.MySql.World.Database};{ConfigManager.Config.MySql.World.ConnectionOptions}" ) ;
2020-05-11 20:51:51 -07:00
foreach ( var file in contentDI . GetFiles ( "*.sql" , content_folders_search_option ) . OrderBy ( f = > f . FullName ) )
2020-05-08 01:44:21 -04:00
{
2020-05-11 20:51:51 -07:00
Console . Write ( $"Found {file.FullName} .... " ) ;
var sqlDBFile = File . ReadAllText ( file . FullName ) ;
2021-04-13 13:58:27 -04:00
sqlDBFile = sqlDBFile . Replace ( "ace_world" , ConfigManager . Config . MySql . World . Database ) ;
2023-07-01 01:58:55 -06:00
var script = new MySqlConnector . MySqlCommand ( sqlDBFile , sqlConnect ) ;
2020-05-11 20:51:51 -07:00
Console . Write ( $"Importing into World database on SQL server at {ConfigManager.Config.MySql.World.Host}:{ConfigManager.Config.MySql.World.Port} .... " ) ;
try
{
2023-07-01 01:58:55 -06:00
ExecuteScript ( script ) ;
2020-05-11 20:51:51 -07:00
//Console.Write($" {count} database records affected ....");
Console . WriteLine ( " complete!" ) ;
}
2023-07-01 01:58:55 -06:00
catch ( MySqlConnector . MySqlException ex )
2020-05-11 20:51:51 -07:00
{
Console . WriteLine ( $" error!" ) ;
Console . WriteLine ( $" Unable to apply patch due to following exception: {ex}" ) ;
}
2020-05-08 01:44:21 -04:00
}
2023-07-01 01:58:55 -06:00
CleanupConnection ( sqlConnect ) ;
2020-05-08 01:44:21 -04:00
}
2020-05-11 20:51:51 -07:00
} ) ;
2020-05-08 01:44:21 -04:00
Console . WriteLine ( $"World Customization SQL scripts import complete!" ) ;
}
private static void AutoApplyDatabaseUpdates ( )
{
log . Info ( $"Automatic Database Patching started..." ) ;
Thread . Sleep ( 1000 ) ;
2022-01-19 12:02:23 -05:00
PatchDatabase ( "Authentication" , ConfigManager . Config . MySql . Authentication . Host , ConfigManager . Config . MySql . Authentication . Port , ConfigManager . Config . MySql . Authentication . Username , ConfigManager . Config . MySql . Authentication . Password , ConfigManager . Config . MySql . Authentication . Database , ConfigManager . Config . MySql . Shard . Database , ConfigManager . Config . MySql . World . Database ) ;
PatchDatabase ( "Shard" , ConfigManager . Config . MySql . Shard . Host , ConfigManager . Config . MySql . Shard . Port , ConfigManager . Config . MySql . Shard . Username , ConfigManager . Config . MySql . Shard . Password , ConfigManager . Config . MySql . Authentication . Database , ConfigManager . Config . MySql . Shard . Database , ConfigManager . Config . MySql . World . Database ) ;
PatchDatabase ( "World" , ConfigManager . Config . MySql . World . Host , ConfigManager . Config . MySql . World . Port , ConfigManager . Config . MySql . World . Username , ConfigManager . Config . MySql . World . Password , ConfigManager . Config . MySql . Authentication . Database , ConfigManager . Config . MySql . Shard . Database , ConfigManager . Config . MySql . World . Database ) ;
2020-05-08 01:44:21 -04:00
Thread . Sleep ( 1000 ) ;
log . Info ( $"Automatic Database Patching complete." ) ;
}
2022-01-19 12:02:23 -05:00
private static void PatchDatabase ( string dbType , string host , uint port , string username , string password , string authDB , string shardDB , string worldDB )
2020-05-08 01:44:21 -04:00
{
var updatesPath = $"DatabaseSetupScripts{Path.DirectorySeparatorChar}Updates{Path.DirectorySeparatorChar}{dbType}" ;
var updatesFile = $"{updatesPath}{Path.DirectorySeparatorChar}applied_updates.txt" ;
2022-08-10 09:37:40 -05:00
if ( ! Directory . Exists ( updatesPath ) )
{
// File not found in Environment.CurrentDirectory
// Lets try the ExecutingAssembly Location
var executingAssemblyLocation = System . Reflection . Assembly . GetExecutingAssembly ( ) . Location ;
var directoryName = Path . GetFullPath ( Path . GetDirectoryName ( executingAssemblyLocation ) ) ;
updatesPath = Path . Combine ( directoryName , $"DatabaseSetupScripts{Path.DirectorySeparatorChar}Updates{Path.DirectorySeparatorChar}{dbType}" ) ;
if ( ! Directory . Exists ( updatesPath ) )
{
Console . WriteLine ( $" error!" ) ;
Console . WriteLine ( $" Unable to locate updates directory" ) ;
}
else
{
updatesFile = $"{updatesPath}{Path.DirectorySeparatorChar}applied_updates.txt" ;
}
}
2020-05-08 01:44:21 -04:00
var appliedUpdates = Array . Empty < string > ( ) ;
var containerUpdatesFile = $"/ace/Config/{dbType}_applied_updates.txt" ;
if ( IsRunningInContainer & & File . Exists ( containerUpdatesFile ) )
File . Copy ( containerUpdatesFile , updatesFile , true ) ;
if ( File . Exists ( updatesFile ) )
appliedUpdates = File . ReadAllLines ( updatesFile ) ;
Console . WriteLine ( $"Searching for {dbType} update SQL scripts .... " ) ;
foreach ( var file in new DirectoryInfo ( updatesPath ) . GetFiles ( "*.sql" ) . OrderBy ( f = > f . Name ) )
{
if ( appliedUpdates . Contains ( file . Name ) )
continue ;
Console . Write ( $"Found {file.Name} .... " ) ;
var sqlDBFile = File . ReadAllText ( file . FullName ) ;
2022-01-19 12:02:23 -05:00
var database = "" ;
2021-04-13 13:58:27 -04:00
switch ( dbType )
{
case "Authentication" :
2022-01-19 12:02:23 -05:00
database = authDB ;
2021-04-13 13:58:27 -04:00
break ;
case "Shard" :
2022-01-19 12:02:23 -05:00
database = shardDB ;
2021-04-13 13:58:27 -04:00
break ;
case "World" :
2022-01-19 12:02:23 -05:00
database = worldDB ;
2021-04-13 13:58:27 -04:00
break ;
}
2023-07-01 01:58:55 -06:00
var sqlConnect = new MySqlConnector . MySqlConnection ( $"server={host};port={port};user={username};password={password};database={database};DefaultCommandTimeout=120;SslMode=None;AllowPublicKeyRetrieval=true" ) ;
2022-01-19 12:02:23 -05:00
sqlDBFile = sqlDBFile . Replace ( "ace_auth" , authDB ) ;
sqlDBFile = sqlDBFile . Replace ( "ace_shard" , shardDB ) ;
sqlDBFile = sqlDBFile . Replace ( "ace_world" , worldDB ) ;
2023-07-01 01:58:55 -06:00
var script = new MySqlConnector . MySqlCommand ( sqlDBFile , sqlConnect ) ;
2020-05-08 01:44:21 -04:00
Console . Write ( $"Importing into {database} database on SQL server at {host}:{port} .... " ) ;
try
{
2023-07-01 01:58:55 -06:00
ExecuteScript ( script ) ;
2020-05-08 01:44:21 -04:00
//Console.Write($" {count} database records affected ....");
Console . WriteLine ( " complete!" ) ;
}
2023-07-01 01:58:55 -06:00
catch ( MySqlConnector . MySqlException ex )
2020-05-08 01:44:21 -04:00
{
Console . WriteLine ( $" error!" ) ;
Console . WriteLine ( $" Unable to apply patch due to following exception: {ex}" ) ;
}
File . AppendAllText ( updatesFile , file . Name + Environment . NewLine ) ;
2023-07-01 01:58:55 -06:00
CleanupConnection ( sqlConnect ) ;
2020-05-08 01:44:21 -04:00
}
2020-08-27 16:25:23 -04:00
if ( IsRunningInContainer & & File . Exists ( updatesFile ) )
2020-05-08 01:44:21 -04:00
File . Copy ( updatesFile , containerUpdatesFile , true ) ;
Console . WriteLine ( $"{dbType} update SQL scripts import complete!" ) ;
}
}
}