Core/Setup

* rewritten to be able to dynamicly load it's components
   - CLISetup -> checks for UtilityScripts (config, setup, dbc reader, etc.) -> checks for SetupScripts (individual sql/file generators)
   - each step may now have a help prompt attached. If none are provided, the containing script may provide it's help.
   - all Scripts are self contained modules. No more editing of 3+ files if some component is added/removed
 * removed intermediaries FileGen & SqlGen
 * functional changes
   - allow providing CLI arguments to siteconfig and account UtilityScript and skip the interactive prompts
   - set slot for consumable enchantment items so they are filtrable
   - zones dataset is now localized and generated from GlobalStrings.lua and DungeonMap.dbc. Related data dumps removed.
   - 'aowow' and 'prQueue' executables now have shebangs

    WARNING - command line options have been renamed!
This commit is contained in:
Sarjuuk
2024-06-20 16:32:20 +02:00
parent ab4cf67e80
commit bf184e7555
149 changed files with 9642 additions and 9191 deletions

14
aowow
View File

@@ -1,12 +1,12 @@
#!/usr/bin/env php
<?php <?php
require 'includes/kernel.php'; if (PHP_SAPI !== 'cli')
if (!CLI)
die("this script must be run from CLI\n"); die("this script must be run from CLI\n");
if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__) if (PHP_SAPI === 'cli' && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
die("this script must be run from root directory\n"); die("this script must be run from the aowow root directory\n");
else
require 'setup/setup.php'; require 'includes/kernel.php';
require 'setup/setup.php';
?> ?>

View File

@@ -1,46 +0,0 @@
Mapper.multiLevelZones = {
206: ['206-1', '206-2', '206-3'],
209: ['209-1', '209-2', '209-3', '209-4', '209-5', '209-6', '209-7'],
616: ['616-1', '616_1', '616_2'],
719: ['719-1', '719-2', '719-3'],
721: ['721-1', '721-2', '721-3', '721-4'],
796: ['796-1', '796-2', '796-3', '796-4'],
1196: ['1196-1', '1196-2'],
1337: ['1337-1', '1337-2'],
1581: ['1581-1', '1581-2'],
1583: ['1583-1', '1583-2', '1583-3', '1583-4', '1583-5', '1583-6', '1583-7'],
1584: ['1584-1', '1584-2'],
2017: ['2017-1', '2017-2'],
2057: ['2057-1', '2057-2', '2057-3', '2057-4'],
2100: ['2100-1', '2100-2'],
2557: ['2557-1', '2557-2', '2557-3', '2557-4', '2557-5', '2557-6'],
2677: ['2677-1', '2677-2', '2677-3', '2677-4'],
3959: ['3959', '3959-1', '3959-2', '3959-3', '3959-4', '3959-5', '3959-6', '3959-7'],
3428: ['3428-1', '3428-2', '3428-3'],
3456: ['3456-1', '3456-2', '3456-3', '3456-4', '3456-5', '3456-6'],
3457: ['3457-1', '3457-2', '3457-3', '3457-4', '3457-5', '3457-6', '3457-7', '3457-8', '3457-9', '3457-10', '3457-11', '3457-12', '3457-13', '3457-14', '3457-15', '3457-16', '3457-17'],
3477: ['3477-1', '3477-2', '3477-3'],
3715: ['3715-1', '3715-2'],
3790: ['3790-1', '3790-2'],
3791: ['3791-1', '3791-2'],
3848: ['3848-1', '3848-2', '3848-3'],
3849: ['3849-1', '3849-2'],
4075: ['4075', '4075-1'],
4100: ['4100-1', '4100-2'],
4131: ['4131-1', '4131-2'],
4196: ['4196-1', '4196-2'],
4228: ['4228-1', '4228-2', '4228-3', '4228-4'],
4272: ['4272-1', '4272-2'],
4273: ['4273-0', '4273-1', '4273-2', '4273-3', '4273-4', '4273-5'],
4277: ['4277-1', '4277-2', '4277-3'],
4395: ['4395-1', '4395-2'],
4494: ['4494-1', '4494-2'],
4714: ['4714-1', '4714_1', '4714_2', '4714_3'],
4722: ['4722-1', '4722-2'],
4812: ['4812-1', '4812-2', '4812-3', '4812-4', '4812-5', '4812-6', '4812-7', '4812-8'],
};
/*
var g_zone_areas = {};
in locale files
*/

View File

@@ -81,7 +81,6 @@ class AjaxData extends AjaxHandler
break; break;
// locale independant // locale independant
case 'quick-excludes': case 'quick-excludes':
case 'zones':
case 'weight-presets': case 'weight-presets':
case 'item-scaling': case 'item-scaling':
case 'realms': case 'realms':
@@ -102,6 +101,7 @@ class AjaxData extends AjaxHandler
case 'enchants': case 'enchants':
case 'itemsets': case 'itemsets':
case 'pets': case 'pets':
case 'zones':
if (!Util::loadStaticFile($set, $result, true) && Cfg::get('DEBUG')) if (!Util::loadStaticFile($set, $result, true) && Cfg::get('DEBUG'))
$result .= "alert('could not fetch static data: ".$set." for locale: ".User::$localeString."');"; $result .= "alert('could not fetch static data: ".$set." for locale: ".User::$localeString."');";

View File

@@ -671,7 +671,7 @@ trait spawnHelper
$floors = []; $floors = [];
foreach ($points as $p) foreach ($points as $p)
{ {
if (isset(Game::$areaFloors[$p['areaId']])) if ($p['floor'])
$floors[$p['areaId']][] = $p['floor']; $floors[$p['areaId']][] = $p['floor'];
if (isset($menu[$p['areaId']])) if (isset($menu[$p['areaId']]))

View File

@@ -140,13 +140,6 @@ class Game
null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7 null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7
); );
public static $areaFloors = array(
206 => 3, 209 => 7, 719 => 3, 721 => 4, 796 => 4, 1196 => 2, 1337 => 2, 1581 => 2, 1583 => 7, 1584 => 2,
2017 => 2, 2057 => 4, 2100 => 2, 2557 => 6, 2677 => 4, 3428 => 3, 3457 => 17, 3790 => 2, 3791 => 2, 3959 => 8,
3456 => 6, 3715 => 2, 3848 => 3, 3849 => 2, 4075 => 2, 4100 => 2, 4131 => 2, 4196 => 2, 4228 => 4, 4272 => 2,
4273 => 6, 4277 => 3, 4395 => 2, 4494 => 2, 4722 => 2, 4812 => 8
);
public static function sideByRaceMask($race) public static function sideByRaceMask($race)
{ {
// Any // Any

View File

@@ -1188,44 +1188,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"Östliche Königreiche", "Kalimdor", "Dungeons", "Schlachtzüge", "Unbenutzt", null, "Östliche Königreiche", "Kalimdor", "Dungeons", "Schlachtzüge", "Unbenutzt", null,
"Schlachtfelder", null, "Scherbenwelt", "Arenen", "Nordend" "Schlachtfelder", null, "Scherbenwelt", "Arenen", "Nordend"
),
'floors' => array(
206 => ["Vorbereitung der Norndir", "Aufstieg der Drachenschinder", "Tyrs Terrasse"],
209 => ["Der Hof", "Speisesaal", "Die Verwaiste Höhle", "Das Tiefere Observatorium", "Das Obere Observatorium", "Lord Godfreys Kammer", "Der Wehrgang"],
719 => ["Der Teich von Ask'Ar", "Mondschreinsanktum", "Der Vergessene Teich"],
721 => ["Die Halle der Zahnräder", "Der Schlafsaal", "Startrampe", "Tüftlerhof"],
796 => ["Friedhof", "Bibliothek", "Waffenkammer", "Kathedrale"],
1196 => ["Untere Spitze", "Obere Spitze"],
1337 => ["Halle der Bewahrer", "Khaz'goroths Sitz"],
1581 => ["Die Todesminen", "Eiserne Bucht"],
1583 => ["Tazz'Alaor", "Listspinnertunnel", "Hordemar", "Schwarzfausthalle", "Drachenspitzhalle", "Der Krähenhorst", "Schwarzfelsstadion"],
1584 => ["Gefängnisblock", "Die Schattenschmiede"],
2017 => ["Kreuzzüglerplatz", "Der Spießrutenlauf"],
2057 => ["Das Reliquiarium", "Kammer der Beschwörung", "Das Arbeitszimmer des Direktors", "Familiengruft der Barovs"],
2100 => ["Höhlen von Maraudon", "Zaetars Grab"],
2557 => ["Gordokhallen", "Hauptstadtgärten", "Hof der Hochgeborenen", "Das Gefängnis von Immol'thar", "Wucherborkenviertel", "Der Schrein von Eldretharr"],
2677 => ["Garnison des Drachenmals", "Hallen des Zwists", "Die Blutroten Labore", "Nefarians Unterschlupf"],
3428 => ["Untergrund des Schwarmbaus", "Die Tempeltore", "Höhle von C'Thun"],
3457 => ["Bedienstetenunterkünfte", "Obere Nobelställe", "Der Bankettsaal", "Die Gästezimmer", "Balkon des Opernsaals", "Die Terrasse des Meisters", "Untere Eingestürzte Treppe", "Obere Eingestürzte Treppe", "Die Menagerie", "Bibliothek des Wächters", "Das Warenlager", "Obere Bibliothek", "Die Himmelswacht", "Halle der Spiele", "Medivhs Gemächer", "Die Energiekammer", "Netherraum"],
3790 => ["Hallen des Jenseits", "Brücke der Seelen"],
3791 => ["Sethekkversteck", "Hallen der Trauer"],
3959 => ["Ausbildungsgelände der Illidari", "Kanäle von Karabor", "Zuflucht der Schatten", "Hallen der Pein", "Blutschattens Wacht", "Hof der Irdischen Gelüste", "Kommandoraum", "Tempelspitze"],
3456 => ["Das Konstruktviertel", "Das Arachnidenviertel", "Das Militärviertel", "Das Seuchenviertel", "Übersicht", "Frostwyrmhort"],
3715 => ["Die Dampfkammer", "Die Kühlteiche"],
3848 => ["Stasisblock: Trion", "Stasisblock: Maximus", "Eindämmungskern"],
3849 => ["Die Mechanar", "Berechnungskammer"],
4075 => ["Sonnenbrunnenplateau", "Schrein der Finsternis"],
4100 => ["Außerhalb von Stratholme", "Stratholme"],
4131 => ["Zuflucht des Großmagisters", "Beobachtungsplatz"],
4196 => ["Die Vorhallen von Drak'Tharon", "Aussichtspunkt von Drak'Tharon"],
4228 => ["Band der Varianz", "Band der Akzeleration", "Band der Transmutation", "Band der Angleichung"],
4272 => ["Die unnachgiebige Garnison", "Straße der Schöpfer"],
4273 => ["Der große Vorstoß", "Die Vorkammer von Ulduar", "Das innere Sanktum von Ulduar", "Das Gefängnis von Yogg-Saron", "Der Funke der Imagination", "Das Gedankenauge"],
4277 => ["Die Brutgrube", "Hadronox' Hort", "Das vergoldete Tor"],
4395 => ["Dalaran", "Die Schattenseite"],
4494 => ["Ahn'kahet", "2. Stockwerk"],
4722 => ["Kolosseum der Kreuzfahrer", "Die eisigen Tiefen"],
4812 => ["Die untere Zitadelle", "Das Schädelbollwerk", "Dom des Todbringers", "Hort der Frostkönigin", "Der obere Bereich", "Königliche Quartiere", "Der Frostthron", "Frostgram"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1189,44 +1189,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"Eastern Kingdoms", "Kalimdor", "Dungeons", "Raids", "Unused", null, "Eastern Kingdoms", "Kalimdor", "Dungeons", "Raids", "Unused", null,
"Battlegrounds", null, "Outland", "Arenas", "Northrend" "Battlegrounds", null, "Outland", "Arenas", "Northrend"
),
'floors' => array(
206 => ["Norndir Preparation", "Dragonflayer Ascent", "Tyr's Terrace"],
209 => ["The Courtyard", "Dining Hall", "The Vacant Den", "Lower Observatory", "Upper Observatory", "Lord Godfrey's Chamber", "The Wall Walk"],
719 => ["The Pool of Ask'Ar", "Moonshrine Sanctum", "The Forgotten Pool"],
721 => ["The Hall of Gears", "The Dormitory", "Launch Bay", "Tinkers' Court"],
796 => ["Graveyard", "Library", "Armory", "Cathedral"],
1196 => ["Lower Pinnacle", "Upper Pinnacle"],
1337 => ["Hall of the Keepers", "Khaz'Goroth's Seat"],
1581 => ["The Deadmines", "Ironclad Cove"],
1583 => ["Tazz'Alaor", "Skitterweb Tunnels", "Hordemar City", "Hall of Blackhand", "Dragonspire Hall", "The Rookery", "Blackrock Stadium"],
1584 => ["Detention Block", "Shadowforge City"],
2017 => ["Crusader's Square", "The Gauntlet"],
2057 => ["The Reliquary", "Chamber of Summoning", "The Headmaster's Study", "Barov Family Vault"],
2100 => ["Caverns of Maraudon", "Zaetar's Grave"],
2557 => ["Gordok Commons", "Capital Gardens", "Court of the Highborne", "Prison of Immol'Thar", "Warpwood Quarter", "The Shrine of Eldretharr"],
2677 => ["Dragonmaw Garrison", "Halls of Strife", "Crimson Laboratories", "Nefarian's Lair"],
3428 => ["The Hive Undergrounds", "The Temple Gates", "Vault of C'Thun"],
3456 => ["The Construct Quarter", "The Arachnid Quarter", "The Military Quarter", "The Plague Quarter", "Overview", "Frostwyrm Lair"],
3457 => ["Servant's Quarters", "Upper Livery Stables", "The Banquet Hall", "The Guest Chambers", "Opera Hall Balcony", "Master's Terrace", "Lower Broken Stair", "Upper Broken Stair", "The Menagerie", "Guardian's Library", "The Repository", "Upper Library", "The Celestial Watch", "Gamesman's Hall", "Medivh's Chambers", "The Power Station", "Netherspace"],
3715 => ["The Steamvault", "The Cooling Pools"],
3790 => ["Halls of the Hereafter", "Bridge of Souls"],
3791 => ["Veil Sethekk", "Halls of Mourning"],
3848 => ["Stasis Block: Trion", "Stasis Block: Maximus", "Containment Core"],
3849 => ["The Mechanar", "Calculation Chamber"],
3959 => ["Illidari Training Grounds", "Karabor Sewers", "Sanctuary of Shadows", "Halls of Anguish", "Gorefiend's Vigil", "Den of Mortal Delights", "Chamber of Command", "Temple Summit"],
4075 => ["Sunwell Plateau", "Shrine of the Eclipse"],
4100 => ["Outside Stratholme", "Stratholme City"],
4131 => ["Grand Magister's Asylum", "Observation Grounds"],
4196 => ["The Vestibules of Drak'Tharon", "Drak'Tharon Overlook"],
4228 => ["Band of Variance", "Band of Acceleration", "Band of Transmutation", "Band of Alignment"],
4272 => ["Unyielding Garrison", "Walk of the Makers"],
4273 => ["The Grand Approach", "The Antechamber of Ulduar", "The Inner Sanctum of Ulduar", "The Prison of Yogg-Saron", "The Spark of Imagination", "The Mind's Eye"],
4277 => ["The Brood Pit", "Hadronox's Lair", "The Gilded Gate"],
4395 => ["Dalaran City", "The Underbelly"],
4494 => ["Ahn'Kahet", "Level 2"],
4722 => ["Crusaders' Coliseum", "The Icy Depths"],
4812 => ["The Lower Citadel", "The Rampart of Skulls", "Deathbringer's Rise", "The Frost Queen's Lair", "The Upper Reaches", "Royal Quarters", "The Frozen Throne", "Frostmourne"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1188,44 +1188,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"Reinos del Este", "Kalimdor", "Mazmorras", "Bandas", "No las uso", null, "Reinos del Este", "Kalimdor", "Mazmorras", "Bandas", "No las uso", null,
"Campos de batalla", null, "Terrallende", "Arenas", "Rasganorte" "Campos de batalla", null, "Terrallende", "Arenas", "Rasganorte"
),
'floors' => array(
206 => ["Preparación Norndir", "Ascenso de los Desuelladragones", "Bancal de Tyr"],
209 => ["El Patio", "Comedor", "El Cubil Vacío", "Observatorio inferior", "Observatorio superior", "Cámara de Lord Godfrey", "El Camino de la Muralla"],
719 => ["La Alberca de Ask'ar", "Sagrario Lunar", "Las Charcas del Olvido"],
721 => ["La Sala de Máquinas", "Los Dormitorios", "Aeropuerto", "Cámara Manitas"],
796 => ["[Cementerio]", "[Biblioteca]", "[Armería]", "[Catedral]"],
1196 => ["Pináculo inferior", "Pináculo superior"],
1337 => ["Sala de los Guardianes", "Trono de Khaz'goroth"],
1581 => ["Las Minas de la Muerte", "Cala del Acorazado"],
1583 => ["Tazz'Alaor", "Túneles de Arácnidas", "Ciudad Hordemar", "Sala de Puño Negro", "Sala Dracopico", "El Grajero", "Estadio de Roca Negra"],
1584 => ["Bloque de Detención", "Ciudad Forjatiniebla"],
2017 => ["Plaza de los Cruzados", "El Guantelete"],
2057 => ["El Relicario", "Cámara de la Invocación", "Sala Rectoral", "[Barov Family Vault]"],
2100 => ["Cavernas de Maraudon", "Tumba de Zaetar"],
2557 => ["Ágora de Gordok", "Jardines de la Capital", "Corte de los Altonato", "Prisión de Immol'thar", "Barrio Alabeo", "Santuario de Eldretharr"],
2677 => ["Cuartel Faucedraco", "Salas de los Conflictos", "Laboratorios Carmesí", "Guarida de Nefarian"],
3428 => ["El Subterráneo de la Colmena", "Las Puertas del Templo", "Cámara de C'Thun"],
3456 => ["El arrabal de los ensamblajes", "El arrabal arácnido", "El arrabal militar", "El arrabal de la peste", "La Necrópolis inferior", "La Necrópolis superior"],
3457 => ["Alcobas de los Sirvientes", "Caballerizas superiores", "La Sala de Banquetes", "Los Aposentos de los Invitados", "Balcón de la Sala de la Ópera", "El Bancal del Maestro", "La Escalera Quebrada inferior", "La Escalera Quebrada superior", "La Sala de las Fieras", "Biblioteca del Guardián", "El Repositorio", "La Biblioteca superior", "El Mirador Celestial", "Sala del Tablero", "Estancias de Medivh", "La Central Eléctrica", "Espacio Abisal"],
3715 => ["La Cámara de Vapor", "Las Charcas Refrescantes"],
3790 => ["Salas del Más Allá", "Puente de las Almas"],
3791 => ["Velo Sethekk", "Salas del Luto"],
3848 => ["Bloque de Estasis: Trion", "Bloque de Estasis: Maximus", "Pabellón de Aislamiento"],
3849 => ["El Mechanar", "Estancias de Calculación"],
3959 => ["Campo de entrenamiento Illidari", "Cloacas de Karabor", "Santuario de las Sombras", "Salas de Angustia", "Vigilia de Sanguino", "Guarida de los Placeres Mortales", "Cámara de Mando", "Cima del Templo"],
4075 => ["Meseta de La Fuente del Sol", "Santuario del Eclipse"],
4100 => ["El Camino a Stratholme", "Stratholme"],
4131 => ["Asilo del Gran Magister", "Sector de Observación"],
4196 => ["El vestíbulo de Drak'Tharon", "Centinela de Drak'Tharon"],
4228 => ["Sortija de discrepancia", "Sortija de Aceleración", "Sortija de transmutación", "Sortija de alineación"],
4272 => ["El Cuartel Implacable", "Camino de los Creadores"],
4273 => ["El Gran Acceso", "La Antecámara de Ulduar", "El Sagrario Interior de Ulduar", "La Prisión de Yogg-Saron", "La Chispa de la Imaginación", "El Ojo de la Mente"],
4277 => ["El Foso del Linaje", "Guarida de Hadronox", "La Puerta dorada"],
4395 => ["Ciudad de Dalaran", "Los Bajos Fondos"],
4494 => ["Ahn'kahet", "Nivel 2"],
4722 => ["El Coliseo Argenta", "Las profundidades heladas"],
4812 => ["La ciudadela inferior", "La Muralla de las Calaveras", "Ascenso del Libramorte", "La guarida de la Reina de Escarcha", "Los Confines superiores", "Cuarteles Reales", "El Trono Helado", "Agonía de Escarcha"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1188,44 +1188,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"Royaumes de l'est", "Kalimdor", "Donjons", "Raids", "Inutilisées", null, "Royaumes de l'est", "Kalimdor", "Donjons", "Raids", "Inutilisées", null,
"Champs de bataille", null, "Outreterre", "Arènes", "Norfendre" "Champs de bataille", null, "Outreterre", "Arènes", "Norfendre"
),
'floors' => array(
206 => ["Préparation de Norndir", "Ascension d'Écorche-dragon", "Terrasse de Tyr"],
209 => ["La cour", "Salle à manger", "Antre Vacant", "Observatoire Inférieur", "Observatoire Supérieur", "Chambre du seigneur Godfrey", "Le chemin de ronde"],
719 => ["Le Bassin d'Ask'ar", "Sanctuaire dÉcrin-de-Lune", "Les bassins Oubliés"],
721 => ["Le Hall des engrenages", "Le dortoir", "Baie de lancement", "Cour du Bricoleur"],
796 => ["[Cimetière]", "[Bibliothèque]", "[Armurerie]", "[Cathédrale]"],
1196 => ["Pinnacle inférieur", "Pinacle Supérieur"],
1337 => ["Hall des Gardiens", "Siège de Khaz'goroth"],
1581 => ["Les Mortemines", "Crique du Cuirassé"],
1583 => ["Tazz'Alaor", "Tunnels de Toile-grouillante", "Cité d'Hordemar", "Hall de Main-noire", "Hall de la Flèche des dragons", "La colonie", "Stade Rochenoire"],
1584 => ["Le mitard", "Ville des Ombreforges"],
2017 => ["Place des Croisés", "Le Défi"],
2057 => ["Le Reliquaire", "Chambre d'invocation", "Bureau du proviseur", "[Barov Family Vault]"],
2100 => ["Cavernes de Maraudon", "Tombe de Zaetar"],
2557 => ["Communs gordok", "Grands jardins", "Cours des Bien-nés", "Prison d'Immol'Thar", "Quartier de Crochebois", "Le sanctuaire d'Eldretharr"],
2677 => ["Garnison des Gueules-de-dragon", "Halls des conflits", "Laboratoires Cramoisis", "Antre de Nefarian"],
3428 => ["Les souterrains de la ruche", "Portes du Temple", "Caveau de C'Thun"],
3456 => ["Le quartier des Assemblages", "Le Quartier des Arachnide", "Le Quartier Militaire", "Le quartier de la Peste", "La Nécropole Inférieure", "La Nécropole Supérieure"],
3457 => ["Quartiers des serviteurs", "Écuries supérieures", "La salle de banquet", "Les Appartements des hôtes", "Balcon de lOpéra", "Terrasse du maître", "Partie inférieure de lEscalier brisé", "Partie supérieure de lEscalier brisé", "La Ménagerie", "Bibliothèque du Gardien", "Le Dépôt", "Bibliothèque supérieure", "Le Guet céleste", "Hall du Flambeur", "Appartements de Medivh", "Centrale électrique", "Néantespace"],
3715 => ["Le caveau de la Vapeur", "Les bassins de refroidissement"],
3790 => ["Les salles de lAprès-vie", "Le pont des âmes"],
3791 => ["Voile Sethekk", "Les salles du Deuil"],
3848 => ["Bloc de stase : Trion", "Bloc de stase : Maximus", "Cœur de confinement"],
3849 => ["Le Méchanar", "Chambre des Calculs"],
3959 => ["Terrain d'entraînement Illidari", "Égouts de Karabor", "Sanctuaire des ombres", "Les salles de lAngoisse", "Veillée de Fielsang", "Tanière des délices mortels", "Chambre de commandement", "Sommet du temple"],
4075 => ["Plateau du Puits de soleil", "Sanctuaire de leclipse"],
4100 => ["La Route de Stratholme", "Stratholme"],
4131 => ["Asile du grand magistère", "Terrain dobservation"],
4196 => ["Le Vestibule de Drak'Tharon", "Surplombe de Drak'Tharon"],
4228 => ["Bande d'Écart", "Bande d'Accélération", "Bande de Transmutation", "Bande d'Alignement"],
4272 => ["La garnison inflexible", "Promenade des Faiseurs"],
4273 => ["Le Grand abord", "L'antichambre d'Ulduar", "Le sanctum intérieur d'Ulduar", "La prison de Yogg-Saron", "L'Étincelle d'imagination", "La Vue de l'esprit"],
4277 => ["La Fosse des couvées", "Antre d'Hadronox", "La Porte de la Daurade"],
4395 => ["Dalaran", "Les Entrailles"],
4494 => ["Ahn'kahet", "Plancher 2"],
4722 => ["L'colisée d'Argent", "Les Profondeurs Glacées"],
4812 => ["La Citadelle Inférieure", "Le Rempart des Cranes", "Ascension de Porte-mort", "Le repaire de la reine du Givre", "Les étages supérieurs", "Quartiers Royaux", "Le Trône Gelé", "Deuillegivre"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1188,44 +1188,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"Восточные королевства", "Калимдор", "Подземелья", "Рейды", "Неактивно", null, "Восточные королевства", "Калимдор", "Подземелья", "Рейды", "Неактивно", null,
"Поля боя", null, "Запределье", "Арены", "Нордскол" "Поля боя", null, "Запределье", "Арены", "Нордскол"
),
'floors' => array(
206 => ["Подготовка Норндира", "Подъем Укротителей драконов", "Терраса Тира"],
209 => ["Внутренний двор", "Обеденный зал", "Свободная берлога", "Нижняя обсерватория", "Верхняя обсерватория", "Палата лорда Годфри", "Крепостной вал"],
719 => ["Пруд Аск'ара", "Алтарь святилища Луны", "Забытый пруд"],
721 => ["Машинный зал", "Спальни", "Пусковая установка", "Двор Механиков"],
796 => ["[Кладбище]", "[Библиотека]", "[Арсенал]", "[Собор]"],
1196 => ["Подножие", "Вершина"],
1337 => ["Зал Хранителей", "Трон Каз'горота"],
1581 => ["Мертвые копи", "Потайная бухта"],
1583 => ["Тазз'Алаор", "Паучий лабиринт", "Ордамар", "Зал Чернорука", "Зал Драконов", "Гнездовье", "Стадион Черной горы"],
1584 => ["Тюремный блок", "Тенегорн"],
2017 => ["Площадь рыцарей", "Улица Испытаний"],
2057 => ["Хранилище реликвий", "Чертог Призыва", "Кабинет ректора", "[Barov Family Vault]"],
2100 => ["Пещеры Мародона", "Могила Зейтара"],
2557 => ["Палаты Гордока", "Центральный сад", "Двор высокорожденных", "Тюрьма Бессмер'тера", "Квартал Криводревов", "Святилище Элдретарра"],
2677 => ["Гарнизон Драконьей Пасти", "Залы Раздора", "Багровые лаборатории", "Логово Нефариана"],
3428 => ["Подземелье улья", "Ворота храма", "Обитель К'Туна"],
3456 => ["Квартал Мерзости", "Паучий квартал", "Военный квартал", "Чумной квартал", "Нижний некрополь", "Верхний некрополь"],
3457 => ["Комнаты cлуг", "ойла", "Пиршественный зал", "Гостевые комнаты", "Балкон в опере", "Терраса Мастера", "Низ разрушенной лестницы", "Верх разрушенной лестницы", "Галерея", "Библиотека Стража", "Хранилище", "Верхний ярус библиотеки", "Обсерватория", "Игровой зал", "Покои Медива", "Энергетический блок", "Пустомарь"],
3715 => ["Паровое подземелье", "Охладительные резервуары"],
3790 => ["Потусторонние залы", "Мост Душ"],
3791 => ["Гнездовье Сетекк", "Залы Плача"],
3848 => ["Изоляционная камера: Трион", "Изоляционная камера: Максимус", "Ядро Сдерживания"],
3849 => ["Механар", "Комната Вычислений"],
3959 => ["Campo de Treinamento Illidari", "Esgotos de Karabor", "Santuário das Sombras", "Salões da Angústia", "Vigia do Sanguinávido", "Covil dos Prazeres Mortais", "Câmara de Comando", "Ápice do Templo"],
4075 => ["Плато Солнечного Колодца", "Святилище Затмения"],
4100 => ["Дорога к Стратхольму", "Стратхольм"],
4131 => ["Пристанище Великого Магистра", "Обзорная площадка"],
4196 => ["Залы крепости Драк'Тарон", "Дозорное укрепление Драк'Тарона"],
4228 => ["Кольцо отклонения", "Кольцо ускорения", "Кольцо трансмутации", "Кольцо управления"],
4272 => ["Стойкий гарнизон", "Галерея Творцов"],
4273 => ["Большой переход", "Вестибюль Ульдуара", "Внутреннее святилище Ульдуара", "Темница Йогг-Сарона", "Искра Воображения", "Око разума"],
4277 => ["Родовая яма", "Логово Хадронокса", "Золоченые врата"],
4395 => ["Даларан", "Клоака"],
4494 => ["Ан'кахет", "Уровень 2"],
4722 => ["Колизей Серебряного Авангарда", "Ледяные глубины"],
4812 => ["Нижний ярус", "Черепной вал", "Подъем Смертоносного", "Логово Королевы Льда", "Верхний ярус", "Королевские палаты", "Ледяной Трон", "Ледяная Скорбь"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1188,44 +1188,6 @@ $lang = array(
'cat' => array( 'cat' => array(
"东部王国", "卡利姆多", "地下城", "团队副本", "未使用", null, "东部王国", "卡利姆多", "地下城", "团队副本", "未使用", null,
"战场", null, "外域", "竞技场", "诺森德" "战场", null, "外域", "竞技场", "诺森德"
),
'floors' => array(
206 => ["诺迪尔备战区", "掠龙氏族高台", "提尔之台"],
209 => ["庭院", "饭厅", "空巢", "下层瞭望台", "上层瞭望台", "高弗雷勋爵的大厅", "城墙走道"],
719 => ["阿斯卡之池", "月神圣地密室", "遗忘之池"],
721 => ["齿轮大厅", "宿舍", "发射台", "工匠议会"],
796 => ["墓地", "图书馆", "军械库", "大教堂"],
1196 => ["尖塔下层", "尖塔上层"],
1337 => ["守护者大厅", "卡兹格罗斯之座"],
1581 => ["死亡矿井", "铁甲湾"],
1583 => ["塔萨洛尔", "蛛网隧道", "霍德玛尔城", "黑手大厅", "龙塔大厅", "孵化间", "黑石竞技场"],
1584 => ["禁闭室", "暗炉城"],
2017 => ["十字军广场", "街巷"],
2057 => ["遗骨之穴", "召唤大厅", "书房上层", "院长的书房"],
2100 => ["玛拉顿的洞穴", "扎尔塔之墓"],
2557 => ["戈多克议会", "中心花园", "上层精灵庭院", "伊莫塔尔的牢笼", "扭木广场", "艾德雷斯神殿"],
2677 => ["龙喉兵营", "征战大厅", "血色实验室", "奈法利安的巢穴"],
3428 => ["地下虫巢", "神殿大门", "克苏恩地穴"],
3456 => ["构造区", "蜘蛛区", "军事区", "瘟疫区", "大墓地下层", "大墓地上层"],
3457 => ["仆役宿舍", "上层马厩", "宴会厅", "会客间", "歌剧院楼座", "主宰的露台", "下层断阶", "上层断阶", "展览馆", "守护者的图书馆", "储藏室", "上层图书馆", "观星大厅", "象棋大厅", "麦迪文的房间", "能量站", "虚空异界"],
3715 => ["蒸汽地窟", "冷却池"],
3790 => ["转生大厅", "灵魂之桥"],
3791 => ["塞泰克鸦巢", "哀悼大厅"],
3848 => ["静止隔间:特雷奥", "静止隔间:玛克希姆", "密封核心"],
3849 => ["能源舰", "计算密室"],
3959 => ["伊利达雷训练场", "卡拉波下水道", "暗影圣殿", "苦痛大厅", "血魔之厅", "欢愉之园", "命令大厅", "神殿之巅"],
4075 => ["太阳之井高地", "日蚀神殿"],
4100 => ["斯坦索姆外围", "斯坦索姆城"],
4131 => ["大魔导师的圣堂", "观测台"],
4196 => ["达克萨隆前庭", "达克萨隆悬崖"],
4228 => ["突变之环", "加速之环", "转化之环", "校准之环"],
4272 => ["坚韧军营", "造物者步道"],
4273 => ["壮阔大道", "奥杜尔的前厅", "奥杜尔的内部圣殿", "尤格-萨隆的监狱", "思想火花", "心灵之眼"],
4277 => ["孵化深渊", "哈多诺克斯之巢", "镀金之门"],
4395 => ["达拉然城", "达拉然下水道"],
4494 => ["安卡赫特", '被亵渎的祭坛'],
4722 => ["银色演武场", "寒冰深渊"],
4812 => ["堡垒下层", "颅骨之墙", "死亡使者之台", "冰霜女王的巢穴", "上层区域", "皇家区", "冰封王座", "霜之哀伤"]
) )
), ),
'quest' => array( 'quest' => array(

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env php
<?php <?php
require 'includes/kernel.php'; require 'includes/kernel.php';

View File

@@ -7,59 +7,25 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
require_once 'setup/tools/CLISetup.class.php';
require_once 'setup/tools/setupScript.class.php'; require_once 'setup/tools/setupScript.class.php';
require_once 'setup/tools/utilityScript.class.php';
require_once 'setup/tools/CLISetup.class.php';
require_once 'setup/tools/dbc.class.php'; require_once 'setup/tools/dbc.class.php';
require_once 'setup/tools/imagecreatefromblp.func.php'; require_once 'setup/tools/imagecreatefromblp.func.php';
function finish() : void
{
if (CLISetup::getOpt('delete')) // generated with TEMPORARY keyword. Manual deletion is not needed
CLI::write('generated dbc_* - tables have been deleted.', CLI::LOG_INFO);
die("\n");
}
CLISetup::init(); CLISetup::init();
CLISetup::loadScripts();
if (!CLISetup::getOpt(0x3)) if (CLISetup::getOpt('help'))
die(CLISetup::optHelp(0x7)); die(CLISetup::writeCLIHelp(true));
else if (!CLISetup::getOpt(1 << CLISetup::OPT_GRP_SETUP | 1 << CLISetup::OPT_GRP_UTIL))
die(CLISetup::writeCLIHelp());
$cmd = CLISetup::getOpt(0x3)[0]; // get arguments present in argGroup 1 or 2, if set. Pick first. if (CLISetup::getOpt('delete')) // generated with TEMPORARY keyword. Manual deletion is not needed
$s = []; CLI::write('generated dbc_* - tables have been deleted.', CLI::LOG_INFO);
$b = [];
switch ($cmd) // we accept only one main parameter
{
case 'setup':
case 'sql':
case 'build':
case 'account':
case 'dbconfig':
case 'siteconfig':
require_once 'setup/tools/clisetup/'.$cmd.'.func.php';
$cmd();
finish();
case 'update':
require_once 'setup/tools/clisetup/update.func.php';
update($s, $b); // return true if we do not rebuild stuff CLISetup::runInitial();
if (!$s && !$b)
finish();
case 'sync':
require_once 'setup/tools/clisetup/sync.func.php';
sync($s, $b); die("\n");
finish();
case 'dbc':
require_once 'setup/tools/clisetup/dbc.func.php';
$args = [];
foreach ($argv as $i => $str)
if ($i && $str[0] != '-')
$args[] = $str;
dbc($args);
break;
}
?> ?>

View File

@@ -20,123 +20,404 @@ class CLISetup
'frFR' => LOCALE_FR, 'frFR' => LOCALE_FR,
'deDE' => LOCALE_DE, 'deDE' => LOCALE_DE,
'zhCN' => LOCALE_CN, 'enCN' => LOCALE_CN, 'zhCN' => LOCALE_CN, 'enCN' => LOCALE_CN,
'esES' => LOCALE_ES, 'esMX' => LOCALE_ES, 'esES' => LOCALE_ES,
'ruRU' => LOCALE_RU 'ruRU' => LOCALE_RU
); );
public const SQL_BATCH = 1000; // max. n items per sql insert
public const LOCK_OFF = 0; public const LOCK_OFF = 0;
public const LOCK_ON = 1; public const LOCK_ON = 1;
public const LOCK_RESTORE = 2; public const LOCK_RESTORE = 2;
private static $lock = 1; private static $lock = self::LOCK_ON;
private const ARGV_REQUIRED = 0x01; public const ARGV_NONE = 0x00;
private const ARGV_OPTIONAL = 0x02; public const ARGV_REQUIRED = 0x01;
private const ARGV_ARRAY = 0x10; public const ARGV_OPTIONAL = 0x02;
public const ARGV_PARAM = 0x04; // parameter to another argument
public const ARGV_ARRAY = 0x10; // arg accepts list of values
public const OPT_GRP_SETUP = 0;
public const OPT_GRP_UTIL = 1;
public const OPT_GRP_MISC = 2;
private const GLOBALSTRINGS_LUA = '%s%sinterface/framexml/globalstrings.lua';
private static $opts = []; private static $opts = [];
private static $optGroups = ['AoWoW Setup', 'Utility Functions', 'Additional Options', 'Additional arguments specific to --build=simpleImg', 'Additional arguments specific to --build=complexImg']; private static $optGroups = ['AoWoW Setup', 'Utility Functions', 'Additional Options'];
private static $optDefs = array( // cmd => [groupId, aliasses[], flags, description, appendix] private static $optDefs = array( // cmd => [groupId, aliases[], argvFlags, description, appendix]
'setup' => [0, ['s', 'firstrun'], 0x00, 'Step by step initial setup. Resumes if interrupted.', '' ], 'delete' => [self::OPT_GRP_MISC, ['d'], self::ARGV_NONE, 'Delete dbc_* tables generated by this prompt when done. (not recommended)', '' ],
'update' => [0, ['u'], 0x00, 'Apply new sql updates fetched from Github and run --sync as needed.', '' ], 'log' => [self::OPT_GRP_MISC, [], self::ARGV_REQUIRED, 'Write CLI ouput to file.', '=logfile' ],
'dbconfig' => [1, [], 0x00, 'Set up DB connection.', '' ], 'help' => [self::OPT_GRP_MISC, ['h'], self::ARGV_NONE, 'Display contextual help, if available.', '' ],
'siteconfig' => [1, [], 0x00, 'Set up site variables.', '' ], 'force' => [self::OPT_GRP_MISC, ['f'], self::ARGV_NONE, 'Force existing files to be overwritten.', '' ],
'account' => [1, [], 0x00, 'Create an account with admin privileges.', '' ], 'locales' => [self::OPT_GRP_MISC, [], self::ARGV_ARRAY | self::ARGV_OPTIONAL, 'Limit setup to enUS, frFR, deDE, zhCN, esES and/or ruRU. (does not override config settings)', '=<regionCodes,>'],
'sql' => [1, [], 0x12, 'Generate DB content from your world tables.', '=<subScriptList,>' ], 'datasrc' => [self::OPT_GRP_MISC, [], self::ARGV_OPTIONAL, 'Manually point to directory with extracted mpq files. This is limited to setup/ (default: setup/mpqdata/)', '=path/' ],
'build' => [1, [], 0x12, 'Compile image files and data dumps.', '=<subScriptList,>' ],
'sync' => [1, [], 0x12, 'Regenerate tables/files that depend on given world DB table.', '=<worldTableList,>'],
'dbc' => [1, [], 0x11, 'Extract dbc files from mpqDataDir into sql table. Structure must be defined in setup/dbc.class.php.', '=<dbcfileList,>' ],
'delete' => [2, ['d'], 0x00, 'Delete dbc_* tables generated by this prompt when done.', '' ],
'log' => [2, [], 0x01, 'Write CLI ouput to file.', '=logfile' ],
'help' => [2, ['h'], 0x00, 'Display contextual help, if available.', '' ],
'force' => [2, ['f'], 0x00, 'Force existing files to be overwritten.', '' ],
'locales' => [2, [], 0x12, 'Limit setup to enUS, frFR, deDE, zhCN, esES and/or ruRU. (does not override config settings)', '=<regionCodes,>' ],
'mpqDataDir' => [2, [], 0x02, 'Manually point to directory with extracted mpq files. This is limited to setup/ (default: setup/mpqData/)', '=path/' ],
'icons' => [3, ['1'], 0x00, 'Generate icons for spells, items, classes, races, ect.', '' ],
'glyphs' => [3, ['2'], 0x00, 'Generate decorative glyph symbols displayed on related item and spell pages.', '' ],
'pagetexts' => [3, ['3'], 0x00, 'Generate images contained in text on readable items and gameobjects.', '' ],
'loadingscreens' => [3, ['4'], 0x00, 'Generate loading screen images (not used on page; skipped by default)', '' ],
'talentbgs' => [4, ['1'], 0x00, 'Generate backgrounds for the talent calculator.', '' ],
'maps' => [4, ['2'], 0x00, 'Generate zone and continental maps.', '' ],
'spawn-maps' => [4, ['3'], 0x00, 'Fallback to generate alpha masks for each zone to match creature and gameobject spawn points.', '' ],
'artwork' => [4, ['4'], 0x00, 'Generate images from /glues/credits (not used on page; skipped by default))', '' ],
'area-maps' => [4, ['5'], 0x00, 'Generate additional area maps with highlighting for subzones (optional; skipped by default)', '' ]
); );
private static $utilScriptRefs = [];
private static $setupScriptRefs = [];
private static $tmpStore = [];
private static $gsFiles = [];
/**************************/ public static function registerUtility(UtilityScript $us) : void
/* command line arguments */ {
/**************************/ if (isset(self::$optDefs[$us::COMMAND]) || isset(self::$utilScriptRefs[$us::COMMAND]))
{
CLI::write(' Utility function '.CLI::bold($us::COMMAND).' already defined.', CLI::LOG_ERROR);
return;
}
self::$optDefs[$us::COMMAND] = [$us->optGroup, $us->argvOpts, $us->argvFlags, $us::DESCRIPTION, $us::APPENDIX];
self::$utilScriptRefs[$us::COMMAND] = $us;
}
public static function registerSetup(string $invoker, SetupScript $ss) : void
{
if (isset(self::$optDefs[$invoker]) || isset(self::$utilScriptRefs[$invoker]))
{
CLI::write(' Utility function '.CLI::bold($invoker).' not defined. Can\'t attach Subscript '.CLI::bold($ss->getName()).', invoker is missing. Skipping...', CLI::LOG_ERROR);
return;
}
if (isset(self::$setupScriptRefs[$invoker][$ss->getName()]))
{
CLI::write(' Subscript function '.CLI::bold($ss->getName()).' already defined for invoker '.CLI::bold($invoker).'. Skipping...', CLI::LOG_ERROR);
return;
}
if ($childArgs = $ss->getSubCommands())
{
if ($duplicates = array_intersect(array_keys($childArgs), array_keys(self::$optDefs)))
{
CLI::write(' Subscript function '.CLI::bold($ss->getName()).'\'s child arguments --'.implode(', --', $duplicates).' are already defined. Skipping...', CLI::LOG_ERROR);
return;
}
$newIdx = count(self::$optGroups);
self::$optGroups[] = '--' . $invoker . '=' . $ss->getName();
foreach ($childArgs as $cmd => [$aliases, $argFlags, $description])
self::$optDefs[$cmd] = [$newIdx, $aliases, $argFlags, $description, ''];
}
// checks done ... store SetupScript
if (self::checkDependencies($ss))
{
self::$setupScriptRefs[] = [$invoker, $ss->getName(), $ss];
// recheck temp stored dependencies
foreach (self::$tmpStore as $idx => [$invoker, $ts])
{
if (!self::checkDependencies($ts))
continue;
self::$setupScriptRefs[] = [$invoker, $ts->getName(), $ts];
unset(self::$tmpStore[$idx]);
}
}
else // if dependencies haven't been stored yet, put aside for later use
self::$tmpStore[] = [$invoker, $ss];
}
private static function checkDependencies(SetupScript &$ss) : bool
{
if ($ss->isOptional) // optional scripts should no depend on anything
return true;
[$sDep, $bDep] = $ss->getSelfDependencies();
return ((!$sDep || $sDep == array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'sql'; }), 1))) &&
(!$bDep || $bDep == array_intersect($bDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'build'; }), 1))));
}
public static function loadScripts() : void
{
foreach (glob('setup/tools/clisetup/*.us.php') as $file)
include_once $file;
if (self::$tmpStore)
{
CLI::write('Some SubScripts have unresolved dependencies and have not been loaded', CLI::LOG_ERROR);
CLI::write();
$tbl = [['Name', '--sql dep.', '--build dep.']];
foreach (self::$tmpStore as [$_, $ssRef])
{
[$sDep, $bDep] = $ssRef->getSelfDependencies();
$missS = array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'sql'; }), 1));
$missB = array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'build'; }), 1));
array_walk($sDep, function (&$x) use($missS) { $x = in_array($x, $missS) ? $x : CLI::red($x); });
array_walk($bDep, function (&$x) use($missB) { $x = in_array($x, $missB) ? $x : CLI::red($x); });
$tbl[] = [$ssRef->getName(), implode(', ', $sDep), implode(', ', $bDep)];
}
CLI::writeTable($tbl);
}
// link SubScipts back to UtilityScript after all UtilityScripts have been loaded
foreach (self::$utilScriptRefs as $name => $us)
if (in_array('TrSubScripts', class_uses($us)))
$us->assignGenerators($name);
self::evalOpts();
}
public static function getSubScripts(string $invoker = '') : generator
{
foreach (self::$setupScriptRefs as [$src, $name, $ref])
if (!$invoker || $src == $invoker)
yield $name => [$src, $ref];
}
public static function init() : void public static function init() : void
{ {
$short = ''; self::evalOpts();
$long = [];
$alias = [];
foreach (self::$optDefs as $opt => [$idx, $aliasses, $flags, , ])
{
if ($flags & self::ARGV_REQUIRED)
$opt .= ':';
else if ($flags & self::ARGV_OPTIONAL)
$opt .= '::';
$long[] = $opt;
foreach ($aliasses as $a)
{
if ($flags & self::ARGV_REQUIRED) // neither should be set with shortOpts
$_a = $a.':';
else if ($flags & self::ARGV_OPTIONAL)
$_a = $a.'::';
else
$_a = $a;
$alias[$a] = $opt;
if (strlen($a) == 1)
$short .= $_a;
else
$long[] = $_a;
}
}
if ($opts = getopt($short, $long))
foreach ($opts as $o => $v)
self::$opts[$alias[$o] ?? $o] = (self::$optDefs[$alias[$o] ?? $o][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
// optional logging // optional logging
if (isset(self::$opts['log'])) if (isset(self::$opts['log']))
CLI::initLogFile(trim(self::$opts['log'])); CLI::initLogFile(trim(self::$opts['log']));
// alternative data source (no quotes, use forward slash) // alternative data source (no quotes, use forward slash)
if (isset(self::$opts['mpqDataDir'])) if (isset(self::$opts['datasrc']))
self::$srcDir = CLI::nicePath(self::$opts['mpqDataDir']); self::$srcDir = CLI::nicePath(self::$opts['datasrc']);
// optional limit handled locales // optional limit handled locales
if (isset(self::$opts['locales'])) if (isset(self::$opts['locales']))
{ {
// engb and enus are identical for all intents and purposes // engb and enus are identical for all intents and purposes
$from = ['engb', 'esmx', 'encn']; $from = ['engb', 'encn'];
$to = ['enus', 'eses', 'zhcn']; $to = ['enus', 'zhcn'];
$opts['locales'] = str_ireplace($from, $to, strtolower($opts['locales']));
self::$locales = array_intersect(Util::$localeStrings, explode(',', $opts['locales'])); self::$opts['locales'] = str_ireplace($from, $to, self::$opts['locales']);
self::$locales = array_intersect(Util::$localeStrings, array_map('strtolower', self::$opts['locales']));
} }
if (!self::$locales) if (!self::$locales)
self::$locales = array_filter(Util::$localeStrings); self::$locales = array_filter(Util::$localeStrings);
// restrict actual locales // restrict actual locales
foreach (self::$locales as $idx => $str) foreach (self::$locales as $idx => $_)
{
if (!($l = Cfg::get('LOCALES')) || ($l & (1 << $idx))) if (!($l = Cfg::get('LOCALES')) || ($l & (1 << $idx)))
self::$localeIds[] = $idx; self::$localeIds[] = $idx;
else
unset(self::$locales[$idx]);
}
if (!self::$localeIds)
CLI::write('No valid locale specified. Check your config or --locales parameter, if used', CLI::LOG_ERROR);
// get site status // get site status
if (DB::isConnected(DB_AOWOW)) if (DB::isConnected(DB_AOWOW))
self::$lock = (int)Cfg::get('MAINTENANCE'); self::$lock = Cfg::get('MAINTENANCE');
else else
self::$lock = self::LOCK_ON; self::$lock = self::LOCK_ON;
} }
public static function getOpt(...$args) public static function writeCLIHelp(bool $full = false) : void
{
$cmd = self::getOpt(1 << self::OPT_GRP_SETUP | 1 << self::OPT_GRP_UTIL);
if (!$cmd || !self::$utilScriptRefs[$cmd[0]]->writeCLIHelp())
{
$lines = [];
foreach (self::$optGroups as $idx => $og)
{
if (!$full && $idx > self::OPT_GRP_SETUP)
continue;
$lines[] = [$og, ''];
foreach (self::$optDefs as $opt => [$group, $alias, , $desc, $app])
{
if ($group != $idx)
continue;
$cmd = ' --'.$opt;
foreach ($alias as $a)
$cmd .= ' | '.(strlen($a) == 1 ? '-'.$a : '--'.$a);
$lines[] = [$cmd.$app, $desc];
}
}
CLI::writeTable($lines);
CLI::write();
}
}
// called from Setup
public static function runInitial() : void
{
global $argc, $argv; // todo .. find better way? argv, argc are effectivley already global
// get arguments present in argGroup 1 or 2, if set. Pick first.
$cmd = self::getOpt(1 << self::OPT_GRP_SETUP | 1 << self::OPT_GRP_UTIL)[0];
$us = &self::$utilScriptRefs[$cmd];
$inOut = [null, null, null, null];
$allOk = true;
$i = 0;
if ($us::USE_CLI_ARGS)
foreach ($argv as $n => $arg)
{
if (!$n || ($arg && $arg[0] == '-')) // not parent; not handled by getOpt()
continue;
$inOut[$i++] = $arg;
if ($i > 3)
break;
}
if ($dbError = array_filter($us::REQUIRED_DB, function ($x) { return !DB::isConnected($x); }))
{
CLI::write('Database on index '.implode(', ', $dbError).' not yet set up!', CLI::LOG_ERROR);
CLI::write('Please use '.CLI::bold('"php aowow --db"').' for setup', CLI::LOG_BLANK);
CLI::write();
return;
}
if ($us::LOCK_SITE != self::LOCK_OFF)
self::siteLock(self::LOCK_ON);
if ($us::NOTE_START)
CLI::write($us::NOTE_START);
if (!$us->run($inOut))
$allOk = false;
$error = [];
if ($allOk && !$us->test($error))
{
if ($us::NOTE_ERROR)
CLI::write($us::NOTE_ERROR, CLI::LOG_ERROR);
foreach ($error as $e)
CLI::write($e, CLI::LOG_BLANK);
CLI::write();
$allOk = false;
}
if ($allOk)
if ($ff = $us->followupFn)
if (array_filter($inOut))
self::run($ff, $inOut);
self::siteLock($us::LOCK_SITE == self::LOCK_RESTORE ? self::LOCK_RESTORE : self::LOCK_OFF);
// end
if ($us::NOTE_END_OK && $allOk)
CLI::write($us::NOTE_END_OK, CLI::LOG_OK);
else if($us::NOTE_END_FAIL && !$allOk)
CLI::write($us::NOTE_END_FAIL, CLI::LOG_ERROR);
}
// consecutive calls
public static function run(string $cmd, &$args) : bool
{
if (!isset(self::$utilScriptRefs[$cmd]))
return false;
$us = &self::$utilScriptRefs[$cmd];
if ($dbError = array_filter($us::REQUIRED_DB, function ($x) { return !DB::isConnected($x); }))
{
CLI::write('Database on index '.implode(', ', $dbError).' not yet set up!', CLI::LOG_ERROR);
CLI::write('Please use '.CLI::bold('"php aowow --db"').' for setup', CLI::LOG_BLANK);
CLI::write();
return false;
}
if ($us::PROMPT)
{
CLI::write($us::PROMPT, -1, false);
CLI::write();
if (!CLI::read(['x' => ['Press any key to continue', true, true]], $_)) // we don't actually care about the input
return false;
}
$args = array_pad($args, 4, []);
$success = $us->run($args);
$error = [];
if ($us::NOTE_ERROR && $success && !$us->test($error))
{
CLI::write($us::NOTE_ERROR, CLI::LOG_ERROR);
foreach ($error as $e)
CLI::write($e, CLI::LOG_BLANK);
CLI::write();
return false;
}
if ($success)
if ($ff = $us->followupFn)
if (array_filter($args))
if (!self::run($ff, $args))
$success = false;
return $success;
}
/**************************/
/* command line arguments */
/**************************/
public static function evalOpts() : void
{
$short = '';
$long = [];
$alias = [];
foreach (self::$optDefs as $opt => [, $aliases, $flags, , ])
{
foreach ($aliases as $i => $a)
{
if (isset($alias[$a]))
$alias[$a][] = $opt;
else
$alias[$a] = [$opt];
if ($flags & self::ARGV_REQUIRED)
$a .= ':';
else if ($flags & self::ARGV_OPTIONAL)
$a .= '::';
if (strlen($aliases[$i]) == 1)
$short .= $a;
else
$long[] = $a;
}
if ($flags & self::ARGV_REQUIRED)
$opt .= ':';
else if ($flags & self::ARGV_OPTIONAL)
$opt .= '::';
$long[] = $opt;
}
if ($opts = getopt($short, $long))
{
foreach ($opts as $o => $v)
{
if (!isset($alias[$o]))
self::$opts[$o] = (self::$optDefs[$o][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
else
foreach ($alias[$o] as $a)
self::$opts[$a] = (self::$optDefs[$a][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
}
}
}
public static function getOpt(/* string|int */ ...$args) // : bool|array|string
{ {
if (!$args) if (!$args)
return false; return false;
@@ -165,39 +446,12 @@ class CLISetup
return $result; return $result;
} }
public static function optHelp(int $groupMask = 0x0) : void
{
$lines = [];
foreach (self::$optGroups as $idx => $og)
{
if ($groupMask && !($groupMask & (1 << $idx)))
continue;
$lines[] = [$og, ''];
foreach (self::$optDefs as $opt => [$group, $alias, , $desc, $app])
{
if ($group != $idx)
continue;
$cmd = ' --'.$opt;
foreach ($alias as $a)
$cmd .= ' | '.(strlen($a) == 1 ? '-'.$a : '--'.$a);
$lines[] = [$cmd.$app, $desc];
}
}
CLI::writeTable($lines);
}
/*******************/ /*******************/
/* web page access */ /* web page access */
/*******************/ /*******************/
public static function siteLock(int $mode = self::LOCK_RESTORE) : void private static function siteLock(int $mode = self::LOCK_RESTORE) : void
{ {
if (DB::isConnected(DB_AOWOW)) if (DB::isConnected(DB_AOWOW))
Cfg::set('MAINTENANCE', $mode == self::LOCK_RESTORE ? self::$lock : $mode); Cfg::set('MAINTENANCE', $mode == self::LOCK_RESTORE ? self::$lock : $mode);
@@ -216,8 +470,7 @@ class CLISetup
*/ */
private static function buildFileList() : bool private static function buildFileList() : bool
{ {
CLI::write(); CLI::write('indexing game data from '.self::$srcDir.' for first time use...', CLI::LOG_INFO, true, true);
CLI::write('indexing game data from '.self::$srcDir.' for first time use...');
$setupDirs = glob('setup/*'); $setupDirs = glob('setup/*');
foreach ($setupDirs as $sd) foreach ($setupDirs as $sd)
@@ -246,8 +499,7 @@ class CLISetup
self::$mpqFiles[strtolower($_)] = $_; self::$mpqFiles[strtolower($_)] = $_;
} }
CLI::write('done'); CLI::write('indexing game data from '.self::$srcDir.' for first time use... done!', CLI::LOG_INFO);
CLI::write();
} }
catch (UnexpectedValueException $e) catch (UnexpectedValueException $e)
{ {
@@ -258,7 +510,7 @@ class CLISetup
return true; return true;
} }
public static function fileExists(&$file) public static function fileExists(string &$file) : bool
{ {
// read mpq source file structure to tree // read mpq source file structure to tree
if (!self::$mpqFiles) if (!self::$mpqFiles)
@@ -281,7 +533,7 @@ class CLISetup
return false; return false;
} }
public static function filesInPath($path, $useRegEx = false) public static function filesInPath(string $path, bool $useRegEx = false) : array
{ {
$result = []; $result = [];
@@ -304,12 +556,82 @@ class CLISetup
return $result; return $result;
} }
public static function filesInPathLocalized(string $pathPattern, ?bool &$status = true, bool $matchAll = true) : array
{
$result = [];
foreach (self::$expectedPaths as $xp => $locId)
{
if (!in_array($locId, self::$localeIds))
continue;
if (isset($result[$locId]))
continue;
if ($xp) // if in subDir add trailing slash
$xp .= '/';
$path = sprintf($pathPattern, $xp);
if (self::fileExists($path))
{
$result[$locId] = $path;
continue;
}
}
if (!$matchAll && !$result)
$status = false;
if ($matchAll && array_diff(self::$localeIds, array_keys($result)))
$status = false;
return $result;
}
public static function loadGlobalStrings() : bool
{
CLI::write('loading required GlobalStrings', CLI::LOG_INFO);
// try to load globalstrings for all selected locales
foreach (self::$expectedPaths as $xp => $lId)
{
if (isset(self::$gsFiles[$lId]))
continue;
if ($xp)
$xp .= '/';
$gsFile = sprintf(self::GLOBALSTRINGS_LUA, self::$srcDir, $xp);
if (self::fileExists($gsFile))
self::$gsFiles[$lId] = file($gsFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
}
if ($missing = array_diff(self::$localeIds, array_keys(self::$gsFiles)))
{
ClI::write('GlobalStrings.lua not found for locale '. Lang::concat($missing), CLI::LOG_WARN);
return false;
}
return true;
}
public static function searchGlobalStrings(string $pattern) : generator
{
if (!self::$gsFiles)
return;
foreach (self::$gsFiles as $lId => $globalStrings)
foreach ($globalStrings as $gs)
if (preg_match($pattern, $gs, $result))
yield $lId => $result;
}
/*****************/ /*****************/
/* file handling */ /* file handling */
/*****************/ /*****************/
public static function writeFile($file, $content) public static function writeFile(string $file, string $content) : bool
{ {
if (Util::writeFile($file, $content)) if (Util::writeFile($file, $content))
{ {
@@ -322,17 +644,23 @@ class CLISetup
return false; return false;
} }
public static function writeDir($dir) public static function writeDir(string $dir, bool &$exist = true) : bool
{ {
if (Util::writeDir($dir)) if (Util::writeDir($dir, $exist))
return true; return true;
CLI::write(error_get_last()['message'].' '.CLI::bold($dir), CLI::LOG_ERROR); CLI::write(error_get_last()['message'].' '.CLI::bold($dir), CLI::LOG_ERROR);
return false; return false;
} }
public static function loadDBC($name) public static function loadDBC( string $name) : bool
{ {
if (!DB::isConnected(DB_AOWOW))
{
CLI::write('CLISetup::loadDBC() - not connected to DB. Cannot write results!', CLI::LOG_ERROR);
return false;
}
if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'dbc_'.$name) && DB::Aowow()->selectCell('SELECT count(1) FROM ?#', 'dbc_'.$name)) if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'dbc_'.$name) && DB::Aowow()->selectCell('SELECT count(1) FROM ?#', 'dbc_'.$name))
return true; return true;

View File

@@ -1,73 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/********************/
/* Account creation */
/********************/
function account() : void
{
$fields = array(
'name' => ['Username', false],
'pass1' => ['Enter Password', true ],
'pass2' => ['Confirm Password', true ]
);
if (!DB::isConnected(DB_AOWOW))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return;
}
User::useLocale(LOCALE_EN);
Lang::load(LOCALE_EN);
if (CLI::read($fields, $uiAccount))
{
CLI::write();
if (!User::isValidName($uiAccount['name'], $e))
CLI::write(Lang::account($e == 1 ? 'errNameLength' : 'errNameChars'), CLI::LOG_ERROR);
else if (!User::isValidPass($uiAccount['pass1'], $e))
CLI::write(Lang::account($e == 1 ? 'errPassLength' : 'errPassChars'), CLI::LOG_ERROR);
else if ($uiAccount['pass1'] != $uiAccount['pass2'])
CLI::write(Lang::account('passMismatch'), CLI::LOG_ERROR);
else if ($_ = DB::Aowow()->SelectCell('SELECT 1 FROM ?_account WHERE user = ? AND (status <> ?d OR (status = ?d AND statusTimer > UNIX_TIMESTAMP()))', $uiAccount['name'], ACC_STATUS_NEW, ACC_STATUS_NEW))
CLI::write(Lang::account('nameInUse'), CLI::LOG_ERROR);
else
{
// write to db
$ok = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, joindate, email, allowExpire, userGroups, userPerms) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, 0, ?d, 1)',
$uiAccount['name'],
User::hashCrypt($uiAccount['pass1']),
Util::ucFirst($uiAccount['name']),
Cfg::get('CONTACT_EMAIL'),
U_GROUP_ADMIN
);
if ($ok)
{
$newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $uiAccount['name']);
Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
CLI::write("account ".$uiAccount['name']." created successfully", CLI::LOG_OK);
}
else // something went wrong
CLI::write(Lang::main('intError'), CLI::LOG_ERROR);
}
}
else
{
CLI::write();
CLI::write("account creation aborted", CLI::LOG_INFO);
}
}
?>

View File

@@ -0,0 +1,130 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/********************/
/* Account creation */
/********************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvOpts = ['a'];
public $optGroup = CLISetup::OPT_GRP_SETUP;
public const COMMAND = 'account';
public const DESCRIPTION = 'Create an account with admin privileges.';
public const APPENDIX = ' [name [password [email]]]';
public const PROMPT = 'Please create your admin account.';
public const NOTE_ERROR = 'There is no user with administrator privileges in the DB.';
public const REQUIRED_DB = [DB_AOWOW];
public const USE_CLI_ARGS = true;
public $runArgs = ['name', 'passw', 'email'];
private $fields = array(
'name' => ['Username', false],
'pass1' => ['Enter Password', true ],
'pass2' => ['Confirm Password', true ],
'email' => ['Email (optional)', false]
);
// args: username, password, email, null // iiin
public function run(&$args) : bool
{
User::useLocale(LOCALE_EN);
Lang::load(LOCALE_EN);
$name = $args[0] ?? '';
$passw = $args[1] ?? '';
$email = $args[2];
if ($name && User::isValidName($name))
unset($this->fields['name']);
else
$name = '';
if ($passw && User::isValidPass($passw))
{
unset($this->fields['pass1']);
unset($this->fields['pass2']);
}
else
$passw = '';
if (is_string($email) && (!strlen($email) || Util::isValidEmail($email)))
unset($this->fields['email']);
else
$email = '';
if ($this->fields && CLI::read($this->fields, $uiAccount))
{
CLI::write();
if (!$name && !User::isValidName($uiAccount['name'], $e))
CLI::write(Lang::account($e == 1 ? 'errNameLength' : 'errNameChars'), CLI::LOG_ERROR);
else if (!$name)
$name = $uiAccount['name'];
if (!$passw && !User::isValidPass($uiAccount['pass1'], $e))
CLI::write(Lang::account($e == 1 ? 'errPassLength' : 'errPassChars'), CLI::LOG_ERROR);
else if (!$passw && $uiAccount['pass1'] != $uiAccount['pass2'])
CLI::write(Lang::account('passMismatch'), CLI::LOG_ERROR);
else if (!$passw)
$passw = $uiAccount['pass1'];
if (!$email && Util::isValidEmail($uiAccount['email']))
$email = $uiAccount['email'];
else if (!$email && $uiAccount['email'])
CLI::write('[account] email invalid ... using default: ' . Cfg::get('CONTACT_EMAIL'), CLI::LOG_INFO);
}
else if ($this->fields)
{
CLI::write();
CLI::write("[account] admin creation aborted", CLI::LOG_INFO);
CLI::write();
return true;
}
if (DB::Aowow()->SelectCell('SELECT 1 FROM ?_account WHERE user = ? AND (status <> ?d OR (status = ?d AND statusTimer > UNIX_TIMESTAMP()))', $name, ACC_STATUS_NEW, ACC_STATUS_NEW))
{
CLI::write('[account] ' . Lang::account('nameInUse'), CLI::LOG_ERROR);
CLI::write();
return false;
}
if (!$name || !$passw)
return false;
if (DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, joindate, email, allowExpire, userGroups, userPerms) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, 0, ?d, 1)',
$name, User::hashCrypt($passw), Util::ucFirst($name), $email ?: Cfg::get('CONTACT_EMAIL'), U_GROUP_ADMIN))
{
$newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $name);
Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
CLI::write("[account] admin ".$name." created successfully", CLI::LOG_OK);
CLI::write();
return true;
}
CLI::write('[account] ' . Lang::main('intError'), CLI::LOG_ERROR);
CLI::write();
return false;
}
public function test(?array &$error = []) : bool
{
$error = [];
return !!DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `userPerms` = 1');
}
});
?>

View File

@@ -1,102 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*************************/
/* Create required files */
/*************************/
function build($syncMe = null) : array
{
if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return [];
}
require_once 'setup/tools/fileGen.class.php';
if(!FileGen::init($syncMe !== null ? FileGen::MODE_UPDATE : FileGen::MODE_NORMAL, $syncMe ?: []))
return [];
$done = [];
if (FileGen::$subScripts)
{
CLISetup::siteLock(CLISetup::LOCK_ON);
$allOk = true;
// start file generation
CLI::write('begin generation of '. implode(', ', FileGen::$subScripts));
CLI::write();
// files with template
foreach (FileGen::$tplFiles as $name => [$file, $destPath, $deps])
{
$reqDBC = [];
if (!in_array($name, FileGen::$subScripts))
continue;
if (!file_exists(FileGen::$tplPath.$file.'.in'))
{
CLI::write(sprintf(ERR_MISSING_FILE, FileGen::$tplPath.$file.'.in'), CLI::LOG_ERROR);
$allOk = false;
continue;
}
if (!CLISetup::writeDir($destPath))
continue;
$syncIds = []; // todo: fetch what exactly must be regenerated
$ok = FileGen::generate($name, $syncIds);
if (!$ok)
$allOk = false;
else
$done[] = $name;
CLI::write(' - subscript \''.$file.'\' returned '.($ok ? 'successfully' : 'with errors'), $ok ? CLI::LOG_OK : CLI::LOG_ERROR);
set_time_limit(FileGen::$defaultExecTime); // reset to default for the next script
}
// files without template
foreach (FileGen::$datasets as $file => $deps)
{
if (!in_array($file, FileGen::$subScripts))
continue;
$syncIds = []; // todo: fetch what exactly must be regenerated
$ok = FileGen::generate($file, $syncIds);
if (!$ok)
$allOk = false;
else
$done[] = $file;
CLI::write(' - subscript \''.$file.'\' returned '.($ok ? 'successfully' : 'with errors'), $ok ? CLI::LOG_OK : CLI::LOG_ERROR);
set_time_limit(FileGen::$defaultExecTime); // reset to default for the next script
}
// end
CLI::write();
if ($allOk)
CLI::write('successfully finished file generation', CLI::LOG_OK);
else
CLI::write('finished file generation with errors', CLI::LOG_ERROR);
CLISetup::siteLock(CLISetup::LOCK_RESTORE);
}
else if (FileGen::getMode() == FileGen::MODE_NORMAL)
CLI::write('no valid script names supplied', CLI::LOG_ERROR);
return $done;
}
?>

View File

@@ -0,0 +1,159 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/************************************************/
/* Create content from world tables / dbc files */
/************************************************/
CLISetup::registerUtility(new class extends UtilityScript
{
use TrSubScripts;
public $argvFlags = CLISetup::ARGV_ARRAY | CLISetup::ARGV_OPTIONAL;
public $optGroup = CLISetup::OPT_GRP_UTIL;
public const COMMAND = 'sql';
public const DESCRIPTION = 'Generate DB content from your world tables.';
public const APPENDIX = '=<SetupScriptList,>';
public const NOTE_START = '[sql] begin generation of:';
public const NOTE_END_OK = 'successfully finished sql generation';
public const NOTE_END_FAIL = 'finished sql generation with errors';
public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
public const LOCK_SITE = CLISetup::LOCK_RESTORE;
public function __construct()
{
if ($this->inited)
return true;
$this->defaultExecTime = ini_get('max_execution_time');
// register subscripts to CLISetup
foreach (glob('setup/tools/sqlgen/*.ss.php') as $file)
include_once $file;
$this->inited = true;
return true;
}
// args: scriptToDo, scriptSuccess, null, null // ionn
public function run(&$args) : bool
{
$todo = &$args['doSql'];
$done = &$args['doneSql'];
if (!$this->inited)
return false;
// check passed subscript names; limit to real scriptNames
if (($sqlArgs = CLISetup::getOpt('sql')) !== false)
{
if ($sqlArgs === []) // used --sql without arguments
$todo = array_keys($this->generators); // do everything
else if ($_ = array_intersect(array_keys($this->generators), $sqlArgs))
$todo = $_;
else
{
CLI::write('[sql] no valid script names supplied', CLI::LOG_ERROR);
return false;
}
// supplement self::NOTE_START
CLI::write(' - '.Lang::concat($todo), CLI::LOG_BLANK, false);
CLI::write();
}
else if ($todo)
{
$todo = array_intersect(array_keys($this->generators), is_array($todo) ? $todo : [$todo]);
if (!$todo)
return false;
}
else
return false;
$allOk = true;
// start file generation
foreach ($todo as $cmd)
{
$syncIds = []; // todo: fetch what exactly must be regenerated
$success = false;
$scriptRef = &$this->generators[$cmd];
CLI::write('[sql] filling aowow_'.$cmd.' with data');
if ($scriptRef->fulfillRequirements())
{
if ($scriptRef->generate($syncIds))
{
if (method_exists($scriptRef, 'applyCustomData'))
$success = $scriptRef->applyCustomData();
$success = true;
}
}
if (!$success)
$allOk = false;
else
$done[] = $cmd;
CLI::write('[sql] subscript \''.$cmd.'\' returned '.($success ? 'successfully' : 'with errors'), $success ? CLI::LOG_OK : CLI::LOG_ERROR);
CLI::write();
set_time_limit($this->defaultExecTime); // reset to default for the next script
}
return $allOk;
}
public function writeCLIHelp() : bool
{
if ($args = CLISetup::getOpt('sql'))
{
$anyHelp = false;
foreach ($args as $cmd)
if (isset($this->generators[$cmd]) && $this->generators[$cmd]->writeCLIHelp())
$anyHelp = true;
if ($anyHelp)
return true;
}
CLI::write(' usage: php aowow --sql=<SetupScriptList,> [--mpqDataDir: --locales:]', -1, false);
CLI::write();
CLI::write(' Regenerates DB table content for a given SetupScript. Dependencies are taken into account by the triggered calls of --sync and --update', -1, false);
CLI::write();
ksort($this->generators);
$lines = [['Command', 'TC dependencies', 'AoWoW dependencies', 'Info']];
foreach ($this->generators as $cmd => $ssRef)
{
$tcDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getRemoteDependencies() ), 35, Lang::FMT_RAW));
$aoDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getSelfDependencies()[0]), 35, Lang::FMT_RAW));
for ($i = 0; $i < max(count($tcDeps), count($aoDeps)); $i++)
$lines[] = array(
$i ? '' : $cmd,
$tcDeps[$i] ?? '',
$aoDeps[$i] ?? '',
$i ? '' : $ssRef->getInfo()
);
}
CLI::writeTable($lines);
CLI::write();
return true;
}
});
?>

View File

@@ -11,53 +11,54 @@ if (!CLI)
/* Create content from world tables / dbc files */ /* Create content from world tables / dbc files */
/************************************************/ /************************************************/
function dbc($args) : bool CLISetup::registerUtility(new class extends UtilityScript
{ {
if (!DB::isConnected(DB_AOWOW)) public $argvFlags = CLISetup::ARGV_ARRAY | CLISetup::ARGV_REQUIRED;
public $optGroup = CLISetup::OPT_GRP_UTIL;
public const COMMAND = 'dbc';
public const DESCRIPTION = 'Extract dbc files from mpqDataDir into sql table. Structural ini file must be defined in setup/tools/dbc/.';
public const APPENDIX = '=<dbcfileList,> [tablename [wowbuild]]';
public const REQUIRED_DB = [DB_AOWOW];
public const USE_CLI_ARGS = true;
// args: tblName, wowbuild, null, null // iinn
public function run(&$args) : bool
{ {
CLI::write('Database not yet set up!', CLI::LOG_WARN); foreach (CLISetup::getOpt('dbc') as $n)
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return false;
}
if (!CLISetup::getOpt('dbc'))
return writeCLIHelp();
foreach (CLISetup::getOpt('dbc') as $n)
{
$n = str_ireplace('.dbc', '', trim($n));
if (empty($n))
continue;
$opts = ['temporary' => false];
if ($args[0])
$opts['tableName'] = $args[0];
$dbc = new DBC(strtolower($n), $opts, $args[1] ?: DBC::DEFAULT_WOW_BUILD);
if ($dbc->error)
{ {
CLI::write('[dbc] required DBC '.CLI::bold($n).'.dbc not found!', CLI::LOG_ERROR); $n = str_ireplace('.dbc', '', trim($n));
CLI::write();
return false; if (empty($n))
continue;
$opts = ['temporary' => false];
if ($args[0])
$opts['tableName'] = $args[0];
$dbc = new DBC(strtolower($n), $opts, $args[1] ?: DBC::DEFAULT_WOW_BUILD);
if ($dbc->error)
{
CLI::write('[dbc] required DBC '.CLI::bold($n).'.dbc not found!', CLI::LOG_ERROR);
return false;
}
if (!$dbc->readFile())
{
CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc could not be written to DB!', CLI::LOG_ERROR);
return false;
}
$self = DB::Aowow()->selectCell('SELECT DATABASE()');
CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc written to '.CLI::bold('`'.($self ?? 'NULL').'`.`'.$dbc->getTableName().'`').'!', CLI::LOG_OK);
} }
if (!$dbc->readFile()) return true;
{
CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc could not be written to DB!', CLI::LOG_ERROR);
CLI::write();
return false;
}
$self = DB::Aowow()->selectCell('SELECT DATABASE()');
CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc written to '.CLI::bold('`'.($self ?? 'NULL').'`.`'.$dbc->getTableName().'`').'!', CLI::LOG_OK);
CLI::write();
} }
return true; public function writeCLIHelp() : bool
function writeCLIHelp() : bool
{ {
CLI::write(' usage: php aowow --dbc=<dbcfileList,..> [--locales=<regionCodes,..>] [tablename [wowbuild]]', -1, false); CLI::write(' usage: php aowow --dbc=<dbcfileList,..> [--locales=<regionCodes,..>] [tablename [wowbuild]]', -1, false);
CLI::write(); CLI::write();
@@ -113,6 +114,6 @@ function dbc($args) : bool
return true; return true;
} }
}; });
?> ?>

View File

@@ -1,207 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/**************************/
/* Configure DB connection*/
/**************************/
function dbconfig() : void
{
$databases = ['aowow', 'world', 'auth', 'characters'];
$AoWoWconf = [];
$dbFields = array(
'host' => ['Hostname / IP', false],
'user' => ['User', false],
'pass' => ['Password', true ],
'db' => ['Database Name', false],
'prefix' => ['Table prefix', false]
);
$testDB = function($idx, $name, $dbInfo)
{
$buff = ['['.CLI::bold($idx).']', $name];
if ($dbInfo['host'])
{
$result = CLI::green('OK');
$note = '';
if (DB::test($dbInfo, $note))
{
DB::load($idx, $dbInfo);
$ok = false;
switch ($idx)
{
case DB_AOWOW:
if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'aowow_dbversion'))
{
if ($date = DB::Aowow()->selectCell('SELECT `date` FROM ?_dbversion'))
{
$note = 'AoWoW DB version @ ' . date(Util::$dateFormatInternal, $date);
$ok = true;
}
else
$note = CLI::yellow('AoWoW DB version empty! Import of DB dump failed?');
}
else
$note = CLI::yellow('DB test failed to find dbversion table. setup/db_structure.sql not yet imported?');
break;
case DB_WORLD:
if (DB::World()->selectCell('SHOW TABLES LIKE ?', 'version'))
{
[$vString, $vNo] = DB::World()->selectRow('SELECT `db_version` AS "0", `cache_id` AS "1" FROM `version`');
if (strpos($vString, 'TDB') === 0)
{
if ($vNo < TDB_WORLD_MINIMUM_VER)
$note = CLI::yellow('DB test found TrinityDB version older than rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER).CLI::yellow('. Please update to at least rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER);
else if ($vNo > TDB_WORLD_EXPECTED_VER)
$note = CLI::yellow('DB test found TrinityDB version newer than rev. ').CLI::bold(TDB_WORLD_EXPECTED_VER).CLI::yellow('. Be advised! DB structure may diverge!');
else
{
$note = 'TrinityDB version @ ' . $vString;
$ok = true;
}
}
else if (strpos($vString, 'ACDB') === 0)
$note = CLI::yellow('DB test found AzerothCore DB version. AzerothCore DB structure is not supported!');
else
$note = CLI::yellow('DB test found unexpected vendor in expected version table. Uhh.. Good Luck..!?');
}
else if (DB::World()->selectCell('SHOW TABLES LIKE ?', 'db_version'))
$note = CLI::yellow('DB test found MaNGOS styled version table. MaNGOS DB structure is not supported!');
else
$note = CLI::yellow('DB test failed to find version table. TrinityDB world not yet imported?');
break;
default:
$ok = true; // no tests right now
}
if (!$ok)
$result = CLI::yellow('WARN');
}
else
$result = CLI::red('ERR');
$buff[] = $result;
$buff[] = 'mysqli://'.$dbInfo['user'].':'.($dbInfo['pass'] ? '**********' : '').'@'.$dbInfo['host'].'/'.$dbInfo['db'];
$buff[] = $dbInfo['prefix'] ? 'pre.: '.$dbInfo['prefix'] : '';
$buff[] = $note;
}
else
$buff[] = CLI::bold('<empty>');
return $buff;
};
if (file_exists('config/config.php'))
require 'config/config.php';
foreach ($databases as $idx => $name)
if (empty($AoWoWconf[$name]) && $name != 'characters' )
$AoWoWconf[$name] = array_combine(array_keys($dbFields), ['', '', '', '', '']);
while (true)
{
CLI::write('select an index to use the corresponding entry', -1, false);
$nCharDBs = 0;
$tblRows = [];
foreach ($databases as $idx => $name)
{
if ($idx != DB_CHARACTERS)
$tblRows[] = $testDB($idx, $name, $AoWoWconf[$name]);
else if (!empty($AoWoWconf[$name]))
foreach ($AoWoWconf[$name] as $charIdx => $dbInfo)
$tblRows[] = $testDB($idx + $nCharDBs++, $name.' ['.$charIdx.']', $AoWoWconf[$name][$charIdx]);
}
$tblRows[] = ['['.CLI::bold('N').']', 'new characters DB'];
$tblRows[] = ['['.CLI::bold('R').']', 'retest / reload DBs'];
CLI::writeTable($tblRows, false, true);
while (true)
{
if (CLI::read(['idx' => ['', true, true, '/\d|R|N/i']], $uiIndex) && $uiIndex)
{
if (strtoupper($uiIndex['idx']) == 'R')
continue 2;
else if (($uiIndex['idx'] >= DB_AOWOW && $uiIndex['idx'] < (DB_CHARACTERS + $nCharDBs)) || strtoupper($uiIndex['idx']) == 'N')
{
$curFields = $uiIndex['idx'] ? $dbFields : array_slice($dbFields, 0, 4);
if (strtoupper($uiIndex['idx']) == 'N') // add new characters DB
$curFields['realmId'] = ['Realm Id', false, false, '/\d{1,3}/'];
if (CLI::read($curFields, $uiRealm))
{
if ($uiIndex['idx'] == DB_AOWOW && $uiRealm)
$uiRealm['prefix'] = 'aowow_';
if (strtoupper($uiIndex['idx']) == 'N') // new char DB
{
if ($uiRealm)
{
$_ = $uiRealm['realmId'];
unset($uiRealm['realmId']);
$AoWoWconf[$databases[DB_CHARACTERS]][$_] = $uiRealm;
}
}
else if ($uiIndex['idx'] < DB_CHARACTERS) // auth, world or aowow
$AoWoWconf[$databases[$uiIndex['idx']]] = $uiRealm ?: array_combine(array_keys($dbFields), ['', '', '', '', '']);
else // existing char DB
{
$i = 0;
foreach ($AoWoWconf[$databases[DB_CHARACTERS]] as $realmId => &$dbInfo)
{
if ($uiIndex['idx'] - DB_CHARACTERS != $i++)
continue;
if ($uiRealm)
$dbInfo = $uiRealm;
else
unset($AoWoWconf[$databases[DB_CHARACTERS]][$realmId]);
}
}
// write config file
$buff = "<?php\n\nif (!defined('AOWOW_REVISION'))\n die('illegal access');\n\n\n";
foreach ($databases as $db)
{
if ($db != 'characters')
$buff .= '$AoWoWconf[\''.$db.'\'] = '.var_export($AoWoWconf[$db], true).";\n\n";
else if (isset($AoWoWconf[$db]))
foreach ($AoWoWconf[$db] as $idx => $charInfo)
$buff .= '$AoWoWconf[\''.$db.'\'][\''.$idx.'\'] = '.var_export($AoWoWconf[$db][$idx], true).";\n\n";
}
$buff .= "?>\n";
CLI::write();
CLISetup::writeFile('config/config.php', $buff);
continue 2;
}
else
{
CLI::write("edit canceled! returning to list...", CLI::LOG_INFO);
CLI::write();
sleep(1);
continue 2;
}
}
}
else
{
CLI::write("leaving db setup...", CLI::LOG_INFO);
CLI::write();
break 2;
}
}
}
}
?>

View File

@@ -0,0 +1,327 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/**************************/
/* Configure DB connection*/
/**************************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvOpts = ['db'];
public $optGroup = CLISetup::OPT_GRP_SETUP;
public const COMMAND = 'database';
public const DESCRIPTION = 'Set up DB connection.';
public const PROMPT = 'Please enter your database credentials.';
public const NOTE_ERROR = 'could not establish connection to:';
private const CONFIG_FILE = 'config/config.php';
private $databases = ['aowow', 'world', 'auth', 'characters'];
private $config = [];
private $dbFields = array(
'host' => ['Server Host', false],
'user' => ['User', false],
'pass' => ['Password', true ],
'db' => ['Database Name', false],
'prefix' => ['Table prefix', false]
);
private $icons = ['Normal[0]', 'PvP', null, null, 'Normal[4]', 'RP', null, 'RP-PvP'];
private $regions = array(
null, 'Development', 'United States', 'Oceanic', 'Latin America', 'Tournament (Americas)', 'Korea', 'Tournament (Korea)', 'English', 'German', 'French', 'Spanish', 'Russian', 'Tournament (EU)', 'Taiwan', 'Tournament (Taiwan)', 'China',
25 => 'Tournament (China)', 26 => 'Test Server', 27 => 'Tournament (Test Server)', 28 => 'QA Server', 30 => 'Test Server 2'
);
// args: null, null, null, null // nnnn
public function run(&$args) : bool
{
if (!$this->config && file_exists(self::CONFIG_FILE))
{
require self::CONFIG_FILE;
$this->config = $AoWoWconf;
unset($AoWoWconf);
}
foreach ($this->databases as $idx => $name)
if (empty($this->config[$name]) && $name != 'characters' )
$this->config[$name] = array_combine(array_keys($this->dbFields), ['', '', '', '', '']);
while (true)
{
CLI::write("select an index to use the corresponding entry", -1, false);
$nCharDBs = 0;
$tblRows = [];
foreach ($this->databases as $idx => $name)
{
if ($idx != DB_CHARACTERS)
$tblRows[] = $this->testDB($idx, $name, $this->config[$name]);
else if (!empty($this->config[$name]))
foreach ($this->config[$name] as $charIdx => $dbInfo)
$tblRows[] = $this->testDB($idx + $nCharDBs++, $name.' ['.$charIdx.']', $this->config[$name][$charIdx]);
}
$tblRows[] = ['['.CLI::bold('N').']', 'new characters DB'];
$tblRows[] = ['['.CLI::bold('S').']', 'show available realms'];
$tblRows[] = ['['.CLI::bold('R').']', 'retest / reload DBs'];
CLI::writeTable($tblRows, false, true);
while (true)
{
if (CLI::read(['idx' => ['', true, true, '/\d|R|N|S/i']], $uiIndex) && $uiIndex)
{
if (strtoupper($uiIndex['idx']) == 'R')
continue 2;
else if (strtoupper($uiIndex['idx']) == 'S')
{
CLI::write();
if (!DB::isConnectable(DB_AUTH) || !$this->test())
CLI::write('[db] auth server not yet set up.', CLI::LOG_ERROR);
else if ($realms = DB::Auth()->select('SELECT id AS "0", name AS "1", icon AS "2", timezone AS "3", isRestricted AS "4" FROM realmlist'))
{
$tbl = [['Realm Id', 'Name', 'Type', 'Region', 'GMLevel', 'Status']];
foreach ($realms as [$id, $name, $icon, $region, $level])
{
$status = [];
$hasRegion = false;
foreach (Profiler::REGIONS as $n => $valid)
if ($hasRegion = in_array($region, $valid))
{
if ($n == 'dev')
$status[] = 'Restricted region (staff only)';
break;
}
if (!$hasRegion && !$status)
$status[] = 'Unsupported region';
if ($level > 0)
$status[] = 'GM-Level locked';
if (DB::isConnectable(DB_CHARACTERS . $id))
$status[] = 'Already in use';
$tbl[] = [$id, $name, $this->icons[$icon] ?? '<UNK:'.$icon.'>', $this->regions[$region] ?? '<UNK:'.$region.'>', $level, $status ? CLI::yellow(implode(', ', $status)) : CLI::green('Usable')];
}
CLI::writeTable($tbl);
}
else
CLI::write('[db] table `realmlist` is empty.', CLI::LOG_WARN);
CLI::write();
continue 2;
}
else if (($uiIndex['idx'] >= DB_AOWOW && $uiIndex['idx'] < (DB_CHARACTERS + $nCharDBs)) || strtoupper($uiIndex['idx']) == 'N')
{
$curFields = $uiIndex['idx'] ? $this->dbFields : array_slice($this->dbFields, 0, 4);
if (strtoupper($uiIndex['idx']) == 'N') // add new characters DB
$curFields['realmId'] = ['Realm Id', false, false, '/\d{1,3}/'];
if (CLI::read($curFields, $uiRealm))
{
if ($uiIndex['idx'] == DB_AOWOW && $uiRealm)
$uiRealm['prefix'] = 'aowow_';
if (strtoupper($uiIndex['idx']) == 'N') // new char DB
{
if ($uiRealm)
{
$_ = $uiRealm['realmId'];
unset($uiRealm['realmId']);
$this->config[$this->databases[DB_CHARACTERS]][$_] = $uiRealm;
}
}
else if ($uiIndex['idx'] < DB_CHARACTERS) // auth, world or aowow
$this->config[$this->databases[$uiIndex['idx']]] = $uiRealm ?: array_combine(array_keys($this->dbFields), ['', '', '', '', '']);
else // existing char DB
{
$i = 0;
foreach ($this->config[$this->databases[DB_CHARACTERS]] as $realmId => &$dbInfo)
{
if ($uiIndex['idx'] - DB_CHARACTERS != $i++)
continue;
if ($uiRealm)
$dbInfo = $uiRealm;
else
unset($this->config[$this->databases[3]][$realmId]);
}
}
// write config file
$buff = "<?php\n\nif (!defined('AOWOW_REVISION'))\n die('illegal access');\n\n\n";
foreach ($this->databases as $db)
{
if ($db != 'characters')
$buff .= '$AoWoWconf[\''.$db.'\'] = '.var_export($this->config[$db], true).";\n\n";
else if (isset($this->config[$db]))
foreach ($this->config[$db] as $idx => $charInfo)
$buff .= '$AoWoWconf[\''.$db.'\'][\''.$idx.'\'] = '.var_export($this->config[$db][$idx], true).";\n\n";
}
$buff .= "?>\n";
CLI::write();
CLISetup::writeFile(self::CONFIG_FILE, $buff);
continue 2;
}
else
{
CLI::write("[db] edit canceled! returning to list...", CLI::LOG_INFO);
CLI::write();
sleep(1);
continue 2;
}
}
}
else
{
CLI::write("[db] leaving db config...", CLI::LOG_INFO);
CLI::write();
break 2;
}
}
}
return true;
}
public function test(?array &$error = []) : bool
{
if (!$this->config)
{
require self::CONFIG_FILE;
$this->config = $AoWoWconf;
unset($AoWoWconf);
}
$error = [];
foreach (['aowow', 'world', 'auth'] as $idx => $what)
{
if ($what == 'auth' && (empty($this->config['auth']) || empty($this->config['auth']['host'])))
continue;
// init proper access for further setup
if (DB::test($this->config[$what], $err))
{
DB::load($idx, $this->config[$what]);
switch ($idx)
{
case DB_AOWOW:
if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'aowow_dbversion'))
Cfg::load(); // first time load after successful db setup
else
$error[] = ' * '.$what.': doesn\'t seem to contain aowow tables!';
break;
case DB_WORLD:
if (!DB::World()->selectCell('SHOW TABLES LIKE ?', 'version'))
$error[] = ' * '.$what.': doesn\'t seem to contain TrinityCore world tables!';
else if (DB::World()->selectCell('SELECT `cache_id` FROM `version`') < TDB_WORLD_MINIMUM_VER)
$error[] = ' * '.$what.': TDB world db is structurally outdated! (min rev.: '.CLI::bold(TDB_WORLD_MINIMUM_VER).')';
break;
default:
// no further checks at this time
}
}
else
$error[] = ' * '.$what.': '.$err;
}
return empty($error);
}
private function testDB($idx, $name, $dbInfo)
{
$buff = ['['.CLI::bold($idx).']', $name];
if ($dbInfo['host'])
{
$result = CLI::green('OK');
$note = '';
if (DB::test($dbInfo, $note))
{
DB::load($idx, $dbInfo);
$ok = false;
switch ($idx)
{
case DB_AOWOW:
if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'aowow_dbversion'))
{
if ($date = DB::Aowow()->selectCell('SELECT `date` FROM ?_dbversion'))
{
$note = 'AoWoW DB version @ ' . date(Util::$dateFormatInternal, $date);
$ok = true;
}
else
$note = CLI::yellow('AoWoW DB version empty! Import of DB dump failed?');
}
else
$note = CLI::yellow('DB test failed to find dbversion table. setup/db_structure.sql not yet imported?');
break;
case DB_WORLD:
if (DB::World()->selectCell('SHOW TABLES LIKE ?', 'version'))
{
[$vString, $vNo] = DB::World()->selectRow('SELECT `db_version` AS "0", `cache_id` AS "1" FROM `version`');
if (strpos($vString, 'TDB') === 0)
{
if ($vNo < TDB_WORLD_MINIMUM_VER)
$note = CLI::yellow('DB test found TrinityDB version older than rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER).CLI::yellow('. Please update to at least rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER);
else if ($vNo > TDB_WORLD_EXPECTED_VER)
$note = CLI::yellow('DB test found TrinityDB version newer than rev. ').CLI::bold(TDB_WORLD_EXPECTED_VER).CLI::yellow('. Be advised! DB structure may diverge!');
else
{
$note = 'TrinityDB version @ ' . $vString;
$ok = true;
}
}
else if (strpos($vString, 'ACDB') === 0)
$note = CLI::yellow('DB test found AzerothCore DB version. AzerothCore DB structure is not supported!');
else
$note = CLI::yellow('DB test found unexpected vendor in expected version table. Uhh.. Good Luck..!?');
}
else if (DB::World()->selectCell('SHOW TABLES LIKE ?', 'db_version'))
$note = CLI::yellow('DB test found MaNGOS styled version table. MaNGOS DB structure is not supported!');
else
$note = CLI::yellow('DB test failed to find version table. TrinityDB world not yet imported?');
break;
default:
$ok = true; // no tests right now
}
if (!$ok)
$result = CLI::yellow('WARN');
}
else
$result = CLI::red('ERR');
$buff[] = $result;
$buff[] = 'mysqli://'.$dbInfo['user'].':'.($dbInfo['pass'] ? '**********' : '').'@'.$dbInfo['host'].'/'.$dbInfo['db'];
$buff[] = $dbInfo['prefix'] ? 'pre.: '.$dbInfo['prefix'] : '';
$buff[] = $note;
}
else
$buff[] = CLI::bold('<empty>');
return $buff;
}
public function writeCLIHelp() : bool
{
CLI::write(' Remember to use the correct Realm Id from '.CLI::bold('`logon`.`realmlist`').' when connecting to your characters DB.', -1, false);
CLI::write(' To remove a db entry edit it and leave all fields empty.', -1, false);
CLI::write();
CLI::write();
return true;
}
});
?>

View File

@@ -0,0 +1,173 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*************************/
/* Create required files */
/*************************/
CLISetup::registerUtility(new class extends UtilityScript
{
use TrSubScripts;
public $argvFlags = CLISetup::ARGV_ARRAY | CLISetup::ARGV_OPTIONAL;
public $optGroup = CLISetup::OPT_GRP_UTIL;
public const COMMAND = 'build';
public const DESCRIPTION = 'Compile image files and data dumps.';
public const APPENDIX = '=<SetupScriptList,>';
public const NOTE_START = '[build] begin generation of:';
public const NOTE_END_OK = 'successfully finished file generation';
public const NOTE_END_FAIL = 'finished file generation with errors';
public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
public const LOCK_SITE = CLISetup::LOCK_RESTORE;
private $uploadDirs = array( // stuff that should be writable by www-data and isn't directly created by setup steps
'static/uploads/screenshots/normal/',
'static/uploads/screenshots/pending/',
'static/uploads/screenshots/resized/',
'static/uploads/screenshots/temp/',
'static/uploads/screenshots/thumb/',
'static/uploads/temp/',
'static/uploads/guide/images/',
);
public function __construct()
{
if ($this->inited)
return true;
$this->defaultExecTime = ini_get('max_execution_time');
// register subscripts to CLISetup
foreach (glob('setup/tools/filegen/*.ss.php') as $file)
include_once $file;
$this->inited = true;
return true;
}
// args: scriptToDo, scriptSuccess, null, null // ionn
public function run(&$args) : bool
{
$todo = &$args['doBuild'];
$done = &$args['doneBuild'];
if (!$this->inited)
return false;
// check passed subscript names; limit to real scriptNames
if (($buildArgs = CLISetup::getOpt('build')) !== false)
{
if ($buildArgs === []) // used --build without arguments
$todo = array_keys($this->generators); // do everything
else if ($_ = array_intersect(array_keys($this->generators), $buildArgs))
$todo = $_;
else
{
CLI::write('[build] no valid script names supplied', CLI::LOG_ERROR);
return false;
}
// supplement self::NOTE_START
CLI::write(' - '.Lang::concat($todo), CLI::LOG_BLANK, false);
CLI::write();
}
else if ($todo)
{
$todo = array_intersect(array_keys($this->generators), is_array($todo) ? $todo : [$todo]);
if (!$todo)
return false;
}
else
return false;
// create user upload dir structure
foreach ($this->uploadDirs as $ud)
{
if (CLISetup::writeDir($ud))
continue;
CLI::write('[build] could not create directory: '.CLI::bold($ud), CLI::LOG_ERROR);
return false;
}
$done = [];
$allOk = true;
// start file generation
foreach ($todo as $cmd)
{
$success = false;
$scriptRef = &$this->generators[$cmd];
CLI::write('[build] gathering data for '.$cmd);
if ($scriptRef->fulfillRequirements())
$success = $scriptRef->generate();
if (!$success)
$allOk = false;
else
$done[] = $cmd;
CLI::write('[build] subscript \''.$cmd.'\' returned '.($success ? 'successfully' : 'with errors'), $success ? CLI::LOG_OK : CLI::LOG_ERROR);
CLI::write();
set_time_limit($this->defaultExecTime); // reset to default for the next script
}
return $allOk;
}
public function writeCLIHelp() : bool
{
if ($args = CLISetup::getOpt('build'))
{
$anyHelp = false;
foreach ($args as $cmd)
if (isset($this->generators[$cmd]) && $this->generators[$cmd]->writeCLIHelp())
$anyHelp = true;
if ($anyHelp)
return true;
}
CLI::write(' usage: php aowow --build=<SetupScriptList,> [--mpqDataDir: --locales:]', -1, false);
CLI::write();
CLI::write(' Compiles files for a given SetupScript. Existing files are kept by default. Dependencies are taken into account by the triggered calls of --sync --update', -1, false);
CLI::write();
ksort($this->generators);
$lines = [['Command', 'TC dependencies', 'AoWoW dependencies', 'Info']];
foreach ($this->generators as $cmd => $ssRef)
{
$tcDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getRemoteDependencies() ), 35, Lang::FMT_RAW));
$aoDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getSelfDependencies()[0]), 35, Lang::FMT_RAW));
for ($i = 0; $i < max(count($tcDeps), count($aoDeps)); $i++)
$lines[] = array(
$i ? '' : $cmd,
$tcDeps[$i] ?? '',
$aoDeps[$i] ?? '',
$i ? '' : $ssRef->getInfo()
);
}
CLI::writeTable($lines);
CLI::write();
return true;
}
});
?>

View File

@@ -1,376 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*********************************************/
/* string setup steps together for first use */
/*********************************************/
function setup() : void
{
require_once 'setup/tools/sqlGen.class.php';
require_once 'setup/tools/fileGen.class.php';
SqlGen::init(SqlGen::MODE_FIRSTRUN);
FileGen::init(FileGen::MODE_FIRSTRUN);
/****************/
/* define steps */
/****************/
$upd = [[], []]; // ref to pass commands from 'update' to 'sync'
$steps = array(
// clisetup, params, test function, introText, errorText
['dbconfig', [null, null], 'testDB', 'Please enter your database credentials.', 'could not establish connection to:'],
['siteconfig', [null, null], 'testSelf', 'SITE_HOST and STATIC_HOST '.CLI::bold('must').' be set. Also enable FORCE_SSL if needed. You may also want to change other variables such as NAME, NAME_SHORT or LOCALES.', 'could not access:'],
// sql- and build- stuff here
['SqlGen::generate', 'dungeonmap', null, null, null],
['SqlGen::generate', 'skilllineability', null, null, null],
['SqlGen::generate', 'soundemitters', null, null, null],
['SqlGen::generate', 'worldmaparea', null, null, null],
['SqlGen::generate', 'areatrigger', null, null, null],
['SqlGen::generate', 'achievementcriteria', null, null, null],
['SqlGen::generate', 'glyphproperties', null, null, null],
['SqlGen::generate', 'itemenchantment', null, null, null],
['SqlGen::generate', 'itemenchantmentcondition', null, null, null],
['SqlGen::generate', 'itemextendedcost', null, null, null],
['SqlGen::generate', 'itemlimitcategory', null, null, null],
['SqlGen::generate', 'itemrandomproppoints', null, null, null],
['SqlGen::generate', 'lock', null, null, null],
['SqlGen::generate', 'mails', null, null, null],
['SqlGen::generate', 'scalingstatdistribution', null, null, null],
['SqlGen::generate', 'scalingstatvalues', null, null, null],
['SqlGen::generate', 'spellfocusobject', null, null, null],
['SqlGen::generate', 'spellrange', null, null, null],
['SqlGen::generate', 'spellvariables', null, null, null],
['SqlGen::generate', 'totemcategory', null, null, null],
['SqlGen::generate', 'talents', null, null, null],
['SqlGen::generate', 'classes', null, null, null],
['SqlGen::generate', 'factions', null, null, null],
['SqlGen::generate', 'factiontemplate', null, null, null],
['SqlGen::generate', 'holidays', null, null, null],
['SqlGen::generate', 'icons', null, null, null],
['SqlGen::generate', 'itemrandomenchant', null, null, null],
['SqlGen::generate', 'races', null, null, null],
['SqlGen::generate', 'shapeshiftforms', null, null, null],
['SqlGen::generate', 'skillline', null, null, null],
['SqlGen::generate', 'emotes', null, null, null],
['SqlGen::generate', 'achievement', null, null, null],
['SqlGen::generate', 'creature', null, null, null],
['SqlGen::generate', 'currencies', null, null, null],
['SqlGen::generate', 'events', null, null, null],
['SqlGen::generate', 'objects', null, null, null],
['SqlGen::generate', 'pet', null, null, null],
['SqlGen::generate', 'quests', null, null, null],
['SqlGen::generate', 'quests_startend', null, null, null],
['SqlGen::generate', 'spell', null, null, null],
['SqlGen::generate', 'spelldifficulty', null, null, null],
['SqlGen::generate', 'taxi', null, null, null],
['SqlGen::generate', 'titles', null, null, null],
['SqlGen::generate', 'items', null, null, null],
['FileGen::generate', 'complexImg', null, null, null], // alphamaps generated here are requires for spawns/waypoints
['SqlGen::generate', 'zones', null, null, null],
['SqlGen::generate', 'spawns', null, null, null], // this one ^_^
['SqlGen::generate', 'itemset', null, null, null],
['SqlGen::generate', 'item_stats', null, null, null],
['SqlGen::generate', 'source', null, null, null],
['SqlGen::generate', 'sounds', null, null, null],
['SqlGen::generate', 'declinedwords', null, null, null],
['FileGen::generate', 'soundfiles', null, null, null],
['FileGen::generate', 'searchplugin', null, null, null],
['FileGen::generate', 'power', null, null, null],
['FileGen::generate', 'searchboxScript', null, null, null],
['FileGen::generate', 'demo', null, null, null],
['FileGen::generate', 'searchboxBody', null, null, null],
['FileGen::generate', 'realmMenu', null, null, null],
['FileGen::generate', 'locales', null, null, null],
['FileGen::generate', 'itemScaling', null, null, null],
['FileGen::generate', 'realms', null, null, null],
['FileGen::generate', 'statistics', null, null, null],
['FileGen::generate', 'simpleImg', null, null, null],
['FileGen::generate', 'talentCalc', null, null, null],
['FileGen::generate', 'pets', null, null, null],
['FileGen::generate', 'talentIcons', null, null, null],
['FileGen::generate', 'glyphs', null, null, null],
['FileGen::generate', 'itemsets', null, null, null],
['FileGen::generate', 'enchants', null, null, null],
['FileGen::generate', 'gems', null, null, null],
['FileGen::generate', 'profiler', null, null, null],
['FileGen::generate', 'weightPresets', null, null, null],
['FileGen::generate', 'markup', null, null, null],
// apply sql-updates from repository
['update', &$upd, null, null, null],
['sync', &$upd, null, null, null],
['account', [null, null], 'testAcc', 'Please create your admin account.', 'There is no user with administrator privileges in the DB.']
);
/**********/
/* helper */
/**********/
$saveProgress = function(int $nStep) : void
{
$h = fopen('cache/firstrun', 'w');
fwrite($h, AOWOW_REVISION."\n".($nStep + 1)."\n");
fclose($h);
};
function testDB(array &$error) : bool
{
require 'config/config.php';
$error = [];
foreach (['aowow', 'world', 'auth'] as $idx => $what)
{
if ($what == 'auth' && (empty($AoWoWconf['auth']) || empty($AoWoWconf['auth']['host'])))
continue;
// init proper access for further setup
if (DB::test($AoWoWconf[$what], $err))
{
DB::load($idx, $AoWoWconf[$what]);
switch ($idx)
{
case DB_AOWOW:
if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'aowow_dbversion'))
Cfg::load(); // first time load after successful db setup
else
$error[] = ' * '.$what.': doesn\'t seem to contain aowow tables!';
break;
case DB_WORLD:
if (!DB::World()->selectCell('SHOW TABLES LIKE ?', 'version'))
$error[] = ' * '.$what.': doesn\'t seem to contain TrinityCore world tables!';
else if (DB::World()->selectCell('SELECT `cache_id` FROM `version`') < TDB_WORLD_MINIMUM_VER)
$error[] = ' * '.$what.': TDB world db is structurally outdated! (min rev.: '.CLI::bold(TDB_WORLD_MINIMUM_VER).')';
break;
default:
// no further checks at this time
}
}
else
$error[] = ' * '.$what.': '.$err;
}
return empty($error);
}
function testSelf(array &$error) : bool
{
$error = [];
$test = function(&$protocol, &$host, $testFile, &$rCode)
{
$res = get_headers($protocol.$host.$testFile, true);
if (!preg_match("/HTTP\/[0-9\.]+\s+([0-9]+)/", $res[0], $m))
return false;
$rCode = $m[1];
if ($rCode == 200)
return true;
if ($rCode == 301 || $rCode == 302)
{
if (!empty($res['Location']) && preg_match("/(https?:\/\/)(.*)".strtr($testFile, ['/' => '\/', '.' => '\.'])."/i", is_array($res['Location']) ? array_pop($res['Location']) : $res['Location'], $n))
{
$protocol = $n[1];
$host = $n[2];
}
return false;
}
$rCode = 0;
return false;
};
if (!DB::isConnected(DB_AOWOW))
{
$error[] = ' * not connected to DB';
return false;
}
$prot = Cfg::get('FORCE_SSL') ? 'https://' : 'http://';
$cases = array(
'site_host' => [$prot, Cfg::get('SITE_HOST'), '/README.md'],
'static_host' => [$prot, Cfg::get('STATIC_HOST'), '/css/aowow.css']
);
foreach ($cases as $conf => [$protocol, $host, $testFile])
{
if ($host)
{
if (!$test($protocol, $host, $testFile, $resp))
{
if ($resp == 301 || $resp == 302)
{
CLI::write('self test received status '.CLI::bold($resp).' (page moved) for '.$conf.', pointing to: '.$protocol.$host.$testFile, CLI::LOG_WARN);
if (!CLI::read(['x' => ['should '.CLI::bold($conf).' be set to '.CLI::bold($host).' and force_ssl be updated? (y/n)', true, true, '/y|n/i']], $uiYN) || !$uiYN || strtolower($uiYN['x']) == 'n')
$error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
else
{
Cfg::set($conf, $host);
Cfg::set('FORCE_SSL', $protocol == 'https://');
}
CLI::write();
}
else
$error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
}
}
else
$error[] = ' * '.strtoupper($conf).' is empty';
}
return empty($error);
}
function testAcc(array &$error) : bool
{
$error = [];
return !!DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `userPerms` = 1');
}
/********************/
/* get current step */
/********************/
$startStep = 0;
if (file_exists('cache/firstrun'))
{
$rows = file('cache/firstrun');
if ((int)$rows[0] == AOWOW_REVISION)
$startStep = (int)$rows[1];
}
if (CLISetup::getOpt('help'))
{
CLI::write();
CLI::write(' usage: php aowow --setup [--locales: --mpqDataDir:]', -1, false);
CLI::write();
CLI::write(' Initially essential connection information are set up and basic connectivity tests are run afterwards.', -1, false);
CLI::write(' In the main stage dbc and world data is compiled into the database and required sound, image and data files are generated.', -1, false);
CLI::write(' This does not require further input and will take about 15-20 minutes, plus 10 minutes per additional locale.', -1, false);
CLI::write(' Lastly pending updates are applied and you are prompted to create an administrator account.', -1, false);
if ($startStep)
{
CLI::write();
CLI::write(' You are currently on step '.($startStep + 1).' / '.count($steps).'. You can resume or restart the setup process.', -1, false);
}
CLI::write();
return;
}
if ($startStep)
{
CLI::write('Found firstrun progression info. (Halted on subscript '.($steps[$startStep][1][0] ? $steps[$startStep][1] : $steps[$startStep][0]).')', CLI::LOG_INFO);
$msg = '';
if (!CLI::read(['x' => ['continue setup? (y/n)', true, true, '/y|n/i']], $uiYN) || !$uiYN || strtolower($uiYN['x']) == 'n')
{
$msg = 'Starting setup from scratch...';
$startStep = 0;
}
else
$msg = 'Resuming setup from step '.$startStep.'...';
CLI::write();
CLI::write($msg);
sleep(1);
}
/*******/
/* run */
/*******/
CLISetup::siteLock(CLISetup::LOCK_ON);
foreach ($steps as $idx => $step)
{
if ($startStep > $idx)
continue;
if (!strpos($step[0], '::') && !is_callable($step[0]))
require_once 'setup/tools/clisetup/'.$step[0].'.func.php';
if ($step[3])
{
CLI::write($step[3]);
if (!CLI::read([['Press any key to continue', true]])) // we don't actually care about the input
return;
}
while (true)
{
if (strpos($step[0], '::'))
$res = call_user_func($step[0], $step[1]);
else
{
$args = &$step[1]; // see: https://github.com/php/php-src/issues/14202
$res = $step[0]($args[0], $args[1]);
}
// check script result
if ($step[2])
{
$errors = [];
if (!$step[2]($errors))
{
CLI::write($step[4], CLI::LOG_ERROR);
foreach ($errors as $e)
CLI::write($e);
}
else
{
$saveProgress($idx);
break;
}
}
else if ($res !== false)
{
$saveProgress($idx);
break;
}
if (CLI::read(['x' => ['['.CLI::bold('c').']ontinue anyway? ['.CLI::bold('r').']etry? ['.CLI::bold('a').']bort?', true, true, '/c|r|a/i']], $uiCRA) && $uiCRA)
{
CLI::write();
switch(strtolower($uiCRA['x']))
{
case 'c':
$saveProgress($idx);
break 2;
case 'r':
break;
case 'a':
return;
}
}
else
{
CLI::write();
return;
}
}
}
unlink('cache/firstrun');
CLISetup::siteLock(CLISetup::LOCK_OFF);
CLI::write('setup finished', CLI::LOG_OK);
return;
}
?>

View File

@@ -0,0 +1,184 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*********************************************/
/* string setup steps together for first use */
/*********************************************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvOpts = ['s'];
public $optGroup = CLISetup::OPT_GRP_SETUP;
public const COMMAND = 'setup';
public const DESCRIPTION = 'Step by step initial setup. Resumes if interrupted.';
public const NOTE_END_OK = 'setup finished successfully';
public const NOTE_END_FAIL = 'setup finished with errors';
public const SITE_LOCK = CLISetup::LOCK_ON;
private $startStep = 0;
private $dynArgs = ['doSql' => [], 'doBuild' => []]; // ref to pass commands from 'update' to 'sync'
private $steps = array(
// [staticUS, $name, [...args]]
['database', '', []],
['configure', '', []],
// sql- and build- stuff here
['update', '', []],
['sync', '', []],
['account', '', []]
);
private const STEP_FILE = 'cache/setup/firstrun';
// Note! Must be loaded after all SetupScripts have been registered
public function __construct()
{
/********************/
/* get current step */
/********************/
if (file_exists(self::STEP_FILE))
{
$rows = file(self::STEP_FILE);
if ((int)$rows[0] == AOWOW_REVISION)
$this->startStep = (int)$rows[1];
}
/****************/
/* define steps */
/****************/
# link required steps with param
$this->steps[2][2] = &$this->dynArgs; // update
$this->steps[3][2] = &$this->dynArgs; // sync
# from /sqlgen + /filegen .. already sorted by CLISetup
foreach (CLISetup::getSubScripts() as $name => [$invoker, $ssRef])
{
if ($ssRef->isOptional)
continue;
if ($invoker == 'sql')
array_splice($this->steps, -3, 0, [[$invoker, $name, ['doSql' => $name]]]);
else if ($invoker == 'build')
array_splice($this->steps, -3, 0, [[$invoker, $name, ['doBuild' => $name]]]);
}
}
// args: null, null, null, null // nnnn
public function run(&$args) : bool
{
if ($this->startStep)
{
CLI::write('[setup] found firstrun progression info. (Halted on subscript: '.($this->steps[$this->startStep][1] ?: $this->steps[$this->startStep][0]).')', CLI::LOG_INFO);
$msg = '';
if (!CLI::read(['x' => ['continue setup? (y/n)', true, true, '/y|n/i']], $uiN) || !$uiN || strtolower($uiN['x']) == 'n')
{
$msg = '[setup] starting from scratch...';
$this->startStep = 0;
}
else
$msg = '[setup] resuming from step '.($this->startStep + 1).'...';
CLI::write();
CLI::write($msg);
sleep(1);
}
// init temp setup dir
if ($info = new SplFileInfo(self::STEP_FILE))
CLISetup::writeDir($info->getPath());
/*******/
/* run */
/*******/
foreach ($this->steps as $idx => [$usName, , $param])
{
if ($this->startStep > $idx)
continue;
while (true)
{
CLI::write('[setup] step '.($idx + 1).' / '.count($this->steps));
if (CLISetup::run($usName, $param))
{
$this->saveProgress($idx);
break;
}
if (CLI::read(['x' => ['['.CLI::bold('c').']ontinue anyway? ['.CLI::bold('r').']etry? ['.CLI::bold('a').']bort?', true, true, '/c|r|a/i']], $uiCRA) && $uiCRA)
{
CLI::write();
switch(strtolower($uiCRA['x']))
{
case 'c':
$this->saveProgress($idx);
break 2;
case 'r':
break;
case 'a':
return false;
}
}
else
{
CLI::write();
return false;
}
}
}
unlink(self::STEP_FILE);
return true;
}
public function writeCLIHelp() : bool
{
CLI::write(' usage: php aowow --setup [--locales: --mpqDataDir:]', -1, false);
CLI::write();
CLI::write(' Initially essential connection information are set up and basic connectivity tests run afterwards.', -1, false);
CLI::write(' In the main stage dbc and world data is compiled into the database and required sound, image and data files are generated.', -1, false);
CLI::write(' This should not require further input and will take about 15-20 minutes, plus 10 minutes per additional locale.', -1, false);
CLI::write(' Lastly pending updates are applied and you are prompted to create an administrator account.', -1, false);
if ($this->startStep)
{
CLI::write();
CLI::write(' You are currently on step '.($this->startStep + 1).' / '.count($this->steps).' ('.($this->steps[$this->startStep][1] ?: $this->steps[$this->startStep][0][1]).'). You can resume or restart the setup process.', -1, false);
}
CLI::write();
CLI::write();
return true;
}
/**********/
/* helper */
/**********/
private function saveProgress (int $nStep) : void
{
if ($h = fopen(self::STEP_FILE, 'w'))
{
fwrite($h, AOWOW_REVISION."\n".($nStep + 1)."\n");
fclose($h);
}
else
CLI::write(' * UtilScript::setup - Could not access step file', CLI::LOG_ERROR);
}
});
?>

View File

@@ -1,315 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/****************************/
/* Configure Site variables */
/****************************/
function siteconfig() : void
{
$updScripts = [];
if (!DB::isConnected(DB_AOWOW))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return;
}
function toOptList(string $options, $curVal, bool $bitmask = false) : string
{
$result = '';
foreach (explode(', ', $options) as $opt)
{
[$val, $name] = explode(':', $opt);
$equal = $bitmask ? ($curVal & (1 << $val)) : $curVal == $val;
$result .= '['.($equal ? 'x' : ' ').']'.$name.' ';
}
return substr($result, 0, -1);
}
function formatValue($value, $flags, $opts) : string
{
if ($flags & Cfg::FLAG_TYPE_BOOL)
return '[bool] '.($value ? '<Enabled>' : '<Disabled>');
if ($flags & Cfg::FLAG_OPT_LIST)
return '[opt] '.toOptList($opts, $value, false);
if ($flags & Cfg::FLAG_BITMASK)
return '[mask] '.toOptList($opts, $value, true);
if ($flags & Cfg::FLAG_TYPE_FLOAT)
return '[float] '.floatVal($value);
if ($flags & Cfg::FLAG_TYPE_INT)
return '[int] '.intVal($value);
// if ($flags & Cfg::FLAG_TYPE_STRING)
if ($value === '')
return '[str] '.(($flags & Cfg::FLAG_REQUIRED) ? CLI::red('<empty>') : CLI::grey('<empty>'));
else
return '[str] "'.$value.'"';
}
while (true)
{
CLI::write('select a numerical index or name to use the corresponding entry');
CLI::write();
$sumNum = 0;
$cfgList = [];
$hasEmpty = false;
$listBuff = [];
foreach (Cfg::$categories as $idx => $cat)
{
$listBuff[] = '===== '.$cat.' =====';
foreach (Cfg::forCategory($idx) as $key => [$value, $flags, $catg, $default, $comment])
{
$isPhp = $flags & Cfg::FLAG_PHP;
if ($value === '' && ($flags & Cfg::FLAG_REQUIRED))
$hasEmpty = true;
$cfgList[$sumNum] = strtolower($key);
$row = '['.CLI::bold($sumNum).'] '.(($sumNum) > 9 ? '' : ' ').($isPhp ? ' PHP ' : ' AOWOW ');
$row .= str_pad($isPhp ? strtolower($key) : strtoupper($key), 35);
$opts = explode(' - ', $comment);
$row .= formatValue($value, $flags, $opts[1] ?? '');
$listBuff[] = $row;
$sumNum++;
}
}
foreach ($listBuff as $b)
CLI::write($b);
CLI::write(str_pad('['.CLI::bold($sumNum).']', 21).'add another php configuration');
CLI::write();
if ($hasEmpty)
{
CLI::write('please configure the required empty settings', CLI::LOG_WARN);
CLI::write();
}
if (CLI::read(['idx' => ['', false, false, Cfg::PATTERN_CONF_KEY]], $uiIndex) && $uiIndex && $uiIndex['idx'] !== '')
{
$idx = array_search(strtolower($uiIndex['idx']), $cfgList);
if ($idx === false)
$idx = intVal($uiIndex['idx']);
// add new php setting
if ($idx == $sumNum)
{
CLI::write('Adding additional php configuration.');
CLI::write();
while (true)
{
$setting = array(
'key' => ['option name', false, false, Cfg::PATTERN_CONF_KEY],
'val' => ['value', ]
);
if (CLI::read($setting, $uiSetting) && $uiSetting)
{
$key = strtolower($uiSetting['key']);
if ($err = Cfg::add($key, $uiSetting['val']))
CLI::write($err, CLI::LOG_ERROR);
else
CLI::write('new php configuration added', CLI::LOG_OK);
sleep(1);
CLI::write();
break;
}
else
{
CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
CLI::write();
sleep(1);
break;
}
}
}
// edit existing setting
else if ($idx >= 0 && $idx < $sumNum)
{
[$value, $flags, , $default, $comment] = Cfg::get($cfgList[$idx], false, true);
$key = $cfgList[$idx];
$info = explode(' - ', $comment);
$buff = '';
$buff .= $flags & Cfg::FLAG_PHP ? 'PHP: ' : 'AOWOW: ';
$buff .= $flags & Cfg::FLAG_PHP ? $key : 'Cfg::'.strtoupper($key);
if (!empty($info[0]))
$buff .= ' - '.$info[0];
CLI::write($buff);
CLI::write();
CLI::write('VALUE: '.formatValue($value, $flags, $info[1] ?? ''));
CLI::write();
CLI::write('['.CLI::bold('E').']dit');
if (!($flags & Cfg::FLAG_PERSISTENT))
CLI::write('['.CLI::bold('D').']elete');
if ($default)
CLI::write('['.CLI::bold('R').']estore Default - '.$default);
CLI::write();
while (true)
{
if (CLI::read(['idx' => ['', true, true, '/[edr]/i']], $uiEDR) && $uiEDR)
{
switch (strtoupper($uiEDR['idx']))
{
case 'E': // edit value
$pattern = false;
$single = false;
$prompt = ['idx' => ['Select new value', false, &$single, &$pattern]];
if ($flags & Cfg::FLAG_OPT_LIST)
{
foreach (explode(', ', $info[1]) as $option)
{
[$val, $name] = explode(':', $option);
CLI::write('['.CLI::bold($val).'] '.$name);
}
$single = true;
$pattern = '/\d/';
}
else if ($flags & Cfg::FLAG_BITMASK)
{
CLI::write('Bitmask: sum fields to select multiple options');
foreach (explode(', ', $info[1]) as $option)
{
[$val, $name] = explode(':', $option);
CLI::write('['.CLI::bold(1 << $val).']'.str_pad('', 6 - strlen(1 << $val)).$name);
}
$pattern = '/\d+/';
}
else if ($flags & Cfg::FLAG_TYPE_BOOL)
{
CLI::write('['.CLI::bold(0).'] Disabled');
CLI::write('['.CLI::bold(1).'] Enabled');
$single = true;
$pattern = '/[01]/';
}
while (true)
{
if (CLI::read($prompt, $uiValue))
{
CLI::write();
$inp = $uiValue['idx'] ?? '';
if ($err = Cfg::set($key, $inp, $updScripts))
{
CLI::write($err, CLI::LOG_ERROR);
sleep(1);
continue;
}
else
{
CLI::write('setting updated', CLI::LOG_OK);
sleep(1);
break 3;
}
}
else
{
CLI::write('edit canceled! returning to selection...', CLI::LOG_INFO);
sleep(1);
break;
}
}
break 2;
case 'R': // restore default
if (!$default)
continue 2;
if ($err = Cfg::reset($key, $updScripts))
CLI::write($err, CLI::LOG_ERROR);
else
CLI::write('default value restored', CLI::LOG_OK);
sleep(1);
break 2;
case 'D': // delete config pair
if ($flags & Cfg::FLAG_PERSISTENT)
continue 2;
if ($err = Cfg::delete($key))
CLI::write($err, CLI::LOG_ERROR);
else
CLI::write("php setting deleted ['".$key."': '".$value."']", CLI::LOG_OK);
sleep(1);
break 2;
}
}
else
{
CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
CLI::write();
sleep(1);
break;
}
}
}
else
{
CLI::write('invalid selection', CLI::LOG_ERROR);
CLI::write();
sleep(1);
}
}
else
{
CLI::write('leaving site configuration...', CLI::LOG_INFO);
CLI::write();
break;
}
// propagate changes to static files
if ($updScripts && (!class_exists('FileGen') || FileGen::getMode() != FileGen::MODE_FIRSTRUN))
{
require_once 'setup/tools/clisetup/build.func.php';
CLI::write();
CLI::write('regenerating affected static content', CLI::LOG_INFO);
CLI::write();
sleep(1);
if ($_ = array_diff($updScripts, build($updScripts)))
{
CLI::write(' - the following updates returned with errors, please recheck those - '.implode(', ', $_), CLI::LOG_ERROR);
sleep(1);
}
sleep(1);
$updScripts = [];
}
}
}
?>

View File

@@ -0,0 +1,468 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/****************************/
/* Configure Site variables */
/****************************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvOpts = ['c'];
public $optGroup = CLISetup::OPT_GRP_SETUP;
public $followupFn = 'build';
public const COMMAND = 'configure';
public const APPENDIX = ' [action<E|R|N|D> cfgName [newValue]]';
public const DESCRIPTION = 'Configure site variables.';
public const PROMPT = 'SITE_HOST and STATIC_HOST *must* be set. Also enable FORCE_SSL if needed. You may also want to change other variables such as NAME, NAME_SHORT or LOCALES.';
public const NOTE_ERROR = 'could not access:';
public const REQUIRED_DB = [DB_AOWOW];
public const USE_CLI_ARGS = true;
private const HTTP_STATUS_OK = 200;
private const HTTP_STATUS_MOVED_PERM = 301;
private const HTTP_STATUS_MOVED_TEMP = 302;
private $updScripts = [];
// args: action, configName, configValue, pendingUpdates[] // iiio
public function run(&$args) : bool
{
$action = $args[0] ?? '';
$name = $args[1] ?? '';
$value = $args[2] ?? '';
$result = true;
switch (strtoupper($action))
{
case 'E':
$result = $this->doEdit($name, $value);
break;
case 'R':
$result = $this->doRestore($name);
break;
case 'N':
$result = $this->doNew($name, $value);
break;
case 'D':
$result = $this->doDelete($name);
break;
default:
$this->showConfigList();
}
$args['doBuild'] = $this->updScripts; // push files to rebuild one level up
return $result;
}
private function showConfigList() : void
{
while (true)
{
CLI::write('select a numerical index or name to use the corresponding entry', -1, false);
CLI::write();
$sumNum = 0;
$cfgList = [];
$hasEmpty = false;
$listBuff = [];
foreach (Cfg::$categories as $idx => $cat)
{
$listBuff[] = '===== '.$cat.' =====';
foreach (Cfg::forCategory($idx) as $key => [$value, $flags, $catg, $default, $comment])
{
$isPhp = $flags & Cfg::FLAG_PHP;
if ($value === '' && ($flags & Cfg::FLAG_REQUIRED))
$hasEmpty = true;
$cfgList[$sumNum] = strtolower($key);
$row = '['.CLI::bold($sumNum).'] '.(($sumNum) > 9 ? '' : ' ').($isPhp ? ' PHP ' : ' AOWOW ');
$row .= str_pad($isPhp ? strtolower($key) : strtoupper($key), 35);
$opts = explode(' - ', $comment);
$row .= $this->formatValue($value, $flags, $opts[1] ?? '');
$listBuff[] = $row;
$sumNum++;
}
}
foreach ($listBuff as $b)
CLI::write($b, -1, false);
CLI::write(str_pad('['.CLI::bold($sumNum).']', 21).'add another php configuration', -1, false);
CLI::write();
if ($hasEmpty)
{
CLI::write('please configure the required empty settings', CLI::LOG_WARN);
CLI::write();
}
if (CLI::read(['idx' => ['', false, false, Cfg::PATTERN_CONF_KEY]], $uiIndex) && $uiIndex && $uiIndex['idx'] !== '')
{
$idx = array_search(strtolower($uiIndex['idx']), $cfgList);
if ($idx === false)
$idx = intVal($uiIndex['idx']);
// add new php setting
if ($idx == $sumNum)
$this->showNewConfig();
// edit existing setting
else if ($idx >= 0 && $idx < $sumNum)
$this->showEditConfig($cfgList[$idx] ?? '');
else
CLI::write('invalid selection', CLI::LOG_ERROR);
CLI::write();
sleep(1);
}
else
{
CLI::write('leaving site configuration...', CLI::LOG_INFO);
CLI::write();
break;
}
}
}
private function showNewConfig() : void
{
CLI::write('Adding additional php configuration.');
CLI::write();
$setting = array(
'key' => ['option name', false, false, Cfg::PATTERN_CONF_KEY],
'val' => ['value', ]
);
if (CLI::read($setting, $uiSetting) && $uiSetting)
$this->doNew($uiSetting['key'], $uiSetting['val']);
else
CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
}
private function showEditConfig(string $key) : void
{
[$value, $flags, , $default, $comment] = Cfg::get($key, false, true);
$info = explode(' - ', $comment);
$buff = '';
$buff .= $flags & Cfg::FLAG_PHP ? 'PHP: ' : 'AOWOW: ';
$buff .= $flags & Cfg::FLAG_PHP ? $key : 'Cfg::'.strtoupper($key);
if (!empty($info[0]))
$buff .= ' - '.$info[0];
CLI::write($buff);
CLI::write();
CLI::write('VALUE: '.$this->formatValue($value, $flags, $info[1] ?? ''));
CLI::write();
CLI::write('['.CLI::bold('E').']dit');
if (!($flags & Cfg::FLAG_PERSISTENT))
CLI::write('['.CLI::bold('D').']elete');
if ($default)
CLI::write('['.CLI::bold('R').']estore Default - '.$default);
CLI::write();
while (true)
{
CLI::write();
sleep(1);
if (CLI::read(['idx' => ['', true, true, '/[edr]/i']], $uiEDR) && $uiEDR)
{
switch (strtoupper($uiEDR['idx']))
{
case 'E': // edit value
if (!$this->doEdit($key))
continue 2;
break 2;
case 'R': // restore default
if (!$this->doRestore($key))
continue 2;
break 2;
case 'D': // delete config pair
if (!$this->doDelete($key))
continue 2;
break 2;
}
}
else
{
CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
break;
}
}
}
private function doEdit(string $key, ?string $newVal = null) : bool
{
[, $flags, , , $comment] = Cfg::get($key, false, true);
$info = explode(' - ', $comment);
$pattern = '/.*/';
$single = false;
$typeHint = [];
if ($flags & Cfg::FLAG_OPT_LIST)
{
foreach (explode(', ', $info[1]) as $option)
{
[$val, $name] = explode(':', $option);
CLI::write('['.CLI::bold($val).'] '.$name);
}
$single = true;
$pattern = '/^\d$/';
}
else if ($flags & Cfg::FLAG_BITMASK)
{
$typeHint[] = 'Bitmask: sum fields to select multiple options';
foreach (explode(', ', $info[1]) as $option)
{
[$val, $name] = explode(':', $option);
CLI::write('['.CLI::bold(1 << $val).']'.str_pad('', 6 - strlen(1 << $val)).$name);
}
$pattern = '/^\d+$/';
}
else if ($flags & Cfg::FLAG_TYPE_BOOL)
{
$typeHint[] = '['.CLI::bold(0).'] Disabled';
$typeHint[] = '['.CLI::bold(1).'] Enabled';
$single = true;
$pattern = '/^[01]$/';
}
else if ($flags & Cfg::FLAG_TYPE_INT)
$pattern = '/^-?\d+$/';
else if ($flags & Cfg::FLAG_TYPE_FLOAT)
$pattern = '/^-?\d*(,|.)?\d+$/i';
while (true)
{
if (!isset($newVal))
foreach ($typeHint as $th)
CLI::write($th);
if ((isset($newVal) && preg_match($pattern, $newVal)) || CLI::read(['idx' => ['Select new value', false, $single, $pattern]], $uiValue))
{
CLI::write();
$val = $newVal ?? $uiValue['idx'] ?? '';
if ($err = Cfg::set($key, $val, $this->updScripts))
{
CLI::write($err, CLI::LOG_ERROR);
continue;
}
else
{
CLI::write('setting updated', CLI::LOG_OK);
return true;
}
}
else
{
CLI::write('edit canceled! returning to selection...', CLI::LOG_INFO);
return false;
}
}
}
private function doRestore(string $key) : bool
{
if ($err = Cfg::reset($key, $this->updScripts))
{
CLI::write($err, CLI::LOG_ERROR);
return false;
}
CLI::write('default value restored', CLI::LOG_OK);
return true;
}
private function doNew(string $key, string $val) : bool
{
if ($err = Cfg::add($key, $val))
{
CLI::write($err, CLI::LOG_ERROR);
return false;
}
CLI::write('new php configuration added', CLI::LOG_OK);
return true;
}
private function doDelete(string $key) : bool
{
if ($err = Cfg::delete($key))
{
CLI::write($err, CLI::LOG_ERROR);
return false;
}
CLI::write('php setting deleted: '.$key, CLI::LOG_OK);
return true;
}
/******************/
/* Unit formating */
/******************/
private function toOptList(string $options, $curVal, bool $bitmask = false) : string
{
$result = '';
foreach (explode(', ', $options) as $opt)
{
[$val, $name] = explode(':', $opt);
$equal = $bitmask ? ($curVal & (1 << $val)) : $curVal == $val;
$result .= '['.($equal ? 'x' : ' ').']'.$name.' ';
}
return substr($result, 0, -1);
}
private function formatValue($value, int $flags, string $opts) : string
{
if ($flags & Cfg::FLAG_TYPE_BOOL)
return '[bool] '.($value ? '<Enabled>' : '<Disabled>');
if ($flags & Cfg::FLAG_OPT_LIST)
return '[opt] '.$this->toOptList($opts, $value, false);
if ($flags & Cfg::FLAG_BITMASK)
return '[mask] '.$this->toOptList($opts, $value, true);
if ($flags & Cfg::FLAG_TYPE_FLOAT)
return '[float] '.floatVal($value);
if ($flags & Cfg::FLAG_TYPE_INT)
return '[int] '.intVal($value);
// if ($flags & Cfg::FLAG_TYPE_STRING)
if ($value === '')
return '[str] '.(($flags & Cfg::FLAG_REQUIRED) ? CLI::red('<empty>') : CLI::grey('<empty>'));
else
return '[str] "'.$value.'"';
}
/****************/
/* Help display */
/****************/
public function writeCLIHelp(string ...$ss) : bool
{
CLI::write(' usage: php aowow --configure [action cfgName [newValue]]', -1, false);
CLI::write();
CLI::write(' Configures site and php variables. If incomplete parameters are passed an interactive prompt will open.', -1, false);
CLI::write();
CLI::write(' action:', -1, false);
CLI::write(' E - Edit variable named '.CLI::bold('cfgName').'. Optionally directly pass a new value.', -1, false);
CLI::write(' R - Restore default value of a variable '.CLI::bold('cfgName').'.', -1, false);
CLI::write(' N - Create a new php config value. Must be a valid php directive. Optionally directly pass a new value.', -1, false);
CLI::write(' D - Delete an existing php config value.', -1, false);
CLI::write();
CLI::write();
return true;
}
/***********/
/* DB test */
/***********/
public function test(?array &$error = []) : bool
{
$error = [];
if (!DB::isConnected(DB_AOWOW))
{
$error[] = ' * not connected to DB';
return false;
}
$prot = Cfg::get('FORCE_SSL') ? 'https://' : 'http://';
$cases = array(
'site_host' => [$prot, Cfg::get('SITE_HOST'), '/robots.txt'],
'static_host' => [$prot, Cfg::get('STATIC_HOST'), '/css/aowow.css']
);
foreach ($cases as $conf => [$protocol, $host, $testFile])
{
if ($host)
{
$resp = 0;
if (!$this->testCase($protocol, $host, $testFile, $resp))
{
if ($resp == self::HTTP_STATUS_MOVED_PERM || $resp == self::HTTP_STATUS_MOVED_TEMP)
{
CLI::write('self test received status '.CLI::bold($resp).' (page moved) for '.$conf.', pointing to: '.$protocol.$host.$testFile, CLI::LOG_WARN);
if (!CLI::read(['x' => ['should '.CLI::bold($conf).' be set to '.CLI::bold($host).' and force_ssl be updated? (y/n)', true, true, '/y|n/i']], $uiN) || !$uiN || strtolower($uiN['x']) == 'n')
$error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
else
{
Cfg::set($conf, $host);
Cfg::set('FORCE_SSL', $protocol == 'https://');
}
CLI::write();
}
else
$error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
}
}
else
$error[] = ' * '.strtoupper($conf).' is empty';
}
return empty($error);
}
private function testCase(&$protocol, &$host, $testFile, &$status) : bool
{
$res = get_headers($protocol.$host.$testFile, true);
if (!preg_match('/HTTP\/[0-9\.]+\s+([0-9]+)/', $res[0], $m))
return false;
$status = $m[1];
if ($status == self::HTTP_STATUS_OK)
return true;
if ($status == self::HTTP_STATUS_MOVED_PERM || $status == self::HTTP_STATUS_MOVED_TEMP)
{
if (!empty($res['Location']) && preg_match('/(https?:\/\/)(.*)'.strtr($testFile, ['/' => '\/', '.' => '\.']).'/i', is_array($res['Location']) ? $res['Location'][0] : $res['Location'], $n))
{
$protocol = $n[1];
$host = $n[2];
}
return false;
}
$status = 0;
return false;
}
});
?>

View File

@@ -1,68 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/************************************************/
/* Create content from world tables / dbc files */
/************************************************/
function sql($syncMe = null) : array
{
if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return [];
}
require_once 'setup/tools/sqlGen.class.php';
if (!SqlGen::init($syncMe !== null ? SqlGen::MODE_UPDATE : SqlGen::MODE_NORMAL, $syncMe ?: []))
return [];
$done = [];
if (SqlGen::$subScripts)
{
CLISetup::siteLock(CLISetup::LOCK_ON);
$allOk = true;
// start file generation
CLI::write('begin generation of '. implode(', ', SqlGen::$subScripts));
CLI::write();
foreach (SqlGen::$subScripts as $tbl)
{
$syncIds = []; // todo: fetch what exactly must be regenerated
$ok = SqlGen::generate($tbl, $syncIds);
if (!$ok)
$allOk = false;
else
$done[] = $tbl;
CLI::write(' - subscript \''.$tbl.'\' returned '.($ok ? 'successfully' : 'with errors'), $ok ? CLI::LOG_OK : CLI::LOG_ERROR);
set_time_limit(SqlGen::$defaultExecTime); // reset to default for the next script
}
// end
CLI::write();
if ($allOk)
CLI::write('successfully finished sql generation', CLI::LOG_OK);
else
CLI::write('finished sql generation with errors', CLI::LOG_ERROR);
CLISetup::siteLock(CLISetup::LOCK_RESTORE);
}
else if (SqlGen::getMode() == SqlGen::MODE_NORMAL)
CLI::write('no valid script names supplied', CLI::LOG_ERROR);
return $done;
}
?>

View File

@@ -1,59 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/************************************************/
/* automaticly synchronize with TC world tables */
/************************************************/
require_once 'setup/tools/clisetup/sql.func.php';
require_once 'setup/tools/clisetup/build.func.php';
function sync(array $s = [], array $b = []) : void
{
if ((!$s && !$b && !CLISetup::getOpt('sync') && !CLISetup::getOpt('setup')) || CLISetup::getOpt('help'))
{
CLI::write();
CLI::write(' usage: php aowow --sync=<tableList,> [--locales: --mpqDataDir: --force -f]', -1, false);
CLI::write();
CLI::write(' Truncates and recreates AoWoW tables and static data files that depend on the given TC world table. Use this command after you updated your world database.', -1, false);
CLI::write();
CLI::write(' e.g.: "php aowow --sync=creature_queststarter" causes the table aowow_quests_startend to be recreated.', -1, false);
CLI::write(' Also quest-related profiler files will be recreated as they depend on aowow_quests_startend and thus indirectly on creature_queststarter.', -1, false);
CLI::write();
return;
}
if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return;
}
$_s = sql($s);
if ($s)
{
$_ = array_diff($s, $_s);
DB::Aowow()->query('UPDATE ?_dbversion SET `sql` = ?', $_ ? implode(' ', $_) : '');
}
$_b = build($b);
if ($b)
{
$_ = array_diff($b, $_b);
DB::Aowow()->query('UPDATE ?_dbversion SET `build` = ?', $_ ? implode(' ', $_) : '');
}
if (!$s && !$_s && !$b && !$_b && !CLISetup::getOpt('setup'))
CLI::write('no valid table names supplied', CLI::LOG_ERROR);
}
?>

View File

@@ -0,0 +1,104 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/************************************************/
/* automaticly synchronize with TC world tables */
/************************************************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvFlags = CLISetup::ARGV_ARRAY | CLISetup::ARGV_OPTIONAL;
public $optGroup = CLISetup::OPT_GRP_UTIL;
public const COMMAND = 'sync';
public const DESCRIPTION = 'Regenerate tables/files that depend on given world DB table.';
public const APPENDIX = '=<worldTableList,>';
public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
// sqlToDo, buildToDo, null, null // iinn
public function run(&$args) : bool
{
$s = &$args['doSql'];
$b = &$args['doBuild'];
// called manually
if ($s === null && $b === null)
{
[$s, $b] = $this->handleCLIOpt();
if (!$s && !$b && !CLISetup::getOpt('setup'))
{
CLI::write('[sync] no valid table names supplied', CLI::LOG_ERROR);
return false;
}
}
if ($s)
{
$io = ['doSql' => $s, 'doneSql' => []];
CLISetup::run('sql', $io);
DB::Aowow()->query('UPDATE ?_dbversion SET `sql` = ?', implode(' ', array_diff($io['doSql'], $io['doneSql'])));
}
if ($b)
{
$io = ['doBuild' => $b, 'doneBuild' => []];
CLISetup::run('build', $io);
DB::Aowow()->query('UPDATE ?_dbversion SET `build` = ?', implode(' ', array_diff($io['doBuild'], $io['doneBuild'])));
}
return true;
}
private function handleCLIOpt() : array
{
$sql = [];
$build = [];
$sync = CLISetup::getOpt('sync');
if (!$sync)
return [$sql, $build];
foreach (CLISetup::getSubScripts() as $name => [$invoker, $ssRef])
if (array_intersect($ssRef->getRemoteDependencies(), $sync))
$$invoker[] = $name;
do
{
$n = count($sql);
foreach (CLISetup::getSubScripts('sql') as $name => [, $ssRef])
if (!in_array($name, $sql) && array_intersect($ssRef->getSelfDependencies()[0], $sql))
$sql[] = $name;
}
while ($n != count($sql));
if ($sql)
foreach (CLISetup::getSubScripts('build') as $name => [, $ssRef])
if (array_intersect($ssRef->getSelfDependencies()[0], $sql))
$build[] = $name;
return [array_unique($sql), array_unique($build)];
}
public function writeCLIHelp() : bool
{
CLI::write(' usage: php aowow --sync=<tableList,> [--locales: --mpqDataDir: --force -f]', -1, false);
CLI::write();
CLI::write(' Truncates and recreates AoWoW tables and static data files that depend on the given TC world table. Use this command after you updated your world database.', -1, false);
CLI::write();
CLI::write(' e.g.: "php aowow --sync=creature_queststarter" causes the table aowow_quests_startend to be recreated.', -1, false);
CLI::write(' Also quest-related profiler files will be recreated as they depend on aowow_quests_startend and thus indirectly on creature_queststarter.', -1, false);
CLI::write();
CLI::write();
return true;
}
})
?>

View File

@@ -1,99 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*********************************/
/* automaticly apply sql-updates */
/*********************************/
function update(?array &$sql = [], ?array &$build = []) : void
{
if (!DB::isConnected(DB_AOWOW))
{
CLI::write('Database not yet set up!', CLI::LOG_WARN);
CLI::write('Please use '.CLI::bold('"php aowow --dbconfig"').' for setup', CLI::LOG_BLANK);
CLI::write();
return;
}
[$date, $part] = array_values(DB::Aowow()->selectRow('SELECT `date`, `part` FROM ?_dbversion'));
if (CLISetup::getOpt('help'))
{
CLI::write();
CLI::write(' usage: php aowow --update', -1, false);
CLI::write();
CLI::write(' Checks /setup/updates for new *.sql files and applies them. If required by an applied update, the --sql and --build command are triggered afterwards.', -1, false);
CLI::write(' Use this after fetching the latest rev. from Github.', -1, false);
CLI::write();
CLI::write(' Last Update: '.date(Util::$dateFormatInternal, $date).' (Part #'.$part.')', -1, false);
CLI::write();
return;
}
CLI::write('checking sql updates');
CLISetup::siteLock(CLISetup::LOCK_ON);
$nFiles = 0;
foreach (glob('setup/updates/*.sql') as $file)
{
$pi = pathinfo($file);
[$fDate, $fPart] = explode('_', $pi['filename']);
$fDate = intVal($fDate);
if ($date && $fDate < $date)
continue;
else if ($part && $date && $fDate == $date && $fPart <= $part)
continue;
$nFiles++;
$updQuery = '';
$nQuerys = 0;
foreach (file($file) as $line)
{
// skip comments
if (substr($line, 0, 2) == '--' || $line == '')
continue;
$updQuery .= $line;
// semicolon at the end -> end of query
if (substr(trim($line), -1, 1) == ';')
{
if (DB::Aowow()->query($updQuery))
$nQuerys++;
$updQuery = '';
}
}
DB::Aowow()->query('UPDATE ?_dbversion SET `date`= ?d, `part` = ?d', $fDate, $fPart);
CLI::write(' -> '.date('d.m.Y', $fDate).' #'.$fPart.': '.$nQuerys.' queries applied', CLI::LOG_OK);
}
CLISetup::siteLock(CLISetup::LOCK_RESTORE);
CLI::write($nFiles ? 'applied '.$nFiles.' update(s)' : 'db is already up to date', CLI::LOG_OK);
// fetch sql/build after applying updates, as they may contain sync-prompts
[$sql, $build] = array_values(DB::Aowow()->selectRow('SELECT `sql`, `build` FROM ?_dbversion'));
sleep(1);
$sql = trim($sql) ? array_unique(explode(' ', trim(preg_replace('/[^a-z_]+/i', ' ', $sql)))) : [];
$build = trim($build) ? array_unique(explode(' ', trim(preg_replace('/[^a-z_]+/i', ' ', $build)))) : [];
if ($sql)
CLI::write('The following table(s) require syncing: '.implode(', ', $sql));
if ($build)
CLI::write('The following file(s) require syncing: '.implode(', ', $build));
}
?>

View File

@@ -0,0 +1,121 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/*********************************/
/* automaticly apply sql-updates */
/*********************************/
CLISetup::registerUtility(new class extends UtilityScript
{
public $argvOpts = ['u'];
public $optGroup = CLISetup::OPT_GRP_SETUP;
public $followupFn = 'sync';
public const COMMAND = 'update';
public const DESCRIPTION = 'Apply new sql updates fetched from Github and run --sync as needed.';
public const REQUIRED_DB = [DB_AOWOW];
public const SITE_LOCK = CLISetup::LOCK_RESTORE;
private $date = 0;
private $part = 0;
public function __construct()
{
if (DB::isConnected(DB_AOWOW))
[$this->date, $this->part] = array_values(DB::Aowow()->selectRow('SELECT `date`, `part` FROM ?_dbversion'));
}
// args: null, null, sqlToDo, buildToDo // nnoo
public function run(&$args) : bool
{
$sql = &$args['doSql'];
$build = &$args['doBuild'];
CLI::write('[update] checking for sql updates...');
$nFiles = 0;
foreach (glob('setup/updates/*.sql') as $file)
{
$pi = pathinfo($file);
// invalid file
if (!preg_match('/(\d{10})_(\d{2})/', $pi['filename'], $m))
continue;
$fDate = intVal($m[1]);
$fPart = intVal($m[2]);
if ($this->date && $fDate < $this->date)
continue;
else if ($this->part && $this->date && $fDate == $this->date && $fPart <= $this->part)
continue;
$nFiles++;
$updQuery = '';
$nQuerys = 0;
foreach (file($file) as $line)
{
// skip comments
if (substr($line, 0, 2) == '--' || $line == '')
continue;
$updQuery .= $line;
// semicolon at the end -> end of query
if (substr(trim($line), -1, 1) == ';')
{
if (DB::Aowow()->query($updQuery))
$nQuerys++;
$updQuery = '';
}
}
DB::Aowow()->query('UPDATE ?_dbversion SET `date`= ?d, `part` = ?d', $fDate, $fPart);
CLI::write(' -> '.date('d.m.Y', $fDate).' #'.$fPart.': '.$nQuerys.' queries applied', CLI::LOG_OK);
}
CLI::write('[update] ' . ($nFiles ? 'applied '.$nFiles.' update(s)' : 'db is already up to date'), CLI::LOG_OK);
// fetch sql/build after applying updates, as they may contain sync-prompts
[$sql, $build] = DB::Aowow()->selectRow('SELECT `sql` AS "0", `build` AS "1" FROM ?_dbversion');
$sql = trim($sql) ? array_unique(explode(' ', trim(preg_replace('/[^a-z]+/i', ' ', $sql)))) : [];
$build = trim($build) ? array_unique(explode(' ', trim(preg_replace('/[^a-z]+/i', ' ', $build)))) : [];
sleep(1);
if ($sql)
CLI::write('[update] The following sql scripts have been scheduled: '.implode(', ', $sql));
if ($build)
CLI::write('[update] The following build scripts have been scheduled: '.implode(', ', $build));
return true;
}
public function writeCLIHelp() : bool
{
CLI::write(' usage: php aowow --update', -1, false);
CLI::write();
CLI::write(' Checks /setup/updates for new *.sql files and applies them. If required by an applied update, the --sql and --build command are triggered afterwards.', -1, false);
CLI::write(' Use this after fetching the latest rev. from Github.', -1, false);
CLI::write();
CLI::write(' Last Update: '.date(Util::$dateFormatInternal, $this->date).' (Part #'.$this->part.')', -1, false);
CLI::write();
CLI::write();
return true;
}
});
?>

View File

@@ -1,243 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
class FileGen
{
const MODE_NORMAL = 1;
const MODE_FIRSTRUN = 2;
const MODE_UPDATE = 3;
private static $mode = 0;
public static $tplPath = 'setup/tools/filegen/templates/';
public static $subScripts = [];
public static $tplFiles = array( // name => [file, path, TCDeps]
'searchplugin' => ['aowow.xml', 'static/download/searchplugins/', []],
'power' => ['power.js', 'static/widgets/', []],
'searchboxScript' => ['searchbox.js', 'static/widgets/', []],
'demo' => ['demo.html', 'static/widgets/power/', []],
'searchboxBody' => ['searchbox.html', 'static/widgets/searchbox/', []],
'realmMenu' => ['profile_all.js', 'static/js/', ['realmlist']],
'locales' => ['locale.js', 'static/js/', []],
'markup' => ['Markup.js', 'static/js/', []],
'itemScaling' => ['item-scaling', 'datasets/', []]
);
public static $datasets = array( // name => [AowowDeps, TCDeps, info]
'realms' => [null, ['realmlist'], 'datasets/realms'],
'statistics' => [null, ['player_levelstats', 'player_classlevelstats'], 'datasets/statistics'],
'simpleImg' => [null, null, 'static/images/wow/[icons, Interface, ]/*'],
'complexImg' => [null, null, 'static/images/wow/[maps, talents/backgrounds, ]/*'],
'talentCalc' => [null, null, 'datasets/<locale>/talents-*'],
'pets' => [['spawns', 'creature'], null, 'datasets/<locale>/pets'],
'talentIcons' => [null, null, 'static/images/wow/talents/icons/*'],
'glyphs' => [['items', 'spell'], null, 'datasets/<locale>/glyphs'],
'itemsets' => [['itemset', 'spell'], null, 'datasets/<locale>/itemsets'],
'enchants' => [['items', 'spell', 'itemenchantment'], null, 'datasets/<locale>/enchants'],
'gems' => [['items', 'spell', 'itemenchantment'], null, 'datasets/<locale>/gems'],
'profiler' => [['quests', 'quests_startend', 'spell', 'currencies', 'achievement', 'titles'], null, 'datasets/<locale>/p-*'],
'weightPresets' => [null, null, 'datasets/weight-presets'],
'soundfiles' => [['sounds'], null, 'static/wowsounds/*']
);
public static $defaultExecTime = 30;
private static $reqDirs = array(
'static/uploads/screenshots/normal/',
'static/uploads/screenshots/pending/',
'static/uploads/screenshots/resized/',
'static/uploads/screenshots/temp/',
'static/uploads/screenshots/thumb/',
'static/uploads/temp/',
'static/uploads/guide/images/',
'static/download/searchplugins/',
'static/wowsounds/'
);
public static function init(int $mode = self::MODE_NORMAL, array $updScripts = []) : bool
{
self::$defaultExecTime = ini_get('max_execution_time');
self::$mode = $mode;
if (!CLISetup::$localeIds)
{
CLI::write('No valid locale specified. Check your config or --locales parameter, if used', CLI::LOG_ERROR);
return false;
}
// create directory structure
CLI::write('FileGen::init() - creating required directories');
$pathOk = 0;
foreach (self::$reqDirs as $rd)
if (CLISetup::writeDir($rd))
$pathOk++;
CLI::write('created '.$pathOk.' extra paths'.($pathOk == count(self::$reqDirs) ? '' : ' with errors'));
CLI::write();
// handle command prompts
if (!self::handleCLIOpts($doScripts) && !$updScripts)
return false;
// check passed subscript names; limit to real scriptNames
self::$subScripts = array_merge(array_keys(self::$tplFiles), array_keys(self::$datasets));
if ($doScripts || $updScripts)
self::$subScripts = array_intersect($doScripts ?: $updScripts, self::$subScripts);
return true;
}
private static function handleCLIOpts(?array &$doScripts) : bool
{
$doScripts = [];
if (CLISetup::getOpt('help') && self::$mode == self::MODE_NORMAL)
{
if (in_array('simpleImg', CLISetup::getOpt('build')))
CLISetup::optHelp(1 << 3);
else if (in_array('complexImg', CLISetup::getOpt('build')))
CLISetup::optHelp(1 << 4);
else
self::printCLIHelp();
return false;
}
// required subScripts
if ($sync = CLISetup::getOpt('sync'))
{
foreach (self::$tplFiles as $name => $info)
if (!empty($info[2]) && array_intersect($sync, $info[2]))
$doScripts[] = $name;
foreach (self::$datasets as $name => $info)
{
// recursive deps from SqlGen
if (!empty($info[0]) && array_intersect(SqlGen::$subScripts, $info[0]))
$doScripts[] = $name;
else if (!empty($info[1]) && array_intersect($sync, $info[1]))
$doScripts[] = $name;
}
if (!$doScripts)
return false;
$doScripts = array_unique($doScripts);
return true;
}
else if (is_array($_ = CLISetup::getOpt('build')))
{
$doScripts = $_;
return true;
}
return false;
}
private static function printCLIHelp()
{
CLI::write();
CLI::write(' usage: php aowow --build=<subScriptList,> [--mpqDataDir: --locales:]', -1, false);
CLI::write();
CLI::write(' Compiles files for a given subScript. Existing files are kept by default. Dependencies are taken into account by the triggered calls of --sync and --update', -1, false);
$lines = [['available subScripts', 'affected files', 'TC dependencies', 'AoWoW dependencies']];
foreach (array_merge(array_keys(self::$tplFiles), array_keys(self::$datasets)) as $s)
$lines[] = array(
' * '.$s,
isset(self::$tplFiles[$s]) ? self::$tplFiles[$s][1].self::$tplFiles[$s][0] : self::$datasets[$s][2],
!empty(self::$tplFiles[$s][2]) ? implode(' ', self::$tplFiles[$s][2]) : (!empty(self::$datasets[$s][1]) ? implode(' ', self::$datasets[$s][1]) : ''),
!empty(self::$datasets[$s][0]) ? implode(' ', self::$datasets[$s][0]) : ''
);
CLI::writeTable($lines);
}
public static function generate($key, array $updateIds = [])
{
$success = false;
$reqDBC = [];
if (file_exists('setup/tools/filegen/'.$key.'.func.php'))
require_once 'setup/tools/filegen/'.$key.'.func.php';
else if (empty(self::$tplFiles[$key]))
{
CLI::write(sprintf(ERR_MISSING_INCL, $key, 'setup/tools/filegen/'.$key.'.func.php', CLI::LOG_ERROR));
return false;
}
CLI::write('FileGen::generate() - gathering data for '.$key);
if (!empty(self::$tplFiles[$key]))
{
[$file, $destPath, $deps] = self::$tplFiles[$key];
if ($content = file_get_contents(self::$tplPath.$file.'.in'))
{
// replace constants
$content = Cfg::applyToString($content);
// check for required auxiliary DBC files
foreach ($reqDBC as $req)
if (!CLISetup::loadDBC($req))
continue;
// must generate content
// PH format: /*setup:<setupFunc>*/
$funcOK = true;
if (preg_match_all('/\/\*setup:([\w\-_]+)\*\//i', $content, $m))
{
foreach ($m[1] as $func)
{
if (function_exists($func))
$content = str_replace('/*setup:'.$func.'*/', $func(), $content);
else
{
$funcOK = false;
CLI::write('No function for was registered for placeholder '.$func.'().', CLI::LOG_ERROR);
if (!array_reduce(get_included_files(), function ($inArray, $itr) use ($func) { return $inArray || false !== strpos($itr, $func); }, false))
CLI::write('Also, expected include setup/tools/filegen/'.$name.'.func.php was not found.');
}
}
}
if ($content && $funcOK)
if (CLISetup::writeFile($destPath.$file, $content))
$success = true;
}
else
CLI::write(sprintf(ERR_READ_FILE, CLI::bold(self::$tplPath.$file.'.in')), CLI::LOG_ERROR);
}
else if (!empty(self::$datasets[$key]))
{
if (function_exists($key))
{
// check for required auxiliary DBC files
foreach ($reqDBC as $req)
if (!CLISetup::loadDBC($req))
return false;
$success = $key($updateIds);
}
else
CLI::write(' - subscript \''.$key.'\' not defined in included file', CLI::LOG_ERROR);
}
set_time_limit(self::$defaultExecTime); // reset to default for the next script
return $success;
}
public static function getMode()
{
return self::$mode;
}
}
?>

View File

@@ -1,749 +0,0 @@
<?php
/*
generate maps - code for extracting regular maps for AoWoW
This file is a part of AoWoW project.
Copyright (C) 2010 Mix <ru-mangos.ru>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// note: for the sake of simplicity, this function handles all images, that must be stitched together (which are mostly maps)
$reqDBC = ['talenttab', 'chrclasses', 'worldmapoverlay', 'worldmaparea'];
function complexImg()
{
$mapWidth = 1002;
$mapHeight = 668;
$threshold = 95; // alpha threshold to define subZones: set it too low and you have unspawnable areas inside a zone; set it too high and the border regions overlap
$runTime = ini_get('max_execution_time');
$locStr = null;
$imgPath = CLISetup::$srcDir.'%sInterface/';
$destDir = 'static/images/wow/';
$success = true;
$modeMask = 0x7; // talentBGs, regular maps, spawn-related alphaMaps
$paths = array(
0x16 => ['WorldMap/', true, null],
0x01 => ['TalentFrame/', false, null],
0x08 => ['Glues/Credits/',false, null]
);
$createAlphaImage = function($w, $h)
{
$img = imagecreatetruecolor($w, $h);
imagesavealpha($img, true);
imagealphablending($img, false);
$bgColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
imagefilledrectangle($img, 0, 0, imagesx($img) - 1, imagesy($img) - 1, $bgColor);
imagecolortransparent($img, $bgColor);
imagealphablending($img, true);
imagecolordeallocate($img, $bgColor);
return $img;
};
// prefer manually converted PNG files (as the imagecreatefromblp-script has issues with some formats)
// alpha channel issues observed with locale deDE Hilsbrad and Elwynn - maps
// see: https://github.com/Kanma/BLPConverter
$loadImageFile = function($path)
{
$result = null;
$file = $path.'.png';
if (CLISetup::fileExists($file))
{
CLI::write('manually converted png file present for '.$path.'.', CLI::LOG_INFO);
$result = imagecreatefrompng($file);
}
if (!$result)
{
$file = $path.'.blp';
if (CLISetup::fileExists($file))
$result = imagecreatefromblp($file);
}
return $result;
};
$assembleImage = function($baseName, $order, $w, $h) use ($loadImageFile)
{
$dest = imagecreatetruecolor($w, $h);
imagesavealpha($dest, true);
imagealphablending($dest, false);
$_h = $h;
foreach ($order as $y => $row)
{
$_w = $w;
foreach ($row as $x => $suffix)
{
$src = $loadImageFile($baseName.$suffix);
if (!$src)
{
CLI::write(' - complexImg: tile '.$baseName.$suffix.'.blp missing.', CLI::LOG_ERROR);
unset($dest);
return null;
}
imagecopyresampled($dest, $src, 256 * $x, 256 * $y, 0, 0, min($_w, 256), min($_h, 256), min($_w, 256), min($_h, 256));
$_w -= 256;
unset($src);
}
$_h -= 256;
}
return $dest;
};
$writeImage = function($name, $ext, $src, $w, $h, $done)
{
$ok = false;
$dest = imagecreatetruecolor($w, $h);
imagesavealpha($dest, true);
imagealphablending($dest, false);
imagecopyresampled($dest, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
switch ($ext)
{
case 'jpg':
$ok = imagejpeg($dest, $name.'.'.$ext, 85);
break;
case 'png':
$ok = imagepng($dest, $name.'.'.$ext);
break;
default:
CLI::write($done.' - unsupported file fromat: '.$ext, CLI::LOG_WARN);
}
imagedestroy($dest);
if ($ok)
{
chmod($name.'.'.$ext, Util::FILE_ACCESS);
CLI::write($done.' - image '.$name.'.'.$ext.' written', CLI::LOG_OK, true, true);
}
else
CLI::write($done.' - could not create image '.$name.'.'.$ext, CLI::LOG_ERROR);
return $ok;
};
$createSpawnMap = function($img, $zoneId) use ($mapHeight, $mapWidth, $threshold)
{
CLI::write(' - creating spawn map');
$tmp = imagecreate(1000, 1000);
$cbg = imagecolorallocate($tmp, 255, 255, 255);
$cfg = imagecolorallocate($tmp, 0, 0, 0);
for ($y = 0; $y < 1000; $y++)
{
for ($x = 0; $x < 1000; $x++)
{
$a = imagecolorat($img, ($x * $mapWidth) / 1000, ($y * $mapHeight) / 1000) >> 24;
imagesetpixel($tmp, $x, $y, $a < $threshold ? $cfg : $cbg);
}
}
imagepng($tmp, 'setup/generated/alphaMaps/' . $zoneId . '.png');
imagecolordeallocate($tmp, $cbg);
imagecolordeallocate($tmp, $cfg);
imagedestroy($tmp);
};
$checkSourceDirs = function($sub) use ($imgPath, &$paths, $modeMask)
{
$hasMissing = false;
foreach ($paths as $idx => [$subDir, $isLocalized, $realPath])
{
if ($realPath && !$isLocalized)
continue;
$p = sprintf($imgPath, $sub).$subDir;
if (CLISetup::fileExists($p))
{
if ($isLocalized)
$paths[$idx][2][substr($sub, 0, -1)] = $p;
else
$paths[$idx][2] = $p;
}
else
$hasMissing = true;
}
return !$hasMissing;
};
// do not change order of params!
$o = CLISetup::getOpt('talentbgs', 'maps', 'spawn-maps', 'artwork', 'area-maps');
$m = 0x0;
$i = 0;
foreach ($o as $k => $v)
{
if ($v)
$m |= 1 << $i;
$i++;
}
if ($m)
$modeMask = $m;
foreach ($paths as $mode => $__)
if (!($mode & $modeMask))
unset($paths[$mode]);
foreach (CLISetup::$expectedPaths as $xp => $locId)
{
if (!in_array($locId, CLISetup::$localeIds))
continue;
if ($xp) // if in subDir add trailing slash
$xp .= '/';
$checkSourceDirs($xp); // do not break; maps are localized
}
$locList = [];
foreach (CLISetup::$expectedPaths as $xp => $locId)
if (in_array($locId, CLISetup::$localeIds))
$locList[] = $xp;
CLI::write('required resources overview:', CLI::LOG_INFO);
foreach ($paths as [$path, $isLocalized, $realPath])
{
if (!$realPath)
CLI::write(CLI::red('MISSING').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $locList).']/').$path);
else if ($isLocalized)
{
$foundLoc = [];
foreach (CLISetup::$localeIds as $locId)
foreach (CLISetup::$expectedPaths as $xp => $lId)
if ($locId == $lId && isset($realPath[$xp]) && !isset($foundLoc[$locId]))
$foundLoc[$locId] = $xp;
if ($diff = array_diff(CLISetup::$localeIds, array_keys($foundLoc)))
{
$buff = [];
foreach ($diff as $d)
$buff[] = CLI::yellow(Util::$localeStrings[$d]);
foreach ($foundLoc as $str)
$buff[] = CLI::green($str);
CLI::write(CLI::yellow('PARTIAL').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $buff).']/').$path);
}
else
CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $foundLoc).']/').$path);
}
else
CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 14).' @ '.$realPath);
}
CLI::write();
// if no subdir had sufficient data, diaf
if (count(array_filter(array_column($paths, 2))) != count($paths))
{
CLI::write('one or more required directories are missing:', CLI::LOG_ERROR);
return;
}
else
sleep(1);
/**************/
/* TalentTabs */
/**************/
if ($modeMask & 0x01)
{
if (CLISetup::writeDir($destDir.'hunterpettalents/') && CLISetup::writeDir($destDir.'talents/backgrounds/'))
{
// [classMask, creatureFamilyMask, tabNr, textureStr]
$tTabs = DB::Aowow()->select('SELECT tt.creatureFamilyMask, tt.textureFile, tt.tabNumber, cc.fileString FROM dbc_talenttab tt LEFT JOIN dbc_chrclasses cc ON cc.id = (LOG(2, tt.classMask) + 1)');
$order = array(
['-TopLeft', '-TopRight'],
['-BottomLeft', '-BottomRight']
);
if ($tTabs)
{
$sum = 0;
$total = count($tTabs);
CLI::write('Processing '.$total.' files from TalentFrame/ ...');
foreach ($tTabs as $tt)
{
ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
$sum++;
$done = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
if ($tt['creatureFamilyMask']) // is PetCalc
{
$size = [244, 364];
$name = $destDir.'hunterpettalents/bg_'.(log($tt['creatureFamilyMask'], 2) + 1);
}
else
{
$size = [204, 554];
$name = $destDir.'talents/backgrounds/'.strtolower($tt['fileString']).'_'.($tt['tabNumber'] + 1);
}
if (!CLISetup::getOpt('force') && file_exists($name.'.jpg'))
{
CLI::write($done.' - file '.$name.'.jpg was already processed', CLI::LOG_BLANK, true, true);
continue;
}
$im = $assembleImage($paths[0x1][2].'/'.$tt['textureFile'], $order, 256 + 44, 256 + 75);
if (!$im)
{
CLI::write(' - could not assemble file '.$tt['textureFile'], CLI::LOG_ERROR);
continue;
}
if (!$writeImage($name, 'jpg', $im, $size[0], $size[1], $done))
$success = false;
}
}
else
$success = false;
ini_set('max_execution_time', $runTime);
}
else
$success = false;
}
/************/
/* Worldmap */
/************/
if ($modeMask & 0x16)
{
$mapDirs = array(
['maps/%snormal/', 'jpg', 488, 325],
['maps/%soriginal/', 'jpg', 0, 0], // 1002, 668
['maps/%ssmall/', 'jpg', 224, 149],
['maps/%szoom/', 'jpg', 772, 515]
);
// as the js expects them
$baseLevelFix = array(
// WotLK maps
// Halls of Stone; The Nexus; Violet Hold; Gundrak; Obsidian Sanctum; Eye of Eternity; Vault of Archavon; Trial of the Champion; The Forge of Souls; Pit of Saron; Halls of Reflection
4264 => 1, 4265 => 1, 4415 => 1, 4416 => 1, 4493 => 0, 4500 => 1, 4603 => 1, 4723 => 1, 4809 => 1, 4813 => 1, 4820 => 1,
// Cata maps for WotLK instances
// TheStockade; TheBloodFurnace; Ragefire; TheUnderbog; TheBotanica; WailingCaverns; TheSlavePens; TheShatteredHalls; HellfireRamparts; RazorfenDowns; RazorfenKraul; ManaTombs
// ShadowLabyrinth; TheTempleOfAtalHakkar (simplified layout); BlackTemple; TempestKeep; MoltenCore; GruulsLair; CoilfangReservoir; MagtheridonsLair; OnyxiasLair; SunwellPlateau;
717 => 1, 3713 => 1, 2437 => 1, 3716 => 1, 3847 => 1, 718 => 1, 3717 => 1, 3714 => 1, 3562 => 1, 722 => 1, 491 => 1, 3792 => 1,
3789 => 1, 1477 => 1, 3959 => 0, 3845 => 1, 2717 => 1, 3923 => 1, 3607 => 1, 3836 => 1, 2159 => 1, 4075 => 0
);
$wmo = DB::Aowow()->select('SELECT *, worldMapAreaId AS ARRAY_KEY, id AS ARRAY_KEY2 FROM dbc_worldmapoverlay WHERE textureString <> ""');
$wma = DB::Aowow()->select('SELECT * FROM dbc_worldmaparea');
if (!$wma || !$wmo)
{
$success = false;
CLI::write(' - could not read required dbc files: WorldMapArea.dbc ['.count($wma).' entries]; WorldMapOverlay.dbc ['.count($wmo).' entries]', CLI::LOG_ERROR);
return;
}
// fixups...
foreach ($wma as &$a)
{
if ($a['areaId'])
continue;
switch ($a['id'])
{
case 13: $a['areaId'] = -6; break; // Kalimdor
case 14: $a['areaId'] = -3; break; // Eastern Kingdoms
case 466: $a['areaId'] = -2; break; // Outland
case 485: $a['areaId'] = -5; break; // Northrend
}
}
array_unshift($wma, ['id' => -1, 'areaId' => -1, 'nameINT' => 'World'], ['id' => -4, 'areaId' => -4, 'nameINT' => 'Cosmic']);
$sumMaps = count(CLISetup::$localeIds) * count($wma);
CLI::write('Processing '.$sumMaps.' files from WorldMap/ ...');
foreach (CLISetup::$localeIds as $progressLoc => $l)
{
// create destination directories
$dirError = false;
foreach ($mapDirs as $md)
if (!CLISetup::writeDir($destDir . sprintf($md[0], strtolower(Util::$localeStrings[$l]).'/')))
$dirError = true;
if ($modeMask & 0x04)
if (!CLISetup::writeDir('setup/generated/alphaMaps'))
$dirError = true;
if ($dirError)
{
$success = false;
CLI::write(' - complexImg: could not create map directories for locale '.$l.'. skipping...', CLI::LOG_ERROR);
continue;
}
// source for mapFiles
$mapSrcDir = null;
$locDirs = array_reverse(array_filter(CLISetup::$expectedPaths, function($var) use ($l) { return !$var || $var == $l; }), true);
foreach ($locDirs as $mapLoc => $__)
{
if(!isset($paths[0x16][2][$mapLoc]))
continue;
$p = sprintf($imgPath, $mapLoc.'/').$paths[0x16][0];
if (CLISetup::fileExists($p))
{
CLI::write(' - using files from '.($mapLoc ?: '/').' for locale '.Util::$localeStrings[$l], CLI::LOG_INFO);
$mapSrcDir = $p.'/';
break;
}
}
if ($mapSrcDir === null)
{
$success = false;
CLI::write(' - no suitable localized map files found for locale '.$l, CLI::LOG_ERROR);
continue;
}
foreach ($wma as $progressArea => $areaEntry)
{
$curMap = $progressArea + count($wma) * $progressLoc;
$progress = ' - ' . str_pad($curMap.'/'.($sumMaps), 10) . str_pad('('.number_format($curMap * 100 / $sumMaps, 2).'%)', 9);
$wmaId = $areaEntry['id'];
$zoneId = $areaEntry['areaId'];
$textureStr = $areaEntry['nameINT'];
$path = $mapSrcDir.$textureStr;
if (!CLISetup::fileExists($path))
{
$success = false;
CLI::write('worldmap file '.$path.' missing for selected locale '.Util::$localeStrings[$l], CLI::LOG_ERROR);
continue;
}
$fmt = array(
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
);
CLI::write($textureStr . " [" . $zoneId . "]");
$overlay = $createAlphaImage($mapWidth, $mapHeight);
// zone has overlays (is in open world; is not multiLeveled)
if (isset($wmo[$wmaId]))
{
CLI::write(' - area has '.count($wmo[$wmaId]).' overlays');
foreach ($wmo[$wmaId] as &$row)
{
$i = 1;
$y = 0;
while ($y < $row['h'])
{
$x = 0;
while ($x < $row['w'])
{
$img = $loadImageFile($path . '/' . $row['textureString'] . $i);
if (!$img)
{
CLI::write(' - complexImg: tile '.$path.'/'.$row['textureString'].$i.'.blp missing.', CLI::LOG_ERROR);
break 2;
}
imagecopy($overlay, $img, $row['x'] + $x, $row['y'] + $y, 0, 0, imagesx($img), imagesy($img));
// prepare subzone image
if ($modeMask & 0x10)
{
if (!isset($row['maskimage']))
{
$row['maskimage'] = $createAlphaImage($row['w'], $row['h']);
$row['maskcolor'] = imagecolorallocatealpha($row['maskimage'], 255, 64, 192, 64);
}
for ($my = 0; $my < imagesy($img); $my++)
for ($mx = 0; $mx < imagesx($img); $mx++)
if ((imagecolorat($img, $mx, $my) >> 24) < $threshold)
imagesetpixel($row['maskimage'], $x + $mx, $y + $my, $row['maskcolor']);
}
imagedestroy($img);
$x += 256;
$i++;
}
$y += 256;
}
}
// create spawn-maps if wanted
if ($modeMask & 0x04)
$createSpawnMap($overlay, $zoneId);
}
// check, if the current zone is multiLeveled
// if there are also files present without layer-suffix assume them as layer: 0
$multiLeveled = false;
$multiLevel = 0;
do
{
if (!CLISetup::filesInPath('/'.$textureStr.'\/'.$textureStr.($multiLevel + 1).'_\d\.(blp|png)/i', true))
break;
$multiLevel++;
$multiLeveled = true;
}
while ($multiLevel < 18); // Karazhan has 17 frickin floors
// check if we can create base map anyway
$png = $path.'/'.$textureStr.'1.png';
$blp = $path.'/'.$textureStr.'1.blp';
$hasBaseMap = CLISetup::fileExists($blp) || CLISetup::fileExists($png);
CLI::write(' - area has '.($multiLeveled ? $multiLevel . ' levels' : 'only base level'));
$map = null;
for ($i = 0; $i <= $multiLevel; $i++)
{
ini_set('max_execution_time', 120); // max 120sec per image
$file = $path.'/'.$textureStr;
if (!$i && !$hasBaseMap)
continue;
// if $multiLeveled also suffix -0 to baseMap if it exists
if ($i && $multiLeveled)
$file .= $i.'_';
$doSkip = 0x0;
$outFile = [];
foreach ($mapDirs as $idx => $info)
{
$outFile[$idx] = $destDir . sprintf($info[0], strtolower(Util::$localeStrings[$l]).'/') . $zoneId;
$floor = $i;
if ($zoneId == 4100) // ToCStratholme: map order fix
$floor += 1;
if ($multiLeveled && !(isset($baseLevelFix[$zoneId]) && $i == $baseLevelFix[$zoneId]))
$outFile[$idx] .= '-'.$floor;
if (!CLISetup::getOpt('force') && file_exists($outFile[$idx].'.'.$info[1]))
{
CLI::write($progress.' - file '.$outFile[$idx].'.'.$info[1].' was already processed', CLI::LOG_BLANK, true, true);
$doSkip |= (1 << $idx);
}
}
if ($doSkip == 0xF)
continue;
$map = $assembleImage($file, $fmt, $mapWidth, $mapHeight);
if (!$map)
{
$success = false;
CLI::write(' - could not create image resource for map '.$zoneId.($multiLevel ? ' level '.$i : ''));
continue;
}
if (!$multiLeveled)
{
imagecopymerge($map, $overlay, 0, 0, 0, 0, imagesx($overlay), imagesy($overlay), 100);
imagedestroy($overlay);
}
// create map
if ($modeMask & 0x02)
{
foreach ($mapDirs as $idx => $info)
{
if ($doSkip & (1 << $idx))
continue;
if (!$writeImage($outFile[$idx], $info[1], $map, $info[2] ?: $mapWidth, $info[3] ?: $mapHeight, $progress))
$success = false;
}
}
}
// also create subzone-maps
if ($map && isset($wmo[$wmaId]) && $modeMask & 0x10)
{
foreach ($wmo[$wmaId] as &$row)
{
$doSkip = 0x0;
$outFile = [];
foreach ($mapDirs as $idx => $info)
{
$outFile[$idx] = $destDir . sprintf($info[0], strtolower(Util::$localeStrings[$l]).'/') . $row['areaTableId'];
if (!CLISetup::getOpt('force') && file_exists($outFile[$idx].'.'.$info[1]))
{
CLI::write($progress.' - file '.$outFile[$idx].'.'.$info[1].' was already processed', CLI::LOG_BLANK, true, true);
$doSkip |= (1 << $idx);
}
}
if ($doSkip == 0xF)
continue;
$subZone = imagecreatetruecolor($mapWidth, $mapHeight);
imagecopy($subZone, $map, 0, 0, 0, 0, imagesx($map), imagesy($map));
imagecopy($subZone, $row['maskimage'], $row['x'], $row['y'], 0, 0, imagesx($row['maskimage']), imagesy($row['maskimage']));
foreach ($mapDirs as $idx => $info)
{
if ($doSkip & (1 << $idx))
continue;
if (!$writeImage($outFile[$idx], $info[1], $subZone, $info[2] ?: $mapWidth, $info[3] ?: $mapHeight, $progress))
$success = false;
}
imagedestroy($subZone);
}
}
if ($map)
imagedestroy($map);
// this takes a while; ping mysql just in case
DB::Aowow()->selectCell('SELECT 1');
}
}
}
/***********/
/* Credits */
/***********/
if ($modeMask & 0x08) // optional tidbits (not used by default)
{
if (CLISetup::writeDir($destDir.'Interface/Glues/Credits/'))
{
// tile ordering
$order = array(
1 => array(
[1]
),
2 => array(
[1],
[2]
),
4 => array(
[1, 2],
[3, 4]
),
6 => array(
[1, 2, 3],
[4, 5, 6]
),
8 => array(
[1, 2, 3, 4],
[5, 6, 7, 8]
)
);
$imgGroups = [];
$files = CLISetup::filesInPath('/'.str_replace('/', '\\/', $paths[0x8][2]).'/i', true);
foreach ($files as $f)
{
if (preg_match('/([^\/]+)(\d).(blp|png)/i', $f, $m))
{
if ($m[1] && $m[2])
{
if (!isset($imgGroups[$m[1]]))
$imgGroups[$m[1]] = $m[2];
else if ($imgGroups[$m[1]] < $m[2])
$imgGroups[$m[1]] = $m[2];
}
}
}
// errör-korrekt
$imgGroups['Desolace'] = 4;
$imgGroups['BloodElf_Female'] = 6;
$total = count($imgGroups);
$sum = 0;
CLI::write('Processing '.$total.' files from Glues/Credits/...');
foreach ($imgGroups as $file => $fmt)
{
ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
$sum++;
$done = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
$name = $destDir.'Interface/Glues/Credits/'.$file;
if (!CLISetup::getOpt('force') && file_exists($name.'.png'))
{
CLI::write($done.' - file '.$name.'.png was already processed', CLI::LOG_BLANK, true, true);
continue;
}
if (!isset($order[$fmt]))
{
CLI::write(' - pattern for file '.$name.' not set. skipping', CLI::LOG_WARN);
continue;
}
$im = $assembleImage($paths[0x8][2].'/'.$file, $order[$fmt], count($order[$fmt][0]) * 256, count($order[$fmt]) * 256);
if (!$im)
{
CLI::write(' - could not assemble file '.$name, CLI::LOG_ERROR);
continue;
}
if (!$writeImage($name, 'png', $im, count($order[$fmt][0]) * 256, count($order[$fmt]) * 256, $done))
$success = false;
}
ini_set('max_execution_time', $runTime);
}
else
$success = false;
}
return $success;
}
?>

View File

@@ -0,0 +1,22 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'demo' => [[], CLISetup::ARGV_PARAM, 'Fills powered tooltip demo page (static/widgets/power/demo.html) with site variables.']
);
protected $fileTemplateSrc = ['demo.html.in'];
protected $fileTemplateDest = ['static/widgets/power/demo.html'];
});
?>

View File

@@ -7,91 +7,95 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
// Create 'enchants'-file for available locales /* Examples
// this script requires the following dbc-files to be parsed and available 15: {
// Spells, SkillLineAbility, SpellItemEnchantment name:'Leichtes Rüstungsset',
quality:1,
icon:'INV_Misc_ArmorKit_17',
source:-2304,
skill:-1,
slots:525008,
enchantment:'Verstärkt (+8 Rüstung)',
jsonequip:{"armor":8,"reqlevel":1},
temp:0,
classes:0,
gearscore:1
},
2928: {
name:'Ring - Zaubermacht',
quality:-1,
icon:'INV_Misc_Note_01',
source:27924,
skill:333,
slots:1024,
enchantment:'+12 Zaubermacht',
jsonequip:{"splpwr":12},
temp:0,
classes:0,
gearscore:15
},
3231: {
name:['Handschuhe - Waffenkunde','Armschiene - Waffenkunde'],
quality:-1,
icon:'spell_holy_greaterheal',
source:[44484,44598],
skill:333,
slots:[512,256],
enchantment:'+15 Waffenkundewertung',
jsonequip:{reqlevel:60,"exprtng":15},
temp:0,
classes:0,
gearscore:20 // fuck, i'm doing this..
},
*/
/* Examples CLISetup::registerSetup("build", new class extends SetupScript
15: { {
name:'Leichtes Rüstungsset', protected $info = array(
quality:1, 'enchants' => [[], CLISetup::ARGV_PARAM, 'Compiles enchantment effects to file for the item comparison tool and profiler tool.']
icon:'INV_Misc_ArmorKit_17', );
source:-2304,
skill:-1,
slots:525008,
enchantment:'Verstärkt (+8 Rüstung)',
jsonequip:{"armor":8,"reqlevel":1},
temp:0,
classes:0,
gearscore:1
},
2928: {
name:'Ring - Zaubermacht',
quality:-1,
icon:'INV_Misc_Note_01',
source:27924,
skill:333,
slots:1024,
enchantment:'+12 Zaubermacht',
jsonequip:{"splpwr":12},
temp:0,
classes:0,
gearscore:15
},
3231: {
name:['Handschuhe - Waffenkunde','Armschiene - Waffenkunde'],
quality:-1,
icon:'spell_holy_greaterheal',
source:[44484,44598],
skill:333,
slots:[512,256],
enchantment:'+15 Waffenkundewertung',
jsonequip:{reqlevel:60,"exprtng":15},
temp:0,
classes:0,
gearscore:20 // fuck, i'm doing this..
},
*/
function enchants() protected $setupAfter = [['items', 'spell', 'itemenchantment', 'icons', 'source'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
public function generate() : bool
{ {
// from g_item_slots: 13:"One-Hand", 15:"Ranged", 17:"Two-Hand", // from g_item_slots: 13:"One-Hand", 15:"Ranged", 17:"Two-Hand",
$slotPointer = [13, 17, 15, 15, 13, 17, 17, 13, 17, null, 17, null, null, 13, null, 13, null, null, null, null, 17]; $slotPointer = [13, 17, 15, 15, 13, 17, 17, 13, 17, null, 17, null, null, 13, null, 13, null, null, null, null, 17];
$castItems = [];
$successs = true;
$enchantSpells = DB::Aowow()->select('
SELECT
s.id AS ARRAY_KEY,
effect1MiscValue,
equippedItemClass, equippedItemInventoryTypeMask, equippedItemSubClassMask,
skillLine1,
IFNULL(i.name, ?) AS iconString,
name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8
FROM
?_spell s
LEFT JOIN
?_icons i ON i.id = s.iconId
WHERE
effect1Id = ?d AND
name_loc0 NOT LIKE "QA%"'
,DEFAULT_ICON, SPELL_EFFECT_ENCHANT_ITEM);
// check directory-structure $castItems = [];
foreach (Util::$localeStrings as $dir) $enchantSpells = DB::Aowow()->select(
if (!CLISetup::writeDir('datasets/'.$dir)) 'SELECT s.id AS ARRAY_KEY,
$success = false; effect1MiscValue,
equippedItemClass, equippedItemInventoryTypeMask, equippedItemSubClassMask,
skillLine1,
IFNULL(i.name, ?) AS iconString,
name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8
FROM ?_spell s
LEFT JOIN ?_icons i ON i.id = s.iconId
WHERE effect1Id = ?d AND
name_loc0 NOT LIKE "QA%"',
DEFAULT_ICON, SPELL_EFFECT_ENCHANT_ITEM
);
$enchIds = array_column($enchantSpells, 'effect1MiscValue'); $enchIds = array_column($enchantSpells, 'effect1MiscValue');
$enchantments = new EnchantmentList(array(['id', $enchIds], Cfg::get('SQL_LIMIT_NONE'))); $enchantments = new EnchantmentList(array(['id', $enchIds], Cfg::get('SQL_LIMIT_NONE')));
if ($enchantments->error) if ($enchantments->error)
{ {
CLI::write('Required table ?_itemenchantment seems to be empty! Leaving enchants()...', CLI::LOG_ERROR); CLI::write('[enchants] Required table ?_itemenchantment seems to be empty!', CLI::LOG_ERROR);
CLI::write(); CLI::write();
return false; return false;
} }
$castItems = new ItemList(array(['spellId1', array_keys($enchantSpells)], ['src.typeId', null, '!'], Cfg::get('SQL_LIMIT_NONE'))); $castItems = new ItemList(array(['spellId1', array_keys($enchantSpells)], ['src.typeId', null, '!'], Cfg::get('SQL_LIMIT_NONE')));
if ($castItems->error)
{
CLI::write('[enchants] Required table ?_items seems to be empty!', CLI::LOG_ERROR);
CLI::write();
return false;
}
foreach (CLISetup::$localeIds as $lId) foreach (CLISetup::$localeIds as $lId)
{ {
@@ -106,20 +110,20 @@ if (!CLI)
$eId = $es['effect1MiscValue']; $eId = $es['effect1MiscValue'];
if (!$enchantments->getEntry($eId)) if (!$enchantments->getEntry($eId))
{ {
CLI::write(' * could not find enchantment #'.$eId.' referenced by spell #'.$esId, CLI::LOG_WARN); CLI::write('[enchants] * could not find enchantment #'.$eId.' referenced by spell #'.$esId, CLI::LOG_WARN);
continue; continue;
} }
// slots have to be recalculated // slots have to be recalculated
$slot = 0; $slot = 0;
if ($es['equippedItemClass'] == 4) // armor if ($es['equippedItemClass'] == ITEM_CLASS_ARMOR)
{ {
if ($invType = $es['equippedItemInventoryTypeMask']) if ($invType = $es['equippedItemInventoryTypeMask'])
$slot = $invType >> 1; $slot = $invType >> 1;
else /* if (equippedItemSubClassMask == 64) */ // shields have it their own way <_< else /* if (equippedItemSubClassMask == 64) */ // shields have it their own way <_<
$slot = (1 << (14 - 1)); $slot = (1 << (14 - 1));
} }
else if ($es['equippedItemClass'] == 2) // weapon else if ($es['equippedItemClass'] == ITEM_CLASS_WEAPON)
{ {
foreach ($slotPointer as $i => $sp) foreach ($slotPointer as $i => $sp)
{ {
@@ -253,10 +257,11 @@ if (!CLI)
$file = 'datasets/'.User::$localeString.'/enchants'; $file = 'datasets/'.User::$localeString.'/enchants';
if (!CLISetup::writeFile($file, $toFile)) if (!CLISetup::writeFile($file, $toFile))
$success = false; $this->success = false;
} }
return $successs; return $this->success;
} }
});
?> ?>

View File

@@ -1,104 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// Create 'gems'-file for available locales
// this script requires the following dbc-files to be parsed and available
// ItemEnchantment, GemProperties, Spells, Icons
/* Example
22460: {
name:'Prismatic Sphere',
quality:3,
icon:'INV_Enchant_PrismaticSphere',
enchantment:'+3 Resist All',
jsonequip:{"arcres":3,"avgbuyout":242980,"firres":3,"frores":3,"holres":3,"natres":3,"shares":3},
colors:14,
expansion:1
gearscore:8 // if only..
},
*/
function gems()
{
// sketchy, but should work
// id < 36'000 || ilevel < 70 ? BC : WOTLK
$gems = DB::Aowow()->Select(
'SELECT i.id AS itemId,
i.name_loc0, i.name_loc2, i.name_loc3, i.name_loc4, i.name_loc6, i.name_loc8,
IF (i.id < 36000 OR i.itemLevel < 70, ?d, ?d) AS expansion,
i.quality,
ic.name AS icon,
i.gemEnchantmentId AS enchId,
i.gemColorMask AS colors,
i.requiredSkill,
i.itemLevel
FROM ?_items i
JOIN ?_icons ic ON ic.id = i.iconId
WHERE i.gemEnchantmentId <> 0
ORDER BY i.id DESC',
EXP_BC, EXP_WOTLK
);
$success = true;
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
$enchIds = [];
foreach ($gems as $pop)
$enchIds[] = $pop['enchId'];
$enchantments = new EnchantmentList(array(['id', $enchIds], Cfg::get('SQL_LIMIT_NONE')));
if ($enchantments->error)
{
CLI::write('Required table ?_itemenchantment seems to be empty! Leaving gems()...', CLI::LOG_ERROR);
CLI::write();
return false;
}
foreach (CLISetup::$localeIds as $lId)
{
set_time_limit(5);
User::useLocale($lId);
Lang::load($lId);
$gemsOut = [];
foreach ($gems as $pop)
{
if (!$enchantments->getEntry($pop['enchId']))
{
CLI::write(' * could not find enchantment #'.$pop['enchId'].' referenced by item #'.$pop['itemId'], CLI::LOG_WARN);
continue;
}
$gemsOut[$pop['itemId']] = array(
'name' => Util::localizedString($pop, 'name'),
'quality' => $pop['quality'],
'icon' => strToLower($pop['icon']),
'enchantment' => $enchantments->getField('name', true),
'jsonequip' => $enchantments->getStatGainForCurrent(),
'colors' => $pop['colors'],
'expansion' => $pop['expansion'],
'gearscore' => Util::getGemScore($pop['itemLevel'], $pop['quality'], $pop['requiredSkill'] == SKILL_JEWELCRAFTING, $pop['itemId'])
);
}
$toFile = "var g_gems = ".Util::toJSON($gemsOut).";";
$file = 'datasets/'.User::$localeString.'/gems';
if (!CLISetup::writeFile($file, $toFile))
$success = false;
}
return $success;
}
?>

View File

@@ -0,0 +1,101 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/* Example
22460: {
name:'Prismatic Sphere',
quality:3,
icon:'INV_Enchant_PrismaticSphere',
enchantment:'+3 Resist All',
jsonequip:{"arcres":3,"avgbuyout":242980,"firres":3,"frores":3,"holres":3,"natres":3,"shares":3},
colors:14,
expansion:1
gearscore:8 // if only..
},
*/
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'gems' => [[], CLISetup::ARGV_PARAM, 'Compiles gems to file for the item comparison tool and profiler tool.']
);
protected $setupAfter = [['items', 'itemenchantment', 'icons'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
public function generate() : bool
{
// sketchy, but should work
// id < 36'000 || ilevel < 70 ? BC : WOTLK
$gems = DB::Aowow()->select(
'SELECT i.id AS itemId,
i.name_loc0, i.name_loc2, i.name_loc3, i.name_loc4, i.name_loc6, i.name_loc8,
IF (i.id < 36000 OR i.itemLevel < 70, ?d, ?d) AS expansion,
i.quality,
ic.name AS icon,
i.gemEnchantmentId AS enchId,
i.gemColorMask AS colors,
i.requiredSkill,
i.itemLevel
FROM ?_items i
JOIN ?_icons ic ON ic.id = i.iconId
WHERE i.gemEnchantmentId <> 0
ORDER BY i.id DESC',
EXP_BC, EXP_WOTLK
);
$enchantments = new EnchantmentList(array(['id', array_column($gems, 'enchId')], Cfg::get('SQL_LIMIT_NONE')));
if ($enchantments->error)
{
CLI::write('[gems] Required table ?_itemenchantment seems to be empty!', CLI::LOG_ERROR);
CLI::write();
return false;
}
foreach (CLISetup::$localeIds as $lId)
{
set_time_limit(5);
User::useLocale($lId);
Lang::load($lId);
$gemsOut = [];
foreach ($gems as $g)
{
if (!$enchantments->getEntry($g['enchId']))
{
CLI::write('[gems] * could not find enchantment #'.$g['enchId'].' referenced by item #'.$g['itemId'], CLI::LOG_WARN);
continue;
}
$gemsOut[$g['itemId']] = array(
'name' => Util::localizedString($g, 'name'),
'quality' => $g['quality'],
'icon' => strToLower($g['icon']),
'enchantment' => $enchantments->getField('name', true),
'jsonequip' => $enchantments->getStatGainForCurrent(),
'colors' => $g['colors'],
'expansion' => $g['expansion'],
'gearscore' => Util::getGemScore($g['itemLevel'], $g['quality'], $g['requiredSkill'] == SKILL_JEWELCRAFTING, $g['itemId'])
);
}
$toFile = "var g_gems = ".Util::toJSON($gemsOut).";";
$file = 'datasets/'.User::$localeString.'/gems';
if (!CLISetup::writeFile($file, $toFile))
$this->success = false;
}
return $this->success;
}
});
?>

View File

@@ -7,24 +7,30 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
/* Example /* Example
40896: { 40896: {
"name":"Glyph of Frenzied Regeneration", "name":"Glyph of Frenzied Regeneration",
"description":"For 6 sec after activating Frenzied Regeneration, healing effects on you are 40% more powerful. However, your Frenzied Regeneration now always costs 60 Rage and no longer converts Rage into health.", "description":"For 6 sec after activating Frenzied Regeneration, healing effects on you are 40% more powerful. However, your Frenzied Regeneration now always costs 60 Rage and no longer converts Rage into health.",
"icon":"ability_bullrush", "icon":"ability_bullrush",
"type":1, "type":1,
"classs":11, "classs":11,
"skill":798, "skill":798,
"level":25, "level":25,
}, },
*/ */
// Create 'glyphs'-file for available locales CLISetup::registerSetup("build", new class extends SetupScript
// this script requires the following dbc-files to be parsed and available {
protected $info = array(
'glyphs' => [[], CLISetup::ARGV_PARAM, 'Compiles glyphs to file for the talent calculator tool.']
);
function glyphs() protected $setupAfter = [['items', 'spell', 'glyphproperties', 'icons'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
public function generate() : bool
{ {
$success = true;
$glyphList = DB::Aowow()->Select( $glyphList = DB::Aowow()->Select(
'SELECT i.id AS itemId, 'SELECT i.id AS itemId,
i.*, i.*,
@@ -41,12 +47,8 @@ if (!CLI)
JOIN ?_glyphproperties g ON g.id = s1.effect1MiscValue JOIN ?_glyphproperties g ON g.id = s1.effect1MiscValue
JOIN ?_spell s2 ON s2.id = g.spellId JOIN ?_spell s2 ON s2.id = g.spellId
JOIN ?_icons ic ON ic.id = s1.iconIdAlt JOIN ?_icons ic ON ic.id = s1.iconIdAlt
WHERE i.classBak = 16'); WHERE i.classBak = ?d',
ITEM_CLASS_GLYPH);
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
$glyphSpells = new SpellList(array(['s.id', array_keys($glyphList)], Cfg::get('SQL_LIMIT_NONE'))); $glyphSpells = new SpellList(array(['s.id', array_keys($glyphList)], Cfg::get('SQL_LIMIT_NONE')));
@@ -83,9 +85,11 @@ if (!CLI)
$file = 'datasets/'.User::$localeString.'/glyphs'; $file = 'datasets/'.User::$localeString.'/glyphs';
if (!CLISetup::writeFile($file, $toFile)) if (!CLISetup::writeFile($file, $toFile))
$success = false; $this->success = false;
} }
return $success; return $this->success;
} }
});
?> ?>

View File

@@ -0,0 +1,130 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrComplexImage;
protected $info = array(
'img-artwork' => [[], CLISetup::ARGV_PARAM, 'Generate images from /glues/credits (not used on page)'],
);
public $isOptional = true;
private const TILEORDER = array(
1 => [ [1] ],
2 => [ [1],
[2] ],
4 => [ [1, 2],
[3, 4] ],
6 => [ [1, 2, 3],
[4, 5, 6] ],
8 => [ [1, 2, 3, 4],
[5, 6, 7, 8] ],
9 => [ [1, 2, 3],
[4, 5, 6],
[7, 8, 9] ]
);
// src, resourcePath, localized, [tileOrder], [[dest, destW, destH]]
private $genSteps = array(
['Glues/Credits/', null, false, self::TILEORDER, [['cache/Artworks/', 0, 0]]]
);
public function __construct()
{
$this->imgPath = CLISetup::$srcDir.$this->imgPath;
$this->maxExecTime = ini_get('max_execution_time');
foreach ($this->genSteps[0][self::$GEN_IDX_DEST_INFO] as $dir)
$this->requiredDirs[] = $dir[0];
}
public function generate() : bool
{
if (!$this->checkSourceDirs())
{
CLI::write('one or more source directories are missing:', CLI::LOG_ERROR);
$this->success = false;
return false;
}
sleep(2);
[, $realPath, , $tileOrder, $outInfo] = $this->genSteps[0];
$sum = 0;
$imgGroups = [];
$files = CLISetup::filesInPath('/'.str_replace('/', '\\/', $realPath).'/i', true);
$fileTpl = $outInfo[0][0].'%s.png';
foreach ($files as $f)
{
if (preg_match('/([^\/]+)(\d).blp/i', $f, $m))
{
if (!$m[1] || !$m[2])
continue;
if (!isset($imgGroups[$m[1]]))
$imgGroups[$m[1]] = $m[2];
else if ($imgGroups[$m[1]] < $m[2])
$imgGroups[$m[1]] = $m[2];
}
}
// errör-korrekt
if (isset($imgGroups['Desolace']))
$imgGroups['Desolace'] = 4;
$total = count($imgGroups);
CLI::write('Processing '.$total.' files from '.$realPath.' ...');
foreach ($imgGroups as $name => $fmt)
{
ini_set('max_execution_time', $this->maxExecTime);
$sum++;
$this->status = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
$file = sprintf($fileTpl, $name);
if (!CLISetup::getOpt('force') && file_exists($file))
{
CLI::write($this->status.' - file '.$file.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
if (!isset($tileOrder[$fmt]))
{
CLI::write(' - pattern for file '.$name.' not set. skipping', CLI::LOG_WARN);
$this->success = false;
continue;
}
$order = $tileOrder[$fmt];
$im = $this->assembleImage($realPath.'/'.$name, $order, count($order[0]) * 256, count($order) * 256);
if (!$im)
{
CLI::write(' - could not assemble file '.$name, CLI::LOG_ERROR);
$this->success = false;
continue;
}
if (!$this->writeImageFile($im, $file, count($order[0]) * 256, count($order) * 256))
$this->success = false;
}
ini_set('max_execution_time', $this->maxExecTime);
return $this->success;
}
});
?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrComplexImage;
protected $info = array(
'img-talentcalc' => [[], CLISetup::ARGV_PARAM, 'Generate backgrounds for the talent calculator.'],
);
protected $dbcSourceFiles = ['talenttab', 'chrclasses'];
private const DEST_DIRS = array(
['static/images/wow/hunterpettalents/', 0, 0],
['static/images/wow/talents/backgrounds/', 0, 0]
);
private const TILEORDER = array(
['-TopLeft', '-TopRight'],
['-BottomLeft', '-BottomRight']
);
// src, resourcePath, localized, [tileOrder], [[dest, destW, destH]]
private $genSteps = array(
['TalentFrame/', null, false, self::TILEORDER, self::DEST_DIRS]
);
public function __construct()
{
$this->imgPath = CLISetup::$srcDir.$this->imgPath;
$this->maxExecTime = ini_get('max_execution_time');
// init directories
foreach (self::DEST_DIRS as $dir)
$this->requiredDirs[] = $dir[0];
}
public function generate() : bool
{
if (!$this->checkSourceDirs())
{
CLI::write('one or more source directories are missing:', CLI::LOG_ERROR);
$this->success = false;
return false;
}
sleep(2);
$tTabs = DB::Aowow()->select('SELECT tt.creatureFamilyMask, tt.textureFile, tt.tabNumber, cc.fileString FROM dbc_talenttab tt LEFT JOIN dbc_chrclasses cc ON cc.id = (LOG(2, tt.classMask) + 1)');
if (!$tTabs)
{
CLI::write(' - TalentTab.dbc or ChrClasses.dbc is empty...?', CLI::LOG_ERROR);
$this->success = false;
return false;
}
$sum = 0;
$total = count($tTabs);
[, $realPath, , $tileOrder, $outInfo] = $this->genSteps[0];
CLI::write('Processing '.$total.' files from '.$realPath.' ...');
foreach ($tTabs as $tt)
{
ini_set('max_execution_time', $this->maxExecTime);
$sum++;
$this->status = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
if ($tt['creatureFamilyMask']) // is PetCalc
{
$size = [244, 364];
$outFile = sprintf($outInfo[0][0].'bg_%d.jpg', log($tt['creatureFamilyMask'], 2) + 1);
}
else
{
$size = [204, 554];
$outFile = sprintf($outInfo[1][0].'%s_%d.jpg', strtolower($tt['fileString']), $tt['tabNumber'] + 1);
}
if (!CLISetup::getOpt('force') && file_exists($outFile))
{
CLI::write($this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
$im = $this->assembleImage($realPath.'/'.$tt['textureFile'], $tileOrder, 256 + 44, 256 + 75);
if (!$im)
{
CLI::write(' - could not assemble file '.$tt['textureFile'], CLI::LOG_ERROR);
$this->success = false;
continue;
}
if (!$this->writeImageFile($im, $outFile, $size[0], $size[1]))
$this->success = false;
}
ini_set('max_execution_time', $this->maxExecTime);
return $this->success;
}
});
?>

View File

@@ -1,114 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// Create 'item-scaling'-file in datasets;
$reqDBC = ['scalingstatdistribution', 'scalingstatvalues', 'gtoctclasscombatratingscalar', 'gtcombatratings'];
function debugify($data)
{
$buff = [];
foreach ($data as $id => $row)
{
foreach ($row as &$r)
$r = str_pad($r, 5, " ", STR_PAD_LEFT);
$buff[] = str_pad($id, 7, " ", STR_PAD_LEFT).": [".implode(', ', $row)."]";
}
return "{\r\n".implode(",\r\n", $buff)."\r\n}";
}
function itemScalingRB()
{
$ratings = array(
12 => 1, // ITEM_MOD_DEFENSE_SKILL_RATING => CR_DEFENSE_SKILL
13 => 2, // ITEM_MOD_DODGE_RATING => CR_DODGE
14 => 3, // ITEM_MOD_PARRY_RATING => CR_PARRY
15 => 4, // ITEM_MOD_BLOCK_RATING => CR_BLOCK
16 => 5, // ITEM_MOD_HIT_MELEE_RATING => CR_HIT_MELEE
17 => 6, // ITEM_MOD_HIT_RANGED_RATING => CR_HIT_RANGED
18 => 7, // ITEM_MOD_HIT_SPELL_RATING => CR_HIT_SPELL
19 => 8, // ITEM_MOD_CRIT_MELEE_RATING => CR_CRIT_MELEE
20 => 9, // ITEM_MOD_CRIT_RANGED_RATING => CR_CRIT_RANGED
21 => 10, // ITEM_MOD_CRIT_SPELL_RATING => CR_CRIT_SPELL
22 => 11, // ITEM_MOD_HIT_TAKEN_MELEE_RATING => CR_HIT_TAKEN_MELEE
23 => 12, // ITEM_MOD_HIT_TAKEN_RANGED_RATING => CR_HIT_TAKEN_RANGED
24 => 13, // ITEM_MOD_HIT_TAKEN_SPELL_RATING => CR_HIT_TAKEN_SPELL
25 => 14, // ITEM_MOD_CRIT_TAKEN_MELEE_RATING => CR_CRIT_TAKEN_MELEE [may be forced 0]
26 => 15, // ITEM_MOD_CRIT_TAKEN_RANGED_RATING => CR_CRIT_TAKEN_RANGED [may be forced 0]
27 => 16, // ITEM_MOD_CRIT_TAKEN_SPELL_RATING => CR_CRIT_TAKEN_SPELL [may be forced 0]
28 => 17, // ITEM_MOD_HASTE_MELEE_RATING => CR_HASTE_MELEE
29 => 18, // ITEM_MOD_HASTE_RANGED_RATING => CR_HASTE_RANGED
30 => 19, // ITEM_MOD_HASTE_SPELL_RATING => CR_HASTE_SPELL
31 => 5, // ITEM_MOD_HIT_RATING => [backRef]
32 => 8, // ITEM_MOD_CRIT_RATING => [backRef]
33 => 11, // ITEM_MOD_HIT_TAKEN_RATING => [backRef] [may be forced 0]
34 => 14, // ITEM_MOD_CRIT_TAKEN_RATING => [backRef] [may be forced 0]
35 => 14, // ITEM_MOD_RESILIENCE_RATING => [backRef]
36 => 17, // ITEM_MOD_HASTE_RATING => [backRef]
37 => 23, // ITEM_MOD_EXPERTISE_RATING => CR_EXPERTISE
44 => 24 // ITEM_MOD_ARMOR_PENETRATION_RATING => CR_ARMOR_PENETRATION
);
$data = $ratings;
$offsets = array_map(function ($v) { // LookupEntry(cr*GT_MAX_LEVEL+level-1)
return $v * 100 + 60 - 1;
}, $ratings);
$base = DB::Aowow()->selectCol('SELECT CAST((idx + 1 - 60) / 100 AS UNSIGNED) AS ARRAY_KEY, ratio FROM dbc_gtcombatratings WHERE idx IN (?a)', $offsets);
$offsets = array_map(function ($v) { // LookupEntry((getClass()-1)*GT_MAX_RATING+cr+1)
return (CLASS_WARRIOR - 1) * 32 + $v + 1;
}, $ratings);
$mods = DB::Aowow()->selectCol('SELECT idx - 1 AS ARRAY_KEY, ratio FROM dbc_gtoctclasscombatratingscalar WHERE idx IN (?a)', $offsets);
foreach ($data as $itemMod => &$val)
$val = Cfg::get('DEBUG') ? $base[$val].' / '.$mods[$val] : $base[$val] / $mods[$val];
if (!Cfg::get('DEBUG'))
return Util::toJSON($data);
$buff = [];
foreach ($data as $k => $v)
$buff[] = $k.': '.$v;
return "{\r\n ".implode(",\r\n ", $buff)."\r\n}";
}
function itemScalingSV()
{
/* so the javascript expects a slightly different structure, than the dbc provides .. f*** it
e.g.
dbc - 80: 97 97 56 41 210 395 878 570 120 156 86 112 108 220 343 131 73 140 280 527 1171 2093
expected - 80: 97 97 56 131 41 210 395 878 1570 120 156 86 112 108 220 343 0 0 73 140 280 527 1171 2093
*/
$fields = Util::$ssdMaskFields;
array_walk($fields, function(&$v, $k) {
$v = $v ?: '0 AS idx'.$k; // NULL => 0 (plus some index so we can have 2x 0)
});
$data = DB::Aowow()->select('SELECT id AS ARRAY_KEY, '.implode(', ', $fields).' FROM dbc_scalingstatvalues');
foreach ($data as &$d)
$d = array_values($d); // strip indizes
return Cfg::get('DEBUG') ? debugify($data) : Util::toJSON($data);
}
function itemScalingSD()
{
$data = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_scalingstatdistribution');
foreach ($data as &$row)
{
$row = array_values($row);
array_splice($row, 0, 1);
}
return Cfg::get('DEBUG') ? debugify($data) : Util::toJSON($data);
}
?>

View File

@@ -0,0 +1,136 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'itemscaling' => [[], CLISetup::ARGV_PARAM, 'Compiles item scaling data to file to make heirloom tooltips interactive.']
);
protected $fileTemplateDest = ['datasets/item-scaling'];
protected $fileTemplateSrc = ['item-scaling.in'];
protected $dbcSourceFiles = ['scalingstatdistribution', 'scalingstatvalues', 'gtoctclasscombatratingscalar', 'gtcombatratings'];
private function debugify(array $data) : string
{
$buff = [];
foreach ($data as $id => $row)
{
foreach ($row as &$r)
$r = str_pad($r, 5, " ", STR_PAD_LEFT);
$buff[] = str_pad($id, 7, " ", STR_PAD_LEFT).": [".implode(', ', $row)."]";
}
return "{\r\n".implode(",\r\n", $buff)."\r\n}";
}
private function itemScalingRB() : string
{
// data and format observed via wayback machine. Not entirely sure about the redunacy within the combat ratings though.
$ratings = array(
Stat::DEFENSE_RTG => CR_DEFENSE_SKILL,
Stat::DODGE_RTG => CR_DODGE,
Stat::PARRY_RTG => CR_PARRY,
Stat::BLOCK_RTG => CR_BLOCK,
Stat::MELEE_HIT_RTG => CR_HIT_MELEE,
Stat::RANGED_HIT_RTG => CR_HIT_RANGED,
Stat::SPELL_HIT_RTG => CR_HIT_SPELL,
Stat::MELEE_CRIT_RTG => CR_CRIT_MELEE,
Stat::RANGED_CRIT_RTG => CR_CRIT_RANGED,
Stat::SPELL_CRIT_RTG => CR_CRIT_SPELL,
Stat::MELEE_HIT_TAKEN_RTG => CR_HIT_TAKEN_MELEE,
Stat::RANGED_HIT_TAKEN_RTG => CR_HIT_TAKEN_RANGED,
Stat::SPELL_HIT_TAKEN_RTG => CR_HIT_TAKEN_SPELL,
Stat::MELEE_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_MELEE, // may be forced 0
Stat::RANGED_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_RANGED, // may be forced 0
Stat::SPELL_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_SPELL, // may be forced 0
Stat::MELEE_HASTE_RTG => CR_HASTE_MELEE,
Stat::RANGED_HASTE_RTG => CR_HASTE_RANGED,
Stat::SPELL_HASTE_RTG => CR_HASTE_SPELL,
Stat::HIT_RTG => CR_HIT_MELEE,
Stat::CRIT_RTG => CR_CRIT_MELEE,
Stat::HIT_TAKEN_RTG => CR_HIT_TAKEN_MELEE, // may be forced 0
Stat::CRIT_TAKEN_RTG => CR_CRIT_TAKEN_MELEE, // may be forced 0
Stat::RESILIENCE_RTG => CR_CRIT_TAKEN_MELEE,
Stat::HASTE_RTG => CR_HASTE_MELEE,
Stat::EXPERTISE_RTG => CR_EXPERTISE,
Stat::ARMOR_PENETRATION_RTG => CR_ARMOR_PENETRATION
);
$data = $ratings;
$offsets = array_map(function ($v) { // LookupEntry(cr*GT_MAX_LEVEL+level-1)
return $v * 100 + 60 - 1; // combat rating where introduced during the transition vanilla > burnig crusade. So at level 60 (at the time) the rating on the item was equal to 1% effect and is still the baseline in 3.3.5a.
}, $ratings);
$base = DB::Aowow()->selectCol('SELECT CAST((idx + 1 - 60) / 100 AS UNSIGNED) AS ARRAY_KEY, ratio FROM dbc_gtcombatratings WHERE idx IN (?a)', $offsets);
/* non-1 scaler in 3.3.5.12340
| ratingId | classId | ratio |
| 17 | 2 | 1.3 |
| 17 | 6 | 1.3 |
| 17 | 7 | 1.3 |
| 17 | 11 | 1.3 |
| 24 | < all > | 1.1 |
*/
$offsets = array_map(function ($v) { // LookupEntry((getClass()-1)*GT_MAX_RATING+cr+1)
return (CLASS_WARRIOR - 1) * 32 + $v + 1; // should this be dynamic per pinned character? ITEM_MOD HASTE has a worse scaler for a subset of classes (see table)
}, $ratings);
$mods = DB::Aowow()->selectCol('SELECT idx - 1 AS ARRAY_KEY, ratio FROM dbc_gtoctclasscombatratingscalar WHERE idx IN (?a)', $offsets);
foreach ($data as &$val)
$val = Cfg::get('DEBUG') ? $base[$val].' / '.$mods[$val] : $base[$val] / $mods[$val];
if (!Cfg::get('DEBUG'))
return Util::toJSON($data);
$buff = [];
foreach ($data as $k => $v)
$buff[] = $k.': '.$v;
return "{\r\n ".implode(",\r\n ", $buff)."\r\n}";
}
private function itemScalingSV() : string
{
/* so the javascript expects a slightly different structure, than the dbc provides .. f*** it
e.g.
dbc - 80: 97 97 56 41 210 395 878 570 120 156 86 112 108 220 343 131 73 140 280 527 1171 2093
expected - 80: 97 97 56 131 41 210 395 878 1570 120 156 86 112 108 220 343 0 0 73 140 280 527 1171 2093
*/
$fields = Util::$ssdMaskFields;
array_walk($fields, function(&$v, $k) {
$v = $v ?: '0 AS idx'.$k; // NULL => 0 (plus some index so we can have 2x 0)
});
$data = DB::Aowow()->select('SELECT id AS ARRAY_KEY, '.implode(', ', $fields).' FROM dbc_scalingstatvalues');
foreach ($data as &$d)
$d = array_values($d); // strip indizes
return Cfg::get('DEBUG') ? $this->debugify($data) : Util::toJSON($data);
}
private function itemScalingSD() : string
{
$data = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_scalingstatdistribution');
foreach ($data as &$row)
{
$row = array_values($row);
array_splice($row, 0, 1);
}
return Cfg::get('DEBUG') ? $this->debugify($data) : Util::toJSON($data);
}
})
?>

View File

@@ -7,41 +7,43 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
/* Example /* Example
"-447": { // internal id, freely chosen "-447": { // internal id, freely chosen
"classes":["6"], // array "classes":["6"], // array
"elite":true, "elite":true,
"heroic":false, "heroic":false,
"id":"-447", "id":"-447",
"idbak":"924", // actual setId "idbak":"924", // actual setId
"maxlevel":"390", "maxlevel":"390",
"minlevel":"390", "minlevel":"390",
"name":"3Cataclysmic Gladiator's Desecration", "name":"3Cataclysmic Gladiator's Desecration",
"note":"37", // contentGroup "note":"37", // contentGroup
"pieces":["73742","73741","73740","73739","73738"], "pieces":["73742","73741","73740","73739","73738"],
"reqclass":"32", // mask "reqclass":"32", // mask
"type":"4", "type":"4",
"setbonus":{ "setbonus":{
"2":{"resirtng":"400","str":"70"}, "2":{"resirtng":"400","str":"70"},
"4":{"str":"90"} "4":{"str":"90"}
} }
}, },
*/ */
// Create 'itemsets'-file for available locales // Create 'itemsets'-file for available locales
// this script requires the following dbc-files to be parsed and available CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'itemsets' => [[], CLISetup::ARGV_PARAM, 'Compiles available item sets used throughout the page to file.']
);
function itemsets() protected $setupAfter = [['itemset', 'spell'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
public function generate() : bool
{ {
$success = true;
$setList = DB::Aowow()->Select('SELECT * FROM ?_itemset ORDER BY refSetId DESC'); $setList = DB::Aowow()->Select('SELECT * FROM ?_itemset ORDER BY refSetId DESC');
$jsonBonus = []; $jsonBonus = [];
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
foreach (CLISetup::$localeIds as $lId) foreach (CLISetup::$localeIds as $lId)
{ {
User::useLocale($lId); User::useLocale($lId);
@@ -55,7 +57,7 @@ if (!CLI)
$setOut = array( $setOut = array(
'id' => $set['id'], 'id' => $set['id'],
'idbak' => $set['refSetId'], 'idbak' => $set['refSetId'],
'name' => (7 - $set['quality']).Util::jsEscape(Util::localizedString($set, 'name')), 'name' => (ITEM_QUALITY_HEIRLOOM - $set['quality']).Util::jsEscape(Util::localizedString($set, 'name')),
'pieces' => [], 'pieces' => [],
'heroic' => !!$set['heroic'], // should be bool 'heroic' => !!$set['heroic'], // should be bool
'maxlevel' => $set['maxLevel'], 'maxlevel' => $set['maxLevel'],
@@ -84,7 +86,7 @@ if (!CLI)
$_spells = []; $_spells = [];
for ($i = 1; $i < 9; $i++) for ($i = 1; $i < 9; $i++)
{ {
if (!$set['spell'.$i] || isset($jsonBonus[$set['spell'.$i]])) if (!$set['bonus'.$i] || isset($jsonBonus[$set['spell'.$i]]))
continue; continue;
$_spells[] = $set['spell'.$i]; $_spells[] = $set['spell'.$i];
@@ -121,9 +123,11 @@ if (!CLI)
$file = 'datasets/'.User::$localeString.'/itemsets'; $file = 'datasets/'.User::$localeString.'/itemsets';
if (!CLISetup::writeFile($file, $toFile)) if (!CLISetup::writeFile($file, $toFile))
$success = false; $this->success = false;
} }
return $success; return $this->success;
} }
});
?> ?>

View File

@@ -7,12 +7,22 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
// Create 'locale.js'-file in static/js // Create 'locale.js'-file in static/js
// available locales have to be set in aowow.aowow_config // available locales have to be set in aowow.aowow_config
function locales() CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'locales' => [[], CLISetup::ARGV_PARAM, 'Compiles the Locale Object (static/js/locale.js) with available languages.']
);
protected $fileTemplateDest = ['static/js/locale.js'];
protected $fileTemplateSrc = ['locale.js.in'];
private function locales() : string
{ {
$result = [];
$available = array( $available = array(
LOCALE_EN => " 0: { // English\r\n" . LOCALE_EN => " 0: { // English\r\n" .
" id: LOCALE_ENUS,\r\n" . " id: LOCALE_ENUS,\r\n" .
@@ -52,11 +62,13 @@ if (!CLI)
" }", " }",
); );
$result = [];
foreach (CLISetup::$localeIds as $l) foreach (CLISetup::$localeIds as $l)
if (isset($available[$l])) if (isset($available[$l]))
$result[] = $available[$l]; $result[] = $available[$l];
return implode(",\r\n", $result); return implode(",\r\n", $result);
} }
});
?> ?>

View File

@@ -0,0 +1,22 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'markup' => [[], CLISetup::ARGV_PARAM, 'Fills the markup parser (static/js/Markup.js) with site variables.']
);
protected $fileTemplateSrc = ['Markup.js.in'];
protected $fileTemplateDest = ['static/js/Markup.js'];
});
?>

View File

@@ -1,97 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// builds 'pets'-file for available locales
/* Example data
30: {
id:30,
name:'Forest Spider',
minlevel:5,
maxlevel:6,
location:[12], // master-AreaTableId's (?)
react:[-1,-1],
classification:0, // 0:"Normal", 1:"Elite", 2:"Rar Elite", 3:"Boss", 4:"Rar"
family:3, // creatureFamily
displayId:382,
skin:'TarantulaSkinOrange',
icon:'Ability_Hunter_Pet_Spider', // from creatureFamily.dbc
type:2 // 0:Ferocity, 1:Tenacity, 2:Cunning
},
*/
$reqDBC = ['creaturefamily'];
function pets()
{
$success = true;
$locations = [];
$petList = DB::Aowow()->Select(
'SELECT cr.id,
cr.name_loc0, cr.name_loc2, cr.name_loc3, cr.name_loc4, cr.name_loc6, cr.name_loc8,
cr.minLevel,
cr.maxLevel,
ft.A,
ft.H,
cr.rank AS classification,
cr.family,
cr.displayId1 AS displayId,
cr.textureString AS skin,
LOWER(SUBSTRING_INDEX(cf.iconString, "\\\\", -1)) AS icon,
cf.petTalentType AS type
FROM ?_creature cr
JOIN ?_factiontemplate ft ON ft.id = cr.faction
JOIN dbc_creaturefamily cf ON cf.id = cr.family
WHERE cr.typeFlags & 0x1 AND (cr.cuFlags & 0x2) = 0
ORDER BY cr.id ASC');
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
foreach (CLISetup::$localeIds as $lId)
{
User::useLocale($lId);
Lang::load($lId);
$petsOut = [];
foreach ($petList as $pet)
{
// get locations
// again: caching will save you time and nerves
if (!isset($locations[$pet['id']]))
$locations[$pet['id']] = DB::Aowow()->SelectCol('SELECT DISTINCT areaId FROM ?_spawns WHERE type = ?d AND typeId = ?d', Type::NPC, $pet['id']);
$petsOut[$pet['id']] = array(
'id' => $pet['id'],
'name' => Util::localizedString($pet, 'name'),
'minlevel' => $pet['minLevel'],
'maxlevel' => $pet['maxLevel'],
'location' => $locations[$pet['id']],
'react' => [$pet['A'], $pet['H']],
'classification' => $pet['classification'],
'family' => $pet['family'],
'displayId' => $pet['displayId'],
'skin' => $pet['skin'],
'icon' => $pet['icon'],
'type' => $pet['type']
);
}
$toFile = "var g_pets = ".Util::toJSON($petsOut).";";
$file = 'datasets/'.User::$localeString.'/pets';
if (!CLISetup::writeFile($file, $toFile))
$success = false;
}
return $success;
}
?>

View File

@@ -0,0 +1,95 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/* Example data
30: {
id:30,
name:'Forest Spider',
minlevel:5,
maxlevel:6,
location:[12], // master-AreaTableId's (?)
react:[-1,-1],
classification:0, // 0:"Normal", 1:"Elite", 2:"Rar Elite", 3:"Boss", 4:"Rar"
family:3, // creatureFamily
displayId:382,
skin:'TarantulaSkinOrange',
icon:'Ability_Hunter_Pet_Spider', // from creatureFamily.dbc
type:2 // 0:Ferocity, 1:Tenacity, 2:Cunning
},
*/
// builds 'pets'-file for available locales
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'pets' => [[], CLISetup::ARGV_PARAM, 'Compiles tameable hunter pets to file for the talent calculator tool.']
);
protected $dbcSourceFiles = ['creaturefamily'];
protected $setupAfter = [['creature', 'factions', 'spawns'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
public function generate() : bool
{
$petList = DB::Aowow()->Select(
'SELECT cr.id,
cr.name_loc0, cr.name_loc2, cr.name_loc3, cr.name_loc4, cr.name_loc6, cr.name_loc8,
cr.minLevel, cr.maxLevel,
ft.A, ft.H,
cr.rank AS classification,
cr.family,
cr.displayId1 AS displayId,
cr.textureString AS skin,
LOWER(SUBSTRING_INDEX(cf.iconString, "\\\\", -1)) AS icon,
cf.petTalentType AS type
FROM ?_creature cr
JOIN ?_factiontemplate ft ON ft.id = cr.faction
JOIN dbc_creaturefamily cf ON cf.id = cr.family
WHERE cr.typeFlags & 0x1 AND (cr.cuFlags & 0x2) = 0
ORDER BY cr.id ASC');
$locations = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, `areaId` AS ARRAY_KEY2, `areaId` FROM ?_spawns WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId`, `areaId`', Type::NPC, array_column($petList, 'id'));
foreach (CLISetup::$localeIds as $lId)
{
User::useLocale($lId);
Lang::load($lId);
$petsOut = [];
foreach ($petList as $pet)
{
$petsOut[$pet['id']] = array(
'id' => $pet['id'],
'name' => Util::localizedString($pet, 'name'),
'minlevel' => $pet['minLevel'],
'maxlevel' => $pet['maxLevel'],
'location' => $locations[$pet['id']] ?? [],
'react' => [$pet['A'], $pet['H']],
'classification' => $pet['classification'],
'family' => $pet['family'],
'displayId' => $pet['displayId'],
'skin' => $pet['skin'],
'icon' => $pet['icon'],
'type' => $pet['type']
);
}
$toFile = "var g_pets = ".Util::toJSON($petsOut).";";
$file = 'datasets/'.User::$localeString.'/pets';
if (!CLISetup::writeFile($file, $toFile))
$this->success = false;
}
return $this->success;
}
});
?>

View File

@@ -1,473 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// gatheres quasi-static data used in profiler: all available quests, achievements, titles, mounts, companions, factions, recipes
// this script requires a fully set up database and is expected to be run last
function profiler()
{
$success = true;
$scripts = [];
$exclusions = [];
$exAdd = function ($type, $typeId, $groups, $comment = '') use(&$exclusions)
{
$k = $type.'-'.$typeId;
if (!isset($exclusions[$k]))
$exclusions[$k] = ['type' => $type, 'typeId' => $typeId, 'groups' => $groups, 'comment' => $comment];
else
{
$exclusions[$k]['groups'] |= $groups;
if ($comment)
$exclusions[$k]['comment'] .= '; '.$comment;
}
};
$sumTotal = function(array &$sumArr, int $raceMask = -1, int $classMask= -1)
{
for ($i = 0; $i < RACE_MASK_ALL; $i++)
{
if (!((1 << $i) & $raceMask) || !((1 << $i) & RACE_MASK_ALL))
continue;
for ($j = 0; $j < CLASS_MASK_ALL; $j++)
{
if (!((1 << $j) & $classMask) || !((1 << $j) & CLASS_MASK_ALL))
continue;
if (!isset($sumArr[$i+1][$j+1]))
$sumArr[$i+1][$j+1] = 1;
else
$sumArr[$i+1][$j+1]++;
}
}
};
$spellFactions = DB::World()->selectCol('SELECT `alliance_id` AS ARRAY_KEY, 1 FROM player_factionchange_spells UNION SELECT `horde_id` AS ARRAY_KEY, 2 FROM player_factionchange_spells');
/**********/
/* Quests */
/**********/
$scripts[] = function() use ($exAdd, $sumTotal)
{
$success = true;
$questorder = [];
$questtotal = [];
$condition = [
Cfg::get('SQL_LIMIT_NONE'),
'AND',
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW | CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0],
[['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE | QUEST_FLAG_AUTO_REWARDED, '&'], 0],
[['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_DUNGEON_FINDER | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]
];
foreach (Game::$questClasses as $cat2 => $cat)
{
if ($cat2 < 0)
continue;
$cond = array_merge($condition, [['zoneOrSort', $cat]]);
$questz = new QuestList($cond);
if ($questz->error)
continue;
$questorder[] = $cat2;
$questtotal[$cat2] = [];
// get quests for exclusion
foreach ($questz->iterate() as $id => $__)
{
$sumTotal($questtotal[$cat2], $questz->getField('reqRaceMask') ?: -1, $questz->getField('reqClassMask') ?: -1);
switch ($questz->getField('reqSkillId'))
{
case 356:
$exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_FISHING);
break;
case 202:
$exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_ENGINEERING);
break;
case 197:
$exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_TAILORING);
break;
}
}
$_ = [];
$currencies = array_column($questz->rewards, Type::CURRENCY);
foreach ($currencies as $curr)
foreach ($curr as $cId => $qty)
$_[] = $cId;
$relCurr = new CurrencyList(array(['id', $_]));
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(20);
User::useLocale($l);
Lang::load($l);
if (!$relCurr->error)
{
$buff = "var _ = g_gatheredcurrencies;\n";
foreach ($relCurr->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
$buff .= "var _ = g_quests;\n";
foreach ($questz->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-quests-'.$cat2, $buff))
$success = false;
}
}
$buff = "g_quest_catorder = ".Util::toJSON($questorder).";\n";
$buff .= "g_quest_catorder_total = {};\n";
foreach ($questtotal as $cat => $totals)
$buff .= "g_quest_catorder_total[".$cat."] = ".Util::toJSON($totals).";\n";
if (!CLISetup::writeFile('datasets/p-quests', $buff))
$success = false;
return $success;
};
/**********/
/* Titles */
/**********/
$scripts[] = function() use ($exAdd)
{
$success = true;
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
);
$titlez = new TitleList($condition);
// get titles for exclusion
foreach ($titlez->iterate() as $id => $__)
if (empty($titlez->sources[$id][4]) && empty($titlez->sources[$id][12]))
$exAdd(Type::TITLE, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
foreach ([0, 1] as $g) // gender
{
$buff = "var _ = g_titles;\n";
foreach ($titlez->getListviewData() as $id => $data)
{
$data['name'] = Util::localizedString($titlez->getEntry($id), $g ? 'female' : 'male');
unset($data['namefemale']);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-titles-'.$g, $buff))
$success = false;
}
}
return $success;
};
/**********/
/* Mounts */
/**********/
$scripts[] = function() use ($exAdd, $spellFactions)
{
$success = true;
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['typeCat', -5],
['castTime', 0, '!']
);
$mountz = new SpellList($condition);
$conditionSet = DB::World()->selectCol('SELECT SourceEntry AS ARRAY_KEY, ConditionValue1 FROM conditions WHERE SourceTypeOrReferenceId = ?d AND ConditionTypeOrReference = ?d AND SourceEntry IN (?a)', Conditions::SRC_SPELL, Conditions::SKILL, $mountz->getFoundIDs());
// get mounts for exclusion
foreach ($conditionSet as $mount => $skill)
{
if ($skill == 202)
$exAdd(Type::SPELL, $mount, PR_EXCLUDE_GROUP_REQ_ENGINEERING);
else if ($skill == 197)
$exAdd(Type::SPELL, $mount, PR_EXCLUDE_GROUP_REQ_TAILORING);
}
foreach ($mountz->iterate() as $id => $_)
if (!$mountz->getSources())
$exAdd(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_spells;\n";
foreach ($mountz->getListviewData(ITEMINFO_MODEL) as $id => $data)
{
// two cases where the spell is unrestricted but the castitem has class restriction (too lazy to formulate ruleset)
if ($id == 66906) // Argent Charger
$data['reqclass'] = CLASS_PALADIN;
else if ($id == 54729) // Winged Steed of the Ebon Blade
$data['reqclass'] = CLASS_DEATHKNIGHT;
rsort($data['skill']); // riding (777) expected at pos 0
$data['side'] = $spellFactions[$id] ?? SIDE_BOTH;
$data['quality'] = $data['name'][0];
$data['name'] = mb_substr($data['name'], 1);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-mounts', $buff))
$success = false;
}
return $success;
};
/**************/
/* Companions */
/**************/
$scripts[] = function() use ($exAdd, $spellFactions)
{
$success = true;
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['typeCat', -6]
);
$companionz = new SpellList($condition);
$legit = DB::Aowow()->selectCol('SELECT `spellId2` FROM ?_items WHERE `class` = ?d AND `subClass` = ?d AND `spellId1` IN (?a) AND `spellId2` IN (?a)', ITEM_CLASS_MISC, 2, LEARN_SPELLS, $companionz->getFoundIDs());
foreach ($companionz->iterate() as $id => $_)
if (!$companionz->getSources())
$exAdd(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_spells;\n";
foreach ($companionz->getListviewData(ITEMINFO_MODEL) as $id => $data)
{
if (!in_array($id, $legit))
continue;
$data['side'] = $spellFactions[$id] ?? SIDE_BOTH;
$data['quality'] = $data['name'][0];
$data['name'] = mb_substr($data['name'], 1);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-companions', $buff))
$success = false;
}
return $success;
};
/************/
/* Factions */
/************/
$scripts[] = function() use ($exAdd)
{
$success = true;
$condition = array( // todo (med): exclude non-gaining reputation-header
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]
);
$factionz = new FactionList($condition);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_factions;\n";
foreach ($factionz->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
$buff .= "\ng_faction_order = [0, 469, 891, 1037, 1118, 67, 1052, 892, 936, 1117, 169, 980, 1097];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-factions', $buff))
$success = false;
}
return $success;
};
/***********/
/* Recipes */
/***********/
$scripts[] = function() use ($exAdd, $spellFactions)
{
// special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order
$skills = array_merge(SKILLS_TRADE_PRIMARY, [[185, 129, 356]]);
$success = true;
$baseCnd = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['effect1Id', [6, 45, 57, 127, 33, 158, 99, 28, 95], '!'], // aura, tradeSkill, Tracking, Prospecting, Decipher, Milling, Disenchant, Summon (Engineering), Skinning
['effect2Id', [118, 60], '!'], // not the skill itself
['OR', ['typeCat', 9], ['typeCat', 11]]
);
foreach ($skills as $s)
{
$file = is_array($s) ? 'sec' : (string)$s;
$cnd = array_merge($baseCnd, [['skillLine1', $s]]);
$recipez = new SpellList($cnd);
$created = '';
foreach ($recipez->iterate() as $id => $__)
{
if (!$recipez->getSources())
$exAdd(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach ($recipez->canCreateItem() as $idx)
{
$id = $recipez->getField('effect'.$idx.'CreateItemId');
$created .= "g_items.add(".$id.", {'icon':'".$recipez->relItems->getEntry($id)['iconString']."'});\n";
}
}
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(10);
User::useLocale($l);
Lang::load($l);
$buff = '';
foreach ($recipez->getListviewData() as $id => $data)
{
$data['side'] = $spellFactions[$id] ?? SIDE_BOTH;
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!$buff)
{
// this behaviour is intended, do not create an error
CLI::write('profiler - file datasets/'.User::$localeString.'/p-recipes-'.$file.' has no content => skipping', CLI::LOG_INFO);
continue;
}
$buff = $created."\nvar _ = g_spells;\n".$buff;
if (is_array($s))
$buff .= "\ng_skill_order = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-recipes-'.$file, $buff))
$success = false;
}
}
return $success;
};
/****************/
/* Achievements */
/****************/
$scripts[] = function() use ($exAdd)
{
$success = true;
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
[['flags', 1, '&'], 0], // no statistics
);
$achievez = new AchievementList($condition);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$sumPoints = 0;
$buff = "var _ = g_achievements;\n";
foreach ($achievez->getListviewData(ACHIEVEMENTINFO_PROFILE) as $id => $data)
{
$sumPoints += $data['points'];
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
// categories to sort by
$buff .= "\ng_achievement_catorder = [92, 14863, 97, 169, 170, 171, 172, 14802, 14804, 14803, 14801, 95, 161, 156, 165, 14806, 14921, 96, 201, 160, 14923, 14808, 14805, 14778, 14865, 14777, 14779, 155, 14862, 14861, 14864, 14866, 158, 162, 14780, 168, 14881, 187, 14901, 163, 14922, 159, 14941, 14961, 14962, 14981, 15003, 15002, 15001, 15041, 15042, 81]";
// sum points
$buff .= "\ng_achievement_points = [".$sumPoints."];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/achievements', $buff))
$success = false;
}
return $success;
};
/******************/
/* Quick Excludes */
/******************/
$scripts[] = function() use (&$exclusions)
{
set_time_limit(2);
CLI::write('applying '.count($exclusions).' baseline exclusions');
DB::Aowow()->query('DELETE FROM ?_profiler_excludes WHERE comment = ""');
foreach ($exclusions as $ex)
DB::Aowow()->query('REPLACE INTO ?_profiler_excludes (?#) VALUES (?a)', array_keys($ex), array_values($ex));
// excludes; type => [excludeGroupBit => [typeIds]]
$excludes = [];
$exData = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `groups` FROM ?_profiler_excludes');
for ($i = 0; (1 << $i) < PR_EXCLUDE_GROUP_ANY; $i++)
foreach ($exData as $type => $data)
if ($ids = array_keys(array_filter($data, function ($x) use ($i) { return $x & (1 << $i); } )))
$excludes[$type][$i + 1] = $ids;
$buff = "g_excludes = ".Util::toJSON($excludes ?: (new Stdclass)).";\n";
return CLISetup::writeFile('datasets/quick-excludes', $buff);
};
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
// run scripts
foreach ($scripts as $func)
if (!$func())
$success = false;
return $success;
}
?>

View File

@@ -0,0 +1,447 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'profiler' => [[ ], CLISetup::ARGV_PARAM, 'Generates data dumps and completion exclusion filters for the profiler tool.'],
'quests' => [['1'], CLISetup::ARGV_OPTIONAL, '...available quests by category'],
'titles' => [['2'], CLISetup::ARGV_OPTIONAL, '...available titles by gender'],
'mounts' => [['3'], CLISetup::ARGV_OPTIONAL, '...available mounts'],
'companions' => [['4'], CLISetup::ARGV_OPTIONAL, '...available companions'],
'factions' => [['5'], CLISetup::ARGV_OPTIONAL, '...available factions'],
'recipes' => [['6'], CLISetup::ARGV_OPTIONAL, '...available recipes by skill'],
'achievements' => [['7'], CLISetup::ARGV_OPTIONAL, '...available achievements'],
'quickexcludes' => [['9'], CLISetup::ARGV_OPTIONAL, '...unobtainable items, mutually exclusive recipes, factions, etc.'],
);
protected $localized = true;
protected $requiredDirs = ['datasets/'];
protected $worldDependency = ['player_factionchange_spells', 'conditions'];
protected $setupAfter = [['quests', 'quests_startend', 'items', 'currencies', 'titles', 'spell', 'factions', 'achievement'], []];
private $spellFactions = [];
private $exclusions = [];
private $opts = [];
public function generate() : bool
{
$anyOpt = array_keys($this->info);
$this->opts = CLISetup::getOpt(...$anyOpt);
$this->opts = array_filter($this->opts);
if (!$this->opts) // none were set -> use default (all)
$this->opts = array_fill_keys(array_slice($anyOpt, 1), true);
$this->spellFactions = DB::World()->selectCol('SELECT `alliance_id` AS ARRAY_KEY, 1 FROM player_factionchange_spells UNION SELECT `horde_id` AS ARRAY_KEY, 2 FROM player_factionchange_spells');
foreach ($this->opts as $fn => $_)
$this->$fn();
return $this->success;
}
private function quests() : void
{
$questorder = [];
$questtotal = [];
$condition = [
Cfg::get('SQL_LIMIT_NONE'),
'AND',
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW | CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0],
[['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE | QUEST_FLAG_AUTO_REWARDED, '&'], 0],
[['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_DUNGEON_FINDER | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]
];
foreach (Game::$questClasses as $cat2 => $cat)
{
if ($cat2 < 0)
continue;
$cond = array_merge($condition, [['zoneOrSort', $cat]]);
$questz = new QuestList($cond);
if ($questz->error)
continue;
$questorder[] = $cat2;
$questtotal[$cat2] = [];
// get quests for exclusion
foreach ($questz->iterate() as $id => $__)
{
$this->sumTotal($questtotal[$cat2], $questz->getField('reqRaceMask') ?: -1, $questz->getField('reqClassMask') ?: -1);
if ($skillEx = $this->getExcludeForSkill($questz->getField('reqSkillId')))
$this->addExclusion(Type::QUEST, $id, $skillEx);
}
$_ = [];
$currencies = array_column($questz->rewards, Type::CURRENCY);
foreach ($currencies as $curr)
foreach ($curr as $cId => $qty)
$_[] = $cId;
$relCurr = new CurrencyList(array(['id', $_]));
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(20);
User::useLocale($l);
Lang::load($l);
if (!$relCurr->error)
{
$buff = "var _ = g_gatheredcurrencies;\n";
foreach ($relCurr->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
$buff .= "var _ = g_quests;\n";
foreach ($questz->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-quests-'.$cat2, $buff))
$this->success = false;
}
}
$buff = "g_quest_catorder = ".Util::toJSON($questorder).";\n";
$buff .= "g_quest_catorder_total = {};\n";
foreach ($questtotal as $cat => $totals)
$buff .= "g_quest_catorder_total[".$cat."] = ".Util::toJSON($totals).";\n";
if (!CLISetup::writeFile('datasets/p-quests', $buff))
$this->success = false;
}
private function titles(): void
{
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
);
$titlez = new TitleList($condition);
// get titles for exclusion
foreach ($titlez->iterate() as $id => $__)
if (empty($titlez->sources[$id][4]) && empty($titlez->sources[$id][12]))
$this->addExclusion(Type::TITLE, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
foreach ([GENDER_MALE, GENDER_FEMALE] as $g)
{
$buff = "var _ = g_titles;\n";
foreach ($titlez->getListviewData() as $id => $data)
{
$data['name'] = Util::localizedString($titlez->getEntry($id), $g ? 'female' : 'male');
unset($data['namefemale']);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-titles-'.$g, $buff))
$this->success = false;
}
}
}
private function mounts() : void
{
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['typeCat', -5],
['castTime', 0, '!']
);
$mountz = new SpellList($condition);
$conditionSet = DB::World()->selectCol('SELECT SourceEntry AS ARRAY_KEY, ConditionValue1 FROM conditions WHERE SourceTypeOrReferenceId = ?d AND ConditionTypeOrReference = ?d AND SourceEntry IN (?a)', Conditions::SRC_SPELL, Conditions::SKILL, $mountz->getFoundIDs());
// get mounts for exclusion
foreach ($conditionSet as $mount => $skill)
if ($skillEx = $this->getExcludeForSkill($skill))
$this->addExclusion(Type::SPELL, $mount, $skillEx);
foreach ($mountz->iterate() as $id => $_)
if (!$mountz->getSources())
$this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_spells;\n";
foreach ($mountz->getListviewData(ITEMINFO_MODEL) as $id => $data)
{
// two cases where the spell is unrestricted but the castitem has class restriction (too lazy to formulate ruleset)
if ($id == 66906) // Argent Charger
$data['reqclass'] = CLASS_PALADIN;
else if ($id == 54729) // Winged Steed of the Ebon Blade
$data['reqclass'] = CLASS_DEATHKNIGHT;
rsort($data['skill']); // riding (777) expected at pos 0
$data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
$data['quality'] = $data['name'][0];
$data['name'] = mb_substr($data['name'], 1);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-mounts', $buff))
$this->success = false;
}
}
private function companions() : void
{
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['typeCat', -6]
);
$companionz = new SpellList($condition);
$legit = DB::Aowow()->selectCol('SELECT `spellId2` FROM ?_items WHERE `class` = ?d AND `subClass` = ?d AND `spellId1` IN (?a) AND `spellId2` IN (?a)', ITEM_CLASS_MISC, 2, LEARN_SPELLS, $companionz->getFoundIDs());
foreach ($companionz->iterate() as $id => $_)
if (!$companionz->getSources())
$this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_spells;\n";
foreach ($companionz->getListviewData(ITEMINFO_MODEL) as $id => $data)
{
if (!in_array($id, $legit))
continue;
$data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
$data['quality'] = $data['name'][0];
$data['name'] = mb_substr($data['name'], 1);
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-companions', $buff))
$this->success = false;
}
}
private function factions() : void
{
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]
);
$factionz = new FactionList($condition);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$buff = "var _ = g_factions;\n";
foreach ($factionz->getListviewData() as $id => $data)
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
$buff .= "\ng_faction_order = [0, 469, 891, 1037, 1118, 67, 1052, 892, 936, 1117, 169, 980, 1097];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-factions', $buff))
$this->success = false;
}
}
private function recipes() : void
{
// special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order
$skills = array(
SKILL_ALCHEMY, SKILL_BLACKSMITHING, SKILL_ENCHANTING, SKILL_ENGINEERING, SKILL_HERBALISM,
SKILL_INSCRIPTION, SKILL_JEWELCRAFTING, SKILL_LEATHERWORKING, SKILL_MINING, SKILL_SKINNING,
SKILL_TAILORING, [SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING]
);
$baseCnd = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
['effect1Id', [6, 45, 57, 127, 33, 158, 99, 28, 95], '!'], // aura, tradeSkill, Tracking, Prospecting, Decipher, Milling, Disenchant, Summon (Engineering), Skinning
['effect2Id', [118, 60], '!'], // not the skill itself
['OR', ['typeCat', 9], ['typeCat', 11]]
);
foreach ($skills as $s)
{
$file = is_array($s) ? 'sec' : (string)$s;
$cnd = array_merge($baseCnd, [['skillLine1', $s]]);
$recipez = new SpellList($cnd);
$created = '';
foreach ($recipez->iterate() as $id => $_)
{
if (!$recipez->getSources())
$this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
foreach ($recipez->canCreateItem() as $idx)
{
$id = $recipez->getField('effect'.$idx.'CreateItemId');
$created .= "g_items.add(".$id.", {'icon':'".$recipez->relItems->getEntry($id)['iconString']."'});\n";
}
}
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(10);
User::useLocale($l);
Lang::load($l);
$buff = '';
foreach ($recipez->getListviewData() as $id => $data)
{
$data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
if (!$buff)
{
// this behaviour is intended, do not create an error
CLI::write('[profiler] - file datasets/'.User::$localeString.'/p-recipes-'.$file.' has no content => skipping', CLI::LOG_INFO);
continue;
}
$buff = $created."\nvar _ = g_spells;\n".$buff;
if (is_array($s))
$buff .= "\ng_skill_order = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-recipes-'.$file, $buff))
$this->success = false;
}
}
}
private function achievements() : void
{
$condition = array(
Cfg::get('SQL_LIMIT_NONE'),
[['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
[['flags', 1, '&'], 0], // no statistics
);
$achievez = new AchievementList($condition);
foreach (CLISetup::$localeIds as $l)
{
set_time_limit(5);
User::useLocale($l);
Lang::load($l);
$sumPoints = 0;
$buff = "var _ = g_achievements;\n";
foreach ($achievez->getListviewData(ACHIEVEMENTINFO_PROFILE) as $id => $data)
{
$sumPoints += $data['points'];
$buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
}
// categories to sort by
$buff .= "\ng_achievement_catorder = [92, 14863, 97, 169, 170, 171, 172, 14802, 14804, 14803, 14801, 95, 161, 156, 165, 14806, 14921, 96, 201, 160, 14923, 14808, 14805, 14778, 14865, 14777, 14779, 155, 14862, 14861, 14864, 14866, 158, 162, 14780, 168, 14881, 187, 14901, 163, 14922, 159, 14941, 14961, 14962, 14981, 15003, 15002, 15001, 15041, 15042, 81]";
// sum points
$buff .= "\ng_achievement_points = [".$sumPoints."];\n";
if (!CLISetup::writeFile('datasets/'.User::$localeString.'/achievements', $buff))
$this->success = false;
}
}
private function quickexcludes() : void
{
set_time_limit(2);
CLI::write('[profiler] applying '.count($this->exclusions).' baseline exclusions');
DB::Aowow()->query('DELETE FROM ?_profiler_excludes WHERE comment = ""');
foreach ($this->exclusions as $ex)
DB::Aowow()->query('REPLACE INTO ?_profiler_excludes (?#) VALUES (?a)', array_keys($ex), array_values($ex));
// excludes; type => [excludeGroupBit => [typeIds]]
$excludes = [];
$exData = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `groups` FROM ?_profiler_excludes');
for ($i = 0; (1 << $i) < PR_EXCLUDE_GROUP_ANY; $i++)
foreach ($exData as $type => $data)
if ($ids = array_keys(array_filter($data, function ($x) use ($i) { return $x & (1 << $i); } )))
$excludes[$type][$i + 1] = $ids;
$buff = "g_excludes = ".Util::toJSON($excludes ?: (new Stdclass)).";\n";
if (!CLISetup::writeFile('datasets/quick-excludes', $buff))
$this->success = false;
}
private function getExcludeForSkill(int $skillId) : int
{
switch ($skillId)
{
case SKILL_FISHING: return PR_EXCLUDE_GROUP_REQ_FISHING;
case SKILL_ENGINEERING: return PR_EXCLUDE_GROUP_REQ_ENGINEERING;
case SKILL_TAILORING: return PR_EXCLUDE_GROUP_REQ_TAILORING;
default: return 0;
}
}
private function addExclusion(int $type, int $typeId, int $groups, string $comment = '') : void
{
$k = $type.'-'.$typeId;
if (!isset($this->exclusions[$k]))
$this->exclusions[$k] = ['type' => $type, 'typeId' => $typeId, 'groups' => $groups, 'comment' => $comment];
else
{
$this->exclusions[$k]['groups'] |= $groups;
if ($comment)
$this->exclusions[$k]['comment'] .= '; '.$comment;
}
}
private function sumTotal (array &$sumArr, int $raceMask = -1, int $classMask= -1) : void
{
for ($i = 0; $i < RACE_MASK_ALL; $i++)
{
if (!((1 << $i) & $raceMask) || !((1 << $i) & RACE_MASK_ALL))
continue;
for ($j = 0; $j < CLASS_MASK_ALL; $j++)
{
if (!((1 << $j) & $classMask) || !((1 << $j) & CLASS_MASK_ALL))
continue;
if (!isset($sumArr[$i+1][$j+1]))
$sumArr[$i+1][$j+1] = 1;
else
$sumArr[$i+1][$j+1]++;
}
}
}
});
?>

View File

@@ -1,72 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// Create 'profile_all.js'-file in static/js;
// this script requires all realms in use to be defined in auth.realmlist
// battlegroups has to be set in config file
/* Example
var mn_profiles = [
["us","US & Oceanic",,[
["bloodlust","Bloodlust",,[
["amanthul","Aman'Thul"],
["barthilas","Barthilas"]
]],
["cyclone","Cyclone",,[
["azjol-nerub","Azjol-Nerub"],
["bloodscalp","Bloodscalp"]
]]
]],
["eu","Europe",,[
["blackout","Blackout",,[
["agamaggan","Agamaggan"],
["aggramar","Aggramar"]
]],
["blutdurst","Blutdurst",,[
["aegwynn","Aegwynn"],
["destromath","Destromath"]
]]
]]
];
*/
function realmMenu()
{
$subs = [];
$set = 0x0;
$menu = [
// skip usage of battlegroup
// ['us', Lang::profiler('regions', 'us'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subUS]]],
// ['eu', Lang::profiler('regions', 'eu'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subEU]]]
];
foreach (Util::$regions as $idx => $n)
$subs[$idx] = [];
foreach (Profiler::getRealms() as $row)
{
$idx = array_search($row['region'], Util::$regions);
if ($idx !== false)
{
$set |= (1 << $idx);
$subs[$idx][] = [Profiler::urlize($row['name'], true), $row['name'], null, null, $row['access'] ? ['requiredAccess' => $row['access']] : null];
}
}
if (!$set)
CLI::write(' - realmMenu: Auth-DB not set up .. realm menu will be empty', CLI::LOG_WARN);
// why is this file not localized!?
foreach (Util::$regions as $idx => $n)
if ($set & (1 << $idx))
$menu[] = [$n, Lang::profiler('regions', $n), null, &$subs[$idx]];
return Util::toJSON($menu);
}
?>

View File

@@ -0,0 +1,85 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/* Example
var mn_profiles = [
["us","US & Oceanic",,[
["bloodlust","Bloodlust",,[
["amanthul","Aman'Thul"],
["barthilas","Barthilas"]
]],
["cyclone","Cyclone",,[
["azjol-nerub","Azjol-Nerub"],
["bloodscalp","Bloodscalp"]
]]
]],
["eu","Europe",,[
["blackout","Blackout",,[
["agamaggan","Agamaggan"],
["aggramar","Aggramar"]
]],
["blutdurst","Blutdurst",,[
["aegwynn","Aegwynn"],
["destromath","Destromath"]
]]
]]
];
*/
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'realmmenu' => [[], CLISetup::ARGV_PARAM, 'Generates \'profile_all.js\'-file that extends the profiler menus.']
);
protected $fileTemplateSrc = ['profile_all.js.in'];
protected $fileTemplateDest = ['static/js/profile_all.js'];
protected $worldDependency = ['realmlist'];
private function realmMenu() : string
{
$subs = [];
$set = 0x0;
$menu = [
// skip usage of battlegroup
// ['us', Lang::profiler('regions', 'us'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subUS]]],
// ['eu', Lang::profiler('regions', 'eu'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subEU]]]
];
foreach (Util::$regions as $idx => $n)
$subs[$idx] = [];
if (!DB::isConnectable(DB_AUTH))
CLI::write('[realmmenu] Auth DB not set up .. realm menu will be empty', CLI::LOG_WARN);
else
foreach (Profiler::getRealms() as $row)
{
$idx = array_search($row['region'], Util::$regions);
if ($idx === false)
continue;
$set |= (1 << $idx);
$subs[$idx][] = [Profiler::urlize($row['name'], true), $row['name'], null, null, $row['access'] ? ['requiredAccess' => $row['access']] : null];
}
if (!$set)
CLI::write('[realmmenu] no viable realms found .. realm menu will be empty', CLI::LOG_WARN);
// why is this file not localized!?
foreach (Util::$regions as $idx => $n)
if ($set & (1 << $idx))
$menu[] = [$n, Lang::profiler('regions', $n), null, &$subs[$idx]];
return Util::toJSON($menu);
}
});
?>

View File

@@ -1,47 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// Create 'realms'-file in datasets
// this script requires all realms in use to be defined in auth.realmlist
// battlegroups has to be set in aowow.aowow_config
/* seems to be unused, was located in the same file as g_realms
var g_regions = {
us => 'www.wowarmory.com',
eu => 'eu.wowarmory.com'
};
*/
/* Examples
1 => {
name:'Eldre\'Thalas',
battlegroup:'Reckoning',
region:'us'
},
*/
function realms()
{
$realms = Profiler::getRealms();
if (!$realms)
CLI::write(' - realms: Auth-DB not set up .. static data g_realms will be empty', CLI::LOG_WARN);
// else
// foreach ($realms as &$r)
// $r['battlegroup'] = Cfg::get('BATTLEGROUP');
// remove access column
array_walk($realms, function (&$x) { unset($x['access']); });
$toFile = "var g_realms = ".Util::toJSON($realms).";";
$file = 'datasets/realms';
return CLISetup::writeFile($file, $toFile);
}
?>

View File

@@ -0,0 +1,56 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/* seems to be unused, was located in the same file as g_realms
var g_regions = {
us => 'www.wowarmory.com',
eu => 'eu.wowarmory.com'
};
*/
/* Examples
1 => {
name:'Eldre\'Thalas',
battlegroup:'Reckoning',
region:'us'
},
*/
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'realms' => [[], CLISetup::ARGV_PARAM, 'Generates \'realms\'-file to be referenced by the profiler tool.']
);
protected $worldDependency = ['realmlist'];
protected $requiredDirs = ['datasets/'];
public function generate() : bool
{
$realms = [];
if (!DB::isConnectable(DB_AUTH))
CLI::write('[realms] Auth DB not set up .. static data g_realms will be empty', CLI::LOG_WARN);
else if (!($realms = Profiler::getRealms()))
CLI::write('[realms] no viable realms found .. static data g_realms will be empty', CLI::LOG_WARN);
// else
// foreach ($realms as &$r)
// $r['battlegroup'] = Cfg::get('BATTLEGROUP');
// remove access column
array_walk($realms, function (&$x) { unset($x['access']); });
$toFile = "var g_realms = ".Util::toJSON($realms).";";
$file = 'datasets/realms';
return CLISetup::writeFile($file, $toFile);
}
});
?>

View File

@@ -0,0 +1,22 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'searchbox' => [[], CLISetup::ARGV_PARAM, 'Fills search widget files (static/widgets/searchbox*) with site variables.']
);
protected $fileTemplateSrc = ['searchbox.js.in', 'searchbox.html.in'];
protected $fileTemplateDest = ['static/widgets/searchbox.js', 'static/widgets/searchbox/searchbox.html'];
});
?>

View File

@@ -0,0 +1,23 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'searchplugin' => [[], CLISetup::ARGV_PARAM, 'Fills browser opensearch plugin (static/download/searchplugins/aowow.xml) with site variables.']
);
protected $fileTemplateSrc = ['aowow.xml.in'];
protected $fileTemplateDest = ['static/download/searchplugins/aowow.xml'];
protected $requiredDirs = ['static/download/searchplugins/'];
});
?>

View File

@@ -1,480 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// note: for the sake of simplicity, this function handles all whole images (which are mostly icons)
// quest icons from GossipFrame have an alphaChannel that cannot be handled by this script
// lfgFrame/lfgIcon*.blp .. candidates for zonePage, but in general too detailed to scale them down from 128 to 56, 36, ect
$reqDBC = ['holidays', 'spellicon', 'itemdisplayinfo'];
function simpleImg()
{
// prefer manually converted PNG files (as the imagecreatefromblp-script has issues with some formats)
// see: https://github.com/Kanma/BLPConverter
$loadImageFile = function($path)
{
$result = null;
if (in_array(mb_substr($path, -4, 4), ['.png', '.blp', '.BLP', '.PNG']))
$path = mb_substr($path, 0, mb_strlen($path) - 4);
$file = $path.'.png';
if (CLISetup::fileExists($file))
{
CLI::write('manually converted png file present for: '.$path, CLI::LOG_INFO);
$result = imagecreatefrompng($file);
}
if (!$result)
{
$file = $path.'.blp';
if (CLISetup::fileExists($file))
$result = imagecreatefromblp($file);
}
return $result;
};
$groups = [];
$imgPath = CLISetup::$srcDir.'%sInterface/';
$destDir = 'static/images/wow/';
$success = true;
$iconDirs = array(
['icons/large/', '.jpg', 0, 56, 4],
['icons/medium/', '.jpg', 0, 36, 4],
['icons/small/', '.jpg', 0, 18, 4],
['icons/tiny/', '.gif', 0, 15, 4]
);
$calendarDirs = array(
['icons/large/', '.jpg', 90, 56, 4],
['icons/medium/', '.jpg', 90, 36, 4],
['icons/small/', '.jpg', 90, 18, 4],
['icons/tiny/', '.gif', 90, 15, 4]
);
$loadScreenDirs = array(
['loadingscreens/large/', '.jpg', 0, 1024, 0],
['loadingscreens/medium/', '.jpg', 0, 488, 0],
['loadingscreens/original/', '.png', 0, 0, 0],
['loadingscreens/small/', '.jpg', 0, 244, 0]
);
$paths = array( // src, [dest, ext, srcSize, destSize, borderOffset], pattern, isIcon, tileSize, resourcePath
0 => ['Icons/', $iconDirs, '/.*\.(blp|png)$', true, 0, null],
1 => ['Spellbook/', [['Interface/Spellbook/', '.png', 0, 0, 0]], '/UI-Glyph-Rune-?\d+.(blp|png)$', true, 0, null],
2 => ['PaperDoll/', array_slice($iconDirs, 0, 3), '/UI-(Backpack|PaperDoll)-.*\.(blp|png)$', true, 0, null],
3 => ['GLUES/CHARACTERCREATE/', $iconDirs, '/UI-CharacterCreate-Races.(blp|png)', true, 64, null],
4 => ['GLUES/CHARACTERCREATE/', $iconDirs, '/UI-CharacterCreate-CLASSES.(blp|png)', true, 64, null],
5 => ['GLUES/CHARACTERCREATE/', $iconDirs, '/UI-CharacterCreate-Factions.(blp|png)', true, 64, null],
// 6 => ['Minimap/', [['icons/tiny/', '.gif', 0, 16, 2]], '/OBJECTICONS.(BLP|png)', true, 32, null],
7 => ['FlavorImages/', [['Interface/FlavorImages/', '.png', 0, 0, 0]], '/.*\.(blp|png)$', false, 0, null],
8 => ['Pictures/', [['Interface/Pictures/', '.png', 0, 0, 0]], '/.*\.(blp|png)$', false, 0, null],
9 => ['PvPRankBadges/', [['Interface/PvPRankBadges/', '.png', 0, 0, 0]], '/.*\.(blp|png)$', false, 0, null],
10 => ['Calendar/Holidays/', $calendarDirs, '/.*(start|[ayhs])\.(blp|png)$', true, 0, null],
11 => ['GLUES/LOADINGSCREENS/', $loadScreenDirs, '/lo.*\.(blp|png)$', false, 0, null],
12 => ['PVPFrame/', array_map(function($x) { $x[4] = 2; return $x; }, $iconDirs), '/PVP-(ArenaPoints|Currency).*\.(blp|png)$', true, 0, null]
);
// textures are composed of 64x64 icons
// numeric indexed arrays mimick the position on the texture
$cuNames = array(
2 => array(
'ui-paperdoll-slot-chest' => 'inventoryslot_chest',
'ui-backpack-emptyslot' => 'inventoryslot_empty',
'ui-paperdoll-slot-feet' => 'inventoryslot_feet',
'ui-paperdoll-slot-finger' => 'inventoryslot_finger',
'ui-paperdoll-slot-hands' => 'inventoryslot_hands',
'ui-paperdoll-slot-head' => 'inventoryslot_head',
'ui-paperdoll-slot-legs' => 'inventoryslot_legs',
'ui-paperdoll-slot-mainhand' => 'inventoryslot_mainhand',
'ui-paperdoll-slot-neck' => 'inventoryslot_neck',
'ui-paperdoll-slot-secondaryhand' => 'inventoryslot_offhand',
'ui-paperdoll-slot-ranged' => 'inventoryslot_ranged',
'ui-paperdoll-slot-relic' => 'inventoryslot_relic',
'ui-paperdoll-slot-shirt' => 'inventoryslot_shirt',
'ui-paperdoll-slot-shoulder' => 'inventoryslot_shoulder',
'ui-paperdoll-slot-tabard' => 'inventoryslot_tabard',
'ui-paperdoll-slot-trinket' => 'inventoryslot_trinket',
'ui-paperdoll-slot-waist' => 'inventoryslot_waist',
'ui-paperdoll-slot-wrists' => 'inventoryslot_wrists'
),
3 => array( // uses nameINT from ChrRaces.dbc
['race_human_male', 'race_dwarf_male', 'race_gnome_male', 'race_nightelf_male', 'race_draenei_male' ],
['race_tauren_male', 'race_scourge_male', 'race_troll_male', 'race_orc_male', 'race_bloodelf_male' ],
['race_human_female', 'race_dwarf_female', 'race_gnome_female', 'race_nightelf_female', 'race_draenei_female' ],
['race_tauren_female', 'race_scourge_female', 'race_troll_female', 'race_orc_female', 'race_bloodelf_female']
),
4 => array( // uses nameINT from ChrClasses.dbc
['class_warrior', 'class_mage', 'class_rogue', 'class_druid' ],
['class_hunter', 'class_shaman', 'class_priest', 'class_warlock'],
['class_paladin', 'class_deathknight' ]
),
5 => array(
['faction_alliance', 'faction_horde']
),
6 => array(
[],
[null, 'quest_start', 'quest_end', 'quest_start_daily', 'quest_end_daily']
),
10 => array( // really should have read holidays.dbc...
'calendar_winterveilstart' => 'calendar_winterveilstart',
'calendar_noblegardenstart' => 'calendar_noblegardenstart',
'calendar_childrensweekstart' => 'calendar_childrensweekstart',
'calendar_fishingextravaganza' => 'calendar_fishingextravaganzastart',
'calendar_harvestfestivalstart' => 'calendar_harvestfestivalstart',
'calendar_hallowsendstart' => 'calendar_hallowsendstart',
'calendar_lunarfestivalstart' => 'calendar_lunarfestivalstart',
'calendar_loveintheairstart' => 'calendar_loveintheairstart',
'calendar_midsummerstart' => 'calendar_midsummerstart',
'calendar_brewfeststart' => 'calendar_brewfeststart',
'calendar_darkmoonfaireelwynnstart' => 'calendar_darkmoonfaireelwynnstart',
'calendar_darkmoonfairemulgorestart' => 'calendar_darkmoonfairemulgorestart',
'calendar_darkmoonfaireterokkarstart' => 'calendar_darkmoonfaireterokkarstart',
'calendar_piratesday' => 'calendar_piratesdaystart',
'calendar_wotlklaunch' => 'calendar_wotlklaunchstart',
'calendar_dayofthedeadstart' => 'calendar_dayofthedeadstart',
'calendar_fireworks' => 'calendar_fireworksstart'
)
);
$writeImage = function($name, $ext, $src, $srcDims, $destDims, $done)
{
$ok = false;
$dest = imagecreatetruecolor($destDims['w'], $destDims['h']);
imagesavealpha($dest, true);
if ($ext == '.png')
imagealphablending($dest, false);
imagecopyresampled($dest, $src, $destDims['x'], $destDims['x'], $srcDims['x'], $srcDims['y'], $destDims['w'], $destDims['h'], $srcDims['w'], $srcDims['h']);
switch ($ext)
{
case '.jpg':
$ok = imagejpeg($dest, $name.$ext, 85);
break;
case '.gif':
$ok = imagegif($dest, $name.$ext);
break;
case '.png':
$ok = imagepng($dest, $name.$ext);
break;
default:
CLI::write($done.' - unsupported file fromat: '.$ext, CLI::LOG_WARN);
}
imagedestroy($dest);
if ($ok)
{
chmod($name.$ext, Util::FILE_ACCESS);
CLI::write($done.' - image '.$name.$ext.' written', CLI::LOG_OK, true, true);
}
else
CLI::write($done.' - could not create image '.$name.$ext, CLI::LOG_ERROR);
return $ok;
};
$checkSourceDirs = function($sub) use ($imgPath, &$paths)
{
$hasMissing = false;
foreach ($paths as $pathIdx => [$subDir, , , , , $realPath])
{
if ($realPath)
continue;
$p = sprintf($imgPath, $sub).$subDir;
if (CLISetup::fileExists($p))
$paths[$pathIdx][5] = $p;
else
$hasMissing = true;
}
return !$hasMissing;
};
if (CLISetup::getOpt('icons'))
array_push($groups, 0, 2, 3, 4, 5, 10, 12);
if (CLISetup::getOpt('glyphs'))
$groups[] = 1;
if (CLISetup::getOpt('pagetexts'))
array_push($groups, 7, 8, 9);
if (CLISetup::getOpt('loadingscreens'))
$groups[] = 11;
// filter by passed options
if (!$groups) // by default do not generate loadingscreens
unset($paths[11]);
else
foreach (array_keys($paths) as $k)
if (!in_array($k, $groups))
unset($paths[$k]);
foreach (CLISetup::$expectedPaths as $xp => $locId)
{
if (!in_array($locId, CLISetup::$localeIds))
continue;
if ($xp) // if in subDir add trailing slash
$xp .= '/';
if ($checkSourceDirs($xp))
break;
}
$locList = [];
foreach (CLISetup::$expectedPaths as $xp => $locId)
if (in_array($locId, CLISetup::$localeIds))
$locList[] = $xp;
CLI::write('required resources overview:', CLI::LOG_INFO);
foreach ($paths as [$path, , , , , $realPath])
{
if ($realPath)
CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 53).' @ '.$realPath);
else
CLI::write(CLI::red('MISSING').' - '.str_pad($path, 53).' @ '.sprintf($imgPath, '['.implode(',', $locList).']/').$path);
}
CLI::write();
// if no subdir had sufficient data, diaf
if (count(array_filter(array_column($paths, 5))) != count($paths))
{
CLI::write('one or more required directories are missing:', CLI::LOG_ERROR);
return false;
}
else
sleep(1);
// init directories
foreach (array_column($paths, 1) as $subDirs)
foreach ($subDirs as $sd)
if (!CLISetup::writeDir($destDir.$sd[0]))
$success = false;
// ok, departure from std::procedure here
// scan ItemDisplayInfo.dbc and SpellIcon.dbc for expected images and save them to an array
// load all icon paths into another array and xor these two
// excess entries for the directory are fine, excess entries for the dbc's are not
$dbcEntries = [];
if (isset($paths[0]) || isset($paths[1])) // generates icons or glyphs
{
if (isset($paths[0]) && !isset($paths[1]))
$siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon WHERE iconPath NOT LIKE "%glyph-rune%"');
else if (!isset($paths[0]) && isset($paths[1]))
$siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon WHERE iconPath LIKE "%glyph-rune%"');
else
$siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon');
foreach ($siRows as $icon)
{
// Icons/
if (isset($paths[0]) && stristr($icon, $paths[0][0]))
$dbcEntries[] = strtolower($paths[0][5].substr(strrchr($icon, '\\'), 1));
// Spellbook/
else if (isset($paths[1]) && stristr($icon, $paths[1][0]))
$dbcEntries[] = strtolower($paths[1][5].substr(strrchr($icon, '\\'), 1));
}
}
if (isset($paths[0]))
{
$itemIcons = DB::Aowow()->selectCol('SELECT inventoryIcon1 FROM dbc_itemdisplayinfo WHERE inventoryIcon1 <> ""');
foreach ($itemIcons as $icon)
$dbcEntries[] = strtolower($paths[0][5].'/'.$icon);
$eventIcons = DB::Aowow()->selectCol('SELECT textureString FROM dbc_holidays WHERE textureString <> ""');
foreach ($eventIcons as $icon)
$dbcEntries[] = strtolower($paths[10][5].'/'.$icon.'start');
}
// case-insensitive array_unique *vomits silently into a corner*
$dbcEntries = array_intersect_key($dbcEntries, array_unique($dbcEntries));
$allPaths = [];
foreach ($paths as $i => [$inPath, $outInfo, $pattern, $isIcon, $tileSize, $path])
{
$search = $path.$pattern;
if ($pattern)
$search = '/'.str_replace('/', '\\/', $search).'/i';
$files = CLISetup::filesInPath($search, !!$pattern);
$allPaths = array_merge($allPaths, array_map(function ($x) { return substr($x, 0, -4); }, $files));
if (!$files)
{
CLI::write('source directory "'.CLI::bold($search).'" does not contain files matching "'.CLI::bold($pattern), CLI::LOG_ERROR);
$success = false;
continue;
}
CLI::write('processing '.count($files).' files in '.$path.'...');
$j = 0;
foreach ($files as $f)
{
ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
$src = null;
$na = explode('/', $f);
$img = explode('.', array_pop($na));
array_pop($img); // there are a hand full of images with multiple file endings or random dots in the name
$img = implode('.', $img);
// file not from dbc -> name from array or skip file
if (!empty($cuNames[$i]))
{
if (!empty($cuNames[$i][strtolower($img)]))
$img = $cuNames[$i][strtolower($img)];
else if (!$tileSize)
{
$j += count($outInfo);
CLI::write('skipping extraneous file '.$img.' (+'.count($outInfo).')');
continue;
}
}
$nFiles = count($outInfo) * ($tileSize ? array_sum(array_map('count', $cuNames[$i])) : count($files));
foreach ($outInfo as [$dest, $ext, $srcSize, $destSize, $borderOffset])
{
if ($tileSize)
{
foreach ($cuNames[$i] as $y => $row)
{
foreach ($row as $x => $name)
{
$j++;
$img = $isIcon ? strtolower($name) : $name;
$done = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
if (!CLISetup::getOpt('force') && file_exists($destDir.$dest.$img.$ext))
{
CLI::write($done.' - file '.$dest.$img.$ext.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
if (!$src)
$src = $loadImageFile($f);
if (!$src) // error should be created by imagecreatefromblp
continue;
/*
ready for some major bullshitery? well, here it comes anyway!
the class-icon tile [idx: 4] isn't 64x64 but 63x64 .. the right side border is 1px short
so if we don't watch out, the icons start to shift over and show the border
also the icon border is displaced by 1px
*/
$from = array(
'x' => $borderOffset + 1 + ($tileSize - ($i == 4 ? 1 : 0)) * $x,
'y' => $borderOffset + 1 + $tileSize * $y,
'w' => ($tileSize - ($i == 4 ? 1 : 0)) - $borderOffset * 2,
'h' => $tileSize - $borderOffset * 2
);
$to = array(
'x' => 0,
'y' => 0,
'w' => $destSize,
'h' => $destSize
);
if (!$writeImage($destDir.$dest.$img, $ext, $src, $from, $to, $done))
$success = false;
}
}
// custom handle for combined icon 'quest_startend'
/* not used due to alphaChannel issues
if ($tileSize == 32)
{
$dest = imagecreatetruecolor(19, 16);
imagesavealpha($dest, true);
imagealphablending($dest, true);
// excalmationmark, questionmark
imagecopyresampled($dest, $src, 0, 1, 32 + 5, 32 + 2, 8, 15, 18, 30);
imagecopyresampled($dest, $src, 5, 0, 64 + 1, 32 + 1, 10, 16, 18, 28);
if (imagegif($dest, $destDir.$dest.'quest_startend.gif'))
CLI::write(' extra - image '.$destDir.$dest.'quest_startend.gif written', CLI::LOG_OK);
else
{
CLI::write(' extra - could not create image '.$destDir.$dest.'quest_startend.gif', CLI::LOG_ERROR);
$success = false;
}
imagedestroy($dest);
}
*/
}
else
{
// icon -> lowercase
if ($isIcon)
$img = strtolower($img);
$j++;
$done = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
if (!CLISetup::getOpt('force') && file_exists($destDir.$dest.$img.$ext))
{
CLI::write($done.' - file '.$dest.$img.$ext.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
if (!$src)
$src = $loadImageFile($f);
if (!$src) // error should be created by imagecreatefromblp
continue;
$from = array(
'x' => $borderOffset,
'y' => $borderOffset,
'w' => ($srcSize ?: imagesx($src)) - $borderOffset * 2,
'h' => ($srcSize ?: imagesy($src)) - $borderOffset * 2
);
$to = array(
'x' => 0,
'y' => 0,
'w' => $destSize ?: imagesx($src),
'h' => $destSize ?: imagesy($src)
);
if (!$writeImage($destDir.$dest.$img, $ext, $src, $from, $to, $done))
$success = false;
}
}
unset($src);
}
}
// reset execTime
ini_set('max_execution_time', FileGen::$defaultExecTime);
if ($missing = array_diff(array_map('strtolower', $dbcEntries), array_map('strtolower', $allPaths)))
{
// hide affected icons from listviews
$iconNames = array_map(function($path) {
preg_match('/\/([^\/]+)\.blp$/i', $path, $m);
return $m ? $m[1] : null;
}, $missing);
DB::Aowow()->query('UPDATE ?_icons SET cuFlags = cuFlags | ?d WHERE name IN (?a)', CUSTOM_EXCLUDE_FOR_LISTVIEW, $iconNames);
asort($missing);
CLI::write('the following '.count($missing).' images where referenced by DBC but not in the mpqData directory. They may need to be converted by hand later on.', CLI::LOG_WARN);
foreach ($missing as $m)
CLI::write(' - '.$m);
}
return $success;
}

View File

@@ -0,0 +1,376 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// quest icons from GossipFrame have an alphaChannel that cannot be handled by this script
// lfgFrame/lfgIcon-*.blp .. candidates for zonePage, but in general too detailed to scale them down from 128 to 56, 36, ect
// linked by lfgDungeons.dbc/28 ?
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrImageProcessor;
protected $info = array(
'simpleimg' => [[ ], CLISetup::ARGV_PARAM, 'Converts and resizes BLP2 images smaller than 255x255 into required formats (mostly icons)'],
'icons' => [['1'], CLISetup::ARGV_OPTIONAL, 'Generate icons for spells, items, classes, races, ect.'],
'glyphs' => [['2'], CLISetup::ARGV_OPTIONAL, 'Generate decorative glyph symbols displayed on related item and spell pages.'],
'pagetexts' => [['3'], CLISetup::ARGV_OPTIONAL, 'Generate images contained in text on readable items and gameobjects.'],
'loadingscreens' => [['4'], CLISetup::ARGV_OPTIONAL, 'Generate loading screen images (not used on page; skipped by default)']
);
protected $dbcSourceFiles = ['holidays', 'spellicon', 'itemdisplayinfo'];
protected $setupAfter = [['icons'], []];
private const ICON_DIRS = array(
['static/images/wow/icons/large/', 'jpg', 0, 56, 4],
['static/images/wow/icons/medium/', 'jpg', 0, 36, 4],
['static/images/wow/icons/small/', 'jpg', 0, 18, 4],
['static/images/wow/icons/tiny/', 'gif', 0, 15, 4]
);
private $genSteps = array(
// srcPath, realPath, localized, [pattern, isIcon, tileSize, localized], [[dest, ext, srcSize, destSize, borderOffset]]
0 => ['Icons/', null, false, ['/.*\.(blp|png)$', true, 0], self::ICON_DIRS, ],
1 => ['Spellbook/', null, false, ['/UI-Glyph-Rune-?\d+.(blp|png)$', false, 0], [['static/images/wow/Interface/Spellbook/', 'png', 0, 0, 0]]],
2 => ['PaperDoll/', null, false, ['/UI-(Backpack|PaperDoll)-.*\.(blp|png)$', true, 0], self::ICON_DIRS, ],
3 => ['GLUES/CHARACTERCREATE/', null, false, ['/UI-CharacterCreate-Races.(blp|png)', true, 64], self::ICON_DIRS, ],
4 => ['GLUES/CHARACTERCREATE/', null, false, ['/UI-CharacterCreate-CLASSES.(blp|png)', true, 64], self::ICON_DIRS, ],
5 => ['GLUES/CHARACTERCREATE/', null, false, ['/UI-CharacterCreate-Factions.(blp|png)', true, 64], self::ICON_DIRS, ],
// 6 => ['Minimap/' , null, false, ['/OBJECTICONS.(BLP|png)', true, 32], [['static/images/wow/icons/tiny/', 'gif', 0, 16, 2]]],
7 => ['FlavorImages/', null, false, ['/.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/FlavorImages/', 'png', 0, 0, 0]]],
8 => ['Pictures/', null, false, ['/.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/Pictures/', 'png', 0, 0, 0]]],
9 => ['PvPRankBadges/', null, false, ['/.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/PvPRankBadges/', 'png', 0, 0, 0]]],
10 => ['Calendar/Holidays/', null, false, ['/.*(start|[ayhs])\.(blp|png)$', true, 0], self::ICON_DIRS, ],
11 => ['GLUES/LOADINGSCREENS/', null, false, ['/lo.*\.(blp|png)$', false, 0], [['cache/loadingscreens/', 'png', 0, 0, 0]]],
12 => ['PVPFrame/', null, false, ['/PVP-(ArenaPoints|Currency).*\.(blp|png)$', true, 0], self::ICON_DIRS, ]
);
// textures are composed of 64x64 icons
// numeric indexed arrays mimick the position on the texture
private $cuNames = array(
2 => array(
'ui-paperdoll-slot-chest' => 'inventoryslot_chest',
'ui-backpack-emptyslot' => 'inventoryslot_empty',
'ui-paperdoll-slot-feet' => 'inventoryslot_feet',
'ui-paperdoll-slot-finger' => 'inventoryslot_finger',
'ui-paperdoll-slot-hands' => 'inventoryslot_hands',
'ui-paperdoll-slot-head' => 'inventoryslot_head',
'ui-paperdoll-slot-legs' => 'inventoryslot_legs',
'ui-paperdoll-slot-mainhand' => 'inventoryslot_mainhand',
'ui-paperdoll-slot-neck' => 'inventoryslot_neck',
'ui-paperdoll-slot-secondaryhand' => 'inventoryslot_offhand',
'ui-paperdoll-slot-ranged' => 'inventoryslot_ranged',
'ui-paperdoll-slot-relic' => 'inventoryslot_relic',
'ui-paperdoll-slot-shirt' => 'inventoryslot_shirt',
'ui-paperdoll-slot-shoulder' => 'inventoryslot_shoulder',
'ui-paperdoll-slot-tabard' => 'inventoryslot_tabard',
'ui-paperdoll-slot-trinket' => 'inventoryslot_trinket',
'ui-paperdoll-slot-waist' => 'inventoryslot_waist',
'ui-paperdoll-slot-wrists' => 'inventoryslot_wrists'
),
3 => array( // uses nameINT from ChrRaces.dbc
['race_human_male', 'race_dwarf_male', 'race_gnome_male', 'race_nightelf_male', 'race_draenei_male' ],
['race_tauren_male', 'race_scourge_male', 'race_troll_male', 'race_orc_male', 'race_bloodelf_male' ],
['race_human_female', 'race_dwarf_female', 'race_gnome_female', 'race_nightelf_female', 'race_draenei_female' ],
['race_tauren_female', 'race_scourge_female', 'race_troll_female', 'race_orc_female', 'race_bloodelf_female']
),
4 => array( // uses nameINT from ChrClasses.dbc
['class_warrior', 'class_mage', 'class_rogue', 'class_druid' ],
['class_hunter', 'class_shaman', 'class_priest', 'class_warlock'],
['class_paladin', 'class_deathknight' ]
),
5 => array(
['faction_alliance', 'faction_horde']
),
6 => array(
[],
[null, 'quest_start', 'quest_end', 'quest_start_daily', 'quest_end_daily']
),
10 => array( // really should have read holidays.dbc...
'calendar_winterveilstart' => 'calendar_winterveilstart',
'calendar_noblegardenstart' => 'calendar_noblegardenstart',
'calendar_childrensweekstart' => 'calendar_childrensweekstart',
'calendar_fishingextravaganza' => 'calendar_fishingextravaganzastart',
'calendar_harvestfestivalstart' => 'calendar_harvestfestivalstart',
'calendar_hallowsendstart' => 'calendar_hallowsendstart',
'calendar_lunarfestivalstart' => 'calendar_lunarfestivalstart',
'calendar_loveintheairstart' => 'calendar_loveintheairstart',
'calendar_midsummerstart' => 'calendar_midsummerstart',
'calendar_brewfeststart' => 'calendar_brewfeststart',
'calendar_darkmoonfaireelwynnstart' => 'calendar_darkmoonfaireelwynnstart',
'calendar_darkmoonfairemulgorestart' => 'calendar_darkmoonfairemulgorestart',
'calendar_darkmoonfaireterokkarstart' => 'calendar_darkmoonfaireterokkarstart',
'calendar_piratesday' => 'calendar_piratesdaystart',
'calendar_wotlklaunch' => 'calendar_wotlklaunchstart',
'calendar_dayofthedeadstart' => 'calendar_dayofthedeadstart',
'calendar_fireworks' => 'calendar_fireworksstart'
)
);
public function __construct()
{
$this->imgPath = CLISetup::$srcDir.$this->imgPath;
$this->maxExecTime = ini_get('max_execution_time');
// init directories to be checked when registered
foreach (array_column($this->genSteps, self::$GEN_IDX_DEST_INFO) as $subDirs)
foreach ($subDirs as $sd)
$this->requiredDirs[] = $sd[0];
// fix genSteps 2 [icons] - no tiny inventory backgrounds
$this->genSteps[2][self::$GEN_IDX_DEST_INFO] = array_slice($this->genSteps[2][self::$GEN_IDX_DEST_INFO], 0, 3);
// fix genSteps 12 [pvp money icons] - smaller border offset for pvp currency icons
array_walk($this->genSteps[12][self::$GEN_IDX_DEST_INFO], function(&$x) { $x[4] = 2; });
// fix genSteps 10 [holoday icons] - img src size is 90px
array_walk($this->genSteps[10][self::$GEN_IDX_DEST_INFO], function(&$x) { $x[2] = 90; });
}
public function generate() : bool
{
// find out what to generate
$groups = [];
if (CLISetup::getOpt('icons'))
array_push($groups, 0, 2, 3, 4, 5, 10, 12);
if (CLISetup::getOpt('glyphs'))
$groups[] = 1;
if (CLISetup::getOpt('pagetexts'))
array_push($groups, 7, 8, 9);
if (CLISetup::getOpt('loadingscreens'))
$groups[] = 11;
if (!$groups) // by default do not generate loadingscreens
$groups = [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 12];
// removed unused generators
foreach ($this->genSteps as $idx => $_)
if (!in_array($idx, $groups))
unset($this->genSteps[$idx]);
if (!$this->checkSourceDirs())
{
CLI::write('[simpleimg] one or more required directories are missing:', CLI::LOG_ERROR);
return false;
}
sleep(2);
$allPaths = [];
foreach ($this->genSteps as $i => [, $path, , [$pattern, $isIcon, $tileSize], $outInfo])
{
$search = $path.$pattern;
if ($pattern)
$search = '/'.str_replace('/', '\\/', $search).'/i';
$files = CLISetup::filesInPath($search, !!$pattern);
$allPaths = array_merge($allPaths, array_map(function ($x) { return substr($x, 0, -4); }, $files));
if (!$files)
{
CLI::write('[simpleimg] source directory "'.CLI::bold($search).'" does not contain files matching "'.CLI::bold($pattern), CLI::LOG_ERROR);
$this->success = false;
continue;
}
CLI::write('[simpleimg] processing '.count($files).' files in '.$path.'...');
$j = 0;
foreach ($files as $f)
{
ini_set('max_execution_time', $this->maxExecTime);
$src = null;
$na = explode('/', $f);
$img = explode('.', array_pop($na));
array_pop($img); // there are a hand full of images with multiple file endings or random dots in the name
$img = implode('.', $img);
if (!empty($this->cuNames[$i])) // file not from dbc -> name from array or skip file
{
if (!empty($this->cuNames[$i][strtolower($img)]))
$img = $this->cuNames[$i][strtolower($img)];
else if (!$tileSize)
{
$j += count($outInfo);
CLI::write('[simpleimg] skipping extraneous file '.$img.' (+'.count($outInfo).')');
continue;
}
}
$nFiles = count($outInfo) * ($tileSize ? array_sum(array_map('count', $this->cuNames[$i])) : count($files));
foreach ($outInfo as [$dest, $ext, $srcSize, $destSize, $borderOffset])
{
if ($tileSize)
{
foreach ($this->cuNames[$i] as $y => $row)
{
foreach ($row as $x => $name)
{
$j++;
$outFile = $dest.($isIcon ? strtolower($name) : $name).'.'.$ext;
$this->status = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
if (!CLISetup::getOpt('force') && file_exists($outFile))
{
CLI::write('[simpleimg] '.$this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
if (!$src)
$src = $this->loadImageFile($f);
if (!$src) // error should be created by imagecreatefromblp
{
$this->success = false;
continue;
}
/*
ready for some major bullshitery? well, here it comes anyway!
the class-icon tile [idx: 4] isn't 64x64 but 63x64 .. the right side border is 1px short
so if we don't watch out, the icons start to shift over and show the border
also the icon border is displaced by 1px
*/
$from = array(
'x' => $borderOffset + 1 + ($tileSize - ($i == 4 ? 1 : 0)) * $x,
'y' => $borderOffset + 1 + $tileSize * $y,
'w' => ($tileSize - ($i == 4 ? 1 : 0)) - $borderOffset * 2,
'h' => $tileSize - $borderOffset * 2
);
$to = array(
'x' => 0,
'y' => 0,
'w' => $destSize,
'h' => $destSize
);
if (!$this->writeImageFile($src, $outFile, $from, $to))
$this->success = false;
}
}
// custom handle for combined icon 'quest_startend'
/* not used due to alphaChannel issues
if ($tileSize == 32)
{
$dest = imagecreatetruecolor(19, 16);
imagesavealpha($dest, true);
imagealphablending($dest, true);
// excalmationmark, questionmark
imagecopyresampled($dest, $src, 0, 1, 32 + 5, 32 + 2, 8, 15, 18, 30);
imagecopyresampled($dest, $src, 5, 0, 64 + 1, 32 + 1, 10, 16, 18, 28);
if (imagegif($dest, $dest.'quest_startend.gif'))
CLI::write(' extra - image '.$dest.'quest_startend.gif written', CLI::LOG_OK);
else
{
CLI::write(' extra - could not create image '.$dest.'quest_startend.gif', CLI::LOG_ERROR);
$this->success = false;
}
imagedestroy($dest);
}
*/
}
else
{
$j++;
$this->status = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
$outFile = $dest.($isIcon ? strtolower($img) : $img).'.'.$ext;
if (!CLISetup::getOpt('force') && file_exists($outFile))
{
CLI::write('[simpleimg] '.$this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
continue;
}
$src = $this->loadImageFile($f);
if (!$src) // error should be created by imagecreatefromblp
{
$this->success = false;
continue;
}
$from = array(
'x' => $borderOffset,
'y' => $borderOffset,
'w' => ($srcSize ?: imagesx($src)) - $borderOffset * 2,
'h' => ($srcSize ?: imagesy($src)) - $borderOffset * 2
);
$to = array(
'x' => 0,
'y' => 0,
'w' => $destSize ?: imagesx($src),
'h' => $destSize ?: imagesy($src)
);
if (!$this->writeImageFile($src, $outFile, $from, $to))
$this->success = false;
}
}
unset($src);
}
}
// scan ItemDisplayInfo.dbc and SpellIcon.dbc for expected images and save them to an array
// load all icon paths into another array and xor these two
// excess entries for the directory are fine, excess entries for the dbc's are not
$dbcEntries = [];
$gens = array_keys($this->genSteps);
if (in_array(0, $gens)) // generates icons
{
if ($siRows = DB::Aowow()->selectCol('SELECT `iconPath` FROM dbc_spellicon WHERE `iconPath` NOT LIKE "%glyph-rune%"'))
foreach ($siRows as $icon)
if (stristr($icon, $this->genSteps[0][self::$GEN_IDX_SRC_PATH])) // Icons/
$dbcEntries[] = strtolower($this->genSteps[0][self::$GEN_IDX_SRC_REAL].substr(strrchr($icon, '\\'), 1));
if ($itemIcons = DB::Aowow()->selectCol('SELECT `inventoryIcon1` FROM dbc_itemdisplayinfo WHERE `inventoryIcon1` <> ""'))
foreach ($itemIcons as $icon)
$dbcEntries[] = strtolower($this->genSteps[0][self::$GEN_IDX_SRC_REAL].'/'.$icon);
}
if (in_array(1, $gens)) // generates glyphs
if ($siRows = DB::Aowow()->selectCol('SELECT `iconPath` FROM dbc_spellicon WHERE `iconPath` LIKE "%glyph-rune%"'))
foreach ($siRows as $icon)
if (stristr($icon, $this->genSteps[1][self::$GEN_IDX_SRC_PATH])) // Spellbook/
$dbcEntries[] = strtolower($this->genSteps[1][self::$GEN_IDX_SRC_REAL].substr(strrchr($icon, '\\'), 1));
if (in_array(10, $gens)) // generates holiday icons
if ($eventIcons = DB::Aowow()->selectCol('SELECT `textureString` FROM dbc_holidays WHERE `textureString` <> ""'))
foreach ($eventIcons as $icon)
$dbcEntries[] = strtolower($this->genSteps[10][self::$GEN_IDX_SRC_REAL].'/'.$icon.'start');
// case-insensitive array_unique *vomits silently into a corner*
$dbcEntries = array_intersect_key($dbcEntries, array_unique($dbcEntries));
if ($missing = array_diff(array_map('strtolower', $dbcEntries), array_map('strtolower', $allPaths)))
{
// hide affected icons from listviews
$iconNames = array_map(function($path) {
preg_match('/\/([^\/]+)\.blp$/i', $path, $m);
return $m ? $m[1] : null;
}, $missing);
DB::Aowow()->query('UPDATE ?_icons SET `cuFlags` = `cuFlags` | ?d WHERE `name` IN (?a)', CUSTOM_EXCLUDE_FOR_LISTVIEW, $iconNames);
CLI::write('[simpleimg] the following '.count($missing).' images where referenced by DBC but not in the mpqData directory. They may need to be converted by hand later on.', CLI::LOG_WARN);
foreach ($missing as $m)
CLI::write(' - '.$m);
}
return $this->success;
}
});
?>

View File

@@ -6,28 +6,31 @@ if (!defined('AOWOW_REVISION'))
if (!CLI) if (!CLI)
die('not in cli mode'); die('not in cli mode');
function soundfiles()
{
$ok = true;
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'soundfiles' => [[], CLISetup::ARGV_PARAM, 'Links converted sound files to database and moves them to destination.']
);
protected $requiredDirs = ['static/wowsounds/'];
protected $setupAfter = [['sounds'], []];
public function generate() : bool
{
// ALL files // ALL files
$files = DB::Aowow()->selectCol('SELECT ABS(id) AS ARRAY_KEY, CONCAT(path, "/", `file`) FROM ?_sounds_files'); $files = DB::Aowow()->selectCol('SELECT ABS(id) AS ARRAY_KEY, CONCAT(path, "/", `file`) FROM ?_sounds_files');
$nFiles = count($files); $nFiles = count($files);
$qtLen = strlen($nFiles); $qtLen = strlen($nFiles);
$sum = 0; $sum = 0;
$time = new Timer(500);
$intv = 0.5;
$time = microtime(true);
$sum = 0;
foreach ($files as $fileId => $filePath) foreach ($files as $fileId => $filePath)
{ {
$sum++; $sum++;
$newTime = microtime(true); if ($time->update())
if ($newTime > $time + $intv)
{ {
CLI::write(sprintf(' * %'.$qtLen.'d / %d (%4.1f%%)', $sum, $nFiles, round(100 * $sum / $nFiles, 1)), CLI::LOG_BLANK, true, true); CLI::write(sprintf('[soundfiles] * %'.$qtLen.'d / %d (%4.1f%%)', $sum, $nFiles, round(100 * $sum / $nFiles, 1)), CLI::LOG_BLANK, true, true);
$time = $newTime;
DB::Aowow()->selectCell('SELECT 1'); // keep mysql busy or it may go away DB::Aowow()->selectCell('SELECT 1'); // keep mysql busy or it may go away
} }
@@ -45,9 +48,9 @@ if (!CLI)
// copy over to static/wowsounds/ // copy over to static/wowsounds/
if (!copy($p, 'static/wowsounds/'.$fileId)) if (!copy($p, 'static/wowsounds/'.$fileId))
{ {
$ok = false; $this->success = false;
CLI::write(' - could not copy '.CLI::bold($p).' into '.CLI::bold('static/wowsounds/'.$fileId), CLI::LOG_ERROR); CLI::write('[soundfiles] - could not copy '.CLI::bold($p).' into '.CLI::bold('static/wowsounds/'.$fileId), CLI::LOG_ERROR);
$time = 0; $time->reset();
break 2; break 2;
} }
@@ -55,14 +58,14 @@ if (!CLI)
} }
} }
CLI::write(' - did not find file: '.CLI::bold(CLI::nicePath($filePath, CLISetup::$srcDir, '['.implode(',', CLISetup::$locales).']')), CLI::LOG_WARN); CLI::write('[soundfiles] - did not find file: '.CLI::bold(CLI::nicePath($filePath, CLISetup::$srcDir, '['.implode(',', CLISetup::$locales).']')), CLI::LOG_WARN);
$time = 0; $time->reset();
// flag as unusable in DB // flag as unusable in DB
DB::Aowow()->query('UPDATE ?_sounds_files SET id = ?d WHERE ABS(id) = ?d', -$fileId, $fileId); DB::Aowow()->query('UPDATE ?_sounds_files SET id = ?d WHERE ABS(id) = ?d', -$fileId, $fileId);
} }
return $ok; return $this->success;
} }
});
?> ?>

View File

@@ -1,201 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
/* deps:
* player_classlevelstats
* player_levelstats
*/
// Create 'statistics'-file in datasets
// this script requires the following dbcs to be available
$reqDBC = ['gtchancetomeleecrit', 'gtchancetomeleecritbase', 'gtchancetospellcrit', 'gtchancetospellcritbase', 'gtoctregenhp', 'gtregenmpperspt', 'gtregenhpperspt'];
function statistics()
{
$classs = function()
{
// constants and mods taken from TrinityCore (Player.cpp, StatSystem.cpp)
/* content per Index
mleatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
rngatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
baseCritPct[phys, spell]
diminishingConstant
baseDodgePct
DodgeCap
baseParryPct
ParryCap
baseBlockPct
classMod1 applies mod directly only one class having something worth mentioning: DK
classMod2 applies mod directly so what were they originally used for..?
*/
$dataz = array(
1 => [[-20, 2, 0, 3], [-10, 0, 1, 1], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 5, [], []],
2 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.4943, 88.129021, 5, 47.003525, 5, [], []],
3 => [[-20, 1, 1, 2], [-10, 0, 1, 2], null, 0.9880, -4.0873, 145.560408, 5, 145.560408, 0, [], []],
4 => [[-20, 1, 1, 2], [-10, 0, 1, 1], null, 0.9880, 2.0957, 145.560408, 5, 145.560408, 0, [], []],
5 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.4178, 150.375940, 0, 0.0, 0, [], []],
6 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 0, [], ['parryrtng' => [0.25, 'percentOf', 'str']]], // Forcefull Deflection (49410)
7 => [[-20, 1, 1, 2], [-10, 0, 1, 0], null, 0.9880, 2.1080, 145.560408, 0, 145.560408, 5, [], []],
8 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.6587, 150.375940, 0, 0.0, 0, [], []],
9 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 2.4211, 150.375940, 0, 0.0, 0, [], []],
11 => [[-20, 2, 0, 0], [-10, 0, 1, 0], null, 0.9720, 5.6097, 116.890707, 0, 0.0, 0, [], []]
);
foreach ($dataz as $class => &$data)
$data[2] = array_values(DB::Aowow()->selectRow('SELECT mle.chance*100 cMle, spl.chance*100 cSpl FROM dbc_gtchancetomeleecritbase mle, dbc_gtchancetospellcritbase spl WHERE mle.idx = spl.idx AND mle.idx = ?d', $class - 1));
return $dataz;
};
$race = function()
{
// { str, agi, sta, int, spi, raceMod1, raceMod2 }
$raceData = DB::World()->select('SELECT `race` AS ARRAY_KEY, MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 GROUP BY `race` ORDER BY `race` ASC');
foreach ($raceData as &$rd)
$rd = array_values($rd + [[], []]);
$racials = new SpellList(array(['typeCat', -4], ['reqClassMask', 0]));
$allMods = $racials->getProfilerMods();
foreach ($allMods as $spellId => $mods)
{
if (!$mods)
continue;
// if there is ever a case where a racial is shared between races i don't want to know about it!
$raceId = log($racials->getEntry($spellId)['reqRaceMask'], 2) + 1;
if (!isset($raceData[$raceId]))
continue;
foreach ($mods as $jsonStat => $mod)
{
if (empty($raceData[$raceId][5][$jsonStat]))
$raceData[$raceId][5][$jsonStat] = $mod;
else
$raceData[$raceId][6][$jsonStat] = $mod;
}
}
return $raceData;
};
$combo = function()
{
$result = [];
$critToDodge = array(
1 => 0.85/1.15, 2 => 1.00/1.15, 3 => 1.11/1.15,
4 => 2.00/1.15, 5 => 1.00/1.15, 6 => 0.85/1.15,
7 => 1.60/1.15, 8 => 1.00/1.15, 9 => 0.97/1.15, 11 => 2.00/1.15
);
// TrinityCore claims, DodgePerAgi per level and class can be constituted from critPerAgi (and level (and class))
// who am i to argue
// rebase stats to a specific race. chosen human as all stats are 20 and tauren for hunter, shaman and druid
// the stat gain per level is only dependant on the class. The race only determines the initial stats at level 0
// level:{ str, agi, sta, int, spi, hp, mana, mleCrt%Agi, splCrt%Int, dodge%Agi, HealthRegenModToBaseStat, HealthRegenModToBonusStat }
foreach ($critToDodge as $class => $mod)
{
// humans can't be hunter, shaman, druids (use tauren here)
if (in_array($class, [3, 7, 11]))
$offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 6'));
else
$offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 1'));
$gtData = DB::Aowow()->select('
SELECT mlecrt.idx - ?d AS ARRAY_KEY, mlecrt.chance * 100, splcrt.chance * 100, mlecrt.chance * 100 * ?f, baseHP5.ratio * 1, extraHP5.ratio * 1
FROM dbc_gtchancetomeleecrit mlecrt
JOIN dbc_gtchancetospellcrit splcrt ON splcrt.idx = mlecrt.idx
JOIN dbc_gtoctregenhp baseHP5 ON baseHP5.idx = mlecrt.idx
JOIN dbc_gtregenhpperspt extraHP5 ON extraHP5.idx = mlecrt.idx
WHERE mlecrt.idx BETWEEN ?d AND ?d',
(($class - 1) * 100) - 1, // class-offset
$mod,
(($class - 1) * 100) + 0, // lvl 1
(($class - 1) * 100) + 79 // lvl 80
);
$rows = DB::World()->select('
SELECT
pls.level AS ARRAY_KEY,
pls.str - ?d, pls.agi - ?d, pls.sta - ?d, pls.inte - ?d, pls.spi - ?d,
pcls.basehp, IF(pcls.basemana <> 0, pcls.basemana, 100)
FROM
player_levelstats pls
JOIN
player_classlevelstats pcls ON pls.level = pcls.level AND pls.class = pcls.class
WHERE
pls.race = ?d AND pls.class = ?d ORDER BY pls.level ASC',
$offset[0], $offset[1], $offset[2], $offset[3], $offset[4],
in_array($class, [3, 7, 11]) ? 6 : 1,
$class
);
$result[$class] = [];
foreach ($rows as $lvl => $row)
$result[$class][$lvl] = array_values(array_merge($row, $gtData[$lvl]));
}
return $result;
};
$level = function()
{
// base mana regeneration per level
// identical across classes (just use one, that acutally has mana (offset: 100))
// content of gtRegenMPPerSpt.dbc
return DB::Aowow()->selectCol('SELECT idx-99 AS ARRAY_KEY, ratio FROM dbc_gtregenmpperspt WHERE idx >= 100 AND idx < 100 + ?d', MAX_LEVEL);
};
$skills = function()
{
// profession perks ... too lazy to formulate a search algorithm for two occurences
return array(
SKILL_MINING => array( // mining / toughness
75 => ['sta' => 3],
150 => ['sta' => 5],
225 => ['sta' => 7],
300 => ['sta' => 10],
375 => ['sta' => 30],
450 => ['sta' => 60],
),
SKILL_SKINNING => array( // skinning / master of anatomy
75 => ['critstrkrtng' => 3],
150 => ['critstrkrtng' => 6],
225 => ['critstrkrtng' => 9],
300 => ['critstrkrtng' => 12],
375 => ['critstrkrtng' => 20],
450 => ['critstrkrtng' => 40],
)
);
};
$sub = ['classs', 'race', 'combo', 'level', 'skills'];
$out = [];
$success = true;
foreach ($sub as $s)
{
$res = $$s();
$out[$s] = $res;
if (!$res)
CLI::write('statistics - generator $'.$s.'() returned empty', CLI::LOG_WARN);
}
$toFile = 'g_statistics = '.preg_replace('/"\$([^$"]+)"/', '\1', Util::toJSON($out)).';';
if (!CLISetup::writeFile('datasets/statistics', $toFile))
$success = false;
return $success;
}
?>

View File

@@ -0,0 +1,209 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// Create 'statistics'-file in datasets
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'statistics' => [[], CLISetup::ARGV_PARAM, 'Compiles player stats into file for the character profiler tool.']
);
protected $worldDependency = ['player_levelstats', 'player_classlevelstats'];
protected $dbcSourceFiles = ['gtchancetomeleecrit', 'gtchancetomeleecritbase', 'gtchancetospellcrit', 'gtchancetospellcritbase', 'gtoctregenhp', 'gtregenmpperspt', 'gtregenhpperspt'];
protected $requiredDirs = ['datasets/'];
public function generate() : bool
{
$sub = ['classs', 'race', 'combo', 'level', 'skills'];
$out = [];
foreach ($sub as $s)
{
if ($out[$s] = $this->$s())
continue;
CLI::write('[statistics] generator '.$s.'() returned empty', CLI::LOG_WARN);
$this->success = false;
}
$toFile = 'g_statistics = '.preg_replace('/"\$([^$"]+)"/', '\1', Util::toJSON($out)).';';
if (!CLISetup::writeFile('datasets/statistics', $toFile))
$this->success = false;
return $this->success;
}
// constants and mods taken from TrinityCore (Player.cpp, StatSystem.cpp)
private function classs() : array
{
/* content per Index
mleatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
rngatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
baseCritPct[phys, spell]
diminishingConstant
baseDodgePct
DodgeCap
baseParryPct
ParryCap
baseBlockPct
classMod1 applies mod directly only one class having something worth mentioning: DK
classMod2 applies mod directly so what were they originally used for..?
*/
$dataz = array(
1 => [[-20, 2, 0, 3], [-10, 0, 1, 1], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 5, [], []],
2 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.4943, 88.129021, 5, 47.003525, 5, [], []],
3 => [[-20, 1, 1, 2], [-10, 0, 1, 2], null, 0.9880, -4.0873, 145.560408, 5, 145.560408, 0, [], []],
4 => [[-20, 1, 1, 2], [-10, 0, 1, 1], null, 0.9880, 2.0957, 145.560408, 5, 145.560408, 0, [], []],
5 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.4178, 150.375940, 0, 0.0, 0, [], []],
6 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 0, [], ['parryrtng' => [0.25, 'percentOf', 'str']]], // Forcefull Deflection (49410)
7 => [[-20, 1, 1, 2], [-10, 0, 1, 0], null, 0.9880, 2.1080, 145.560408, 0, 145.560408, 5, [], []],
8 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.6587, 150.375940, 0, 0.0, 0, [], []],
9 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 2.4211, 150.375940, 0, 0.0, 0, [], []],
11 => [[-20, 2, 0, 0], [-10, 0, 1, 0], null, 0.9720, 5.6097, 116.890707, 0, 0.0, 0, [], []]
);
foreach ($dataz as $class => &$data)
$data[2] = array_values(DB::Aowow()->selectRow('SELECT mle.chance*100 cMle, spl.chance*100 cSpl FROM dbc_gtchancetomeleecritbase mle, dbc_gtchancetospellcritbase spl WHERE mle.idx = spl.idx AND mle.idx = ?d', $class - 1));
return $dataz;
}
// { str, agi, sta, int, spi, raceMod1, raceMod2 }
private function race() : array
{
$raceData = DB::World()->select('SELECT `race` AS ARRAY_KEY, MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 GROUP BY `race` ORDER BY `race` ASC');
foreach ($raceData as &$rd)
$rd = array_values($rd + [[], []]);
$racials = new SpellList(array(['typeCat', -4], ['reqClassMask', 0]));
$allMods = $racials->getProfilerMods();
foreach ($allMods as $spellId => $mods)
{
if (!$mods)
continue;
// if there is ever a case where a racial is shared between races i don't want to know about it!
$raceId = log($racials->getEntry($spellId)['reqRaceMask'], 2) + 1;
if (!isset($raceData[$raceId]))
continue;
foreach ($mods as $jsonStat => $mod)
{
if (empty($raceData[$raceId][5][$jsonStat]))
$raceData[$raceId][5][$jsonStat] = $mod;
else
$raceData[$raceId][6][$jsonStat] = $mod;
}
}
return $raceData;
}
// TrinityCore claims, DodgePerAgi per level and class can be constituted from critPerAgi (and level (and class))
// who am i to argue
// rebase stats to a specific race. chosen human as all stats are 20 and tauren for hunter, shaman and druid
// level:{ str, agi, sta, int, spi, hp, mana, mleCrt%Agi, splCrt%Int, dodge%Agi, HealthRegenModToBaseStat, HealthRegenModToBonusStat }
private function combo() : array
{
$result = [];
$critToDodge = array(
1 => 0.85/1.15, 2 => 1.00/1.15, 3 => 1.11/1.15,
4 => 2.00/1.15, 5 => 1.00/1.15, 6 => 0.85/1.15,
7 => 1.60/1.15, 8 => 1.00/1.15, 9 => 0.97/1.15, 11 => 2.00/1.15
);
foreach ($critToDodge as $class => $mod)
{
// humans can't be hunter, shaman, druids (use tauren here)
if (in_array($class, [3, 7, 11]))
$offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 6'));
else
$offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 1'));
$gtData = DB::Aowow()->select(
'SELECT mlecrt.idx - ?d AS ARRAY_KEY, mlecrt.chance * 100, splcrt.chance * 100, mlecrt.chance * 100 * ?f, baseHP5.ratio * 1, extraHP5.ratio * 1
FROM dbc_gtchancetomeleecrit mlecrt
JOIN dbc_gtchancetospellcrit splcrt ON splcrt.idx = mlecrt.idx
JOIN dbc_gtoctregenhp baseHP5 ON baseHP5.idx = mlecrt.idx
JOIN dbc_gtregenhpperspt extraHP5 ON extraHP5.idx = mlecrt.idx
WHERE mlecrt.idx BETWEEN ?d AND ?d',
(($class - 1) * 100) - 1, // class-offset
$mod,
(($class - 1) * 100) + 0, // lvl 1
(($class - 1) * 100) + 79 // lvl 80
);
$rows = DB::World()->select(
'SELECT pls.level AS ARRAY_KEY,
pls.str - ?d, pls.agi - ?d, pls.sta - ?d, pls.inte - ?d, pls.spi - ?d,
pcls.basehp, IF(pcls.basemana <> 0, pcls.basemana, 100)
FROM player_levelstats pls
JOIN player_classlevelstats pcls ON pls.level = pcls.level AND pls.class = pcls.class
WHERE pls.race = ?d AND
pls.class = ?d
ORDER BY pls.level ASC',
$offset[0], $offset[1], $offset[2], $offset[3], $offset[4],
in_array($class, [3, 7, 11]) ? 6 : 1,
$class
);
$result[$class] = [];
foreach ($rows as $lvl => $row)
$result[$class][$lvl] = array_values(array_merge($row, $gtData[$lvl]));
}
return $result;
}
// base mana regeneration per level
// identical across classes (just use one, that acutally has mana (offset: 100))
// content of gtRegenMPPerSpt.dbc
private function level() : array
{
return DB::Aowow()->selectCol('SELECT idx-99 AS ARRAY_KEY, ratio FROM dbc_gtregenmpperspt WHERE idx >= 100 AND idx < 100 + ?d', MAX_LEVEL);
}
// profession perks ... too lazy to formulate a search algorithm for two occurences
private function skills() : array
{
// DB::Aowow()->select(
// 'SELECT sk.id AS "skillId", sla.reqSkillLevel, s.effect1AuraId AS "auraId", s.effect1MiscValue, s.effect1BasePoints + s.effect1DieSides AS "qty"
// FROM dbc_skilllineability sla
// JOIN dbc_skillline sk ON sk.id = sla.skilllineid
// JOIN dbc_spell s ON s.id = sla.spellId
// WHERE sla.acquiremethod = 1 AND // learn on skillup
// sk.categoryId = 11 AND // primary profession
// s.effect1Id = 6 AND // ApplyAura
// s.effect1AuraId IN (29, 189) // ModStatFlat, ModRating (need more?)
// ');
return array(
SKILL_MINING => array( // mining / toughness
75 => ['sta' => 3],
150 => ['sta' => 5],
225 => ['sta' => 7],
300 => ['sta' => 10],
375 => ['sta' => 30],
450 => ['sta' => 60],
),
SKILL_SKINNING => array( // skinning / master of anatomy
75 => ['critstrkrtng' => 3],
150 => ['critstrkrtng' => 6],
225 => ['critstrkrtng' => 9],
300 => ['critstrkrtng' => 12],
375 => ['critstrkrtng' => 20],
450 => ['critstrkrtng' => 40],
)
);
}
});
?>

View File

@@ -1,211 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// talents
// i - int talentId (id of aowow_talent)
// n - str name (spellname of aowow_spell for spellID = rank1)
// m - int number of ranks (6+ are empty)
// s - array:int spells to ranks (rank1, rank2, ..., rank5 of aowow_talent)
// d - array:str description of spells
// x - int column (col from aowow_talent)
// y - int row (row of aowow_talent)
// r - array:int on what the talent depends on: "r:[u, v]", u - nth talent in tree, v - required rank of u
// f - array:int [pets only] creatureFamilies, that use this spell
// t - array:str if the talent teaches a spell, this is the upper tooltip-table containing castTime, cost, cooldown
// j - array of modifier-arrays per rank for the Profiler [nyi]
// tabs
// n - name of the tab
// t - array of talent-objects
// f - array:int [pets only] creatureFamilies in that category
// builds talent-tree-data for the talent-calculator
// this script requires the following dbc-files to be available
$reqDBC = ['talenttab', 'talent', 'spell', 'creaturefamily', 'spellicon'];
function talentCalc()
{
$success = true;
$spellMods = (new SpellList(array(['typeCat', -2], Cfg::get('SQL_LIMIT_NONE'))))->getProfilerMods();
$buildTree = function ($class) use (&$petFamIcons, &$tSpells, $spellMods)
{
$petCategories = [];
$mask = $class ? 1 << ($class - 1) : 0;
// All "tabs" of a given class talent
$tabs = DB::Aowow()->select('SELECT * FROM dbc_talenttab WHERE classMask = ?d ORDER BY `tabNumber`, `creatureFamilyMask`', $mask);
$result = [];
for ($tabIdx = 0; $tabIdx < count($tabs); $tabIdx++)
{
$talents = DB::Aowow()->select('SELECT t.id AS tId, t.*, s.name_loc0, s.name_loc2, s.name_loc3, s.name_loc4, s.name_loc6, s.name_loc8, LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1)) AS iconString FROM dbc_talent t, dbc_spell s, dbc_spellicon si WHERE si.`id` = s.`iconId` AND t.`tabId`= ?d AND s.`id` = t.`rank1` ORDER by t.`row`, t.`column`', $tabs[$tabIdx]['id']);
$result[$tabIdx] = array(
'n' => Util::localizedString($tabs[$tabIdx], 'name'),
't' => []
);
if (!$class)
{
$petFamId = log($tabs[$tabIdx]['creatureFamilyMask'], 2);
$result[$tabIdx]['icon'] = $petFamIcons[$petFamId];
$petCategories = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, categoryEnumID FROM dbc_creaturefamily WHERE petTalentType = ?d', $petFamId);
$result[$tabIdx]['f'] = array_keys($petCategories);
}
// talent dependencies go here
$depLinks = [];
$tNums = [];
for ($talentIdx = 0; $talentIdx < count($talents); $talentIdx++)
{
$tNums[$talents[$talentIdx]['tId']] = $talentIdx;
$d = [];
$s = [];
$i = $talents[$talentIdx]['tId'];
$n = Util::localizedString($talents[$talentIdx], 'name');
$x = $talents[$talentIdx]['column'];
$y = $talents[$talentIdx]['row'];
$r = null;
$t = [];
$j = [];
$icon = $talents[$talentIdx]['iconString'];
$m = $talents[$talentIdx]['rank2'] == 0 ? 1 : (
$talents[$talentIdx]['rank3'] == 0 ? 2 : (
$talents[$talentIdx]['rank4'] == 0 ? 3 : (
$talents[$talentIdx]['rank5'] == 0 ? 4 : 5
)
)
);
// duplet handling
$f = [];
foreach ($petCategories as $k => $v)
{
// cant handle 64bit integer .. split
if ($v >= 32 && ((1 << ($v - 32)) & $talents[$talentIdx]['petCategory2']))
$f[] = $k;
else if ($v < 32 && ((1 << $v) & $talents[$talentIdx]['petCategory1']))
$f[] = $k;
}
for ($itr = 0; $itr <= ($m - 1); $itr++)
{
if (!$tSpells->getEntry($talents[$talentIdx]['rank'.($itr + 1)]))
continue;
$d[] = $tSpells->parseText()[0];
$s[] = $talents[$talentIdx]['rank'.($itr + 1)];
if (isset($spellMods[$talents[$talentIdx]['rank'.($itr + 1)]]))
$j[] = $spellMods[$talents[$talentIdx]['rank'.($itr + 1)]];
else
$j[] = null;
if ($talents[$talentIdx]['talentSpell'])
$t[] = $tSpells->getTalentHeadForCurrent();
}
if ($talents[$talentIdx]['reqTalent'])
{
// we didn't encounter the required talent yet => create reference
if (!isset($tNums[$talents[$talentIdx]['reqTalent']]))
$depLinks[$talents[$talentIdx]['reqTalent']] = $talentIdx;
$r = [$tNums[$talents[$talentIdx]['reqTalent']] ?? 0, $talents[$talentIdx]['reqRank'] + 1];
}
$result[$tabIdx]['t'][$talentIdx] = array(
'i' => $i, // talent id
'n' => $n, // talent name
'm' => $m, // maxRank
'd' => $d, // [descriptions]
's' => $s, // [spellIds]
'x' => $x, // col #
'y' => $y, // row #
'j' => $j // spell mods applied when used in profiler
);
if (isset($r)) // [reqTalentId, reqRank]
$result[$tabIdx]['t'][$talentIdx]['r'] = $r;
if (!empty($t)) // talentspell tooltip
$result[$tabIdx]['t'][$talentIdx]['t'] = $t;
if (!empty($f)) // [petFamilyId]
$result[$tabIdx]['t'][$talentIdx]['f'] = $f;
if ($class)
$result[$tabIdx]['t'][$talentIdx]['iconname'] = $icon;
// If this talent is a reference, add it to the array of talent dependencies
if (isset($depLinks[$talents[$talentIdx]['tId']]))
{
$result[$tabIdx]['t'][$depLinks[$talents[$talentIdx]['tId']]]['r'][0] = $talentIdx;
unset($depLinks[$talents[$talentIdx]['tId']]);
}
}
// Remove all dependencies for which the talent has not been found
foreach ($depLinks as $dep_link)
unset($result[$tabIdx]['t'][$dep_link]['r']);
}
return $result;
};
// my neighbour is noisy as fuck and my head hurts, so ..
$petFamIcons = ['Ability_Druid_KingoftheJungle', 'Ability_Druid_DemoralizingRoar', 'Ability_EyeOfTheOwl']; // .. i've no idea where to fetch these from
$classes = [CLASS_WARRIOR, CLASS_PALADIN, CLASS_HUNTER, CLASS_ROGUE, CLASS_PRIEST, CLASS_DEATHKNIGHT, CLASS_SHAMAN, CLASS_MAGE, CLASS_WARLOCK, CLASS_DRUID];
$petIcons = '';
// check directory-structure
foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir))
$success = false;
$tSpellIds = DB::Aowow()->selectCol('SELECT rank1 FROM dbc_talent UNION SELECT rank2 FROM dbc_talent UNION SELECT rank3 FROM dbc_talent UNION SELECT rank4 FROM dbc_talent UNION SELECT rank5 FROM dbc_talent');
$tSpells = new SpellList(array(['s.id', $tSpellIds], Cfg::get('SQL_LIMIT_NONE')));
foreach (CLISetup::$localeIds as $lId)
{
User::useLocale($lId);
Lang::load($lId);
// TalentCalc
foreach ($classes as $cMask)
{
set_time_limit(20);
$cId = log($cMask, 2) + 1;
$file = 'datasets/'.User::$localeString.'/talents-'.$cId;
$toFile = '$WowheadTalentCalculator.registerClass('.$cId.', '.Util::toJSON($buildTree($cId)).')';
if (!CLISetup::writeFile($file, $toFile))
$success = false;
}
// PetCalc
if (empty($petIcons))
{
$pets = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, LOWER(SUBSTRING_INDEX(iconString, "\\\\", -1)) AS iconString FROM dbc_creaturefamily WHERE petTalentType IN (0, 1, 2)');
$petIcons = Util::toJSON($pets);
}
$toFile = "var g_pet_icons = ".$petIcons.";\n\n";
$toFile .= 'var g_pet_talents = '.Util::toJSON($buildTree(0)).';';
$file = 'datasets/'.User::$localeString.'/pet-talents';
if (!CLISetup::writeFile($file, $toFile))
$success = false;
}
return $success;
}
?>

View File

@@ -1,102 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// builds image-textures for the talent-calculator
// spellIcons must be extracted and converted to at least medium size
// this script requires the following dbc-files to be available
$reqDBC = ['talenttab', 'talent', 'spell'];
function talentIcons()
{
$success = true;
$query = 'SELECT ic.name AS iconString FROM ?_icons ic JOIN ?_spell s ON s.iconId = ic.id JOIN dbc_talent t ON t.rank1 = s.id JOIN dbc_talenttab tt ON tt.id = t.tabId WHERE tt.?# = ?d AND tt.tabNumber = ?d ORDER BY t.row, t.column ASC, s.id DESC';
$dims = 36; //v-pets
$filenames = ['icons', 'warrior', 'paladin', 'hunter', 'rogue', 'priest', 'deathknight', 'shaman', 'mage', 'warlock', null, 'druid'];
// create directory if missing
if (!CLISetup::writeDir('static/images/wow/talents/icons'))
$success = false;
if (!CLISetup::writeDir('static/images/wow/hunterpettalents'))
$success = false;
foreach ($filenames as $k => $v)
{
if (!$v)
continue;
set_time_limit(10);
for ($tree = 0; $tree < 3; $tree++)
{
$what = $k ? 'classMask' : 'creatureFamilyMask';
$set = $k ? 1 << ($k - 1) : 1 << $tree;
$subset = $k ? $tree : 0;
$path = $k ? 'talents/icons' : 'hunterpettalents';
$outFile = 'static/images/wow/'.$path.'/'.$v.'_'.($tree + 1).'.jpg';
$icons = DB::Aowow()->SelectCol($query, $what, $set, $subset);
if (empty($icons))
{
CLI::write('talentIcons - query for '.$v.' tree: '.$k.' returned empty', CLI::LOG_ERROR);
$success = false;
continue;
}
if ($res = imageCreateTrueColor(count($icons) * $dims, 2 * $dims))
{
for ($i = 0; $i < count($icons); $i++)
{
$imgFile = 'static/images/wow/icons/medium/'.strtolower($icons[$i]).'.jpg';
if (!file_exists($imgFile))
{
CLI::write('talentIcons - raw image '.CLI::bold($imgFile). ' not found', CLI::LOG_ERROR);
$success = false;
break;
}
$im = imagecreatefromjpeg($imgFile);
// colored
imagecopymerge($res, $im, $i * $dims, 0, 0, 0, imageSX($im), imageSY($im), 100);
// grayscale
if (imageistruecolor($im))
imagetruecolortopalette($im, false, 256);
for ($j = 0; $j < imagecolorstotal($im); $j++)
{
$color = imagecolorsforindex($im, $j);
$gray = round(0.299 * $color['red'] + 0.587 * $color['green'] + 0.114 * $color['blue']);
imagecolorset($im, $j, $gray, $gray, $gray);
}
imagecopymerge($res, $im, $i * $dims, $dims, 0, 0, imageSX($im), imageSY($im), 100);
}
if (@imagejpeg($res, $outFile))
CLI::write(sprintf(ERR_NONE, CLI::bold($outFile)), CLI::LOG_OK, true, true);
else
{
$success = false;
CLI::write('talentIcons - '.CLI::bold($outFile.'.jpg').' could not be written', CLI::LOG_ERROR);
}
}
else
{
$success = false;
CLI::write('talentIcons - image resource not created', CLI::LOG_ERROR);
continue;
}
}
}
return $success;
}
?>

View File

@@ -0,0 +1,192 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// talents
// i - int talentId (id of aowow_talent)
// n - str name (spellname of aowow_spell for spellID = rank1)
// m - int number of ranks (6+ are empty)
// s - array:int spells to ranks (rank1, rank2, ..., rank5 of aowow_talent)
// d - array:str description of spells
// x - int column (col from aowow_talent)
// y - int row (row of aowow_talent)
// r - array:int on what the talent depends on: "r:[u, v]", u - nth talent in tree, v - required rank of u
// f - array:int [pets only] creatureFamilies, that use this spell
// t - array:str if the talent teaches a spell, this is the upper tooltip-table containing castTime, cost, cooldown
// j - array of modifier-arrays per rank for the Profiler
// tabs
// n - name of the tab
// t - array of talent-objects
// f - array:int [pets only] creatureFamilies in that category
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'talentcalc' => [[], CLISetup::ARGV_PARAM, 'Compiles talent tree data to file for the talent calculator tool.']
);
protected $dbcSourceFiles = ['talenttab', 'talent', 'spell', 'creaturefamily', 'spellicon'];
protected $setupAfter = [['spell'], []];
protected $requiredDirs = ['datasets/'];
protected $localized = true;
private $petFamIcons = [];
private $tSpells = null;
private $spellMods = [];
public function generate() : bool
{
// target direcotries are missing
if (!$this->success)
return false;
// my neighbour is noisy as fuck and my head hurts, so ..
$this->petFamIcons = ['Ability_Druid_KingoftheJungle', 'Ability_Druid_DemoralizingRoar', 'Ability_EyeOfTheOwl']; // .. i've no idea where to fetch these from
$this->spellMods = (new SpellList(array(['typeCat', -2], Cfg::get('SQL_LIMIT_NONE'))))->getProfilerMods();
$petIcons = Util::toJSON(DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, LOWER(SUBSTRING_INDEX(iconString, "\\\\", -1)) AS iconString FROM dbc_creaturefamily WHERE petTalentType IN (0, 1, 2)'));
$tSpellIds = DB::Aowow()->selectCol('SELECT rank1 FROM dbc_talent UNION SELECT rank2 FROM dbc_talent UNION SELECT rank3 FROM dbc_talent UNION SELECT rank4 FROM dbc_talent UNION SELECT rank5 FROM dbc_talent');
$this->tSpells = new SpellList(array(['s.id', $tSpellIds], Cfg::get('SQL_LIMIT_NONE')));
foreach (CLISetup::$localeIds as $lId)
{
User::useLocale($lId);
Lang::load($lId);
// TalentCalc
for ($i = 1; (1 << ($i - 1)) < CLASS_MASK_ALL; $i++ )
{
if (!((1 << ($i - 1)) & CLASS_MASK_ALL))
continue;
set_time_limit(20);
$file = 'datasets/'.User::$localeString.'/talents-'.$i;
$toFile = '$WowheadTalentCalculator.registerClass('.$i.', '.Util::toJSON($this->buildTree(1 << ($i - 1))).')';
if (!CLISetup::writeFile($file, $toFile))
$this->success = false;
}
// PetCalc
$toFile = "var g_pet_icons = ".$petIcons.";\n\n";
$toFile .= 'var g_pet_talents = '.Util::toJSON($this->buildTree(0)).';';
$file = 'datasets/'.User::$localeString.'/pet-talents';
if (!CLISetup::writeFile($file, $toFile))
$this->success = false;
}
return $this->success;
}
private function buildTree(int $classMask) : array
{
$petCategories = [];
// All "tabs" of a given class talent
$tabs = DB::Aowow()->select('SELECT * FROM dbc_talenttab WHERE classMask = ?d ORDER BY `tabNumber`, `creatureFamilyMask`', $classMask);
$result = [];
for ($tabIdx = 0; $tabIdx < count($tabs); $tabIdx++)
{
$talents = DB::Aowow()->select('SELECT t.id AS tId, t.*, IF(t.rank5, 5, IF(t.rank4, 4, IF(t.rank3, 3, IF(t.rank2, 2, 1)))) AS maxRank, s.name_loc0, s.name_loc2, s.name_loc3, s.name_loc4, s.name_loc6, s.name_loc8, LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1)) AS iconString FROM dbc_talent t, dbc_spell s, dbc_spellicon si WHERE si.`id` = s.`iconId` AND t.`tabId`= ?d AND s.`id` = t.`rank1` ORDER by t.`row`, t.`column`', $tabs[$tabIdx]['id']);
$result[$tabIdx] = array(
'n' => Util::localizedString($tabs[$tabIdx], 'name'),
't' => []
);
if (!$classMask)
{
$petFamId = log($tabs[$tabIdx]['creatureFamilyMask'], 2);
$result[$tabIdx]['icon'] = $this->petFamIcons[$petFamId];
$petCategories = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, categoryEnumID FROM dbc_creaturefamily WHERE petTalentType = ?d', $petFamId);
$result[$tabIdx]['f'] = array_keys($petCategories);
}
// talent dependencies go here
$depLinks = [];
$tNums = [];
for ($talentIdx = 0; $talentIdx < count($talents); $talentIdx++)
{
$tNums[$talents[$talentIdx]['tId']] = $talentIdx;
$talent = array(
'i' => $talents[$talentIdx]['tId'], // talent id
'n' => Util::localizedString($talents[$talentIdx], 'name'), // talent name
'm' => $talents[$talentIdx]['maxRank'], // maxRank
'd' => [], // [descriptions]
's' => [], // [spellIds]
'x' => $talents[$talentIdx]['column'], // col #
'y' => $talents[$talentIdx]['row'], // row #
'j' => [] // [spellMods] that are applied when used in profiler
// 'r' => [] // [reqTalentId, reqRank] (can be omitted)
// 't' => [] // talentspell tooltip (can be omitted)
// 'f' => [] // [petFamilyIds] (can be omitted)
);
if ($classMask)
$talent['iconname'] = $talents[$talentIdx]['iconString'];
for ($itr = 0; $itr <= ($talent['m'] - 1); $itr++)
{
$spell = $talents[$talentIdx]['rank'.($itr + 1)];
if (!$this->tSpells->getEntry($spell))
continue;
$talent['d'][] = $this->tSpells->parseText()[0];
$talent['s'][] = $talents[$talentIdx]['rank'.($itr + 1)];
if ($classMask && isset($this->spellMods[$spell]))
if ($mod = $this->spellMods[$spell])
$talent['j'][] = $mod;
if ($talents[$talentIdx]['talentSpell'])
$talent['t'][] = $this->tSpells->getTalentHeadForCurrent();
}
foreach ($petCategories as $k => $v)
{
// cant handle 64bit integer .. split
if ($v >= 32 && ((1 << ($v - 32)) & $talents[$talentIdx]['petCategory2']))
$talent['f'][] = $k;
else if ($v < 32 && ((1 << $v) & $talents[$talentIdx]['petCategory1']))
$talent['f'][] = $k;
}
if ($talents[$talentIdx]['reqTalent'])
{
// we didn't encounter the required talent yet => create reference
if (!isset($tNums[$talents[$talentIdx]['reqTalent']]))
$depLinks[$talents[$talentIdx]['reqTalent']] = $talentIdx;
$talent['r'] = [$tNums[$talents[$talentIdx]['reqTalent']] ?? 0, $talents[$talentIdx]['reqRank'] + 1];
}
$result[$tabIdx]['t'][$talentIdx] = $talent;
// If this talent is a reference, add it to the array of talent dependencies
if (isset($depLinks[$talents[$talentIdx]['tId']]))
{
$result[$tabIdx]['t'][$depLinks[$talents[$talentIdx]['tId']]]['r'][0] = $talentIdx;
unset($depLinks[$talents[$talentIdx]['tId']]);
}
}
// Remove all dependencies for which the talent has not been found
foreach ($depLinks as $dep_link)
unset($result[$tabIdx]['t'][$dep_link]['r']);
}
return $result;
}
});
?>

View File

@@ -0,0 +1,107 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'talenticons' => [[], CLISetup::ARGV_PARAM, 'Generates icon textures for the talent calculator tool.']
);
protected $dbcSourceFiles = ['talenttab', 'talent', 'spell'];
protected $setupAfter = [['icons', 'spell'], ['simpleimg']];
protected $requiredDirs = ['static/images/wow/talents/icons', 'static/images/wow/hunterpettalents'];
private const ICON_SIZE = 36; // px
private $filenames = ['icons', 'warrior', 'paladin', 'hunter', 'rogue', 'priest', 'deathknight', 'shaman', 'mage', 'warlock', null, 'druid'];
public function generate() : bool
{
foreach ($this->filenames as $k => $v)
{
if (!$v)
continue;
set_time_limit(10);
for ($tree = 0; $tree < 3; $tree++)
{
$what = $k ? 'classMask' : 'creatureFamilyMask';
$set = $k ? 1 << ($k - 1) : 1 << $tree;
$subset = $k ? $tree : 0;
$path = $k ? 'talents/icons' : 'hunterpettalents';
$outFile = 'static/images/wow/'.$path.'/'.$v.'_'.($tree + 1).'.jpg';
$icons = DB::Aowow()->SelectCol(
'SELECT ic.name AS iconString
FROM ?_icons ic
JOIN ?_spell s ON s.iconId = ic.id
JOIN dbc_talent t ON t.rank1 = s.id
JOIN dbc_talenttab tt ON tt.id = t.tabId
WHERE tt.?# = ?d AND tt.tabNumber = ?d
ORDER BY t.row, t.column ASC, s.id DESC',
$what, $set, $subset);
if (empty($icons))
{
CLI::write('[talenticons] - query for '.$v.' tree: '.$k.' returned empty', CLI::LOG_ERROR);
$this->success = false;
continue;
}
$res = imageCreateTrueColor(count($icons) * self::ICON_SIZE, 2 * self::ICON_SIZE);
if (!$res)
{
$this->success = false;
CLI::write('[talenticons] - image resource not created', CLI::LOG_ERROR);
continue;
}
for ($i = 0; $i < count($icons); $i++)
{
$imgFile = 'static/images/wow/icons/medium/'.strtolower($icons[$i]).'.jpg';
if (!file_exists($imgFile))
{
CLI::write('[talenticons] - raw image '.CLI::bold($imgFile). ' not found', CLI::LOG_ERROR);
$this->success = false;
break;
}
$im = imagecreatefromjpeg($imgFile);
// colored
imagecopymerge($res, $im, $i * self::ICON_SIZE, 0, 0, 0, imageSX($im), imageSY($im), 100);
// grayscale
if (imageistruecolor($im))
imagetruecolortopalette($im, false, 256);
for ($j = 0; $j < imagecolorstotal($im); $j++)
{
$color = imagecolorsforindex($im, $j);
$gray = round(0.299 * $color['red'] + 0.587 * $color['green'] + 0.114 * $color['blue']);
imagecolorset($im, $j, $gray, $gray, $gray);
}
imagecopymerge($res, $im, $i * self::ICON_SIZE, self::ICON_SIZE, 0, 0, imageSX($im), imageSY($im), 100);
}
if (@imagejpeg($res, $outFile))
CLI::write('[talenticons] '.sprintf(ERR_NONE, CLI::bold($outFile)), CLI::LOG_OK, true, true);
else
{
$this->success = false;
CLI::write('[talenticons] - '.CLI::bold($outFile.'.jpg').' could not be written', CLI::LOG_ERROR);
}
}
}
return $this->success;
}
});
?>

View File

@@ -0,0 +1,28 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("build", new class extends SetupScript
{
use TrTemplateFile;
protected $info = array(
'tooltips' => [[], CLISetup::ARGV_PARAM, 'Fills powered tooltips (static/widgets/power.js) with site variables.']
);
protected $fileTemplateSrc = ['power.js.in'];
protected $fileTemplateDest = ['static/widgets/power.js'];
public function generate() : bool
{
$this->templateFill();
return $this->success;
}
});
?>

View File

@@ -7,14 +7,17 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
// Creates 'weight-presets'-file // Creates 'weight-presets'-file
CLISetup::registerSetup("build", new class extends SetupScript
{
protected $info = array(
'weightpresets' => [[], CLISetup::ARGV_PARAM, 'Generates stat weight presets file for the item comparison tool.']
);
function weightPresets() protected $requiredDirs = ['datasets/'];
public function generate() : bool
{ {
// check directory-structure
if (!CLISetup::writeDir('datasets/'))
return false;
$wtPresets = []; $wtPresets = [];
$scales = DB::Aowow()->select('SELECT id, name, icon, class FROM ?_account_weightscales WHERE userId = 0 ORDER BY class, id ASC'); $scales = DB::Aowow()->select('SELECT id, name, icon, class FROM ?_account_weightscales WHERE userId = 0 ORDER BY class, id ASC');
@@ -24,7 +27,7 @@ if (!CLI)
$wtPresets[$s['class']]['pve'][$s['name']] = array_merge(['__icon' => $s['icon']], $weights); $wtPresets[$s['class']]['pve'][$s['name']] = array_merge(['__icon' => $s['icon']], $weights);
else else
{ {
CLI::write('WeightScale \''.CLI::bold($s['name']).'\' has no data set.', CLI::LOG_WARN); CLI::write('[weightpresets] Scale \''.CLI::bold($s['name']).'\' has no data set.', CLI::LOG_WARN);
$wtPresets[$s['class']]['pve'][$s['name']] = ['__icon' => $s['icon']]; $wtPresets[$s['class']]['pve'][$s['name']] = ['__icon' => $s['icon']];
} }
} }
@@ -37,4 +40,6 @@ if (!CLI)
return true; return true;
} }
});
?> ?>

View File

@@ -9,17 +9,24 @@ if (!CLI)
trait TrDBCcopy trait TrDBCcopy
{ {
public function __construct()
{
$this->info = array(
$this->command => [[], CLISetup::ARGV_PARAM, 'COPY: ' . $this->dbcSourceFiles[0] . '.dbc -> aowow_'.$this->command],
);
}
public function generate() : bool public function generate() : bool
{ {
if (!$this->dbcSourceFiles) if (!$this->dbcSourceFiles)
{ {
CLI::write(' SetupScript '.$this->command.' is set up for DBCcopy but has no source set!', CLI::LOG_ERROR); CLI::write('[sql] SetupScript '.$this->command.' is set up for DBCcopy but has no source set!', CLI::LOG_ERROR);
return false; return false;
} }
else if (count($this->dbcSourceFiles) != 1) else if (count($this->dbcSourceFiles) != 1)
CLI::write(' SetupScript '.$this->command.' is set up for DBCcopy but has multiple sources set!', CLI::LOG_WARN); CLI::write('[sql] SetupScript '.$this->command.' is set up for DBCcopy but has multiple sources set!', CLI::LOG_WARN);
CLI::write('SqlGen::generate() - copying '.$this->dbcSourceFiles[0].'.dbc into aowow_'.$this->command); CLI::write('[sql] copying '.$this->dbcSourceFiles[0].'.dbc into aowow_'.$this->command);
$dbc = new DBC($this->dbcSourceFiles[0], ['temporary' => false, 'tableName' => 'aowow_'.$this->command]); $dbc = new DBC($this->dbcSourceFiles[0], ['temporary' => false, 'tableName' => 'aowow_'.$this->command]);
if ($dbc->error) if ($dbc->error)
@@ -36,18 +43,18 @@ trait TrCustomData
{ {
$ok = true; $ok = true;
$this->customData = $this->customData ?? []; $this->customData = $this->customData ?? [];
if ($cd = DB::Aowow()->selectCol('SELECT `entry` AS ARRAY_KEY, `field` AS ARRAY_KEY2, `value` FROM ?_setup_custom_data WHERE `command` = ?', $this->command)) if ($cd = DB::Aowow()->selectCol('SELECT `entry` AS ARRAY_KEY, `field` AS ARRAY_KEY2, `value` FROM ?_setup_custom_data WHERE `command` = ?', $this->getName()))
$this->customData += $cd; $this->customData += $cd;
foreach ($this->customData as $id => $data) foreach ($this->customData as $id => $data)
{ {
try try
{ {
DB::Aowow()->query('UPDATE ?_'.$this->command.' SET ?a WHERE id = ?d', $data, $id); DB::Aowow()->query('UPDATE ?_'.$this->getName().' SET ?a WHERE id = ?d', $data, $id);
} }
catch (Exception $e) catch (Exception $e)
{ {
trigger_error('Custom Data for entry #'.$id.': '.$e->getMessage(), E_USER_ERROR); trigger_error('custom data for entry #'.$id.': '.$e->getMessage(), E_USER_ERROR);
$ok = false; $ok = false;
} }
} }
@@ -56,17 +63,351 @@ trait TrCustomData
} }
} }
trait TrTemplateFile
{
public function generate() : bool
{
$this->templateFill();
return $this->success;
}
private function &templateCopy() : iterable
{
if (!$this->fileTemplateSrc || count($this->fileTemplateSrc) != count($this->fileTemplateDest))
{
CLI::write('[build] template file definitions missing or malformed', CLI::LOG_ERROR);
$this->success = false;
return;
}
foreach ($this->fileTemplateSrc as $idx => $srcFile)
{
$file = $this->fileTemplatePath.$srcFile;
if (!file_exists($file))
{
CLI::write('[build] template file is missing - '.CLI::bold($file), CLI::LOG_ERROR);
$this->success = false;
return;
}
$content = file_get_contents($file);
if (!$content)
{
CLI::write('[build] template file is not readable - '.CLI::bold($file), CLI::LOG_ERROR);
$this->success = false;
return;
}
// replace constants
$content = Cfg::applyToString($content);
yield $content;
if (CLISetup::writeFile($this->fileTemplateDest[$idx], $content))
continue;
$this->success = false;
return;
}
}
private function templateFill() : void
{
foreach ($this->templateCopy() as &$content)
{
if (!$this->success) //templateCopy() fucked up somehow?
return;
// PH format: /*setup:<setupFunc>*/
if (preg_match_all('/\/\*setup:([\w\-_]+)\*\//i', $content, $m))
{
foreach ($m[1] as $func)
{
if (method_exists($this, $func))
$content = str_replace('/*setup:'.$func.'*/', $this->$func(), $content);
else
{
CLI::write('['.$this->getName().'] No function for was registered for placeholder '.$func.'().', CLI::LOG_ERROR);
$this->success = false;
return;
}
}
}
}
}
}
trait TrImageProcessor
{
private $imgPath = '%sInterface/';
private $status = '';
private $maxExecTime = 30;
private static $GEN_IDX_SRC_PATH = 0; // php v8.0 make static > const
private static $GEN_IDX_SRC_REAL = 1;
private static $GEN_IDX_LOCALE = 2;
private static $GEN_IDX_SRC_INFO = 3;
private static $GEN_IDX_DEST_INFO = 4;
private static $JPEG_QUALITY = 85; // 0: worst - 100: best
private function checkSourceDirs() : bool
{
$outTblLen = 0;
$foundCache = [];
foreach ($this->genSteps as $i => [$subDir, $realPaths, $localized, , ])
{
if ($realPaths)
continue;
// multiple genSteps can require the same resource
if (isset($foundCache[$subDir]))
{
$this->genSteps[$i][self::$GEN_IDX_SRC_REAL] = $foundCache[$subDir];
continue;
}
$outTblLen = max($outTblLen, strlen($subDir));
$path = $this->imgPath.$subDir;
if ($p = CLISetup::filesInPathLocalized($path, $this->success, $localized))
{
$foundCache[$subDir] = $p;
$this->genSteps[$i][self::$GEN_IDX_SRC_REAL] = $p; // pvp v7.3+ - make $realPaths areferene
}
else
$this->success = false;
}
$locList = [];
foreach (CLISetup::$expectedPaths as $xp => $locId)
if (in_array($locId, CLISetup::$localeIds))
$locList[] = $xp;
CLI::write('[img-proc] required resources overview:', CLI::LOG_INFO);
$foundCache = [];
foreach ($this->genSteps as [$subDir, $realPaths, $localized, , ])
{
// one line per unique resource
if (isset($foundCache[$subDir]))
continue;
$foundCache[$subDir] = true;
if (!$realPaths)
{
CLI::write(CLI::red('MISSING').' - '.str_pad($subDir, 14).' @ '.sprintf($this->imgPath, '['.implode('/,', $locList).'/]').$subDir);
$this->success = false;
}
else if ($localized)
{
$foundLoc = [];
foreach (CLISetup::$expectedPaths as $xp => $lId)
if (in_array($lId, CLISetup::$localeIds))
if (isset($realPaths[$lId]) && ($n = stripos($realPaths[$lId], '/'.$xp.'/')))
$foundLoc[$lId] = substr($realPaths[$lId], $n + 1, 4);
if ($diff = array_diff(CLISetup::$localeIds, array_keys($foundLoc)))
{
$buff = [];
foreach ($diff as $d)
$buff[] = CLI::red(Util::$localeStrings[$d]);
foreach ($foundLoc as $str)
$buff[] = CLI::green($str);
CLI::write(CLI::yellow('PARTIAL').' - '.str_pad($subDir, $outTblLen).' @ '.sprintf($this->imgPath, '['.implode('/,', $buff).'/]').$subDir);
}
else
CLI::write(CLI::green('FOUND ').' - '.str_pad($subDir, $outTblLen).' @ '.sprintf($this->imgPath, '['.implode('/,', $foundLoc).'/]').$subDir);
}
else
CLI::write(CLI::green('FOUND ').' - '.str_pad($subDir, $outTblLen).' @ '.reset($realPaths));
}
CLI::write();
// if not localized directly return result
foreach ($this->genSteps as $i => [$subDir, $realPaths, $localized, , ])
if (!$localized)
$this->genSteps[$i][self::$GEN_IDX_SRC_REAL] = reset($realPaths);
return $this->success;
}
// prefer manually converted PNG files (as the imagecreatefromblp-script has issues with some formats)
// alpha channel issues observed with locale deDE Hilsbrad and Elwynn - maps
// see: https://github.com/Kanma/BLPConverter
private function loadImageFile(string $path) // : ?GdImage
{
$result = null;
$path = preg_replace('/\.(png|blp)$/i', '', $path);
$file = $path.'.png';
if (CLISetup::fileExists($file))
{
CLI::write('[img-proc] manually converted png file present for '.$file, CLI::LOG_INFO);
$result = imagecreatefrompng($file);
}
if (!$result)
{
$file = $path.'.blp';
if (CLISetup::fileExists($file))
$result = imagecreatefromblp($file);
}
return $result;
}
private function writeImageFile(/* GdImage */ $src, string $outFile, array $srcDims, array $destDims) : bool
{
$success = false;
$outRes = imagecreatetruecolor($destDims['w'], $destDims['h']);
$ext = substr($outFile, -3, 3);
imagesavealpha($outRes, true);
if ($ext == 'png')
{
imagealphablending($outRes, false);
$transparentindex = imagecolorallocatealpha($outRes, 255, 255, 255, 127);
imagefill($outRes, 0, 0, $transparentindex);
}
imagecopyresampled($outRes, $src, $destDims['x'], $destDims['x'], $srcDims['x'], $srcDims['y'], $destDims['w'], $destDims['h'], $srcDims['w'], $srcDims['h']);
switch ($ext)
{
case 'jpg':
$success = imagejpeg($outRes, $outFile, self::$JPEG_QUALITY);
break;
case 'gif':
$success = imagegif($outRes, $outFile);
break;
case 'png':
$success = imagepng($outRes, $outFile);
break;
default:
CLI::write('[img-proc] '.$this->status.' - unsupported file fromat: '.$ext, CLI::LOG_WARN);
}
imagedestroy($outRes);
if ($success)
{
chmod($outFile, Util::FILE_ACCESS);
CLI::write('[img-proc] '.$this->status.' - image '.$outFile.' written', CLI::LOG_OK, true, true);
}
else
CLI::write('[img-proc] '.$this->status.' - could not create image '.$outFile, CLI::LOG_ERROR);
return $success;
}
}
trait TrComplexImage
{
use TrImageProcessor { TrImageProcessor::writeImageFile as _writeImageFile; }
private function writeImageFile(/* GdImage */ $src, string $outFile, int $w, int $h) : bool
{
$srcDims = array(
'x' => 0,
'y' => 0,
'w' => imagesx($src),
'h' => imagesy($src)
);
$destDims = array(
'x' => 0,
'y' => 0,
'w' => $w,
'h' => $h
);
return $this->_writeImageFile($src, $outFile, $srcDims, $destDims);
}
private function createAlphaImage(int $w, int $h) //: ?GdImage
{
$img = imagecreatetruecolor($w, $h);
if (!$img)
return null;
imagesavealpha($img, true);
imagealphablending($img, false);
$bgColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
imagefilledrectangle($img, 0, 0, imagesx($img) - 1, imagesy($img) - 1, $bgColor);
imagecolortransparent($img, $bgColor);
imagealphablending($img, true);
imagecolordeallocate($img, $bgColor);
return $img;
}
private function assembleImage(string $baseName, array $tileData, int $destW, int $destH) //: ?GdImage
{
$dest = imagecreatetruecolor($destW, $destH);
if (!$dest)
return null;
imagesavealpha($dest, true);
imagealphablending($dest, false);
$tileH = $destH;
foreach ($tileData as $y => $row)
{
$tileW = $destW;
foreach ($row as $x => $suffix)
{
$src = $this->loadImageFile($baseName.$suffix);
if (!$src)
{
CLI::write('[img-proc-c] tile '.$baseName.$suffix.'.blp missing.', CLI::LOG_ERROR);
unset($dest);
return null;
}
imagecopyresampled($dest, $src, 256 * $x, 256 * $y, 0, 0, min($tileW, 256), min($tileH, 256), min($tileW, 256), min($tileH, 256));
$tileW -= 256;
unset($src);
}
$tileH -= 256;
}
return $dest;
}
}
abstract class SetupScript abstract class SetupScript
{ {
protected $fileTemplatePath = ''; // FileGen
protected $fileTemplateFile = ''; protected $requiredDirs = [];
protected $fileTemplateDest = [];
protected $fileTemplatePath = 'setup/tools/filegen/templates/';
protected $fileTemplateSrc = [];
protected $tblDependencyAowow = []; // SQLGen
protected $tblDependencyTC = []; protected $result = '';
protected $dbcSourceFiles = []; // FileGen + SQLGen
protected $dbcSourceFiles = []; // relies on these dbc files. Read into db if related table is missing
protected $worldDependency = []; // query when this table changed (--sync command)
protected $info = []; // arr: 0 => self, n => genSteps cmd => [[arr<str>:optionalArgs], int:argFlags, str:description]
protected $setupAfter = [[], []]; // [[sqlgen], [filegen]] used to sort scripts that rely on each other being executed in the right order (script names are not nessecarily the same as their table names)
protected $success = true;
protected $localized = false; // push locale directories onto $requiredDirs?
protected $useGlobalStrings = false; // uses data from interface/framexml/globalstrings.lua
public $isOptional = false; // not a part of the setup chain
// abstract protected $command;
abstract public function generate() : bool; abstract public function generate() : bool;
@@ -75,23 +416,107 @@ abstract class SetupScript
return $this->dbcSourceFiles; return $this->dbcSourceFiles;
} }
public function getDependencies(bool $aowow) : array public function getSelfDependencies() : array
{ {
return $aowow ? $this->tblDependencyAowow : $this->tblDependencyTC; return $this->setupAfter;
}
public function getRemoteDependencies() : array
{
return $this->worldDependency;
} }
public function getName() : string public function getName() : string
{ {
return $this->command; reset($this->info);
return key($this->info);
}
public function getInfo() // : string|int
{
// info: name => [param, paramFlags, description]
return (reset($this->info)[2] ?? '').($this->isOptional ? ' - '.Cli::yellow('[omitted by setup]') : '');
}
public function getSubCommands() : array
{
$sub = [];
if (count($this->info) > 1)
$sub = array_slice($this->info, 1, 10, true);
return $sub;
}
public function getRequiredDirs(): array
{
return $this->requiredDirs;
}
public function fulfillRequirements() : bool
{
// create directory structure
$newDirs = 0;
$existed = false;
foreach ($this->getRequiredDirs() as $dir)
{
$dirs = [];
if (!$this->localized)
$dirs[] = $dir;
else
foreach (CLISetup::$locales as $str)
$dirs[] = $dir . $str . '/';
foreach ($dirs as $d)
{
if (!CLISetup::writeDir($d, $existed))
{
CLI::write('[build] could not create directory: '.CLI::bold($d), CLI::LOG_ERROR);
return false;
}
}
$newDirs += ($existed ? 0 : 1);
}
if ($newDirs)
CLI::write('[build] created '.$newDirs.' extra paths');
// load DBC files
if (!in_array('TrDBCcopy', class_uses($this)))
{
foreach ($this->getRequiredDBCs() as $req)
{
if (CLISetup::loadDBC($req))
continue;
CLI::write('[sql/build] '. $this->getName() . ' is missing dbc file ' . $req . '. Skipping...', CLI::LOG_ERROR);
return false;
}
}
if ($this->useGlobalStrings)
if (!CLISetup::loadGlobalStrings())
return false;
return true;
}
public function writeCLIHelp() : bool
{
// CLI::write('example info', -1, false); // some info
// CLI::write(); // empty new line
// return true; // help was provided, skip help in parent
return false;
} }
protected function reapplyCCFlags(string $tbl, int $type) : void protected function reapplyCCFlags(string $tbl, int $type) : void
{ {
// reaply flags for community content as these are lost when the table is rebuild // reapply flags for community content as these are lost when the table is rebuild
if (preg_match('/[^a-z]/i', $tbl)) if (preg_match('/\W/i', $tbl))
{ {
trigger_error('SetupScript::reapplyCCFlags() - invalid table name'); trigger_error('[sql] reapplyCCFlags() - invalid table name');
return; return;
} }

View File

@@ -1,256 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
class SqlGen
{
const MODE_NORMAL = 1;
const MODE_FIRSTRUN = 2;
const MODE_UPDATE = 3;
private static $mode = 0;
private static $tables = [];
private static $tmpStore = [];
public static $subScripts = [];
public static $defaultExecTime = 30;
public static $sqlBatchSize = 1000;
public static function init(int $mode = self::MODE_NORMAL, array $updScripts = []) : bool
{
self::$defaultExecTime = ini_get('max_execution_time');
self::$mode = $mode;
if (!CLISetup::$localeIds)
{
CLI::write('No valid locale specified. Check your config or --locales parameter, if used', CLI::LOG_ERROR);
return false;
}
// register subscripts
foreach (glob('setup/tools/sqlgen/*.func.php') as $file)
include_once $file;
while (self::$tmpStore)
{
$nDepsMissing = count(self::$tmpStore);
foreach (self::$tmpStore as $idx => $ts)
{
$depsOK = true;
foreach ($ts->getDependencies(true) as $d)
{
if (isset(self::$tables[$d]))
continue;
$depsOK = false;
break;
}
if ($depsOK)
{
if (isset(self::$tables[$ts->getName()]))
{
CLI::write('a SetupScript named '.CLI::bold($ts->getName()).' was already registered. Skipping...', CLI::LOG_WARN);
unset(self::$tmpStore[$idx]);
continue;
}
self::$tables[$ts->getName()] = $ts;
unset(self::$tmpStore[$idx]);
}
}
if ($nDepsMissing == count(self::$tmpStore))
{
CLI::write('the following SetupScripts have unresolved dependencies and have not been registered:', CLI::LOG_ERROR);
foreach (self::$tmpStore as $ts)
CLI::write(' * '.CLI::bold($ts->getName()).' => '.implode(', ', $ts->getDependencies(true)));
self::$tmpStore = [];
break;
}
}
// handle command prompts
if (!self::handleCLIOpts($doScripts) && !$updScripts)
return false;
// check passed subscript names; limit to real scriptNames
self::$subScripts = array_keys(self::$tables);
if ($doScripts || $updScripts)
self::$subScripts = array_intersect($doScripts ?: $updScripts, self::$subScripts);
return true;
}
public static function register(SetupScript $ssRef) : void
{
// if dependencies haven't been stored yet, put aside for later use
foreach ($ssRef->getDependencies(true) as $d)
{
if (isset(self::$tables[$d]))
continue;
self::$tmpStore[] = $ssRef;
return;
}
if (isset(self::$tables[$ssRef->getName()]))
{
CLI::write('a SetupScript named '.CLI::bold($ssRef->getName()).' was already registered. Skipping...', CLI::LOG_WARN);
return;
}
self::$tables[$ssRef->getName()] = $ssRef;
// recheck temp stored dependencies
foreach (self::$tmpStore as $idx => $ts)
{
$depsOK = true;
foreach ($ts->getDependencies(true) as $d)
{
if (isset(self::$tables[$d]))
continue;
$depsOK = false;
break;
}
if ($depsOK)
{
self::$tables[$ts->getName()] = $ts;
unset(self::$tmpStore[$idx]);
}
}
}
private static function handleCLIOpts(?array &$doTbls) : bool
{
$doTbls = [];
if (CLISetup::getOpt('help') && self::$mode == self::MODE_NORMAL)
{
self::printCLIHelp();
return false;
}
// required subScripts
if ($sync = CLISetup::getOpt('sync'))
{
foreach (self::$tables as $name => &$ssRef)
if (array_intersect($sync, $ssRef->getDependencies(false)))
$doTbls[] = $name;
// recursive dependencies
foreach (self::$tables as $name => &$ssRef)
if (array_intersect($sync, $ssRef->getDependencies(true)))
$doTbls[] = $name;
if (!$doTbls)
return false;
$doTbls = array_unique($doTbls);
return true;
}
else if (is_array($_ = CLISetup::getOpt('sql')))
{
$doTbls = $_;
return true;
}
return false;
}
private static function printCLIHelp() : void
{
CLI::write();
CLI::write(' usage: php aowow --sql=<subScriptList,> [--mpqDataDir: --locales:]', -1, false);
CLI::write();
CLI::write(' Regenerates DB table content for a given subScript. Dependencies are taken into account by the triggered calls of --sync and --update', -1, false);
$lines = [['available subScripts', 'TC dependencies', 'AoWoW dependencies']];
foreach (self::$tables as $tbl => &$ssRef)
{
$aoRef = $ssRef->getDependencies(true);
$len = 0;
$first = true;
$row = [" * ".$tbl, '', ''];
if ($tc = $ssRef->getDependencies(false))
{
$len = 0;
foreach ($tc as $t)
{
$n = strlen($t) + 1;
if ($n + $len > 60)
{
// max 2 tables, no multi-row shenanigans required
if ($first && $aoRef)
$row[2] = implode(' ', $aoRef);
$lines[] = $row;
$row = ['', '', ''];
$len = $n;
$first = false;
}
else
$len += $n;
$row[1] .= $t." ";
}
}
if ($first && $aoRef)
$row[2] = implode(' ', $aoRef);
$lines[] = $row;
}
CLI::writeTable($lines);
}
public static function generate(string $tableName, array $updateIds = []) : bool
{
if (!isset(self::$tables[$tableName]))
{
CLI::write('SqlGen::generate - invalid table given', CLI::LOG_ERROR);
return false;
}
$ssRef = &self::$tables[$tableName];
CLI::write('SqlGen::generate() - filling aowow_'.$tableName.' with data');
// check for required auxiliary DBC files
if (!in_array('TrDBCcopy', class_uses($ssRef)))
foreach ($ssRef->getRequiredDBCs() as $req)
if (!CLISetup::loadDBC($req))
return false;
if ($ssRef->generate($updateIds))
{
if (method_exists($ssRef, 'applyCustomData'))
return $ssRef->applyCustomData();
return true;
}
return false;
}
public static function getMode() : int
{
return self::$mode;
}
}
?>

View File

@@ -1,132 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $command = 'achievement';
protected $tblDependencyAowow = ['icons'];
protected $tblDependencyTC = ['dbc_achievement', 'disables'];
protected $dbcSourceFiles = ['achievement_category', 'achievement', 'spellicon'];
public function generate(array $ids = []) : bool
{
/**************/
/* categories */
/**************/
CLI::write(' - resolving categories');
DB::Aowow()->query('REPLACE INTO ?_achievementcategory SELECT ac.id, IFNULL(ac.parentcategory, 0), IFNULL(ac1.parentcategory, 0)
FROM dbc_achievement_category ac LEFT JOIN dbc_achievement_category ac1 ON ac1.id = ac.parentCategory');
/************/
/* dbc data */
/************/
CLI::write(' - basic dbc data');
DB::Aowow()->query('
REPLACE INTO
?_achievement
SELECT
a.id,
2 - a.faction,
a.map,
0,
0,
a.category,
ac.parentCategory,
a.points,
a.orderInGroup,
IFNULL(i.id, 0),
a.iconId,
a.flags,
a.reqCriteriaCount,
a.refAchievement,
0,
0,
a.name_loc0, a.name_loc2, a.name_loc3, a.name_loc4, a.name_loc6, a.name_loc8,
a.description_loc0, a.description_loc2, a.description_loc3, a.description_loc4, a.description_loc6, a.description_loc8,
a.reward_loc0, a.reward_loc2, a.reward_loc3, a.reward_loc4, a.reward_loc6, a.reward_loc8
FROM
dbc_achievement a
LEFT JOIN
dbc_achievement_category ac ON ac.id = a.category
LEFT JOIN
dbc_spellicon si ON si.id = a.iconId
LEFT JOIN
?_icons i ON LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1)) = i.name
{ WHERE a.id IN (?a) }
', $ids ?: DBSIMPLE_SKIP);
/*******************/
/* serverside data */
/*******************/
CLI::write(' - serverside achievement data');
$serverAchievements = DB::World()->select('SELECT ID, IF(requiredFaction = -1, 3, IF(requiredFaction = 0, 2, 1)) AS "faction", mapID, points, flags, count, refAchievement FROM achievement_dbc{ WHERE id IN (?a)}',
$ids ?: DBSIMPLE_SKIP
);
foreach ($serverAchievements as $sa)
DB::Aowow()->query('REPLACE INTO ?_achievement (id, faction, map, points, flags, reqCriteriaCount, refAchievement, cuFlags, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8) VALUES (?d, ?d, ?d, ?d, ?d, ?d, ?d, ?d, ?, ?, ?, ?, ?, ?)',
$sa['ID'], $sa['faction'], $sa['mapID'], $sa['points'], $sa['flags'], $sa['count'], $sa['refAchievement'], CUSTOM_SERVERSIDE,
'Serverside - #'.$sa['ID'], 'Serverside - #'.$sa['ID'], 'Serverside - #'.$sa['ID'], 'Serverside - #'.$sa['ID'], 'Serverside - #'.$sa['ID'], 'Serverside - #'.$sa['ID']
);
/********************************/
/* create chain of achievements */
/********************************/
CLI::write(' - linking achievements to chain');
$chainIdx = 0;
$parents = DB::Aowow()->selectCol('SELECT a.id FROM dbc_achievement a JOIN dbc_achievement b ON b.previous = a.id WHERE a.previous = 0');
foreach ($parents as $chainId => $next)
{
$tree = [null, $next];
while ($next = DB::Aowow()->selectCell('SELECT id FROM dbc_achievement WHERE previous = ?d', $next))
$tree[] = $next;
foreach ($tree as $idx => $aId)
{
if (!$aId)
continue;
DB::Aowow()->query('UPDATE ?_achievement SET cuFlags = cuFlags | ?d, chainId = ?d, chainPos = ?d WHERE id = ?d',
$idx == 1 ? ACHIEVEMENT_CU_FIRST_SERIES : (count($tree) == $idx + 1 ? ACHIEVEMENT_CU_LAST_SERIES : 0),
$chainId + 1,
$idx,
$aId
);
}
}
/*********************/
/* applying disables */
/*********************/
CLI::write(' - disabling disabled achievements from table disables');
if ($criteria = DB::World()->selectCol('SELECT entry FROM disables WHERE sourceType = 4'))
DB::Aowow()->query('UPDATE aowow_achievement a JOIN aowow_achievementcriteria ac ON a.id = ac.refAchievementId SET a.cuFlags = ?d WHERE ac.id IN (?a)', CUSTOM_DISABLED, $criteria);
$this->reapplyCCFlags('achievement', Type::ACHIEVEMENT);
return true;
}
});
?>

View File

@@ -0,0 +1,138 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup('sql', new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $info = array(
'achievement' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Achievement from dbc and world db.']
);
protected $dbcSourceFiles = ['achievement_category', 'achievement', 'spellicon'];
protected $worldDependency = ['dbc_achievement', 'disables'];
protected $setupAfter = [['icons'], []];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_achievement');
DB::Aowow()->query('TRUNCATE ?_achievementcategory');
/**************/
/* categories */
/**************/
CLI::write('[achievement] - resolving categories');
DB::Aowow()->query('INSERT INTO ?_achievementcategory SELECT ac.id, GREATEST(ac.parentcategory, 0), GREATEST(IFNULL(ac1.parentcategory, 0), 0) FROM dbc_achievement_category ac LEFT JOIN dbc_achievement_category ac1 ON ac1.id = ac.parentCategory');
/************/
/* dbc data */
/************/
CLI::write('[achievement] - basic dbc data');
DB::Aowow()->query(
'INSERT INTO ?_achievement
SELECT a.id,
2 - a.faction,
a.map,
0,
0,
a.category,
ac.parentCategory,
a.points,
a.orderInGroup,
IFNULL(i.id, 0),
a.iconId,
a.flags,
a.reqCriteriaCount,
a.refAchievement,
0,
0,
a.name_loc0, a.name_loc2, a.name_loc3, a.name_loc4, a.name_loc6, a.name_loc8,
a.description_loc0, a.description_loc2, a.description_loc3, a.description_loc4, a.description_loc6, a.description_loc8,
a.reward_loc0, a.reward_loc2, a.reward_loc3, a.reward_loc4, a.reward_loc6, a.reward_loc8
FROM dbc_achievement a
LEFT JOIN dbc_achievement_category ac ON ac.id = a.category
LEFT JOIN dbc_spellicon si ON si.id = a.iconId
LEFT JOIN ?_icons i ON LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1)) = i.name
{ WHERE a.id IN (?a) }',
$ids ?: DBSIMPLE_SKIP
);
/*******************/
/* serverside data */
/*******************/
CLI::write('[achievement] - serverside achievement data');
$serverAchievements = DB::World()->select('SELECT ID AS "id", IF(requiredFaction = -1, 3, IF(requiredFaction = 0, 2, 1)) AS "faction", mapID AS "map", points, flags, count AS "reqCriteriaCount", refAchievement FROM achievement_dbc{ WHERE id IN (?a)}',
$ids ?: DBSIMPLE_SKIP
);
foreach ($serverAchievements as &$sa)
{
$sa['cuFlags'] = CUSTOM_SERVERSIDE;
foreach (Util::$localeStrings as $i => $_)
if ($_)
$sa['name_loc'.$i] = 'Serverside - #'.$sa['id'];
}
unset($sa);
foreach ($serverAchievements as $sa)
DB::Aowow()->query('INSERT INTO ?_achievement (?#) VALUES (?a)', array_keys($sa), array_values($sa));
/********************************/
/* create chain of achievements */
/********************************/
CLI::write('[achievement] - linking achievements to chain');
$parents = DB::Aowow()->selectCol('SELECT a.id FROM dbc_achievement a JOIN dbc_achievement b ON b.previous = a.id WHERE a.previous = 0');
foreach ($parents as $chainId => $next)
{
$tree = [null, $next];
while ($next = DB::Aowow()->selectCell('SELECT id FROM dbc_achievement WHERE previous = ?d', $next))
$tree[] = $next;
foreach ($tree as $idx => $aId)
{
if (!$aId)
continue;
DB::Aowow()->query('UPDATE ?_achievement SET cuFlags = cuFlags | ?d, chainId = ?d, chainPos = ?d WHERE id = ?d',
$idx == 1 ? ACHIEVEMENT_CU_FIRST_SERIES : (count($tree) == $idx + 1 ? ACHIEVEMENT_CU_LAST_SERIES : 0),
$chainId + 1,
$idx,
$aId
);
}
}
/*********************/
/* applying disables */
/*********************/
CLI::write('[achievement] - disabling disabled achievements from table disables');
if ($criteria = DB::World()->selectCol('SELECT entry FROM disables WHERE sourceType = 4'))
DB::Aowow()->query('UPDATE ?_achievement a JOIN ?_achievementcriteria ac ON a.id = ac.refAchievementId SET a.cuFlags = ?d WHERE ac.id IN (?a)', CUSTOM_DISABLED, $criteria);
$this->reapplyCCFlags('achievement', Type::ACHIEVEMENT);
return true;
}
});
?>

View File

@@ -1,18 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrDBCcopy;
protected $command = 'achievementcriteria';
protected $dbcSourceFiles = ['achievement_criteria'];
});
?>

View File

@@ -0,0 +1,18 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup('sql', new class extends SetupScript
{
use TrDBCcopy;
protected $command = 'achievementcriteria';
protected $dbcSourceFiles = ['achievement_criteria'];
});
?>

View File

@@ -7,12 +7,15 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup('sql', new class extends SetupScript
{ {
protected $command = 'areatrigger'; protected $info = array(
'areatrigger' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Areatrigger from dbc and world db.']
);
protected $tblDependencyTC = ['areatrigger_involvedrelation', 'areatrigger_scripts', 'areatrigger_tavern', 'areatrigger_teleport', 'quest_template', 'quest_template_addon']; protected $dbcSourceFiles = ['areatrigger'];
protected $dbcSourceFiles = ['areatrigger', 'worldmaparea', 'dungeonmap']; protected $worldDependency = ['areatrigger_involvedrelation', 'areatrigger_scripts', 'areatrigger_tavern', 'areatrigger_teleport', 'quest_template', 'quest_template_addon'];
protected $setupAfter = [['dungeonmap', 'worldmaparea'], []];
public function generate(array $ids = []) : bool public function generate(array $ids = []) : bool
{ {
@@ -25,22 +28,22 @@ SqlGen::register(new class extends SetupScript
*/ */
// 1: Taverns // 1: Taverns
CLI::write(' - fetching taverns'); CLI::write('[areatrigger] - fetching taverns');
$addData = DB::World()->select('SELECT `id` AS ARRAY_KEY, `name`, ?d AS `type` FROM areatrigger_tavern', AT_TYPE_TAVERN); $addData = DB::World()->select('SELECT `id` AS ARRAY_KEY, `name`, ?d AS `type` FROM areatrigger_tavern', AT_TYPE_TAVERN);
foreach ($addData as $id => $ad) foreach ($addData as $id => $ad)
DB::Aowow()->query('UPDATE ?_areatrigger SET ?a WHERE `id` = ?d', $ad, $id); DB::Aowow()->query('UPDATE ?_areatrigger SET ?a WHERE `id` = ?d', $ad, $id);
// 2: Teleporter + teleporting 4: Smart Trigger // 2: Teleporter + teleporting 4: Smart Trigger
CLI::write(' - calculation teleporter coordinates'); CLI::write('[areatrigger] - calculation teleporter coordinates');
$addData = DB::World()->select( $addData = DB::World()->select(
'SELECT `ID` AS ARRAY_KEY, `Name` AS `name`, `target_map` AS `map`, `target_position_x` AS `posY`, `target_position_y` AS `posX`, `target_orientation` AS `orientation` 'SELECT ID AS ARRAY_KEY, Name AS `name`, target_map AS `map`, target_position_x AS `posY`, target_position_y AS `posX`, target_orientation AS `orientation`
FROM areatrigger_teleport FROM areatrigger_teleport
UNION UNION
SELECT `entryorguid` AS ARRAY_KEY, "TBD" AS `name`, `action_param1` AS `map`, `target_x` AS `posY`, `target_y` AS `posX`, `target_o` AS `orientation` SELECT entryorguid AS ARRAY_KEY, "TBD" AS `name`, action_param1 AS `map`, target_x AS `posY`, target_y AS `posX`, target_o AS `orientation`
FROM smart_scripts FROM smart_scripts
WHERE `source_type` = 2 AND `action_type` = 62' WHERE source_type = 2 AND action_type = 62'
); );
foreach ($addData as $id => $ad) foreach ($addData as $id => $ad)
@@ -48,7 +51,7 @@ SqlGen::register(new class extends SetupScript
$points = Game::worldPosToZonePos($ad['map'], $ad['posX'], $ad['posY']); $points = Game::worldPosToZonePos($ad['map'], $ad['posX'], $ad['posY']);
if (!$points) if (!$points)
{ {
CLI::write(' * AT '.$id.' teleporter endpoint '.CLI::bold($ad['name']).' could not be matched to displayable area [M:'.$ad['map'].'; X:'.$ad['posY'].'; Y:'.$ad['posX'].']', CLI::LOG_WARN); CLI::write('[areatrigger] '.str_pad('['.$id.']', 8).' teleporter endpoint '.CLI::bold($ad['name']).' could not be matched to displayable area [M:'.$ad['map'].'; X:'.$ad['posY'].'; Y:'.$ad['posX'].']', CLI::LOG_WARN);
DB::Aowow()->query('UPDATE ?_areatrigger SET `name` = ?, `type` = ?d WHERE `id` = ?d', $ad['name'], AT_TYPE_TELEPORT, $id); DB::Aowow()->query('UPDATE ?_areatrigger SET `name` = ?, `type` = ?d WHERE `id` = ?d', $ad['name'], AT_TYPE_TELEPORT, $id);
continue; continue;
} }
@@ -67,19 +70,19 @@ SqlGen::register(new class extends SetupScript
} }
// 3: Quest Objectives // 3: Quest Objectives
CLI::write(' - satisfying quest objectives'); CLI::write('[areatrigger] - satisfying quest objectives');
$addData = DB::World()->select('SELECT atir.`id` AS ARRAY_KEY, qt.`ID` AS `quest`, NULLIF(qt.AreaDescription, "") AS `name`, qta.`SpecialFlags` FROM quest_template qt LEFT JOIN quest_template_addon qta ON qta.`ID` = qt.`ID` JOIN areatrigger_involvedrelation atir ON atir.`quest` = qt.`ID`'); $addData = DB::World()->select('SELECT atir.`id` AS ARRAY_KEY, `qt`.ID AS `quest`, NULLIF(qt.`AreaDescription`, "") AS `name`, qta.`SpecialFlags` FROM quest_template qt LEFT JOIN quest_template_addon qta ON qta.`ID` = qt.`ID` JOIN areatrigger_involvedrelation atir ON atir.`quest` = qt.`ID`');
foreach ($addData as $id => $ad) foreach ($addData as $id => $ad)
{ {
if (!($ad['SpecialFlags'] & QUEST_FLAG_SPECIAL_EXT_COMPLETE)) if (!($ad['SpecialFlags'] & QUEST_FLAG_SPECIAL_EXT_COMPLETE))
CLI::write(' * Areatrigger '.CLI::bold($id).' is involved in quest '.CLI::bold($ad['quest']).', but quest is not flagged for external completion (SpecialFlags & '.Util::asHex(QUEST_FLAG_SPECIAL_EXT_COMPLETE).')', CLI::LOG_WARN); CLI::write('[areatrigger] '.str_pad('['.$id.']', 8).' is involved in quest '.CLI::bold($ad['quest']).', but quest is not flagged for external completion (SpecialFlags & '.Util::asHex(QUEST_FLAG_SPECIAL_EXT_COMPLETE).')', CLI::LOG_WARN);
DB::Aowow()->query('UPDATE ?_areatrigger SET `name` = ?, `type` = ?d, `quest` = ?d WHERE `id` = ?d', $ad['name'], AT_TYPE_OBJECTIVE, $ad['quest'], $id); DB::Aowow()->query('UPDATE ?_areatrigger SET `name` = ?, type = ?d, `quest` = ?d WHERE `id` = ?d', $ad['name'], AT_TYPE_OBJECTIVE, $ad['quest'], $id);
} }
// 4/5 Scripted // 4/5 Scripted
CLI::write(' - assigning scripts'); CLI::write('[areatrigger] - assigning scripts');
$addData = DB::World()->select('SELECT `entry` AS ARRAY_KEY, IF(`ScriptName` = "SmartTrigger", NULL, `ScriptName`) AS `name`, IF(`ScriptName` = "SmartTrigger", 4, 5) AS `type` FROM areatrigger_scripts'); $addData = DB::World()->select('SELECT `entry` AS ARRAY_KEY, IF(`ScriptName` = "SmartTrigger", NULL, `ScriptName`) AS `name`, IF(`ScriptName` = "SmartTrigger", 4, 5) AS `type` FROM areatrigger_scripts');
foreach ($addData as $id => $ad) foreach ($addData as $id => $ad)

View File

@@ -7,16 +7,20 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
use TrCustomData; // import custom data from DB use TrCustomData; // import custom data from DB
protected $command = 'classes'; protected $info = array(
'classes' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: PlayerClass from dbc.']
);
protected $dbcSourceFiles = ['spell', 'charbaseinfo', 'skillraceclassinfo', 'skilllineability', 'chrclasses']; protected $dbcSourceFiles = ['spell', 'charbaseinfo', 'skillraceclassinfo', 'skilllineability', 'chrclasses'];
public function generate(array $ids = []) : bool public function generate(array $ids = []) : bool
{ {
DB::Aowow()->query('TRUNCATE ?_classes');
$classes = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_chrclasses'); $classes = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_chrclasses');
// add raceMask // add raceMask
@@ -36,7 +40,7 @@ SqlGen::register(new class extends SetupScript
} }
foreach ($classes as $cl) foreach ($classes as $cl)
DB::Aowow()->query('REPLACE INTO ?_classes (?#) VALUES (?a)', array_keys($cl), array_values($cl)); DB::Aowow()->query('INSERT INTO ?_classes (?#) VALUES (?a)', array_keys($cl), array_values($cl));
$this->reapplyCCFlags('classes', Type::CHR_CLASS); $this->reapplyCCFlags('classes', Type::CHR_CLASS);

View File

@@ -1,185 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
protected $command = 'creature';
protected $tblDependencyTC = ['creature_template', 'creature_template_locale', 'creature_template_resistance', 'creature_template_spell', 'creature_classlevelstats', 'instance_encounters'];
protected $dbcSourceFiles = ['creaturedisplayinfo', 'creaturedisplayinfoextra'];
public function generate(array $ids = []) : bool
{
$baseQuery = '
SELECT
ct.entry,
IF(ie.creditEntry IS NULL, 0, ?d) AS cuFlags, -- cuFlags
difficulty_entry_1, difficulty_entry_2, difficulty_entry_3,
KillCredit1, KillCredit2,
modelid1, modelid2, modelid3, modelid4,
"" AS textureString, -- textureString
0 AS modelId, -- modelId
0 AS humanoid, -- uses creaturedisplayinfoextra
"" AS iconString, -- iconString
ct.name, IFNULL(ctl2.`Name`, "") AS n2, IFNULL(ctl3.`Name`, "") AS n3, IFNULL(ctl4.`Name`, "") AS n4, IFNULL(ctl6.`Name`, "") AS n6, IFNULL(ctl8.`Name`, "") AS n8,
subname, IFNULL(ctl2.`Title`, "") AS t2, IFNULL(ctl3.`Title`, "") AS t3, IFNULL(ctl4.`Title`, "") AS t4, IFNULL(ctl6.`Title`, "") AS t6, IFNULL(ctl8.`Title`, "") AS t8,
minLevel, maxLevel,
exp,
faction,
npcflag,
IF(`rank` > 4, 0, `rank`),
dmgSchool,
DamageModifier,
BaseAttackTime,
RangeAttackTime,
BaseVariance,
RangeVariance,
unit_class,
unit_flags, unit_flags2, dynamicflags,
family,
IFNULL(t.Type, 0),
IFNULL(t.Requirement, 0),
(CASE ct.exp WHEN 0 THEN min.damage_base WHEN 1 THEN min.damage_exp1 ELSE min.damage_exp2 END) AS dmgMin,
(CASE ct.exp WHEN 0 THEN max.damage_base WHEN 1 THEN max.damage_exp1 ELSE max.damage_exp2 END) AS dmgMax,
min.attackpower AS mleAtkPwrMin,
max.attackpower AS mleAtkPwrMax,
min.rangedattackpower AS rmgAtkPwrMin,
max.rangedattackpower AS rmgAtkPwrMax,
ct.type,
type_flags,
lootid, pickpocketloot, skinloot,
IFNULL(cts0.Spell, 0), IFNULL(cts1.Spell, 0), IFNULL(cts2.Spell, 0), IFNULL(cts3.Spell, 0), IFNULL(cts4.Spell, 0), IFNULL(cts5.Spell, 0), IFNULL(cts6.Spell, 0), IFNULL(cts7.Spell, 0),
PetSpellDataId,
VehicleId,
mingold, maxgold,
AIName,
(CASE ct.exp WHEN 0 THEN min.basehp0 WHEN 1 THEN min.basehp1 ELSE min.basehp2 END) * ct.HealthModifier AS healthMin,
(CASE ct.exp WHEN 0 THEN max.basehp0 WHEN 1 THEN max.basehp1 ELSE max.basehp2 END) * ct.HealthModifier AS healthMax,
min.basemana * ct.ManaModifier AS manaMin,
max.basemana * ct.ManaModifier AS manaMax,
min.basearmor * ct.ArmorModifier AS armorMin,
max.basearmor * ct.ArmorModifier AS armorMax,
IFNULL(ctr1.Resistance, 0), IFNULL(ctr2.Resistance, 0), IFNULL(ctr3.Resistance, 0), IFNULL(ctr4.Resistance, 0), IFNULL(ctr5.Resistance, 0), IFNULL(ctr6.Resistance, 0),
RacialLeader,
mechanic_immune_mask,
flags_extra,
ScriptName
FROM
creature_template ct
JOIN
creature_classlevelstats min ON ct.unit_class = min.class AND ct.minlevel = min.level
JOIN
creature_classlevelstats max ON ct.unit_class = max.class AND ct.maxlevel = max.level
LEFT JOIN
creature_default_trainer cdt ON cdt.CreatureId = ct.entry
LEFT JOIN
trainer t ON t.Id = cdt.TrainerId
LEFT JOIN
creature_template_locale ctl2 ON ct.entry = ctl2.entry AND ctl2.`locale` = "frFR"
LEFT JOIN
creature_template_locale ctl3 ON ct.entry = ctl3.entry AND ctl3.`locale` = "deDE"
LEFT JOIN
creature_template_locale ctl4 ON ct.entry = ctl4.entry AND ctl4.`locale` = "zhCN"
LEFT JOIN
creature_template_locale ctl6 ON ct.entry = ctl6.entry AND ctl6.`locale` = "esES"
LEFT JOIN
creature_template_locale ctl8 ON ct.entry = ctl8.entry AND ctl8.`locale` = "ruRU"
LEFT JOIN
(SELECT creditEntry FROM instance_encounters WHERE creditType = 0 GROUP BY creditEntry) ie ON ie.creditEntry = ct.entry
LEFT JOIN
creature_template_spell cts0 ON ct.entry = cts0.CreatureID AND cts0.Index = 0
LEFT JOIN
creature_template_spell cts1 ON ct.entry = cts1.CreatureID AND cts1.Index = 1
LEFT JOIN
creature_template_spell cts2 ON ct.entry = cts2.CreatureID AND cts2.Index = 2
LEFT JOIN
creature_template_spell cts3 ON ct.entry = cts3.CreatureID AND cts3.Index = 3
LEFT JOIN
creature_template_spell cts4 ON ct.entry = cts4.CreatureID AND cts4.Index = 4
LEFT JOIN
creature_template_spell cts5 ON ct.entry = cts5.CreatureID AND cts5.Index = 5
LEFT JOIN
creature_template_spell cts6 ON ct.entry = cts6.CreatureID AND cts6.Index = 6
LEFT JOIN
creature_template_spell cts7 ON ct.entry = cts7.CreatureID AND cts7.Index = 7
LEFT JOIN
creature_template_resistance ctr1 ON ct.entry = ctr1.CreatureID AND ctr1.School = 1
LEFT JOIN
creature_template_resistance ctr2 ON ct.entry = ctr2.CreatureID AND ctr2.School = 2
LEFT JOIN
creature_template_resistance ctr3 ON ct.entry = ctr3.CreatureID AND ctr3.School = 3
LEFT JOIN
creature_template_resistance ctr4 ON ct.entry = ctr4.CreatureID AND ctr4.School = 4
LEFT JOIN
creature_template_resistance ctr5 ON ct.entry = ctr5.CreatureID AND ctr5.School = 5
LEFT JOIN
creature_template_resistance ctr6 ON ct.entry = ctr6.CreatureID AND ctr6.School = 6
{
WHERE
ct.entry IN (?a)
}
LIMIT
?d, ?d';
$dummyQuery = '
UPDATE
?_creature a
JOIN
(
SELECT b.difficultyEntry1 AS dummy FROM ?_creature b UNION
SELECT c.difficultyEntry2 AS dummy FROM ?_creature c UNION
SELECT d.difficultyEntry3 AS dummy FROM ?_creature d
) j
SET
a.cuFlags = a.cuFlags | ?d
WHERE
a.id = j.dummy';
$displayInfoQuery = '
UPDATE
?_creature c
JOIN
dbc_creaturedisplayinfo cdi ON c.displayId1 = cdi.id
LEFT JOIN
dbc_creaturedisplayinfoextra cdie ON cdi.extraInfoId = cdie.id
SET
c.textureString = IFNULL(cdie.textureString, cdi.skin1),
c.modelId = cdi.modelId,
c.iconString = cdi.iconString,
c.humanoid = IF(cdie.id IS NULL, 0, 1)';
$i = 0;
DB::Aowow()->query('TRUNCATE ?_creature');
while ($npcs = DB::World()->select($baseQuery, NPC_CU_INSTANCE_BOSS, $ids ?: DBSIMPLE_SKIP, SqlGen::$sqlBatchSize * $i, SqlGen::$sqlBatchSize))
{
CLI::write(' * batch #' . ++$i . ' (' . count($npcs) . ')', CLI::LOG_BLANK, true, true);
foreach ($npcs as $npc)
DB::Aowow()->query('INSERT INTO ?_creature VALUES (?a)', array_values($npc));
}
// apply "textureString", "modelId" and "iconSring"
DB::Aowow()->query($displayInfoQuery);
// apply cuFlag: difficultyDummy
DB::Aowow()->query($dummyQuery, NPC_CU_DIFFICULTY_DUMMY | CUSTOM_EXCLUDE_FOR_LISTVIEW);
// apply cuFlag: excludeFromListview [for trigger-creatures]
DB::Aowow()->query('UPDATE ?_creature SET cuFlags = cuFlags | ?d WHERE flagsExtra & ?d', CUSTOM_EXCLUDE_FOR_LISTVIEW, 0x80);
// apply cuFlag: exCludeFromListview [for nameparts indicating internal usage]
DB::Aowow()->query('UPDATE ?_creature SET cuFlags = cuFlags | ?d WHERE name_loc0 LIKE "%[%" OR name_loc0 LIKE "%(%" OR name_loc0 LIKE "%visual%" OR name_loc0 LIKE "%trigger%" OR name_loc0 LIKE "%credit%" OR name_loc0 LIKE "%marker%"', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('creature', Type::NPC);
return true;
}
});
?>

View File

@@ -0,0 +1,146 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
protected $info = array(
'creature' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: NPC from dbc and world db.']
);
protected $dbcSourceFiles = ['creaturedisplayinfo', 'creaturedisplayinfoextra'];
protected $worldDependency = ['creature_template', 'creature_template_locale', 'creature_template_resistance', 'creature_template_spell', 'creature_classlevelstats', 'creature_default_trainer', 'trainer', 'instance_encounters'];
public function generate(array $ids = []) : bool
{
$baseQuery =
'SELECT ct.entry,
IF(ie.creditEntry IS NULL, 0, ?d) AS cuFlags,
difficulty_entry_1, difficulty_entry_2, difficulty_entry_3,
KillCredit1, KillCredit2,
modelid1, modelid2, modelid3, modelid4,
"" AS textureString,
0 AS modelId,
0 AS humanoid, -- uses creaturedisplayinfoextra
"" AS iconString,
ct.name, IFNULL(ctl2.`Name`, "") AS n2, IFNULL(ctl3.`Name`, "") AS n3, IFNULL(ctl4.`Name`, "") AS n4, IFNULL(ctl6.`Name`, "") AS n6, IFNULL(ctl8.`Name`, "") AS n8,
subname, IFNULL(ctl2.`Title`, "") AS t2, IFNULL(ctl3.`Title`, "") AS t3, IFNULL(ctl4.`Title`, "") AS t4, IFNULL(ctl6.`Title`, "") AS t6, IFNULL(ctl8.`Title`, "") AS t8,
minLevel, maxLevel,
exp,
faction,
npcflag,
IF(`rank` > 4, 0, `rank`),
dmgSchool,
DamageModifier,
BaseAttackTime,
RangeAttackTime,
BaseVariance,
RangeVariance,
unit_class,
unit_flags, unit_flags2, dynamicflags,
family,
IFNULL(t.Type, 0),
IFNULL(t.Requirement, 0),
(CASE ct.exp WHEN 0 THEN min.damage_base WHEN 1 THEN min.damage_exp1 ELSE min.damage_exp2 END) AS dmgMin,
(CASE ct.exp WHEN 0 THEN max.damage_base WHEN 1 THEN max.damage_exp1 ELSE max.damage_exp2 END) AS dmgMax,
min.attackpower AS mleAtkPwrMin,
max.attackpower AS mleAtkPwrMax,
min.rangedattackpower AS rmgAtkPwrMin,
max.rangedattackpower AS rmgAtkPwrMax,
ct.type,
type_flags,
lootid, pickpocketloot, skinloot,
IFNULL(cts0.Spell, 0), IFNULL(cts1.Spell, 0), IFNULL(cts2.Spell, 0), IFNULL(cts3.Spell, 0), IFNULL(cts4.Spell, 0), IFNULL(cts5.Spell, 0), IFNULL(cts6.Spell, 0), IFNULL(cts7.Spell, 0),
PetSpellDataId,
VehicleId,
mingold, maxgold,
AIName,
(CASE ct.exp WHEN 0 THEN min.basehp0 WHEN 1 THEN min.basehp1 ELSE min.basehp2 END) * ct.HealthModifier AS healthMin,
(CASE ct.exp WHEN 0 THEN max.basehp0 WHEN 1 THEN max.basehp1 ELSE max.basehp2 END) * ct.HealthModifier AS healthMax,
min.basemana * ct.ManaModifier AS manaMin,
max.basemana * ct.ManaModifier AS manaMax,
min.basearmor * ct.ArmorModifier AS armorMin,
max.basearmor * ct.ArmorModifier AS armorMax,
IFNULL(ctr1.Resistance, 0), IFNULL(ctr2.Resistance, 0), IFNULL(ctr3.Resistance, 0), IFNULL(ctr4.Resistance, 0), IFNULL(ctr5.Resistance, 0), IFNULL(ctr6.Resistance, 0),
RacialLeader,
mechanic_immune_mask,
flags_extra,
ScriptName
FROM creature_template ct
JOIN creature_classlevelstats min ON ct.unit_class = min.class AND ct.minlevel = min.level
JOIN creature_classlevelstats max ON ct.unit_class = max.class AND ct.maxlevel = max.level
LEFT JOIN creature_default_trainer cdt ON cdt.CreatureId = ct.entry
LEFT JOIN trainer t ON t.Id = cdt.TrainerId
LEFT JOIN creature_template_locale ctl2 ON ct.entry = ctl2.entry AND ctl2.`locale` = "frFR"
LEFT JOIN creature_template_locale ctl3 ON ct.entry = ctl3.entry AND ctl3.`locale` = "deDE"
LEFT JOIN creature_template_locale ctl4 ON ct.entry = ctl4.entry AND ctl4.`locale` = "zhCN"
LEFT JOIN creature_template_locale ctl6 ON ct.entry = ctl6.entry AND ctl6.`locale` = "esES"
LEFT JOIN creature_template_locale ctl8 ON ct.entry = ctl8.entry AND ctl8.`locale` = "ruRU"
LEFT JOIN (SELECT creditEntry FROM instance_encounters WHERE creditType = 0 GROUP BY creditEntry) ie ON ie.creditEntry = ct.entry
LEFT JOIN creature_template_spell cts0 ON ct.entry = cts0.CreatureID AND cts0.Index = 0
LEFT JOIN creature_template_spell cts1 ON ct.entry = cts1.CreatureID AND cts1.Index = 1
LEFT JOIN creature_template_spell cts2 ON ct.entry = cts2.CreatureID AND cts2.Index = 2
LEFT JOIN creature_template_spell cts3 ON ct.entry = cts3.CreatureID AND cts3.Index = 3
LEFT JOIN creature_template_spell cts4 ON ct.entry = cts4.CreatureID AND cts4.Index = 4
LEFT JOIN creature_template_spell cts5 ON ct.entry = cts5.CreatureID AND cts5.Index = 5
LEFT JOIN creature_template_spell cts6 ON ct.entry = cts6.CreatureID AND cts6.Index = 6
LEFT JOIN creature_template_spell cts7 ON ct.entry = cts7.CreatureID AND cts7.Index = 7
LEFT JOIN creature_template_resistance ctr1 ON ct.entry = ctr1.CreatureID AND ctr1.School = 1
LEFT JOIN creature_template_resistance ctr2 ON ct.entry = ctr2.CreatureID AND ctr2.School = 2
LEFT JOIN creature_template_resistance ctr3 ON ct.entry = ctr3.CreatureID AND ctr3.School = 3
LEFT JOIN creature_template_resistance ctr4 ON ct.entry = ctr4.CreatureID AND ctr4.School = 4
LEFT JOIN creature_template_resistance ctr5 ON ct.entry = ctr5.CreatureID AND ctr5.School = 5
LEFT JOIN creature_template_resistance ctr6 ON ct.entry = ctr6.CreatureID AND ctr6.School = 6
{ WHERE ct.entry IN (?a) }
LIMIT ?d, ?d';
$i = 0;
DB::Aowow()->query('TRUNCATE ?_creature');
while ($npcs = DB::World()->select($baseQuery, NPC_CU_INSTANCE_BOSS, $ids ?: DBSIMPLE_SKIP, CLISetup::SQL_BATCH * $i, CLISetup::SQL_BATCH))
{
CLI::write(' * batch #' . ++$i . ' (' . count($npcs) . ')', CLI::LOG_BLANK, true, true);
foreach ($npcs as $npc)
DB::Aowow()->query('INSERT INTO ?_creature VALUES (?a)', array_values($npc));
}
// apply "textureString", "modelId" and "iconSring"
DB::Aowow()->query(
'UPDATE ?_creature c
JOIN dbc_creaturedisplayinfo cdi ON c.displayId1 = cdi.id
LEFT JOIN dbc_creaturedisplayinfoextra cdie ON cdi.extraInfoId = cdie.id
SET c.textureString = IFNULL(cdie.textureString, cdi.skin1),
c.modelId = cdi.modelId,
c.iconString = cdi.iconString,
c.humanoid = IF(cdie.id IS NULL, 0, 1)'
);
// apply cuFlag: difficultyDummy
DB::Aowow()->query(
'UPDATE ?_creature a
JOIN (SELECT b.difficultyEntry1 AS dummy FROM ?_creature b UNION
SELECT c.difficultyEntry2 AS dummy FROM ?_creature c UNION
SELECT d.difficultyEntry3 AS dummy FROM ?_creature d) j
SET a.cuFlags = a.cuFlags | ?d
WHERE a.id = j.dummy',
NPC_CU_DIFFICULTY_DUMMY | CUSTOM_EXCLUDE_FOR_LISTVIEW
);
// apply cuFlag: excludeFromListview [for trigger-creatures]
DB::Aowow()->query('UPDATE ?_creature SET cuFlags = cuFlags | ?d WHERE flagsExtra & ?d', CUSTOM_EXCLUDE_FOR_LISTVIEW, 0x80);
// apply cuFlag: exCludeFromListview [for nameparts indicating internal usage]
DB::Aowow()->query('UPDATE ?_creature SET cuFlags = cuFlags | ?d WHERE name_loc0 LIKE "%[%" OR name_loc0 LIKE "%(%" OR name_loc0 LIKE "%visual%" OR name_loc0 LIKE "%trigger%" OR name_loc0 LIKE "%credit%" OR name_loc0 LIKE "%marker%"', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('creature', Type::NPC);
return true;
}
});
?>

View File

@@ -1,84 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $command = 'currencies';
protected $tblDependencyAowow = ['icons'];
protected $tblDependencyTC = ['item_template', 'item_template_locale'];
protected $dbcSourceFiles = ['itemdisplayinfo', 'currencytypes'];
public function generate(array $ids = []) : bool
{
if (!$ids)
DB::Aowow()->query('REPLACE INTO ?_currencies (`id`, `category`, `itemId`) SELECT `id`, LEAST(`category`, 41), `itemId` FROM dbc_currencytypes');
$moneyItems = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `itemId` FROM dbc_currencytypes{ WHERE `id` IN (?a)}', $ids ?: DBSIMPLE_SKIP);
// apply names & cap
$moneyNames = DB::World()->select('
SELECT
it.entry AS ARRAY_KEY,
it.name AS name_loc0, IFNULL(itl2.Name, "") AS name_loc2, IFNULL(itl3.Name, "") AS name_loc3, IFNULL(itl4.Name, "") AS name_loc4, IFNULL(itl6.Name, "") AS name_loc6, IFNULL(itl8.Name, "") AS name_loc8,
it.maxCount AS cap
FROM
item_template it
LEFT JOIN
item_template_locale itl2 ON it.entry = itl2.ID AND itl2.locale = "frFR"
LEFT JOIN
item_template_locale itl3 ON it.entry = itl3.ID AND itl3.locale = "deDE"
LEFT JOIN
item_template_locale itl4 ON it.entry = itl4.ID AND itl4.locale = "zhCN"
LEFT JOIN
item_template_locale itl6 ON it.entry = itl6.ID AND itl6.locale = "esES"
LEFT JOIN
item_template_locale itl8 ON it.entry = itl8.ID AND itl8.locale = "ruRU"
WHERE
it.entry IN (?a)',
$moneyItems);
foreach ($moneyItems as $cId => $itemId)
{
if (!empty($moneyNames[$itemId]))
$strings = $moneyNames[$itemId];
else
{
CLI::write('item #'.$itemId.' referenced by currency #'.$cId.' not in item_template', CLI::LOG_WARN);
$strings = ['name_loc0' => 'Item #'.$itemId.' not in DB', 'iconId' => 0, 'cuFlags' => CUSTOM_EXCLUDE_FOR_LISTVIEW, 'category' => 3];
}
DB::Aowow()->query('UPDATE ?_currencies SET ?a WHERE itemId = ?d', $strings, $itemId);
}
// apply icons
$displayIds = DB::World()->selectCol('SELECT entry AS ARRAY_KEY, displayid FROM item_template WHERE entry IN (?a)', $moneyItems);
foreach ($displayIds as $itemId => $iconId)
DB::Aowow()->query('
UPDATE
?_currencies c,
?_icons i,
dbc_itemdisplayinfo idi
SET
c.iconId = i.id
WHERE
i.name = LOWER(idi.inventoryIcon1) AND
idi.id = ?d AND
c.itemId = ?d
', $iconId, $itemId);
$this->reapplyCCFlags('currencies', Type::CURRENCY);
return true;
}
});
?>

View File

@@ -0,0 +1,73 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $info = array(
'currencies' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Currency from dbc and world db.']
);
protected $dbcSourceFiles = ['itemdisplayinfo', 'currencytypes'];
protected $worldDependency = ['item_template', 'item_template_locale'];
protected $setupAfter = [['icons'], []];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_currencies');
DB::Aowow()->query('INSERT INTO ?_currencies (`id`, `category`, `itemId`) SELECT `id`, LEAST(`category`, 41), `itemId` FROM dbc_currencytypes');
$moneyItems = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `itemId` FROM dbc_currencytypes{ WHERE `id` IN (?a)}', $ids ?: DBSIMPLE_SKIP);
// apply names & cap
$moneyNames = DB::World()->select(
'SELECT it.`entry` AS ARRAY_KEY,
it.`name` AS `name_loc0`, IFNULL(itl2.`Name`, "") AS `name_loc2`, IFNULL(itl3.`Name`, "") AS `name_loc3`, IFNULL(itl4.`Name`, "") AS `name_loc4`, IFNULL(itl6.`Name`, "") AS `name_loc6`, IFNULL(itl8.`Name`, "") AS `name_loc8`,
it.`maxCount` AS `cap`
FROM item_template it
LEFT JOIN item_template_locale itl2 ON it.entry = itl2.ID AND itl2.locale = "frFR"
LEFT JOIN item_template_locale itl3 ON it.entry = itl3.ID AND itl3.locale = "deDE"
LEFT JOIN item_template_locale itl4 ON it.entry = itl4.ID AND itl4.locale = "zhCN"
LEFT JOIN item_template_locale itl6 ON it.entry = itl6.ID AND itl6.locale = "esES"
LEFT JOIN item_template_locale itl8 ON it.entry = itl8.ID AND itl8.locale = "ruRU"
WHERE it.entry IN (?a)',
$moneyItems
);
foreach ($moneyItems as $cId => $itemId)
{
if (!empty($moneyNames[$itemId]))
$strings = $moneyNames[$itemId];
else
{
CLI::write('[currencies] '.str_pad('['.$cId.']', 6).' referenced item '.CLI::bold($itemId).' is not in item_template', CLI::LOG_WARN);
$strings = ['name_loc0' => 'Item #'.$itemId.' not in DB', 'iconId' => 0, 'cuFlags' => CUSTOM_EXCLUDE_FOR_LISTVIEW, 'category' => 3];
}
DB::Aowow()->query('UPDATE ?_currencies SET ?a WHERE itemId = ?d', $strings, $itemId);
}
// apply icons
$displayIds = DB::World()->selectCol('SELECT `entry` AS ARRAY_KEY, `displayid` FROM item_template WHERE `entry` IN (?a)', $moneyItems);
foreach ($displayIds as $itemId => $iconId)
DB::Aowow()->query(
'UPDATE ?_currencies c, ?_icons i, dbc_itemdisplayinfo idi
SET c.`iconId` = i.`id`
WHERE i.`name` = LOWER(idi.`inventoryIcon1`) AND idi.`id` = ?d AND c.`itemId` = ?d',
$iconId, $itemId
);
$this->reapplyCCFlags('currencies', Type::CURRENCY);
return true;
}
});
?>

View File

@@ -7,18 +7,21 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
protected $command = 'declinedwords'; protected $info = array(
'declinedwords' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Enchantment from dbc and world db.']
);
protected $dbcSourceFiles = ['declinedword', 'declinedwordcases']; protected $dbcSourceFiles = ['declinedword', 'declinedwordcases'];
public function generate() : bool public function generate(array $ids = []) : bool
{ {
CLI::write('SqlGen::generate() - copying declinedword.dbc into aowow_declinedword'); CLI::write('[declinedwords] - copying declinedword.dbc into aowow_declinedword');
DB::Aowow()->query('TRUNCATE ?_declinedword'); DB::Aowow()->query('TRUNCATE ?_declinedword');
DB::Aowow()->query('INSERT INTO ?_declinedword SELECT * FROM dbc_declinedword'); DB::Aowow()->query('INSERT INTO ?_declinedword SELECT * FROM dbc_declinedword');
CLI::write('SqlGen::generate() - copying declinedwordcases.dbc into aowow_declinedwordcases'); CLI::write('[declinedwords] - copying declinedwordcases.dbc into aowow_declinedwordcases');
DB::Aowow()->query('TRUNCATE ?_declinedwordcases'); DB::Aowow()->query('TRUNCATE ?_declinedwordcases');
DB::Aowow()->query('INSERT INTO ?_declinedwordcases SELECT `wordId`, `caseIdx`, `word` FROM dbc_declinedwordcases'); DB::Aowow()->query('INSERT INTO ?_declinedwordcases SELECT `wordId`, `caseIdx`, `word` FROM dbc_declinedwordcases');

View File

@@ -7,7 +7,7 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup('sql', new class extends SetupScript
{ {
use TrDBCcopy; use TrDBCcopy;

View File

@@ -7,14 +7,113 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
protected $command = 'emotes'; protected $info = array(
'emotes' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Emote from dbc and GlobalStrings.lua.']
);
protected $dbcSourceFiles = ['emotes', 'emotestext', 'emotestextdata']; protected $useGlobalStrings = true;
protected $dbcSourceFiles = ['emotes', 'emotestext', 'emotestextdata'];
private $textData = []; private $textData = [];
public function generate(array $ids = []) : bool
{
$globStrPath = CLISetup::$srcDir.'%sInterface/FrameXML/GlobalStrings.lua';
$allOK = true;
$locPath = CLISetup::filesInPathLocalized($globStrPath, $allOK);
if ($x = array_diff(CLISetup::$localeIds, array_keys($locPath)))
{
$locs = array_intersect_key(CLISetup::$locales, array_flip($x));
CLI::write('[emotes] '.sprintf($globStrPath, CLI::bold('['.implode('/,', $locs).'/]')) . ' not found!', CLI::LOG_WARN);
CLI::write(' Emote aliasses can not be generated for affected locales!', CLI::LOG_WARN);
}
DB::Aowow()->query('TRUNCATE ?_emotes');
DB::Aowow()->query('TRUNCATE ?_emotes_aliasses');
/*********************/
/* Player controlled */
/*********************/
/* EmotesText Data offsets
gender seenBy hasTarget mergedWith example
0 male others yes 8 %s raises <his/her> fist in anger at %s.
1 male self yes 9 %s raises <his/her> fist in anger at you.
2 self self yes You raise your fist in anger at %s.
4 male others no 12 %s raises <his/her> fist in anger.
6 self self no You raise your fist in anger.
8 female others yes 0 -
9 female self yes 1 -
12 female others no 4 -
*/
$this->textData = DB::Aowow()->select('SELECT id AS ARRAY_KEY, text_loc0 AS "0", text_loc2 AS "2", text_loc3 AS "3", text_loc4 AS "4", text_loc6 AS "6", text_loc8 AS "8" FROM dbc_emotestextdata');
$texts = DB::Aowow()->select('SELECT et.id AS ARRAY_KEY, LOWER(command) AS `cmd`, IF(e.animationId, 1, 0) AS `anim`, -emoteId AS "parent", e.soundId, etd0, etd1, etd2, etd4, etd6, etd8, etd9, etd12 FROM dbc_emotestext et LEFT JOIN dbc_emotes e ON e.id = et.emoteId');
foreach ($texts AS $id => $t)
{
DB::Aowow()->query(
'INSERT INTO ?_emotes (
`id`, `cmd`, `isAnimated`, `parentEmote`, `soundId`,
`extToExt_loc0`, `extToMe_loc0`, `meToExt_loc0`, `extToNone_loc0`, `meToNone_loc0`,
`extToExt_loc2`, `extToMe_loc2`, `meToExt_loc2`, `extToNone_loc2`, `meToNone_loc2`,
`extToExt_loc3`, `extToMe_loc3`, `meToExt_loc3`, `extToNone_loc3`, `meToNone_loc3`,
`extToExt_loc4`, `extToMe_loc4`, `meToExt_loc4`, `extToNone_loc4`, `meToNone_loc4`,
`extToExt_loc6`, `extToMe_loc6`, `meToExt_loc6`, `extToNone_loc6`, `meToNone_loc6`,
`extToExt_loc8`, `extToMe_loc8`, `meToExt_loc8`, `extToNone_loc8`, `meToNone_loc8`)
VALUES
(?d, ?, ?d, ?d, ?d, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
$id, $t['cmd'], $t['anim'], $t['parent'], $t['soundId'],
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_EN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_EN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_EN), $this->textData[$t['etd2']][LOCALE_EN] ?? '', $this->textData[$t['etd6']][LOCALE_EN] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_FR), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_FR), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_FR), $this->textData[$t['etd2']][LOCALE_FR] ?? '', $this->textData[$t['etd6']][LOCALE_FR] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_DE), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_DE), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_DE), $this->textData[$t['etd2']][LOCALE_DE] ?? '', $this->textData[$t['etd6']][LOCALE_DE] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_CN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_CN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_CN), $this->textData[$t['etd2']][LOCALE_CN] ?? '', $this->textData[$t['etd6']][LOCALE_CN] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_ES), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_ES), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_ES), $this->textData[$t['etd2']][LOCALE_ES] ?? '', $this->textData[$t['etd6']][LOCALE_ES] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_RU), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_RU), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_RU), $this->textData[$t['etd2']][LOCALE_RU] ?? '', $this->textData[$t['etd6']][LOCALE_RU] ?? ''
);
}
// i have no idea, how the indexing in this file works.
// sometimes the \d+ after EMOTE is the emoteTextId, but not nearly often enough
$aliasses = [];
foreach (CLISetup::searchGlobalStrings('/^EMOTE(\d+)_CMD\d+\s=\s\"\/([^"]+)\";$/') as $lId => $match)
$aliasses[$match[1]][] = [$lId, $match[2]];
$emotes = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, cmd FROM ?_emotes');
foreach($emotes as $eId => $cmd)
{
foreach ($aliasses as $data)
{
if (!in_array($cmd, array_column($data, 1)))
continue;
foreach ($data as $d)
DB::Aowow()->query('INSERT IGNORE INTO ?_emotes_aliasses VALUES (?d, ?d, ?) ON DUPLICATE KEY UPDATE locales = locales | ?d', $eId, (1 << $d[0]), strtolower($d[1]), (1 << $d[0]));
continue 2;
}
DB::Aowow()->query('UPDATE ?_emotes SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', CUSTOM_EXCLUDE_FOR_LISTVIEW | EMOTE_CU_MISSING_CMD, $eId);
}
/*********************/
/* Server controlled */
/*********************/
DB::Aowow()->query('INSERT INTO ?_emotes (`id`, `cmd`, `flags`, `isAnimated`, `parentEmote`, `soundId`, `state`, `stateParam`, `cuFlags`) SELECT -`id`, `name`, `flags`, IF(`animationId`, 1, 0), 0, `soundId`, `state`, `stateParam`, ?d FROM dbc_emotes WHERE 1', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('emotes', Type::EMOTE);
return $allOK;
}
private function mergeGenderedStrings(int $maleTextId, int $femaleTextId, int $locale) : string private function mergeGenderedStrings(int $maleTextId, int $femaleTextId, int $locale) : string
{ {
$maleText = $this->textData[$maleTextId][$locale] ?? ''; $maleText = $this->textData[$maleTextId][$locale] ?? '';
@@ -77,121 +176,6 @@ SqlGen::register(new class extends SetupScript
return implode(' ', array_merge($front, [$mid], array_reverse($back))); return implode(' ', array_merge($front, [$mid], array_reverse($back)));
} }
public function generate(array $ids = []) : bool
{
/**********/
/* Basics */
/**********/
$globStrPath = CLISetup::$srcDir.'%sInterface/FrameXML/GlobalStrings.lua';
$allOK = true;
$locPath = [];
DB::Aowow()->query('TRUNCATE ?_emotes');
DB::Aowow()->query('TRUNCATE ?_emotes_aliasses');
/*********************/
/* Player controlled */
/*********************/
foreach (CLISetup::$localeIds as $lId)
{
foreach (CLISetup::$expectedPaths as $xp => $locId)
{
if ($lId != $locId)
continue;
if ($xp) // if in subDir add trailing slash
$xp .= '/';
$path = sprintf($globStrPath, $xp);
if (CLISetup::fileExists($path))
{
$locPath[$lId] = $path;
continue 2;
}
}
CLI::write('GlobalStrings.lua not found for selected locale '.CLI::bold(Util::$localeStrings[$lId]), CLI::LOG_WARN);
$allOK = false;
}
/* EmotesText Data offsets
gender seenBy hasTarget mergedWith example
0 male others yes 8 %s raises <his/her> fist in anger at %s.
1 male self yes 9 %s raises <his/her> fist in anger at you.
2 self self yes You raise your fist in anger at %s.
4 male others no 12 %s raises <his/her> fist in anger.
6 self self no You raise your fist in anger.
8 female others yes 0 -
9 female self yes 1 -
12 female others no 4 -
*/
$this->textData = DB::Aowow()->select('SELECT id AS ARRAY_KEY, text_loc0 AS "0", text_loc2 AS "2", text_loc3 AS "3", text_loc4 AS "4", text_loc6 AS "6", text_loc8 AS "8" FROM dbc_emotestextdata');
$texts = DB::Aowow()->select('SELECT et.id AS ARRAY_KEY, LOWER(command) AS `cmd`, IF(e.animationId, 1, 0) AS `anim`, -emoteId AS "parent", e.soundId, etd0, etd1, etd2, etd4, etd6, etd8, etd9, etd12 FROM dbc_emotestext et LEFT JOIN dbc_emotes e ON e.id = et.emoteId');
foreach ($texts AS $id => $t)
{
DB::Aowow()->query(
'INSERT INTO ?_emotes (
`id`, `cmd`, `isAnimated`, `parentEmote`, `soundId`,
`extToExt_loc0`, `extToMe_loc0`, `meToExt_loc0`, `extToNone_loc0`, `meToNone_loc0`,
`extToExt_loc2`, `extToMe_loc2`, `meToExt_loc2`, `extToNone_loc2`, `meToNone_loc2`,
`extToExt_loc3`, `extToMe_loc3`, `meToExt_loc3`, `extToNone_loc3`, `meToNone_loc3`,
`extToExt_loc4`, `extToMe_loc4`, `meToExt_loc4`, `extToNone_loc4`, `meToNone_loc4`,
`extToExt_loc6`, `extToMe_loc6`, `meToExt_loc6`, `extToNone_loc6`, `meToNone_loc6`,
`extToExt_loc8`, `extToMe_loc8`, `meToExt_loc8`, `extToNone_loc8`, `meToNone_loc8`)
VALUES
(?d, ?, ?d, ?d, ?d, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
$id, $t['cmd'], $t['anim'], $t['parent'], $t['soundId'],
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_EN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_EN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_EN), $this->textData[$t['etd2']][LOCALE_EN] ?? '', $this->textData[$t['etd6']][LOCALE_EN] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_FR), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_FR), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_FR), $this->textData[$t['etd2']][LOCALE_FR] ?? '', $this->textData[$t['etd6']][LOCALE_FR] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_DE), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_DE), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_DE), $this->textData[$t['etd2']][LOCALE_DE] ?? '', $this->textData[$t['etd6']][LOCALE_DE] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_CN), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_CN), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_CN), $this->textData[$t['etd2']][LOCALE_CN] ?? '', $this->textData[$t['etd6']][LOCALE_CN] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_ES), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_ES), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_ES), $this->textData[$t['etd2']][LOCALE_ES] ?? '', $this->textData[$t['etd6']][LOCALE_ES] ?? '',
$this->mergeGenderedStrings($t['etd0'], $t['etd8'], LOCALE_RU), $this->mergeGenderedStrings($t['etd1'], $t['etd9'], LOCALE_RU), $this->mergeGenderedStrings($t['etd4'], $t['etd12'], LOCALE_RU), $this->textData[$t['etd2']][LOCALE_RU] ?? '', $this->textData[$t['etd6']][LOCALE_RU] ?? ''
);
}
// i have no idea, how the indexing in this file works.
// sometimes the \d+ after EMOTE is the emoteTextId, but not nearly often enough
$aliasses = [];
foreach ($locPath as $lId => $path)
foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line)
if (preg_match('/^EMOTE(\d+)_CMD\d+\s=\s\"\/([^"]+)\";$/', $line, $m))
$aliasses[$m[1]][] = [$lId, $m[2]];
$emotes = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, cmd FROM ?_emotes');
foreach($emotes as $eId => $cmd)
{
foreach ($aliasses as $data)
{
if (in_array($cmd, array_column($data, 1)))
{
foreach ($data as $d)
DB::Aowow()->query('INSERT IGNORE INTO ?_emotes_aliasses VALUES (?d, ?d, ?) ON DUPLICATE KEY UPDATE locales = locales | ?d', $eId, (1 << $d[0]), strtolower($d[1]), (1 << $d[0]));
continue 2;
}
}
DB::Aowow()->query('UPDATE ?_emotes SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', CUSTOM_EXCLUDE_FOR_LISTVIEW | EMOTE_CU_MISSING_CMD, $eId);
}
/*********************/
/* Server controlled */
/*********************/
DB::Aowow()->query('INSERT INTO ?_emotes (`id`, `cmd`, `flags`, `isAnimated`, `parentEmote`, `soundId`, `state`, `stateParam`, `cuFlags`) SELECT -`id`, `name`, `flags`, IF(`animationId`, 1, 0), 0, `soundId`, `state`, `stateParam`, ?d FROM dbc_emotes WHERE 1', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('emotes', Type::EMOTE);
return $allOK;
}
}); });
?> ?>

View File

@@ -1,51 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
protected $command = 'events';
protected $tblDependencyTC = ['game_event', 'game_event_prerequisite'];
public function generate(array $ids = []) : bool
{
$eventQuery = '
SELECT
ge.eventEntry,
holiday,
0, -- cuFlags
IFNULL(UNIX_TIMESTAMP(start_time), 0),
IFNULL(UNIX_TIMESTAMP(end_time), 0),
occurence * 60,
length * 60,
IF (gep.eventEntry IS NOT NULL, GROUP_CONCAT(prerequisite_event SEPARATOR " "), NULL),
description
FROM
game_event ge
LEFT JOIN
game_event_prerequisite gep ON gep.eventEntry = ge.eventEntry
{
WHERE
ge.eventEntry IN (?a)
}
GROUP BY
ge.eventEntry';
$events = DB::World()->select($eventQuery, $ids ?: DBSIMPLE_SKIP);
foreach ($events as $e)
DB::Aowow()->query('REPLACE INTO ?_events VALUES (?a)', array_values($e));
$this->reapplyCCFlags('events', Type::WORLDEVENT);
return true;
}
});
?>

View File

@@ -0,0 +1,48 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
protected $info = array(
'events' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Event from world db.']
);
protected $worldDependency = ['game_event', 'game_event_prerequisite'];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_events');
$events = DB::World()->select(
'SELECT ge.eventEntry,
holiday,
0, -- cuFlags
IFNULL(UNIX_TIMESTAMP(start_time), 0),
IFNULL(UNIX_TIMESTAMP(end_time), 0),
occurence * 60,
length * 60,
IF (gep.eventEntry IS NOT NULL, GROUP_CONCAT(prerequisite_event SEPARATOR " "), NULL),
description
FROM game_event ge
LEFT JOIN game_event_prerequisite gep ON gep.eventEntry = ge.eventEntry
{ WHERE ge.eventEntry IN (?a) }
GROUP BY ge.eventEntry',
$ids ?: DBSIMPLE_SKIP
);
foreach ($events as $e)
DB::Aowow()->query('INSERT INTO ?_events VALUES (?a)', array_values($e));
$this->reapplyCCFlags('events', Type::WORLDEVENT);
return true;
}
});
?>

View File

@@ -1,105 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $command = 'factions';
protected $dbcSourceFiles = ['faction', 'factiontemplate'];
public function generate(array $ids = []) : bool
{
$factionQuery = '
REPLACE INTO
?_factions
SELECT
f.id,
f.repIdx,
baseRepRaceMask1, baseRepRaceMask2, baseRepRaceMask3, baseRepRaceMask4,
baseRepClassMask1, baseRepClassMask2, baseRepClassMask3, baseRepClassMask4,
baseRepValue1, baseRepValue2, baseRepValue3, baseRepValue4,
IF(SUM(ft.ourMask & 0x6) / COUNT(1) = 0x4, 2, IF(SUM(ft.ourMask & 0x6) / COUNT(1) = 0x2, 1, 0)) as side,
0, -- expansion
"", -- quartermasterNpcIds
"", -- factionTemplateIds
0, -- cuFlags
parentFaction,
spilloverRateIn, spilloverRateOut, spilloverMaxRank,
name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8
FROM
dbc_faction f
LEFT JOIN
dbc_factiontemplate ft ON ft.factionid = f.id
GROUP BY
f.id';
$templateQuery = '
UPDATE
?_factions f
JOIN
(SELECT ft.factionId, GROUP_CONCAT(ft.id SEPARATOR " ") AS tplIds FROM dbc_factiontemplate ft GROUP BY ft.factionId) temp ON f.id = temp.factionId
SET
f.templateIds = temp.tplIds';
$recursiveUpdateQuery = '
UPDATE
?_factions top
JOIN
(SELECT id, parentFactionId FROM ?_factions) mid ON mid.parentFactionId IN (?a)
LEFT JOIN
(SELECT id, parentFactionId FROM ?_factions) low ON low.parentFactionId = mid.id
SET
?a
WHERE
repIdx > 0 AND (
top.id IN (?a) OR
top.id = mid.id OR
top.id = low.id
)';
$excludeQuery = '
UPDATE
?_factions x
JOIN
dbc_faction f ON f.id = x.id
LEFT JOIN
dbc_factiontemplate ft ON f.id = ft.factionId
SET
cuFlags = cuFlags | ?d
WHERE
f.repIdx < 0 OR
(
f.repIdx > 0 AND
(f.repFlags1 & 0x8 OR ft.id IS NULL) AND
(f.repFlags1 & 0x80) = 0
)';
$pairs = array(
[[980], ['expansion' => 1]],
[[1097], ['expansion' => 2]],
[[469, 891, 1037], ['side' => 1]],
[[ 67, 892, 1052], ['side' => 2]],
);
DB::Aowow()->query($factionQuery);
DB::Aowow()->query($templateQuery);
DB::Aowow()->query($excludeQuery, CUSTOM_EXCLUDE_FOR_LISTVIEW);
foreach ($pairs as $p)
DB::Aowow()->query($recursiveUpdateQuery, $p[0], $p[1], $p[0]);
$this->reapplyCCFlags('factions', Type::FACTION);
return true;
}
});
?>

View File

@@ -0,0 +1,94 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $info = array(
'factions' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Faction from dbc.']
);
protected $dbcSourceFiles = ['faction', 'factiontemplate'];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_factions');
DB::Aowow()->query('TRUNCATE ?_factiontemplate');
DB::Aowow()->query(
'INSERT INTO ?_factions
SELECT f.id,
f.repIdx,
baseRepRaceMask1, baseRepRaceMask2, baseRepRaceMask3, baseRepRaceMask4,
baseRepClassMask1, baseRepClassMask2, baseRepClassMask3, baseRepClassMask4,
baseRepValue1, baseRepValue2, baseRepValue3, baseRepValue4,
IF(SUM(ft.ourMask & 0x6) / COUNT(1) = 0x4, 2, IF(SUM(ft.ourMask & 0x6) / COUNT(1) = 0x2, 1, 0)) as side,
0, -- expansion
"", -- quartermasterNpcIds
"", -- factionTemplateIds
0, -- cuFlags
parentFaction,
spilloverRateIn, spilloverRateOut, spilloverMaxRank,
name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8
FROM dbc_faction f
LEFT JOIN dbc_factiontemplate ft ON ft.factionid = f.id
GROUP BY f.id'
);
DB::Aowow()->query(
'INSERT INTO ?_factiontemplate
SELECT id,
factionId,
IF(friendFactionId1 = 1 OR friendFactionId2 = 1 OR friendFactionId3 = 1 OR friendFactionId4 = 1 OR friendlyMask & 0x3, 1,
IF(enemyFactionId1 = 1 OR enemyFactionId2 = 1 OR enemyFactionId3 = 1 OR enemyFactionId4 = 1 OR hostileMask & 0x3, -1, 0)),
IF(friendFactionId1 = 2 OR friendFactionId2 = 2 OR friendFactionId3 = 2 OR friendFactionId4 = 2 OR friendlyMask & 0x5, 1,
IF(enemyFactionId1 = 2 OR enemyFactionId2 = 2 OR enemyFactionId3 = 2 OR enemyFactionId4 = 2 OR hostileMask & 0x5, -1, 0))
FROM dbc_factiontemplate'
);
DB::Aowow()->query(
'UPDATE ?_factions f
JOIN (SELECT ft.factionId, GROUP_CONCAT(ft.id SEPARATOR " ") AS tplIds FROM dbc_factiontemplate ft GROUP BY ft.factionId) temp ON f.id = temp.factionId
SET f.templateIds = temp.tplIds'
);
DB::Aowow()->query(
'UPDATE ?_factions x
JOIN dbc_faction f ON f.id = x.id
LEFT JOIN dbc_factiontemplate ft ON f.id = ft.factionId
SET cuFlags = cuFlags | ?d
WHERE f.repIdx < 0 OR ( f.repIdx > 0 AND (f.repFlags1 & 0x8 OR ft.id IS NULL) AND (f.repFlags1 & 0x80) = 0 )',
CUSTOM_EXCLUDE_FOR_LISTVIEW
);
$pairs = array(
[[980], ['expansion' => 1]],
[[1097], ['expansion' => 2]],
[[469, 891, 1037], ['side' => 1]],
[[ 67, 892, 1052], ['side' => 2]],
);
foreach ($pairs as $p)
DB::Aowow()->query(
'UPDATE ?_factions top
JOIN (SELECT id, parentFactionId FROM ?_factions) mid ON mid.parentFactionId IN (?a)
LEFT JOIN (SELECT id, parentFactionId FROM ?_factions) low ON low.parentFactionId = mid.id
SET ?a
WHERE repIdx > 0 AND (top.id IN (?a) OR top.id = mid.id OR top.id = low.id)',
$p[0], $p[1], $p[0]
);
$this->reapplyCCFlags('factions', Type::FACTION);
return true;
}
});
?>

View File

@@ -1,41 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
protected $command = 'factiontemplate';
protected $dbcSourceFiles = ['factiontemplate'];
public function generate(array $ids = []) : bool
{
$query = '
REPLACE INTO
?_factiontemplate
SELECT
id,
factionId,
IF(friendFactionId1 = 1 OR friendFactionId2 = 1 OR friendFactionId3 = 1 OR friendFactionId4 = 1 OR friendlyMask & 0x3,
1,
IF(enemyFactionId1 = 1 OR enemyFactionId2 = 1 OR enemyFactionId3 = 1 OR enemyFactionId4 = 1 OR hostileMask & 0x3, -1, 0)
),
IF(friendFactionId1 = 2 OR friendFactionId2 = 2 OR friendFactionId3 = 2 OR friendFactionId4 = 2 OR friendlyMask & 0x5,
1,
IF(enemyFactionId1 = 2 OR enemyFactionId2 = 2 OR enemyFactionId3 = 2 OR enemyFactionId4 = 2 OR hostileMask & 0x5, -1, 0)
)
FROM
dbc_factiontemplate';
DB::Aowow()->query($query);
return true;
}
});
?>

View File

@@ -1,27 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
protected $command = 'glyphproperties';
protected $tblDependencyAowow = ['icons'];
protected $dbcSourceFiles = ['glyphproperties', 'spellicon'];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('REPLACE INTO ?_glyphproperties SELECT id, spellId, typeFlags, 0, iconId FROM dbc_glyphproperties');
DB::Aowow()->query('UPDATE ?_glyphproperties gp, ?_icons ic, dbc_spellicon si SET gp.iconId = ic.id WHERE gp.iconIdBak = si.id AND ic.name = LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1))');
return true;
}
});
?>

View File

@@ -0,0 +1,30 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
protected $info = array(
'glyphproperties' => [[], CLISetup::ARGV_PARAM, 'Compiles supplemental data for type: Item & Spell from dbc.']
);
protected $dbcSourceFiles = ['glyphproperties', 'spellicon'];
protected $setupAfter = [['icons'], []];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_glyphproperties');
DB::Aowow()->query('INSERT INTO ?_glyphproperties SELECT id, spellId, typeFlags, 0, iconId FROM dbc_glyphproperties');
DB::Aowow()->query('UPDATE ?_glyphproperties gp, ?_icons ic, dbc_spellicon si SET gp.iconId = ic.id WHERE gp.iconIdBak = si.id AND ic.name = LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1))');
return true;
}
});
?>

View File

@@ -1,39 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $command = 'holidays';
protected $dbcSourceFiles = ['holidays', 'holidaydescriptions', 'holidaynames'];
public function generate(array $ids = []) : bool
{
$query = '
REPLACE INTO
?_holidays (id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, description_loc0, description_loc2, description_loc3, description_loc4, description_loc6, description_loc8, looping, scheduleType, textureString)
SELECT
h.id, n.name_loc0, n.name_loc2, n.name_loc3, n.name_loc4, n.name_loc6, n.name_loc8, d.description_loc0, d.description_loc2, d.description_loc3, d.description_loc4, d.description_loc6, d.description_loc8, h.looping, h.scheduleType, h.textureString
FROM
dbc_holidays h
LEFT JOIN
dbc_holidaynames n ON n.id = h.nameId
LEFT JOIN
dbc_holidaydescriptions d ON d.id = h.descriptionId';
DB::Aowow()->query($query);
return true;
}
});
?>

View File

@@ -0,0 +1,36 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
// really should be part of events.func.php, but applying the custom data prevents this for now
CLISetup::registerSetup("sql", new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $info = array(
'holidays' => [[], CLISetup::ARGV_PARAM, 'Compiles supplemental data for type: Event from dbc.']
);
protected $dbcSourceFiles = ['holidays', 'holidaydescriptions', 'holidaynames'];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_holidays');
DB::Aowow()->query(
'INSERT INTO ?_holidays (id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, description_loc0, description_loc2, description_loc3, description_loc4, description_loc6, description_loc8, looping, scheduleType, textureString)
SELECT h.id, n.name_loc0, n.name_loc2, n.name_loc3, n.name_loc4, n.name_loc6, n.name_loc8, d.description_loc0, d.description_loc2, d.description_loc3, d.description_loc4, d.description_loc6, d.description_loc8, h.looping, h.scheduleType, h.textureString
FROM dbc_holidays h
LEFT JOIN dbc_holidaynames n ON n.id = h.nameId
LEFT JOIN dbc_holidaydescriptions d ON d.id = h.descriptionId'
);
return true;
}
});
?>

View File

@@ -7,9 +7,11 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
protected $command = 'icons'; protected $info = array(
'icons' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Icons from dbc.']
);
protected $dbcSourceFiles = ['spellicon', 'itemdisplayinfo', 'creaturefamily']; protected $dbcSourceFiles = ['spellicon', 'itemdisplayinfo', 'creaturefamily'];
@@ -17,20 +19,14 @@ SqlGen::register(new class extends SetupScript
{ {
DB::Aowow()->query('TRUNCATE ?_icons'); DB::Aowow()->query('TRUNCATE ?_icons');
DB::Aowow()->query('ALTER TABLE ?_icons AUTO_INCREMENT = 1'); DB::Aowow()->query('ALTER TABLE ?_icons AUTO_INCREMENT = 1');
DB::Aowow()->query(
$baseQuery = ' 'INSERT INTO ?_icons (`name`) SELECT x FROM
INSERT INTO ?_icons (`name`) SELECT x FROM
( (
(SELECT LOWER(SUBSTRING_INDEX(iconPath, "\\\\", -1)) AS x FROM dbc_spellicon WHERE iconPath LIKE "%icons%") (SELECT LOWER(SUBSTRING_INDEX(iconPath, "\\\\", -1)) AS x FROM dbc_spellicon WHERE iconPath LIKE "%icons%") UNION
UNION (SELECT LOWER(inventoryIcon1) AS x FROM dbc_itemdisplayinfo WHERE inventoryIcon1 <> "") UNION
(SELECT LOWER(inventoryIcon1) AS x FROM dbc_itemdisplayinfo WHERE inventoryIcon1 <> "") (SELECT LOWER(SUBSTRING_INDEX(iconString, "\\\\", -1)) AS x FROM dbc_creaturefamily WHERE iconString LIKE "%icons%")
UNION ) y GROUP BY x'
(SELECT LOWER(SUBSTRING_INDEX(iconString, "\\\\", -1)) AS x FROM dbc_creaturefamily WHERE iconString LIKE "%icons%") );
) y
GROUP BY
x';
DB::Aowow()->query($baseQuery);
$this->reapplyCCFlags('icons', Type::ICON); $this->reapplyCCFlags('icons', Type::ICON);

View File

@@ -1,43 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
protected $command = 'itemenchantment';
protected $tblDependencyTC = ['spell_enchant_proc_data'];
protected $dbcSourceFiles = ['spellitemenchantment'];
public function generate(array $ids = []) : bool
{
$baseQuery = '
REPLACE INTO
?_itemenchantment
SELECT
Id, charges, 0, 0, 0, type1, type2, type3, amount1, amount2, amount3, object1, object2, object3, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, conditionId, skillLine, skillLevel, requiredLevel
FROM
dbc_spellitemenchantment';
DB::Aowow()->query($baseQuery);
$cuProcs = DB::World()->select('SELECT EnchantID AS ARRAY_KEY, Chance AS procChance, ProcsPerMinute AS ppmRate FROM spell_enchant_proc_data');
foreach ($cuProcs as $id => $vals)
DB::Aowow()->query('UPDATE ?_itemenchantment SET ?a WHERE id = ?d', $vals, $id);
// hide strange stuff
DB::Aowow()->query('UPDATE ?_itemenchantment SET cuFlags = ?d WHERE type1 = 0 AND type2 = 0 AND type3 = 0', CUSTOM_EXCLUDE_FOR_LISTVIEW);
DB::Aowow()->query('UPDATE ?_itemenchantment SET cuFlags = ?d WHERE name_loc0 LIKE "%test%"', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('itemenchantment', Type::ENCHANTMENT);
return true;
}
});
?>

View File

@@ -0,0 +1,42 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
CLISetup::registerSetup("sql", new class extends SetupScript
{
protected $info = array(
'itemenchantment' => [[], CLISetup::ARGV_PARAM, 'Compiles data for type: Enchantment from dbc and world db.']
);
protected $dbcSourceFiles = ['spellitemenchantment'];
protected $worldDependency = ['spell_enchant_proc_data'];
public function generate(array $ids = []) : bool
{
DB::Aowow()->query('TRUNCATE ?_itemenchantment');
DB::Aowow()->query(
'INSERT INTO ?_itemenchantment
SELECT `Id`, `charges`, 0, 0, 0, `type1`, `type2`, `type3`, `amount1`, `amount2`, `amount3`, `object1`, `object2`, `object3`, `name_loc0`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc6`, `name_loc8`, `conditionId`, `skillLine`, `skillLevel`, `requiredLevel`
FROM dbc_spellitemenchantment'
);
$cuProcs = DB::World()->select('SELECT `EnchantID` AS ARRAY_KEY, `Chance` AS `procChance`, `ProcsPerMinute` AS `ppmRate` FROM spell_enchant_proc_data');
foreach ($cuProcs as $id => $vals)
DB::Aowow()->query('UPDATE ?_itemenchantment SET ?a WHERE `id` = ?d', $vals, $id);
// hide strange stuff
DB::Aowow()->query('UPDATE ?_itemenchantment SET `cuFlags` = ?d WHERE `type1` = 0 AND `type2` = 0 AND `type3` = 0', CUSTOM_EXCLUDE_FOR_LISTVIEW);
DB::Aowow()->query('UPDATE ?_itemenchantment SET `cuFlags` = ?d WHERE `name_loc0` LIKE "%test%"', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$this->reapplyCCFlags('itemenchantment', Type::ENCHANTMENT);
return true;
}
});
?>

View File

@@ -7,7 +7,7 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
use TrDBCcopy; use TrDBCcopy;

View File

@@ -7,7 +7,7 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
use TrDBCcopy; use TrDBCcopy;

View File

@@ -7,7 +7,7 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
use TrDBCcopy; use TrDBCcopy;

View File

@@ -7,21 +7,22 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
protected $command = 'itemrandomenchant'; protected $info = array(
'itemrandomenchant' => [[], CLISetup::ARGV_PARAM, 'Compiles supplemental data for type: Item from dbc.']
);
protected $dbcSourceFiles = ['itemrandomsuffix', 'itemrandomproperties']; protected $dbcSourceFiles = ['itemrandomsuffix', 'itemrandomproperties'];
public function generate(array $ids = []) : bool public function generate(array $ids = []) : bool
{ {
$query = ' DB::Aowow()->query('TRUNCATE ?_itemrandomenchant');
REPLACE INTO ?_itemrandomenchant DB::Aowow()->query(
SELECT -id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, nameINT, enchantId1, enchantId2, enchantId3, enchantId4, enchantId5, allocationPct1, allocationPct2, allocationPct3, allocationPct4, allocationPct5 FROM dbc_itemrandomsuffix 'INSERT INTO ?_itemrandomenchant
UNION SELECT -id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, nameINT, enchantId1, enchantId2, enchantId3, enchantId4, enchantId5, allocationPct1, allocationPct2, allocationPct3, allocationPct4, allocationPct5 FROM dbc_itemrandomsuffix UNION
SELECT id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, nameINT, enchantId1, enchantId2, enchantId3, enchantId4, enchantId5, 0, 0, 0, 0, 0 FROM dbc_itemrandomproperties'; SELECT id, name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8, nameINT, enchantId1, enchantId2, enchantId3, enchantId4, enchantId5, 0, 0, 0, 0, 0 FROM dbc_itemrandomproperties'
);
DB::Aowow()->query($query);
return true; return true;
} }

View File

@@ -7,7 +7,7 @@ if (!CLI)
die('not in cli mode'); die('not in cli mode');
SqlGen::register(new class extends SetupScript CLISetup::registerSetup("sql", new class extends SetupScript
{ {
use TrDBCcopy; use TrDBCcopy;

View File

@@ -1,265 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
if (!CLI)
die('not in cli mode');
SqlGen::register(new class extends SetupScript
{
use TrCustomData; // import custom data from DB
protected $command = 'items';
protected $tblDependencyAowow = ['icons'];
protected $tblDependencyTC = ['item_template', 'item_template_locale', 'spell_group', 'game_event'];
protected $dbcSourceFiles = ['gemproperties', 'itemdisplayinfo', 'spell', 'glyphproperties', 'durabilityquality', 'durabilitycosts'];
private $skill2cat = array(
SKILL_INSCRIPTION => 11,
SKILL_FISHING => 9,
SKILL_MINING => 12,
SKILL_COOKING => 5,
SKILL_ALCHEMY => 6
);
public function generate(array $ids = []) : bool
{
$baseQuery = '
SELECT
it.entry,
class, class as classBak,
subclass, subclass AS subClassBak,
SoundOverrideSubclass,
IFNULL(sg.id, 0) AS subSubClass,
it.name, IFNULL(itl2.Name, ""), IFNULL(itl3.Name, ""), IFNULL(itl4.Name, ""), IFNULL(itl6.Name, ""), IFNULL(itl8.Name, ""),
0 AS iconId,
displayid,
0 AS spellVisualId,
Quality,
Flags, FlagsExtra,
BuyCount, BuyPrice, SellPrice,
0 AS repairPrice,
InventoryType AS slot, InventoryType AS slotBak,
AllowableClass, AllowableRace,
ItemLevel,
RequiredLevel,
RequiredSkill, RequiredSkillRank,
requiredspell,
requiredhonorrank,
RequiredCityRank,
RequiredReputationFaction,
RequiredReputationRank,
maxcount,
0 AS cuFlags,
0 AS model,
stackable,
ContainerSlots,
stat_type1, stat_value1,
stat_type2, stat_value2,
stat_type3, stat_value3,
stat_type4, stat_value4,
stat_type5, stat_value5,
stat_type6, stat_value6,
stat_type7, stat_value7,
stat_type8, stat_value8,
stat_type9, stat_value9,
stat_type10, stat_value10,
ScalingStatDistribution,
ScalingStatValue,
dmg_min1, dmg_max1, dmg_type1,
dmg_min2, dmg_max2, dmg_type2,
delay,
armor, ArmorDamageModifier,
block,
holy_res, fire_res, nature_res, frost_res, shadow_res, arcane_res,
ammo_type,
RangedModRange,
spellid_1, spelltrigger_1, spellcharges_1, spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1,
spellid_2, spelltrigger_2, spellcharges_2, spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2,
spellid_3, spelltrigger_3, spellcharges_3, spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3,
spellid_4, spelltrigger_4, spellcharges_4, spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4,
spellid_5, spelltrigger_5, spellcharges_5, spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5,
bonding,
it.description, IFNULL(itl2.Description, ""), IFNULL(itl3.Description, ""), IFNULL(itl4.Description, ""), IFNULL(itl6.Description, ""), IFNULL(itl8.Description, ""),
PageText,
LanguageID,
startquest,
lockid,
Material,
IF(RandomProperty > 0, RandomProperty, -RandomSuffix) AS randomEnchant,
itemset,
MaxDurability,
area,
Map,
BagFamily,
TotemCategory,
socketColor_1, socketContent_1,
socketColor_2, socketContent_2,
socketColor_3, socketContent_3,
socketBonus,
GemProperties,
RequiredDisenchantSkill,
DisenchantID,
duration,
ItemLimitCategory,
IFNULL(ge.eventEntry, 0),
ScriptName,
FoodType,
0 AS gemEnchantmentId,
minMoneyLoot, maxMoneyLoot,
0 AS pickUpSoundId,
0 AS dropDownSoundId,
0 AS sheatheSoundId,
0 AS unsheatheSoundId,
flagsCustom
FROM
item_template it
LEFT JOIN
item_template_locale itl2 ON it.entry = itl2.ID AND itl2.locale = "frFR"
LEFT JOIN
item_template_locale itl3 ON it.entry = itl3.ID AND itl3.locale = "deDE"
LEFT JOIN
item_template_locale itl4 ON it.entry = itl4.ID AND itl4.locale = "zhCN"
LEFT JOIN
item_template_locale itl6 ON it.entry = itl6.ID AND itl6.locale = "esES"
LEFT JOIN
item_template_locale itl8 ON it.entry = itl8.ID AND itl8.locale = "ruRU"
LEFT JOIN
spell_group sg ON sg.spell_id = it.spellid_1 AND it.class = 0 AND it.subclass = 2 AND sg.id IN (1, 2)
LEFT JOIN
game_event ge ON ge.holiday = it.HolidayId AND it.HolidayId > 0
{
WHERE
it.entry IN (?a)
}
LIMIT
?d, ?d';
$i = 0;
DB::Aowow()->query('TRUNCATE ?_items');
while ($items = DB::World()->select($baseQuery, $ids ?: DBSIMPLE_SKIP, SqlGen::$sqlBatchSize * $i, SqlGen::$sqlBatchSize))
{
CLI::write(' * batch #' . ++$i . ' (' . count($items) . ')', CLI::LOG_BLANK, true, true);
foreach ($items as $item)
DB::Aowow()->query('INSERT INTO ?_items VALUES (?a)', array_values($item));
}
// merge with gemProperties
DB::Aowow()->query('UPDATE ?_items i, dbc_gemproperties gp SET i.gemEnchantmentId = gp.enchantmentId, i.gemColorMask = gp.colorMask WHERE i.gemColorMask = gp.id');
// get modelString
DB::Aowow()->query('UPDATE ?_items i, dbc_itemdisplayinfo idi SET i.model = IF(idi.leftModelName = "", idi.rightModelName, idi.leftModelName) WHERE i.displayId = idi.id');
// get iconId
DB::Aowow()->query('UPDATE ?_items i, dbc_itemdisplayinfo idi, ?_icons ic SET i.iconId = ic.id WHERE i.displayId = idi.id AND LOWER(idi.inventoryIcon1) = ic.name');
// unify slots: Robes => Chest; Ranged (right) => Ranged
DB::Aowow()->query('UPDATE ?_items SET slot = 15 WHERE slotbak = 26');
DB::Aowow()->query('UPDATE ?_items SET slot = 5 WHERE slotbak = 20');
// custom sub-classes
DB::Aowow()->query('
UPDATE ?_items SET subclass = IF(
slotbak = 4, -8, IF( -- shirt
slotbak = 19, -7, IF( -- tabard
slotbak = 16, -6, IF( -- cloak
slotbak = 23, -5, IF( -- held in offhand
slotbak = 12, -4, IF( -- trinket
slotbak = 2, -3, IF( -- amulet
slotbak = 11, -2, subClassBak -- ring
))))))) WHERE class = 4');
// move alchemist stones to trinkets (Armor)
DB::Aowow()->query('UPDATE ?_items SET class = 4, subClass = -4 WHERE classBak = 7 AND subClassBak = 11 AND slotBak = 12');
// mark keys as key (if not quest items)
DB::Aowow()->query('UPDATE ?_items SET class = 13, subClass = 0 WHERE classBak IN (0, 15) AND bagFamily & 0x100');
// set subSubClass for Glyphs (major/minor)
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s, dbc_glyphproperties gp SET i.subSubClass = IF(gp.typeFlags & 0x1, 2, 1) WHERE i.spellId1 = s.id AND s.effect1MiscValue = gp.id AND i.classBak = 16');
// filter misc(class:15) junk(subclass:0) to appropriate categories
// assign pets and mounts to category
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET subClass = IF(effect1AuraId <> 78, 2, IF(effect2AuraId = 207 OR effect3AuraId = 207 OR (s.id <> 65917 AND effect2AuraId = 4 AND effect3Id = 77), -7, 5)) WHERE s.id = spellId2 AND class = 15 AND spellId1 IN (?a)', LEARN_SPELLS);
// more corner cases (mounts that are not actualy learned)
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET i.subClass = -7 WHERE (effect1Id = 64 OR (effect1AuraId = 78 AND effect2AuraId = 4 AND effect3Id = 77) OR effect1AuraId = 207 OR effect2AuraId = 207 OR effect3AuraId = 207) AND s.id = i.spellId1 AND i.class = 15 AND i.subClass = 5');
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET i.subClass = 5 WHERE s.effect1AuraId = 78 AND s.id = i.spellId1 AND i.class = 15 AND i.subClass = 0');
// move some permanent enchantments to own category
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET i.class = 0, i.subClass = 6 WHERE s.effect1Id = 53 AND s.id = i.spellId1 AND i.class = 15');
// move temporary enchantments to own category
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET i.subClass = -3 WHERE s.effect1Id = 54 AND s.id = i.spellId1 AND i.class = 0 AND i.subClassBak = 8');
// move armor tokens to own category
DB::Aowow()->query('UPDATE ?_items SET subClass = -2 WHERE quality = 4 AND class = 15 AND subClassBak = 0 AND requiredClass AND (requiredClass & 0x5FF) <> 0x5FF');
// move some junk to holiday if it requires one
DB::Aowow()->query('UPDATE ?_items SET subClass = 3 WHERE classBak = 15 AND subClassBak = 0 AND eventId <> 0');
// move misc items that start quests to class: quest (except Sayges scrolls for consistency)
DB::Aowow()->query('UPDATE ?_items SET class = 12 WHERE classBak = 15 AND startQuest <> 0 AND name_loc0 NOT LIKE "sayge\'s fortune%"');
// move perm. enchantments into appropriate cat/subcat
DB::Aowow()->query('UPDATE ?_items i, dbc_spell s SET i.class = 0, i.subClass = 6 WHERE s.id = i.spellId1 AND s.effect1Id = 53 AND i.classBak = 12');
// move some generic recipes into appropriate sub-categories
foreach ($this->skill2cat as $skill => $cat)
DB::Aowow()->query('UPDATE ?_items SET subClass = ?d WHERE classBak = 9 AND subClassBak = 0 AND requiredSkill = ?d', $cat, $skill);
// calculate durabilityCosts
DB::Aowow()->query('
UPDATE
?_items i
JOIN
dbc_durabilityquality dq ON dq.id = ((i.quality + 1) * 2)
JOIN
dbc_durabilitycosts dc ON dc.id = i.itemLevel
SET
i.repairPrice = (durability* dq.mod * IF(i.classBak = 2,
CASE i.subClassBak
WHEN 0 THEN w0 WHEN 1 THEN w1 WHEN 2 THEN w2 WHEN 3 THEN w3 WHEN 4 THEN w4
WHEN 5 THEN w5 WHEN 6 THEN w6 WHEN 7 THEN w7 WHEN 8 THEN w8 WHEN 10 THEN w10
WHEN 11 THEN w11 WHEN 12 THEN w12 WHEN 13 THEN w13 WHEN 14 THEN w14 WHEN 15 THEN w15
WHEN 16 THEN w16 WHEN 17 THEN w17 WHEN 18 THEN w18 WHEN 19 THEN w19 WHEN 20 THEN w20
END,
CASE i.subClassBak
WHEN 1 THEN a1 WHEN 2 THEN a2 WHEN 3 THEN a3 WHEN 4 THEN a4 WHEN 6 THEN a6
END
))
WHERE
durability > 0 AND ((classBak = 4 AND subClassBak IN (1, 2, 3, 4, 6)) OR (classBak = 2 AND subClassBak <> 9))');
// hide some nonsense
DB::Aowow()->query('UPDATE ?_items SET `cuFlags` = `cuFlags` | ?d WHERE
`name_loc0` LIKE "Monster - %" OR `name_loc0` LIKE "Creature - %" OR
`name_loc0` LIKE "%[PH]%" OR `name_loc0` LIKE "% PH %" OR
`name_loc0` LIKE "%(new)%" OR `name_loc0` LIKE "%(old)%" OR
`name_loc0` LIKE "%deprecated%" OR `name_loc0` LIKE "%obsolete%" OR
`name_loc0` LIKE "%1H%" OR `name_loc0` LIKE "%QA%" OR
`name_loc0` LIKE "%(test)%" OR `name_loc0` LIKE "test %" OR (`name_loc0` LIKE "% test %" AND `class` > 0)',
CUSTOM_EXCLUDE_FOR_LISTVIEW
);
// sanity check weapon class and invtype relation
$checks = array(
[[INVTYPE_WEAPONOFFHAND, INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPON], [0, 4, 7, 13, 14, 15]],
[[INVTYPE_2HWEAPON], [1, 5, 6, 8, 10, 14, 20]],
[[INVTYPE_RANGED, INVTYPE_RANGEDRIGHT], [2, 3, 16, 18, 14, 19]]
);
foreach ($checks as [$slots, $subclasses])
DB::Aowow()->query('UPDATE ?_items SET `cuFlags` = `cuFlags` | ?d WHERE `class`= ?d AND `slotBak` IN (?a) AND `subClass` NOT IN (?a)', CUSTOM_EXCLUDE_FOR_LISTVIEW, ITEM_CLASS_WEAPON, $slots, $subclasses);
$this->reapplyCCFlags('items', Type::ITEM);
return true;
}
});
?>

Some files were not shown because too many files have changed in this diff Show More