Profiler/Backend

* added core functions nessecary for profiler
This commit is contained in:
Sarjuuk
2018-03-22 16:48:43 +01:00
parent b0a5f477c1
commit 3fd25ca889
32 changed files with 4386 additions and 619 deletions

View File

@@ -10,6 +10,10 @@ Order Deny,Allow
ForceType application/x-httpd-php ForceType application/x-httpd-php
</Files> </Files>
<Files "prQueue">
ForceType application/x-httpd-php
</Files>
# Block view of some folders # Block view of some folders
Options -Indexes Options -Indexes
DirectoryIndex index.php DirectoryIndex index.php

View File

@@ -7,12 +7,15 @@ class AjaxAccount extends AjaxHandler
{ {
protected $validParams = ['exclude', 'weightscales']; protected $validParams = ['exclude', 'weightscales'];
protected $_post = array( protected $_post = array(
// 'groups' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']], 'groups' => [FILTER_SANITIZE_NUMBER_INT, null],
'save' => [FILTER_SANITIZE_NUMBER_INT, null], 'save' => [FILTER_SANITIZE_NUMBER_INT, null],
'delete' => [FILTER_SANITIZE_NUMBER_INT, null], 'delete' => [FILTER_SANITIZE_NUMBER_INT, null],
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']], 'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'name' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkName']], 'name' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkName']],
'scale' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkScale']], 'scale' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkScale']],
'reset' => [FILTER_SANITIZE_NUMBER_INT, null],
'mode' => [FILTER_SANITIZE_NUMBER_INT, null],
'type' => [FILTER_SANITIZE_NUMBER_INT, null],
); );
protected $_get = array( protected $_get = array(
'locale' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkLocale']] 'locale' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkLocale']]
@@ -37,10 +40,41 @@ class AjaxAccount extends AjaxHandler
protected function handleExclude() protected function handleExclude()
{ {
// profiler completion exclude handler if (!User::$id)
// $this->_post['groups'] = bitMask of excludeGroupIds when using .. excludeGroups .. duh return;
// should probably occur in g_user.excludegroups (dont forget to also set g_users.settings = {})
return ''; if ($this->_post['mode'] == 1) // directly set exludes
{
$type = $this->_post['type'];
$ids = $this->_post['id'];
if (!isset(Util::$typeStrings[$type]) || empty($ids))
return;
// ready for some bullshit? here it comes!
// we don't get signaled whether an id should be added to or removed from either includes or excludes
// so we throw everything into one table and toggle the mode if its already in here
$includes = DB::Aowow()->selectCol('SELECT typeId FROM ?_profiler_excludes WHERE type = ?d AND typeId IN (?a)', $type, $ids);
foreach ($ids as $typeId)
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE mode = (mode ^ 0x3)', array(
User::$id, $type, $typeId, in_array($includes, $typeId) ? 2 : 1
));
return;
}
else if ($this->_post['reset'] == 1) // defaults to unavailable
{
$mask = PR_EXCLUDE_GROUP_UNAVAILABLE;
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE userId = ?d', User::$id);
}
else // clamp to real groups
$mask = $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY;
DB::Aowow()->query('UPDATE ?_account SET excludeGroups = ?d WHERE id = ?d', $mask, User::$id);
return;
} }
protected function handleWeightscales() protected function handleWeightscales()

View File

@@ -0,0 +1,82 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxArenaTeam extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'profile' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
switch ($this->params[0])
{
case 'resync':
$this->handler = 'handleResync';
break;
case 'status':
$this->handler = 'handleStatus';
break;
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
profile: <empty> [optional, also get related chars]
return: 1
*/
protected function handleResync()
{
if ($teams = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_arena_team WHERE id IN (?a)', $this->_get['id']))
foreach ($teams as $t)
Profiler::scheduleResync(TYPE_ARENA_TEAM, $t['realm'], $t['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles p JOIN ?_profiler_arena_team_member atm ON atm.profileId = p.id WHERE atm.arenaTeamId IN (?a)', $this->_get['id']))
foreach ($chars as $c)
Profiler::scheduleResync(TYPE_PROFILE, $c['realm'], $c['realmGUID']);
return '1';
}
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus()
{
$response = Profiler::resyncStatus(TYPE_ARENA_TEAM, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -62,6 +62,15 @@ class AjaxFilter extends AjaxHandler
case 'spells': case 'spells':
$this->filter = (new SpellListFilter(true, $opts)); $this->filter = (new SpellListFilter(true, $opts));
break; break;
case 'profiles':
$this->filter = (new ProfileListFilter(true, $opts));
break;
case 'guilds':
$this->filter = (new GuildListFilter(true, $opts));
break;
case 'arena-teams':
$this->filter = (new ArenaTeamListFilter(true, $opts));
break;
default: default:
return; return;
} }

View File

@@ -0,0 +1,82 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxGuild extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'profile' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
switch ($this->params[0])
{
case 'resync':
$this->handler = 'handleResync';
break;
case 'status':
$this->handler = 'handleStatus';
break;
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
profile: <empty> [optional, also get related chars]
return: 1
*/
protected function handleResync()
{
if ($guilds = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_guild WHERE id IN (?a)', $this->_get['id']))
foreach ($guilds as $g)
Profiler::scheduleResync(TYPE_GUILD, $g['realm'], $g['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']))
foreach ($chars as $c)
Profiler::scheduleResync(TYPE_PROFILE, $c['realm'], $c['realmGUID']);
return '1';
}
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus()
{
$response = Profiler::resyncStatus(TYPE_GUILD, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -5,11 +5,39 @@ if (!defined('AOWOW_REVISION'))
class AjaxProfile extends AjaxHandler class AjaxProfile extends AjaxHandler
{ {
protected $validParams = ['link', 'unlink', 'pin', 'unpin', 'public', 'private', 'avatar', 'resync', 'status', 'delete', 'purge', 'summary', 'load']; private $undo = false;
protected $validParams = ['link', 'unlink', 'pin', 'unpin', 'public', 'private', 'avatar', 'resync', 'status', 'save', 'delete', 'purge', 'summary', 'load'];
protected $_get = array( protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkId']], 'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
// 'items' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItems']], 'items' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItemList']],
'size' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH 'size' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
'guild' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
'arena-team' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
);
protected $_post = array(
'name' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext']],
'level' => [FILTER_SANITIZE_NUMBER_INT, null],
'class' => [FILTER_SANITIZE_NUMBER_INT, null],
'race' => [FILTER_SANITIZE_NUMBER_INT, null],
'gender' => [FILTER_SANITIZE_NUMBER_INT, null],
'nomodel' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree1' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree2' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree3' => [FILTER_SANITIZE_NUMBER_INT, null],
'activespec' => [FILTER_SANITIZE_NUMBER_INT, null],
'talentbuild1' => [FILTER_SANITIZE_STRING, 0xC],// FILTER_FLAG_STRIP_LOW | *_HIGH
'glyphs1' => [FILTER_SANITIZE_STRING, 0xC],
'talentbuild2' => [FILTER_SANITIZE_STRING, 0xC],
'glyphs2' => [FILTER_SANITIZE_STRING, 0xC],
'icon' => [FILTER_SANITIZE_STRING, 0xC],
'description' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext']],
'source' => [FILTER_SANITIZE_NUMBER_INT, null],
'copy' => [FILTER_SANITIZE_NUMBER_INT, null],
'public' => [FILTER_SANITIZE_NUMBER_INT, null],
'gearscore' => [FILTER_SANITIZE_NUMBER_INT, null],
'inv' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItemString', 'flags' => FILTER_REQUIRE_ARRAY]],
); );
public function __construct(array $params) public function __construct(array $params)
@@ -21,24 +49,29 @@ class AjaxProfile extends AjaxHandler
switch ($this->params[0]) switch ($this->params[0])
{ {
case 'link':
case 'unlink': case 'unlink':
$this->undo = true;
case 'link':
$this->handler = 'handleLink'; // always returns null $this->handler = 'handleLink'; // always returns null
break; break;
case 'pin':
case 'unpin': case 'unpin':
$this->undo = true;
case 'pin':
$this->handler = 'handlePin'; // always returns null $this->handler = 'handlePin'; // always returns null
break; break;
case 'public':
case 'private': case 'private':
$this->undo = true;
case 'public':
$this->handler = 'handlePrivacy'; // always returns null $this->handler = 'handlePrivacy'; // always returns null
break; break;
case 'avatar': case 'avatar':
$this->handler = 'handleAvatar'; // sets an image header $this->handler = 'handleAvatar'; // sets an image header
break; // so it has to die here or another header will be set break; // so it has to die here or another header will be set
case 'resync': case 'resync':
$this->handler = 'handleResync'; // always returns "1"
break;
case 'status': case 'status':
$this->handler = 'handleResync'; $this->handler = 'handleStatus'; // returns status object
break; break;
case 'save': case 'save':
$this->handler = 'handleSave'; $this->handler = 'handleSave';
@@ -57,33 +90,86 @@ class AjaxProfile extends AjaxHandler
} }
} }
protected function handleLink($id, $mode) // links char with account /* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handleLink() // links char with account
{ {
/* params if (!User::$id || empty($this->_get['id']))
id: <prId1,prId2,..,prIdN> return;
user: <string> [optional]
return: null $uid = User::$id;
*/ if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
return;
if ($this->undo)
DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE accountId = ?d AND profileId IN (?a)', $uid, $this->_get['id']);
else
foreach ($this->_get['id'] as $prId) // only link characters, not custom profiles
if ($prId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE id = ?d AND realm IS NOT NULL', $prId))
DB::Aowow()->query('INSERT IGNORE INTO ?_account_profiles VALUES (?d, ?d, 0)', $uid, $prId);
} }
protected function handlePin($id, $mode) // (un)favorite /* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handlePin() // (un)favorite
{ {
/* params if (!User::$id || empty($this->_get['id'][0]))
id: <prId1,prId2,..,prIdN> return;
user: <string> [optional]
return: null $uid = User::$id;
*/ if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
return;
// since only one character can be pinned at a time we can reset everything
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags & ?d WHERE accountId = ?d', ~PROFILER_CU_PINNED, $uid);
// and set a single char if nesecary
if (!$this->undo)
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags | ?d WHERE profileId = ?d AND accountId = ?d', PROFILER_CU_PINNED, $this->_get['id'][0], $uid);
} }
protected function handlePrivacy($id, $mode) // public visibility /* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handlePrivacy() // public visibility
{ {
/* params if (!User::$id || empty($this->_get['id'][0]))
id: <prId1,prId2,..,prIdN> return;
user: <string> [optional]
return: null $uid = User::$id;
*/ if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
return;
if ($this->undo)
{
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags & ?d WHERE profileId IN (?a) AND accountId = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags & ?d WHERE id IN (?a) AND user = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
}
else
{
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags | ?d WHERE profileId IN (?a) AND accountId = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags | ?d WHERE id IN (?a) AND user = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
}
} }
/* params
id: <prId>
size: <string> [optional]
return: image-header
*/
protected function handleAvatar() // image protected function handleAvatar() // image
{ {
// something happened in the last years: those textures do not include tiny icons // something happened in the last years: those textures do not include tiny icons
@@ -125,199 +211,506 @@ class AjaxProfile extends AjaxHandler
return; return;
} }
protected function handleResync() // resync init and status requests /* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
return: 1
*/
protected function handleResync()
{ {
/* params if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE id IN (?a)', $this->_get['id']))
id: <prId1,prId2,..,prIdN> foreach ($chars as $c)
user: <string> [optional] Profiler::scheduleResync(TYPE_PROFILE, $c['realm'], $c['realmGUID']);
return
null [onOK]
int or str [onError]
*/
if ($this->params[0] == 'resync') return '1';
return '1';
else // $this->params[0] == 'status'
{
/*
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
[
processId,
[StatusCode, timeToRefresh, iCount, errorCode, iNResyncs],
[<anotherStatus>]...
]
*/
return '[0, [4, 10000, 1, 2]]';
}
} }
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus()
{
// roster resync for this guild was requested -> get char list
if ($this->_get['guild'])
$ids = DB::Aowow()->selectCol('SELECT id FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']);
else if ($this->_get['arena-team'])
$ids = DB::Aowow()->selectCol('SELECT profileId FROM ?_profiler_arena_team_member WHERE arenaTeamId IN (?a)', $this->_get['id']);
else
$ids = $this->_get['id'];
$response = Profiler::resyncStatus(TYPE_PROFILE, $ids);
return Util::toJSON($response);
}
/* params (get))
id: <prId1,0> [0: new profile]
params (post)
<various char data> [see below]
return:
proileId [onSuccess]
-1 [onError]
*/
protected function handleSave() // unKill a profile protected function handleSave() // unKill a profile
{ {
/* params GET // todo (med): detail check this post-data
id: <prId1,prId2,..,prIdN> $cuProfile = array(
params POST 'user' => User::$id,
name, level, class, race, gender, nomodel, talenttree1, talenttree2, talenttree3, activespec, talentbuild1, glyphs1, talentbuild2, glyphs2, gearscore, icon, public [always] // 'userName' => User::$displayName,
description, source, copy, inv { inventory: array containing itemLinks } [optional] 'name' => $this->_post['name'],
} 'level' => $this->_post['level'],
return 'class' => $this->_post['class'],
int > 0 [profileId, if we came from an armoryProfile create a new one] 'race' => $this->_post['race'],
int < 0 [onError] 'gender' => $this->_post['gender'],
str [onError] 'nomodelMask' => $this->_post['nomodel'],
*/ 'talenttree1' => $this->_post['talenttree1'],
'talenttree2' => $this->_post['talenttree2'],
'talenttree3' => $this->_post['talenttree3'],
'talentbuild1' => $this->_post['talentbuild1'],
'talentbuild2' => $this->_post['talentbuild2'],
'activespec' => $this->_post['activespec'],
'glyphs1' => $this->_post['glyphs1'],
'glyphs2' => $this->_post['glyphs2'],
'gearscore' => $this->_post['gearscore'],
'icon' => $this->_post['icon'],
'cuFlags' => PROFILER_CU_PROFILE | ($this->_post['public'] ? PROFILER_CU_PUBLISHED : 0)
);
return 'NYI'; if (strstr($cuProfile['icon'], 'profile=avatar')) // how the profiler is supposed to handle icons is beyond me
$cuProfile['icon'] = '';
if ($_ = $this->_post['description'])
$cuProfile['description'] = $_;
if ($_ = $this->_post['source']) // should i also set sourcename?
$cuProfile['sourceId'] = $_;
if ($_ = $this->_post['copy']) // gets set to source profileId when "save as" is clicked. Whats the difference to 'source' though?
{
// get character origin info if possible
if ($r = DB::Aowow()->selectCell('SELECT realm FROM ?_profiler_profiles WHERE id = ?d AND realm IS NOT NULL', $_))
$cuProfile['realm'] = $r;
$cuProfile['sourceId'] = $_;
}
if ($cuProfile['sourceId'])
$cuProfile['sourceName'] = DB::Aowow()->selectCell('SELECT name FROM ?_profiler_profiles WHERE id = ?d', $cuProfile['sourceId']);
$charId = -1;
if ($id = $this->_get['id'][0]) // update
{
if ($charId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE id = ?d', $id))
DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE id = ?d', $cuProfile, $id);
}
else // new
{
$nProfiles = DB::Aowow()->selectCell('SELECT COUNT(*) FROM ?_profiler_profiles WHERE user = ?d AND realmGUID IS NULL', User::$id);
if ($nProfiles < 10 || User::isPremium())
if ($newId = DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES (?a)', array_keys($cuProfile), array_values($cuProfile)))
$charId = $newId;
}
// update items
if ($charId != -1)
{
// ok, 'funny' thing: wether an item has en extra prismatic sockel is determined contextual
// either the socket is -1 or it has an itemId in a socket where there shouldn't be one
$keys = ['id', 'slot', 'item', 'subitem', 'permEnchant', 'tempEnchant', 'gem1', 'gem2', 'gem3', 'gem4'];
// validate Enchantments
$enchIds = array_merge(
array_column($this->_post['inv'], 3), // perm enchantments
array_column($this->_post['inv'], 4) // temp enchantments (not used..?)
);
$enchs = new EnchantmentList(array(['id', $enchIds]));
// validate items
$itemIds = array_merge(
array_column($this->_post['inv'], 1), // base item
array_column($this->_post['inv'], 5), // gem slot 1
array_column($this->_post['inv'], 6), // gem slot 2
array_column($this->_post['inv'], 7), // gem slot 3
array_column($this->_post['inv'], 8) // gem slot 4
);
$items = new ItemList(array(['id', $itemIds]));
if (!$items->error)
{
foreach ($this->_post['inv'] as $slot => $itemData)
{
if ($slot + 1 == array_sum($itemData)) // only slot definition set => empty slot
{
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d AND slot = ?d', $charId, $itemData[0]);
continue;
}
// item does not exist
if (!$items->getEntry($itemData[1]))
continue;
// sub-item check
if (!$items->getRandEnchantForItem($itemData[1]))
$itemData[2] = 0;
// item sockets are fubar
$nSockets = $items->json[$itemData[1]]['nsockets'];
$nSockets += in_array($slot, [SLOT_WAIST, SLOT_WRISTS, SLOT_HANDS]) ? 1 : 0;
for ($i = 5; $i < 9; $i++)
if ($itemData[$i] > 0 && (!$items->getEntry($itemData[$i]) || $i >= (5 + $nSockets)))
$itemData[$i] = 0;
// item enchantments are borked
if ($itemData[3] && !$enchs->getEntry($itemData[3]))
$itemData[3] = 0;
if ($itemData[4] && !$enchs->getEntry($itemData[4]))
$itemData[4] = 0;
// looks good
array_unshift($itemData, $charId);
DB::Aowow()->query('REPLACE INTO ?_profiler_items (?#) VALUES (?a)', $keys, $itemData);
}
}
}
return $charId;
} }
/* params
id: <prId1,prId2,..,prIdN>
return
null
*/
protected function handleDelete() // kill a profile protected function handleDelete() // kill a profile
{ {
/* params if (!$this->_get['id'])
id: <prId1,prId2,..,prIdN> return;
return
null
*/
return 'NYI'; // only flag as deleted; only custom profiles
} DB::Aowow()->query(
'UPDATE ?_profiler_profiles SET cuFlags = cuFlags | ?d WHERE id IN (?a) AND cuFlags & ?d {AND user = ?d}',
protected function handlePurge() // removes certain saved information but not the entire character PROFILER_CU_DELETED,
{ $this->_get['id'],
/* params PROFILER_CU_PROFILE,
id: <prId1,prId2,..,prIdN> User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) ? DBSIMPLE_SKIP : User::$id
data: <mode> [string, tabName?] );
return
null
*/
return 'NYI';
} }
/* params
id: profileId
items: string [itemIds.join(':')]
unnamed: unixtime [only to force the browser to reload instead of cache]
return
lots...
*/
protected function handleLoad() protected function handleLoad()
{ {
/* params // titles, achievements, characterData, talents, pets
id: profileId
items: string [itemIds.join(':')]
unnamed: unixtime [only to force the browser to reload instead of cache]
return
lots...
*/
// titles, achievements, characterData, talents (, pets)
// and some onLoad-hook to .. load it registerProfile($data) // and some onLoad-hook to .. load it registerProfile($data)
// everything else goes through data.php .. strangely enough // everything else goes through data.php .. strangely enough
if (!$this->_get['id']) if (!$this->_get['id'])
return; return;
$char = new ProfileList(array(['id', $this->_get['id'][0]])); // or string or whatever $pBase = DB::Aowow()->selectRow('SELECT pg.name AS guildname, p.* FROM ?_profiler_profiles p LEFT JOIN ?_profiler_guild pg ON pg.id = p.guild WHERE p.id = ?d', $this->_get['id'][0]);
if (!$pBase)
{
trigger_error('Profiler::handleLoad() - called with invalid profileId #'.$this->_get['id'][0], E_USER_WARNING);
return;
}
if (($pBase['cuFlags'] & PROFILER_CU_DELETED) && !User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
return;
$rData = [];
foreach (Profiler::getRealms() as $rId => $rData)
if ($rId == $pBase['realm'])
break;
$profile = array(
'id' => $pBase['id'],
'source' => $pBase['id'],
'level' => $pBase['level'],
'classs' => $pBase['class'],
'race' => $pBase['race'],
'faction' => Game::sideByRaceMask(1 << ($pBase['race'] - 1)) - 1,
'gender' => $pBase['gender'],
'skincolor' => $pBase['skincolor'],
'hairstyle' => $pBase['hairstyle'],
'haircolor' => $pBase['haircolor'],
'facetype' => $pBase['facetype'],
'features' => $pBase['features'],
'title' => $pBase['title'],
'name' => $pBase['name'],
'guild' => "$'".$pBase['guildname']."'",
'published' => !!($pBase['cuFlags'] & PROFILER_CU_PUBLISHED),
'pinned' => !!($pBase['cuFlags'] & PROFILER_CU_PINNED),
'nomodel' => $pBase['nomodelMask'],
'playedtime' => $pBase['playedtime'],
'lastupdated' => $pBase['lastupdated'] * 1000,
'talents' => array(
'builds' => array( // notice the bullshit to prevent the talent-string from becoming a float! NOTICE IT!!
['talents' => '$"'.$pBase['talentbuild1'].'"', 'glyphs' => $pBase['glyphs1']],
['talents' => '$"'.$pBase['talentbuild2'].'"', 'glyphs' => $pBase['glyphs2']]
),
'active' => $pBase['activespec']
),
// set later
'inventory' => [],
'bookmarks' => [], // list of userIds who claimed this profile (claiming and owning are two different things)
// completion lists: [subjectId => amount/timestamp/1]
'skills' => [], // skillId => [curVal, maxVal]
'reputation' => [], // factionId => curVal
'titles' => [], // titleId => 1
'spells' => [], // spellId => 1; recipes, vanity pets, mounts
'achievements' => [], // achievementId => timestamp
'quests' => [], // questId => 1
'achievementpoints' => 0, // max you have
'statistics' => [], // all raid activity [achievementId => killCount]
'activity' => [], // recent raid activity [achievementId => 1] (is a subset of statistics)
);
if ($pBase['cuFlags'] & PROFILER_CU_PROFILE)
{
// this parameter is _really_ strange .. probably still not doing this right
$profile['source'] = $pBase['realm'] ? $pBase['sourceId'] : 0;
$profile['sourcename'] = $pBase['sourceName'];
$profile['description'] = $pBase['description'];
$profile['user'] = $pBase['user'];
$profile['username'] = DB::Aowow()->selectCell('SELECT displayName FROM ?_account WHERE id = ?d', $pBase['user']);
}
// custom profiles inherit this when copied from real char :(
if ($pBase['realm'])
{
$profile['region'] = [$rData['region'], Lang::profiler('regions', $rData['region'])];
$profile['battlegroup'] = [Profiler::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP];
$profile['realm'] = [Profiler::urlize($rData['name']), $rData['name']];
}
// bookmarks
if ($_ = DB::Aowow()->selectCol('SELECT accountId FROM ?_account_profiles WHERE profileId = ?d', $pBase['id']))
$profile['bookmarks'] = $_;
// arena teams - [size(2|3|5) => DisplayName]; DisplayName gets urlized to use as link
if ($at = DB::Aowow()->selectCol('SELECT type AS ARRAY_KEY, name FROM ?_profiler_arena_team at JOIN ?_profiler_arena_team_member atm ON atm.arenaTeamId = at.id WHERE atm.profileId = ?d', $pBase['id']))
$profile['arenateams'] = $at;
// pets if hunter fields: [name:name, family:petFamily, npc:npcId, displayId:modelId, talents:talentString]
if ($pets = DB::Aowow()->select('SELECT name, family, npc, displayId, talents FROM ?_profiler_pets WHERE owner = ?d', $pBase['id']))
$profile['pets'] = $pets;
// source for custom profiles; profileId => [name, ownerId, iconString(optional)]
if ($customs = DB::Aowow()->select('SELECT id AS ARRAY_KEY, name, user, icon FROM ?_profiler_profiles WHERE sourceId = ?d AND sourceId <> id', $pBase['id']))
{
foreach ($customs as $id => $cu)
{
if (!$cu['icon'])
unset($cu['icon']);
$profile['customs'][$id] = array_values($cu);
}
}
/* $profile[]
// CUSTOM
'auras' => [], // custom list of buffs, debuffs [spellId]
// UNUSED
'glyphs' => [], // provided list of already known glyphs (post cataclysm feature)
*/
$completion = DB::Aowow()->select('SELECT type AS ARRAY_KEY, typeId AS ARRAY_KEY2, cur, max FROM ?_profiler_completion WHERE id = ?d', $pBase['id']);
foreach ($completion as $type => $data)
{
switch ($type)
{
case TYPE_FACTION: // factionId => amount
$profile['reputation'] = array_combine(array_keys($data), array_column($data, 'cur'));
break;
case TYPE_TITLE:
foreach ($data as &$d)
$d = 1;
$profile['titles'] = $data;
break;
case TYPE_QUEST:
foreach ($data as &$d)
$d = 1;
$profile['quests'] = $data;
break;
case TYPE_SPELL:
foreach ($data as &$d)
$d = 1;
$profile['spells'] = $data;
break;
case TYPE_ACHIEVEMENT:
$achievements = array_filter($data, function ($x) { return $x['max'] === null; });
$statistics = array_filter($data, function ($x) { return $x['max'] !== null; });
// achievements
$profile['achievements'] = array_combine(array_keys($achievements), array_column($achievements, 'cur'));
$profile['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_keys($achievements));
// raid progression
$activity = array_filter($statistics, function ($x) { return $x['cur'] > (time() - MONTH); });
foreach ($activity as &$r)
$r = 1;
// ony .. subtract 10-man from 25-man
$profile['statistics'] = array_combine(array_keys($statistics), array_column($statistics, 'max'));
$profile['activity'] = $activity;
break;
case TYPE_SKILL:
foreach ($data as &$d)
$d = [$d['cur'], $d['max']];
$profile['skills'] = $data;
break;
}
}
$buff = ''; $buff = '';
if ($it = array_column($char->getField('inventory'), 0)) $usedSlots = [];
if ($this->_get['items'])
{ {
$itemz = new ItemList(array(['id', $it, CFG_SQL_LIMIT_NONE])); $phItems = new ItemList(array(['id', $this->_get['items']], ['slot', INVTYPE_NON_EQUIP, '!']));
$data = $itemz->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS); if (!$phItems->error)
// get and apply inventory
foreach ($itemz->iterate() as $iId => $__)
$buff .= 'g_items.add('.$iId.', {name_'.User::$localeString.":'".Util::jsEscape($itemz->getField('name', true))."', quality:".$itemz->getField('quality').", icon:'".$itemz->getField('iconString')."', jsonequip:".Util::toJSON($data[$iId])."});\n";
$buff .= "\n";
}
if ($au = $char->getField('auras'))
{
$auraz = new SpellList(array(['id', $char->getField('auras')], CFG_SQL_LIMIT_NONE));
$dataz = $auraz->getListviewData();
$modz = $auraz->getProfilerMods();
// get and apply aura-mods
foreach ($dataz as $id => $data)
{ {
$mods = []; $data = $phItems->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
if (!empty($modz[$id])) foreach ($phItems->iterate() as $iId => $__)
{ {
foreach ($modz[$id] as $k => $v) $sl = $phItems->getField('slot');
foreach (Profiler::$slot2InvType as $slot => $invTypes)
{ {
if (is_array($v)) if (in_array($sl, $invTypes) && !in_array($slot, $usedSlots))
$mods[] = $v; {
else if ($str = @Game::$itemMods[$k]) // get and apply inventory
$mods[$str] = $v; $buff .= 'g_items.add('.$iId.', {name_'.User::$localeString.":'".Util::jsEscape($phItems->getField('name', true))."', quality:".$phItems->getField('quality').", icon:'".$phItems->getField('iconString')."', jsonequip:".Util::toJSON($data[$iId])."});\n";
$profile['inventory'][$slot] = [$iId, 0, 0, 0, 0, 0, 0, 0];
$usedSlots[] = $slot;
break;
}
} }
} }
$buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', modifier:".Util::toJSON($mods)."});\n";
} }
$buff .= "\n";
} }
/* depending on progress-achievements if ($items = DB::Aowow()->select('SELECT * FROM ?_profiler_items WHERE id = ?d', $pBase['id']))
// required by progress in JScript move to handleLoad()? {
Util::$pageTemplate->extendGlobalIds(TYPE_NPC, [29120, 31134, 29306, 29311, 23980, 27656, 26861, 26723, 28923, 15991]); $itemz = new ItemList(array(['id', array_column($items, 'item')], CFG_SQL_LIMIT_NONE));
*/ if (!$itemz->error)
{
$data = $itemz->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
foreach ($items as $i)
{
if ($itemz->getEntry($i['item']) && !in_array($i['slot'], $usedSlots))
{
// get and apply inventory
$buff .= 'g_items.add('.$i['item'].', {name_'.User::$localeString.":'".Util::jsEscape($itemz->getField('name', true))."', quality:".$itemz->getField('quality').", icon:'".$itemz->getField('iconString')."', jsonequip:".Util::toJSON($data[$i['item']])."});\n";
$profile['inventory'][$i['slot']] = [$i['item'], $i['subItem'], $i['permEnchant'], $i['tempEnchant'], $i['gem1'], $i['gem2'], $i['gem3'], $i['gem4']];
}
}
}
}
if ($buff)
$buff .= "\n";
// if ($au = $char->getField('auras'))
// {
// $auraz = new SpellList(array(['id', $char->getField('auras')], CFG_SQL_LIMIT_NONE));
// $dataz = $auraz->getListviewData();
// $modz = $auraz->getProfilerMods();
// // get and apply aura-mods
// foreach ($dataz as $id => $data)
// {
// $mods = [];
// if (!empty($modz[$id]))
// {
// foreach ($modz[$id] as $k => $v)
// {
// if (is_array($v))
// $mods[] = $v;
// else if ($str = @Game::$itemMods[$k])
// $mods[$str] = $v;
// }
// }
// $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', modifier:".Util::toJSON($mods)."});\n";
// }
// $buff .= "\n";
// }
// load available titles // load available titles
Util::loadStaticFile('p-titles-'.$char->getField('gender'), $buff, true); Util::loadStaticFile('p-titles-'.$pBase['gender'], $buff, true);
// load available achievements
if (!Util::loadStaticFile('p-achievements', $buff, true))
{
$buff .= "\n\ng_achievement_catorder = [];";
$buff .= "\n\ng_achievement_points = [0];";
}
// excludes; structure UNK type => [maskBit => [typeIds]] ?
/*
g_user.excludes = [type:[typeIds]]
g_user.includes = [type:[typeIds]]
g_user.excludegroups = groupMask // requires g_user.settings != null
maskBit are matched against fieldId from excludeGroups
id: 1, label: LANG.dialog_notavail
id: 2, label: LANG.dialog_tcg
id: 4, label: LANG.dialog_collector
id: 8, label: LANG.dialog_promo
id: 16, label: LANG.dialog_nonus
id: 96, label: LANG.dialog_faction
id: 896, label: LANG.dialog_profession
id: 1024, label: LANG.dialog_noexalted
*/
// $buff .= "\n\ng_excludes = {};";
// add profile to buffer // add profile to buffer
$buff .= "\n\n\$WowheadProfiler.registerProfile(".Util::toJSON($char->getEntry(2)).");"; // can't use JSON_NUMERIC_CHECK or the talent-string becomes a float $buff .= "\n\n\$WowheadProfiler.registerProfile(".Util::toJSON($profile).");";
return $buff."\n"; return $buff."\n";
} }
protected function checkId($val) /* params
id: <prId>
data: <mode> [string, tabName]
return
null
*/
protected function handlePurge() { } // removes completion data (as uploaded by the wowhead client) Just fail silently if someone triggers this manually
protected function checkItemList($val)
{ {
// expecting id-list // expecting item-list
if (preg_match('/\d+(,\d+)*/', $val)) if (preg_match('/\d+(:\d+)*/', $val))
return array_map('intVal', explode(',', $val)); return array_map('intval', explode(':', $val));
return null; return null;
} }
protected function checkItems($val) protected function checkItemString($val)
{ {
// expecting item-list // expecting item-list
if (preg_match('/\d+(:\d+)*/', $val)) if (preg_match('/\d+(,\d+)*/', $val))
return array_map('intVal', explode(': ', $val)); return array_map('intval', explode(',', $val));
return null; return null;
} }
} }
?> ?>

View File

@@ -222,8 +222,8 @@ abstract class BaseType
if (!in_array($k, $prefixes)) if (!in_array($k, $prefixes))
unset($this->queryOpts[$k]); unset($this->queryOpts[$k]);
// prepare usage of guids if using multiple DBs // prepare usage of guids if using multiple realms (which have non-zoro indizes)
if (count($this->dbNames) > 1) if (key($this->dbNames) != 0)
$this->queryBase = preg_replace('/\s([^\s]+)\sAS\sARRAY_KEY/i', ' CONCAT("DB_IDX", ":", \1) AS ARRAY_KEY', $this->queryBase); $this->queryBase = preg_replace('/\s([^\s]+)\sAS\sARRAY_KEY/i', ' CONCAT("DB_IDX", ":", \1) AS ARRAY_KEY', $this->queryBase);
// insert additional selected fields // insert additional selected fields
@@ -278,10 +278,6 @@ abstract class BaseType
if (!$this->templates) if (!$this->templates)
return; return;
// assign query results to template
foreach ($rows as $k => $tpl)
$this->templates[$k] = $tpl;
// push first element for instant use // push first element for instant use
$this->reset(); $this->reset();
@@ -388,12 +384,8 @@ abstract class BaseType
protected function extendQueryOpts($extra) // needs to be called from __construct protected function extendQueryOpts($extra) // needs to be called from __construct
{ {
foreach ($extra as $tbl => $sets) foreach ($extra as $tbl => $sets)
{ {
if (!isset($this->queryOpts[$tbl])) // allow adding only to known tables
continue;
foreach ($sets as $module => $value) foreach ($sets as $module => $value)
{ {
if (!$value || !is_array($value)) if (!$value || !is_array($value))
@@ -419,7 +411,7 @@ abstract class BaseType
break; break;
// additional (arr) // additional (arr)
case 'j': // join case 'j': // join
if (is_array($this->queryOpts[$tbl][$module])) if (!empty($this->queryOpts[$tbl][$module]) && is_array($this->queryOpts[$tbl][$module]))
$this->queryOpts[$tbl][$module][0][] = $value; $this->queryOpts[$tbl][$module][0][] = $value;
else else
$this->queryOpts[$tbl][$module] = $value; $this->queryOpts[$tbl][$module] = $value;
@@ -744,6 +736,32 @@ trait spawnHelper
} }
} }
trait profilerHelper
{
public static $type = 0; // arena teams dont actually have one
public static $brickFile = 'profile'; // profile is multipurpose
private static $subjectGUID = 0;
public function selectRealms($fi)
{
$this->dbNames = [];
foreach(Profiler::getRealms() as $idx => $r)
{
if (!empty($fi['sv']) && Profiler::urlize($r['name']) != Profiler::urlize($fi['sv']) && intVal($fi['sv']) != $idx)
continue;
if (!empty($fi['rg']) && Profiler::urlize($r['region']) != Profiler::urlize($fi['rg']))
continue;
$this->dbNames[$idx] = 'Characters';
}
return !!$this->dbNames;
}
}
/* /*
roight! roight!
just noticed, that the filters on pages originally pointed to ?filter=<pageName> just noticed, that the filters on pages originally pointed to ?filter=<pageName>

View File

@@ -28,7 +28,10 @@ define('TYPE_SKILL', 15);
define('TYPE_CURRENCY', 17); define('TYPE_CURRENCY', 17);
define('TYPE_SOUND', 19); define('TYPE_SOUND', 19);
define('TYPE_ICON', 29); define('TYPE_ICON', 29);
define('TYPE_PROFILE', 100);
// internal types (not published to js) // internal types (not published to js)
define('TYPE_GUILD', 101);
define('TYPE_ARENA_TEAM', 102);
define('TYPE_USER', 500); define('TYPE_USER', 500);
define('TYPE_EMOTE', 501); define('TYPE_EMOTE', 501);
define('TYPE_ENCHANTMENT', 502); define('TYPE_ENCHANTMENT', 502);
@@ -165,6 +168,7 @@ define('BUTTON_FORUM', 5);
define('BUTTON_TALENT', 6); define('BUTTON_TALENT', 6);
define('BUTTON_EQUIP', 7); define('BUTTON_EQUIP', 7);
define('BUTTON_PLAYLIST', 8); define('BUTTON_PLAYLIST', 8);
define('BUTTON_RESYNC', 9);
// generic filter handler // generic filter handler
define('FILTER_CR_BOOLEAN', 1); define('FILTER_CR_BOOLEAN', 1);
@@ -204,11 +208,17 @@ define('NPCINFO_REP', 0x4);
define('ACHIEVEMENTINFO_PROFILE', 0x1); define('ACHIEVEMENTINFO_PROFILE', 0x1);
define('PROFILEINFO_PROFILE', 0x1);
define('PROFILEINFO_CHARACTER', 0x2);
define('PROFILEINFO_GUILD', 0x10); // like &roster
define('PROFILEINFO_ARENA', 0x20);
define('SPAWNINFO_ZONES', 1); // not a mask, mutually exclusive define('SPAWNINFO_ZONES', 1); // not a mask, mutually exclusive
define('SPAWNINFO_SHORT', 2); define('SPAWNINFO_SHORT', 2);
define('SPAWNINFO_FULL', 3); define('SPAWNINFO_FULL', 3);
define('SPAWNINFO_QUEST', 4); define('SPAWNINFO_QUEST', 4);
// Community Content // Community Content
define('CC_FLAG_STICKY', 0x1); define('CC_FLAG_STICKY', 0x1);
define('CC_FLAG_DELETED', 0x2); define('CC_FLAG_DELETED', 0x2);
@@ -287,6 +297,12 @@ define('QUEST_CU_SKIP_LOG', 0x10);
define('QUEST_CU_AUTO_ACCEPT', 0x20); define('QUEST_CU_AUTO_ACCEPT', 0x20);
define('QUEST_CU_PVP_ENABLED', 0x40); define('QUEST_CU_PVP_ENABLED', 0x40);
define('PROFILER_CU_PUBLISHED', 0x01);
define('PROFILER_CU_PINNED', 0x02);
define('PROFILER_CU_DELETED', 0x04);
define('PROFILER_CU_PROFILE', 0x08);
define('PROFILER_CU_NEEDS_RESYNC', 0x10);
define('MAX_LEVEL', 80); define('MAX_LEVEL', 80);
define('WOW_BUILD', 12340); define('WOW_BUILD', 12340);
@@ -869,4 +885,31 @@ define('CND_DISTANCE_TO', 35); // distance to targe
define('CND_ALIVE', 36); // target is alive: NULL, NULL, NULL define('CND_ALIVE', 36); // target is alive: NULL, NULL, NULL
define('CND_HP_VAL', 37); // targets absolute health: amount, operator, NULL define('CND_HP_VAL', 37); // targets absolute health: amount, operator, NULL
define('CND_HP_PCT', 38); // targets relative health: amount, operator, NULL define('CND_HP_PCT', 38); // targets relative health: amount, operator, NULL
// profiler queue interactions
define('PR_QUEUE_STATUS_ENDED', 0);
define('PR_QUEUE_STATUS_WAITING', 1);
define('PR_QUEUE_STATUS_WORKING', 2);
define('PR_QUEUE_STATUS_READY', 3);
define('PR_QUEUE_STATUS_ERROR', 4);
define('PR_QUEUE_ERROR_UNK', 0);
define('PR_QUEUE_ERROR_CHAR', 1);
define('PR_QUEUE_ERROR_ARMORY', 2);
// profiler completion manager
define('PR_EXCLUDE_GROUP_UNAVAILABLE', 0x001);
define('PR_EXCLUDE_GROUP_TCG', 0x002);
define('PR_EXCLUDE_GROUP_COLLECTORS_EDITION', 0x004);
define('PR_EXCLUDE_GROUP_PROMOTION', 0x008);
define('PR_EXCLUDE_GROUP_WRONG_REGION', 0x010);
define('PR_EXCLUDE_GROUP_REQ_ALLIANCE', 0x020);
define('PR_EXCLUDE_GROUP_REQ_HORDE', 0x040);
define('PR_EXCLUDE_GROUP_OTHER_FACTION', PR_EXCLUDE_GROUP_REQ_ALLIANCE | PR_EXCLUDE_GROUP_REQ_HORDE);
define('PR_EXCLUDE_GROUP_REQ_FISHING', 0x080);
define('PR_EXCLUDE_GROUP_REQ_ENGINEERING', 0x100);
define('PR_EXCLUDE_GROUP_REQ_TAILORING', 0x200);
define('PR_EXCLUDE_GROUP_WRONG_PROFESSION', PR_EXCLUDE_GROUP_REQ_FISHING | PR_EXCLUDE_GROUP_REQ_ENGINEERING | PR_EXCLUDE_GROUP_REQ_TAILORING);
define('PR_EXCLUDE_GROUP_REQ_CANT_BE_EXALTED', 0x400);
define('PR_EXCLUDE_GROUP_ANY', 0x7FF);
?> ?>

View File

@@ -20,6 +20,7 @@ require_once 'includes/defines.php';
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master) require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
require_once 'includes/utilities.php'; // helper functions require_once 'includes/utilities.php'; // helper functions
require_once 'includes/game.php'; // game related data & functions require_once 'includes/game.php'; // game related data & functions
require_once 'includes/profiler.class.php';
require_once 'includes/user.class.php'; require_once 'includes/user.class.php';
require_once 'includes/markup.class.php'; // manipulate markup text require_once 'includes/markup.class.php'; // manipulate markup text
require_once 'includes/database.class.php'; // wrap DBSimple require_once 'includes/database.class.php'; // wrap DBSimple
@@ -43,10 +44,18 @@ spl_autoload_register(function ($class) {
{ {
require_once 'includes/basetype.class.php'; require_once 'includes/basetype.class.php';
if (file_exists('includes/types/'.strtr($class, ['list' => '']).'.class.php')) $cl = strtr($class, ['list' => '']);
require_once 'includes/types/'.strtr($class, ['list' => '']).'.class.php'; if ($cl == 'remoteprofile' || $cl == 'localprofile')
$cl = 'profile';
if ($cl == 'remotearenateam' || $cl == 'localarenateam')
$cl = 'arenateam';
if ($cl == 'remoteguild' || $cl == 'localguild')
$cl = 'guild';
if (file_exists('includes/types/'.$cl.'.class.php'))
require_once 'includes/types/'.$cl.'.class.php';
else else
throw new Exception('could not register type class: '.$class); throw new Exception('could not register type class: '.$cl);
return; return;
} }
@@ -206,7 +215,7 @@ if (!CLI)
die('error: SITE_HOST or STATIC_HOST not configured'); die('error: SITE_HOST or STATIC_HOST not configured');
// Setup Session // Setup Session
if (CFG_SESSION_CACHE_DIR && Util::checkOrCreateDirectory(CFG_SESSION_CACHE_DIR)) if (CFG_SESSION_CACHE_DIR && Util::writeDir(CFG_SESSION_CACHE_DIR))
session_save_path(getcwd().'/'.CFG_SESSION_CACHE_DIR); session_save_path(getcwd().'/'.CFG_SESSION_CACHE_DIR);
session_set_cookie_params(15 * YEAR, '/', '', $secure, true); session_set_cookie_params(15 * YEAR, '/', '', $secure, true);

828
includes/profiler.class.php Normal file
View File

@@ -0,0 +1,828 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class Profiler
{
const PID_FILE = 'config/pr-queue-pid';
const CHAR_GMFLAGS = 0x1 | 0x8 | 0x10 | 0x20; // PLAYER_EXTRA_ :: GM_ON | TAXICHEAT | GM_INVISIBLE | GM_CHAT
private static $realms = [];
public static $slot2InvType = array(
1 => [INVTYPE_HEAD], // head
2 => [INVTYPE_NECK], // neck
3 => [INVTYPE_SHOULDERS], // shoulder
4 => [INVTYPE_BODY], // shirt
5 => [INVTYPE_CHEST, INVTYPE_ROBE], // chest
6 => [INVTYPE_WAIST], // waist
7 => [INVTYPE_LEGS], // legs
8 => [INVTYPE_FEET], // feet
9 => [INVTYPE_WRISTS], // wrists
10 => [INVTYPE_HANDS], // hands
11 => [INVTYPE_FINGER], // finger1
12 => [INVTYPE_FINGER], // finger2
13 => [INVTYPE_TRINKET], // trinket1
14 => [INVTYPE_TRINKET], // trinket2
15 => [INVTYPE_CLOAK], // chest
16 => [INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPON, INVTYPE_2HWEAPON], // mainhand
17 => [INVTYPE_WEAPONOFFHAND, INVTYPE_WEAPON, INVTYPE_HOLDABLE, INVTYPE_SHIELD], // offhand
18 => [INVTYPE_RANGED, INVTYPE_THROWN, INVTYPE_RELIC], // ranged + relic
19 => [INVTYPE_TABARD], // tabard
);
public static $raidProgression = array( // statisticAchievement => relevantCriterium
1098 => 3271, // Onyxia's Lair 10
1756 => 13345, // Onyxia's Lair 25
4031 => 12230, 4034 => 12234, 4038 => 12238, 4042 => 12242, 4046 => 12246, // Trial of the Crusader 25 nh
4029 => 12231, 4035 => 12235, 4039 => 12239, 4043 => 12243, 4047 => 12247, // Trial of the Crusader 25 hc
4030 => 12229, 4033 => 12233, 4037 => 12237, 4041 => 12241, 4045 => 12245, // Trial of the Crusader 10 hc
4028 => 12228, 4032 => 12232, 4036 => 12236, 4040 => 12240, 4044 => 12244, // Trial of the Crusader 10 nh
4642 => 13091, 4656 => 13106, 4661 => 13111, 4664 => 13114, 4667 => 13117, 4670 => 13120, 4673 => 13123, 4676 => 13126, 4679 => 13129, 4682 => 13132, 4685 => 13135, 4688 => 13138, // Icecrown Citadel 25 hc
4641 => 13092, 4655 => 13105, 4660 => 13109, 4663 => 13112, 4666 => 13115, 4669 => 13118, 4672 => 13121, 4675 => 13124, 4678 => 13127, 4681 => 13130, 4683 => 13133, 4687 => 13136, // Icecrown Citadel 25 nh
4640 => 13090, 4654 => 13104, 4659 => 13110, 4662 => 13113, 4665 => 13116, 4668 => 13119, 4671 => 13122, 4674 => 13125, 4677 => 13128, 4680 => 13131, 4684 => 13134, 4686 => 13137, // Icecrown Citadel 10 hc
4639 => 13089, 4643 => 13093, 4644 => 13094, 4645 => 13095, 4646 => 13096, 4647 => 13097, 4648 => 13098, 4649 => 13099, 4650 => 13100, 4651 => 13101, 4652 => 13102, 4653 => 13103, // Icecrown Citadel 10 nh
// 4823 => 13467, // Ruby Sanctum 25 hc
// 4820 => 13465, // Ruby Sanctum 25 nh
// 4822 => 13468, // Ruby Sanctum 10 hc
// 4821 => 13466, // Ruby Sanctum 10 nh
);
public static function getBuyoutForItem($itemId)
{
if (!$itemId)
return 0;
// try, when having filled char-DB at hand
// return DB::Characters()->selectCell('SELECT SUM(a.buyoutprice) / SUM(ii.count) FROM auctionhouse a JOIN item_instance ii ON ii.guid = a.itemguid WHERE ii.itemEntry = ?d', $itemId);
return 0;
}
public static function queueStart(&$msg = '')
{
$queuePID = self::queueStatus();
if ($queuePID)
{
$msg = 'queue already running';
return true;
}
if (OS_WIN) // here be gremlins! .. suggested was "start /B php prQueue" as background process. but that closes itself
pclose(popen('start php prQueue --log=cache/profiling.log', 'r'));
else
exec('php prQueue --log=cache/profiling.log > /dev/null 2>/dev/null &');
usleep(500000);
if (self::queueStatus())
return true;
else
{
$msg = 'failed to start queue';
return false;
}
}
public static function queueStatus()
{
if (!file_exists(self::PID_FILE))
return 0;
$pid = file_get_contents(self::PID_FILE);
$cmd = OS_WIN ? 'tasklist /NH /FO CSV /FI "PID eq %d"' : 'ps --no-headers p %d';
exec(sprintf($cmd, $pid), $out);
if ($out && stripos($out[0], $pid) !== false)
return $pid;
// have pidFile but no process with this pid
self::queueFree();
return 0;
}
public static function queueLock($pid)
{
$queuePID = self::queueStatus();
if ($queuePID && $queuePID != $pid)
{
trigger_error('pSync - another queue with PID #'.$queuePID.' is already running', E_USER_ERROR);
CLI::write('Profiler::queueLock() - another queue with PID #'.$queuePID.' is already runnung', CLI::LOG_ERROR);
return false;
}
// no queue running; create or overwrite pidFile
$ok = false;
if ($fh = fopen(self::PID_FILE, 'w'))
{
if (fwrite($fh, $pid))
$ok = true;
fclose($fh);
}
return $ok;
}
public static function queueFree()
{
unlink(self::PID_FILE);
}
public static function urlize($str, $allowLocales = false, $profile = false)
{
$search = ['<', '>', ' / ', "'"];
$replace = ['&lt;', '&gt;', '-', '' ];
$str = str_replace($search, $replace, $str);
if ($profile)
{
$str = str_replace(['(', ')'], ['', ''], $str);
$accents = array(
"ß" => "ss",
"á" => "a", "ä" => "a", "à" => "a", "â" => "a",
"è" => "e", "ê" => "e", "é" => "e", "ë" => "e",
"í" => "i", "î" => "i", "ì" => "i", "ï" => "i",
"ñ" => "n",
"ò" => "o", "ó" => "o", "ö" => "o", "ô" => "o",
"ú" => "u", "ü" => "u", "û" => "u", "ù" => "u",
"œ" => "oe",
"Á" => "A", "Ä" => "A", "À" => "A", "Â" => "A",
"È" => "E", "Ê" => "E", "É" => "E", "Ë" => "E",
"Í" => "I", "Î" => "I", "Ì" => "I", "Ï" => "I",
"Ñ" => "N",
"Ò" => "O", "Ó" => "O", "Ö" => "O", "Ô" => "O",
"Ú" => "U", "Ü" => "U", "Û" => "U", "Ù" => "U",
"Œ" => "Oe"
);
$str = strtr($str, $accents);
}
$str = trim($str);
if ($allowLocales)
$str = str_replace(' ', '-', $str);
else
$str = preg_replace('/[^a-z0-9]/i', '-', $str);
$str = str_replace('--', '-', $str);
$str = str_replace('--', '-', $str);
$str = rtrim($str, '-');
$str = strtolower($str);
return $str;
}
public static function getRealms()
{
if (DB::isConnectable(DB_AUTH) && !self::$realms)
{
self::$realms = DB::Auth()->select('SELECT id AS ARRAY_KEY, name, IF(timezone IN (8, 9, 10, 11, 12), "eu", "us") AS region FROM realmlist WHERE allowedSecurityLevel = 0 AND gamebuild = ?d', WOW_BUILD);
foreach (self::$realms as $rId => $rData)
{
if (DB::isConnectable(DB_CHARACTERS . $rId))
continue;
// realm in db but no connection info set
unset(self::$realms[$rId]);
}
}
return self::$realms;
}
private static function queueInsert($realmId, $guid, $type, $localId)
{
if ($rData = DB::Aowow()->selectRow('SELECT requestTime AS time, status FROM ?_profiler_sync WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d AND status <> ?d', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WORKING))
{
// not on already scheduled - recalc time and set status to PR_QUEUE_STATUS_WAITING
if ($rData['status'] != PR_QUEUE_STATUS_WAITING)
{
$newTime = CFG_DEBUG ? time() : max($rData['time'] + CFG_PROFILER_RESYNC_DELAY, time());
DB::Aowow()->query('UPDATE ?_profiler_sync SET requestTime = ?d, status = ?d, errorCode = 0 WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d', $newTime, PR_QUEUE_STATUS_WAITING, $realmId, $guid, $type, $localId);
}
}
else
DB::Aowow()->query('REPLACE INTO ?_profiler_sync (realm, realmGUID, `type`, typeId, requestTime, status, errorCode) VALUES (?d, ?d, ?d, ?d, UNIX_TIMESTAMP(), ?d, 0)', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WAITING);
}
public static function scheduleResync($type, $realmId, $guid)
{
$newId = 0;
switch ($type)
{
case TYPE_PROFILE:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_PROFILE, $newId);
break;
case TYPE_GUILD:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_GUILD, $newId);
break;
case TYPE_ARENA_TEAM:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_ARENA_TEAM, $newId);
break;
default:
trigger_error('scheduling resync for unknown type #'.$type.' omiting..', E_USER_WARNING);
return 0;
}
if (!$newId)
trigger_error('Profiler::scheduleResync() - tried to resync type #'.$type.' guid #'.$guid.' from realm #'.$realmId.' without preloaded data', E_USER_ERROR);
else if (!self::queueStart($msg))
trigger_error('Profiler::scheduleResync() - '.$msg, E_USER_ERROR);
return $newId;
}
public static function resyncStatus($type, array $subjectGUIDs)
{
$response = [CFG_PROFILER_QUEUE ? 2 : 0]; // in theory you could have multiple queues; used as divisor for: (15 / x) + 2
if (!$subjectGUIDs)
$response[] = [PR_QUEUE_STATUS_ENDED, 0, 0, PR_QUEUE_ERROR_CHAR];
else
{
// error out all profiles with status WORKING, that are older than 60sec
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE status = ?d AND requestTime < ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_UNK, PR_QUEUE_STATUS_WORKING, time() - MINUTE);
$subjectStatus = DB::Aowow()->select('SELECT typeId AS ARRAY_KEY, status, realm FROM ?_profiler_sync WHERE `type` = ?d AND typeId IN (?a)', $type, $subjectGUIDs);
$queue = DB::Aowow()->selectCol('SELECT CONCAT(type, ":", typeId) FROM ?_profiler_sync WHERE status = ?d AND requestTime < UNIX_TIMESTAMP() ORDER BY requestTime ASC', PR_QUEUE_STATUS_WAITING);
foreach ($subjectGUIDs as $guid)
{
if (empty($subjectStatus[$guid])) // whelp, thats some error..
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, PR_QUEUE_ERROR_UNK];
else if ($subjectStatus[$guid]['status'] == PR_QUEUE_STATUS_ERROR)
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, $subjectStatus[$guid]['errCode']];
else
$response[] = array(
$subjectStatus[$guid]['status'],
$subjectStatus[$guid]['status'] != PR_QUEUE_STATUS_READY ? CFG_PROFILER_RESYNC_PING : 0,
array_search($type.':'.$guid, $queue) + 1,
0,
1 // nResycTries - unsure about this one
);
}
}
return $response;
}
public static function getCharFromRealm($realmId, $charGuid)
{
$char = DB::Characters($realmId)->selectRow('SELECT c.* FROM characters c WHERE c.guid = ?d', $charGuid);
if (!$char)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a char listview is created or profile detail page is called
$profileId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $char['guid']);
CLI::write('fetching char #'.$charGuid.' from realm #'.$realmId);
CLI::write('writing...');
/*************/
/* equipment */
/*************/
/* enchantment-Indizes
* 0: permEnchant
* 3: tempEnchant
* 6: gem1
* 9: gem2
* 12: gem3
* 15: socketBonus [not used]
* 18: extraSocket [only check existance]
* 21 - 30: randomProp enchantments
*/
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d', $profileId);
$items = DB::Characters($realmId)->select('SELECT ci.slot AS ARRAY_KEY, ii.itemEntry, ii.enchantments, ii.randomPropertyId FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ?d AND bag = 0 AND slot BETWEEN 0 AND 18', $char['guid']);
$gemItems = [];
$permEnch = [];
$mhItem = 0;
$ohItem = 0;
foreach ($items as $slot => $item)
{
$ench = explode(' ', $item['enchantments']);
$gEnch = [];
foreach ([6, 9, 12] as $idx)
if ($ench[$idx])
$gEnch[$idx] = $ench[$idx];
if ($gEnch)
{
$gi = DB::Aowow()->selectCol('SELECT gemEnchantmentId AS ARRAY_KEY, id FROM ?_items WHERE class = 3 AND gemEnchantmentId IN (?a)', $gEnch);
foreach ($gEnch as $eId)
{
if (isset($gemItems[$eId]))
$gemItems[$eId][1]++;
else
$gemItems[$eId] = [$gi[$eId], 1];
}
}
if ($slot + 1 == 16)
$mhItem = $item['itemEntry'];
if ($slot + 1 == 17)
$ohItem = $item['itemEntry'];
if ($ench[0])
$permEnch[$slot] = $ench[0];
$data = array(
'id' => $profileId,
'slot' => $slot + 1,
'item' => $item['itemEntry'],
'subItem' => $item['randomPropertyId'],
'permEnchant' => $ench[0],
'tempEnchant' => $ench[3],
'extraSocket' => (int)!!$ench[18],
'gem1' => isset($gemItems[$ench[6]]) ? $gemItems[$ench[6]][0] : 0,
'gem2' => isset($gemItems[$ench[9]]) ? $gemItems[$ench[9]][0] : 0,
'gem3' => isset($gemItems[$ench[12]]) ? $gemItems[$ench[12]][0] : 0,
'gem4' => 0 // serverside items cant have more than 3 sockets. (custom profile thing)
);
DB::Aowow()->query('INSERT INTO ?_profiler_items (?#) VALUES (?a)', array_keys($data), array_values($data));
}
CLI::write(' ..inventory');
/**************/
/* basic info */
/**************/
$data = array(
'realm' => $realmId,
'realmGUID' => $charGuid,
'name' => $char['name'],
'race' => $char['race'],
'class' => $char['class'],
'level' => $char['level'],
'gender' => $char['gender'],
'skincolor' => $char['playerBytes'] & 0xFF,
'facetype' => ($char['playerBytes'] >> 8) & 0xFF, // maybe features
'hairstyle' => ($char['playerBytes'] >> 16) & 0xFF,
'haircolor' => ($char['playerBytes'] >> 24) & 0xFF,
'features' => $char['playerBytes2'] & 0xFF, // maybe facetype
'title' => $char['chosenTitle'] ? DB::Aowow()->selectCell('SELECT id FROM ?_titles WHERE bitIdx = ?d', $char['chosenTitle']) : 0,
'playedtime' => $char['totaltime'],
'nomodelMask' => ($char['playerFlags'] & 0x400 ? (1 << SLOT_HEAD) : 0) | ($char['playerFlags'] & 0x800 ? (1 << SLOT_BACK) : 0),
'talenttree1' => 0,
'talenttree2' => 0,
'talenttree3' => 0,
'talentbuild1' => '',
'talentbuild2' => '',
'glyphs1' => '',
'glyphs2' => '',
'activespec' => $char['activespec'],
'guild' => null,
'guildRank' => null,
'gearscore' => 0,
'achievementpoints' => 0
);
/********************/
/* talents + glyphs */
/********************/
$t = DB::Characters($realmId)->selectCol('SELECT spec AS ARRAY_KEY, spell AS ARRAY_KEY2, spell FROM character_talent WHERE guid = ?d', $char['guid']);
$g = DB::Characters($realmId)->select('SELECT spec AS ARRAY_KEY, glyph1 AS g1, glyph2 AS g4, glyph3 AS g5, glyph4 AS g2, glyph5 AS g3, glyph6 AS g6 FROM character_glyphs WHERE guid = ?d', $char['guid']);
for ($i = 0; $i < 2; $i++)
{
// talents
for ($j = 0; $j < 3; $j++)
{
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell in (?a), rank, 0)) FROM ?_talents WHERE class = ?d AND tab = ?d GROUP BY id ORDER BY row, col ASC', !empty($t[$i]) ? $t[$i] : [0], $char['class'], $j);
$data['talentbuild'.($i + 1)] .= implode('', $_);
if ($char['activespec'] == $i)
$data['talenttree'.($j + 1)] = array_sum($_);
}
// glyphs
if (isset($g[$i]))
{
$gProps = [];
for ($j = 1; $j <= 6; $j++)
if ($g[$i]['g'.$j])
$gProps[$j] = $g[$i]['g'.$j];
if ($gProps)
if ($gItems = DB::Aowow()->selectCol('SELECT i.id FROM ?_glyphproperties gp JOIN ?_spell s ON s.effect1MiscValue = gp.id AND s.effect1Id = 74 JOIN ?_items i ON i.class = 16 AND i.spellId1 = s.id WHERE gp.id IN (?a)', $gProps))
$data['glyphs'.($i + 1)] = implode(':', $gItems);
}
}
$t = array(
'spent' => [$data['talenttree1'], $data['talenttree2'], $data['talenttree3']],
'spec' => 0
);
if ($t['spent'][0] > $t['spent'][1] && $t['spent'][0] > $t['spent'][2])
$t['spec'] = 1;
else if ($t['spent'][1] > $t['spent'][0] && $t['spent'][1] > $t['spent'][2])
$t['spec'] = 2;
else if ($t['spent'][2] > $t['spent'][1] && $t['spent'][2] > $t['spent'][0])
$t['spec'] = 3;
// calc gearscore
if ($items)
$data['gearscore'] += (new ItemList(array(['id', array_column($items, 'itemEntry')])))->getScoreTotal($data['class'], $t, $mhItem, $ohItem);
if ($gemItems)
{
$gemScores = new ItemList(array(['id', array_column($gemItems, 0)]));
foreach ($gemItems as list($itemId, $mult))
if (isset($gemScores->json[$itemId]['gearscore']))
$data['gearscore'] += $gemScores->json[$itemId]['gearscore'] * $mult;
}
if ($permEnch) // fuck this shit .. we are guestimating this!
{
// enchantId => multiple spells => multiple items with varying itemlevels, quality, whatevs
// cant reasonably get to the castItem from enchantId and slot
$profSpec = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, skillLevel AS "1", skillLine AS "0" FROM ?_itemenchantment WHERE id IN (?a)', $permEnch);
foreach ($permEnch as $eId)
{
if ($x = Util::getEnchantmentScore(0, 0, !!$profSpec[$eId][1], $eId))
$data['gearscore'] += $x;
else if ($profSpec[$eId][0] != 776) // not runeforging
$data['gearscore'] += 17; // assume high quality enchantment for unknown cases
}
}
$data['lastupdated'] = time();
CLI::write(' ..basic info');
/***************/
/* hunter pets */
/***************/
if ((1 << ($char['class'] - 1)) == CLASS_HUNTER)
{
DB::Aowow()->query('DELETE FROM ?_profiler_pets WHERE owner = ?d', $profileId);
$pets = DB::Characters($realmId)->select('SELECT id AS ARRAY_KEY, id, entry, modelId, name FROM character_pet WHERE owner = ?d', $charGuid);
foreach ($pets as $petGuid => $petData)
{
$morePet = DB::Aowow()->selectRow('SELECT p.`type`, c.family FROM ?_pet p JOIN ?_creature c ON c.family = p.id WHERE c.id = ?d', $petData['entry']);
$petSpells = DB::Characters($realmId)->selectCol('SELECT spell FROM pet_spell WHERE guid = ?d', $petGuid);
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell in (?a), rank, 0)) FROM ?_talents WHERE class = 0 AND petTypeMask = ?d GROUP BY id ORDER BY row, col ASC', $petSpells ?: [0], 1 << $morePet['type']);
$pet = array(
'id' => $petGuid,
'owner' => $profileId,
'name' => $petData['name'],
'family' => $morePet['family'],
'npc' => $petData['entry'],
'displayId' => $petData['modelId'],
'talents' => implode('', $_)
);
DB::Aowow()->query('INSERT INTO ?_profiler_pets (?#) VALUES (?a)', array_keys($pet), array_values($pet));
}
CLI::write(' ..hunter pets');
}
/*******************/
/* completion data */
/*******************/
DB::Aowow()->query('DELETE FROM ?_profiler_completion WHERE id = ?d', $profileId);
// done quests
if ($quests = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, quest AS typeId FROM character_queststatus_rewarded WHERE guid = ?d', $profileId, TYPE_QUEST, $char['guid']))
foreach (Util::createSqlBatchInsert($quests) as $q)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$q, array_keys($quests[0]));
CLI::write(' ..quests');
// known skills (professions only)
$skAllowed = DB::Aowow()->selectCol('SELECT id FROM ?_skillline WHERE typeCat IN (9, 11) AND (cuFlags & ?d) = 0', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$skills = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, skill AS typeId, `value` AS cur, max FROM character_skills WHERE guid = ?d AND skill IN (?a)', $profileId, TYPE_SKILL, $char['guid'], $skAllowed);
// manually apply racial profession bonuses
foreach ($skills as &$sk)
{
// Blood Elves - Arcane Affinity
if ($sk['typeId'] == 333 && $char['race'] == 10)
{
$sk['cur'] += 10;
$sk['max'] += 10;
}
// Draenei - Gemcutting
if ($sk['typeId'] == 755 && $char['race'] == 11)
{
$sk['cur'] += 5;
$sk['max'] += 5;
}
// Tauren - Cultivation
// Gnomes - Engineering Specialization
if (($sk['typeId'] == 182 && $char['race'] == 6) ||
($sk['typeId'] == 202 && $char['race'] == 7))
{
$sk['cur'] += 15;
$sk['max'] += 15;
}
}
unset($sk);
if ($skills)
foreach (Util::createSqlBatchInsert($skills) as $sk)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$sk, array_keys($skills[0]));
CLI::write(' ..professions');
// reputation
if ($reputation = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, faction AS typeId, standing AS cur FROM character_reputation WHERE guid = ?d AND (flags & 0xC) = 0', $profileId, TYPE_FACTION, $char['guid']))
foreach (Util::createSqlBatchInsert($reputation) as $rep)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$rep, array_keys($reputation[0]));
CLI::write(' ..reputation');
// known titles
$tBlocks = explode(' ', $char['knownTitles']);
$indizes = [];
for ($i = 0; $i < 6; $i++)
for ($j = 0; $j < 32; $j++)
if ($tBlocks[$i] & (1 << $j))
$indizes[] = $j + ($i * 32);
if ($indizes)
DB::Aowow()->query('INSERT INTO ?_profiler_completion SELECT ?d, ?d, id, NULL, NULL FROM ?_titles WHERE bitIdx IN (?a)', $profileId, TYPE_TITLE, $indizes);
CLI::write(' ..titles');
// achievements
if ($achievements = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, achievement AS typeId, date AS cur FROM character_achievement WHERE guid = ?d', $profileId, TYPE_ACHIEVEMENT, $char['guid']))
{
foreach (Util::createSqlBatchInsert($achievements) as $a)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$a, array_keys($achievements[0]));
$data['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_column($achievements, 'typeId'));
}
CLI::write(' ..achievements');
// raid progression
if ($progress = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, criteria AS typeId, date AS cur, counter AS `max` FROM character_achievement_progress WHERE guid = ?d AND criteria IN (?a)', $profileId, TYPE_ACHIEVEMENT, $char['guid'], self::$raidProgression))
{
array_walk($progress, function (&$val) { $val['typeId'] = array_search($val['typeId'], self::$raidProgression); });
foreach (Util::createSqlBatchInsert($progress) as $p)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$p, array_keys($progress[0]));
}
CLI::write(' ..raid progression');
// known spells
if ($spells = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, spell AS typeId FROM character_spell WHERE guid = ?d AND disabled = 0', $profileId, TYPE_SPELL, $char['guid']))
foreach (Util::createSqlBatchInsert($spells) as $s)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$s, array_keys($spells[0]));
CLI::write(' ..known spells (vanity pets & mounts)');
/****************/
/* related data */
/****************/
// guilds
if ($guild = DB::Characters($realmId)->selectRow('SELECT g.name AS name, g.guildid AS id, gm.rank FROM guild_member gm JOIN guild g ON g.guildid = gm.guildid WHERE gm.guid = ?d', $char['guid']))
{
$guildId = 0;
if (!($guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['id'])))
{
$gData = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $guild['id'],
'name' => $guild['name'],
'nameUrl' => self::urlize($guild['name']),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$guildId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES (?a)', array_keys($gData), array_values($gData));
}
$data['guild'] = $guildId;
$data['guildRank'] = $guild['rank'];
}
// arena teams
$teams = DB::Characters($realmId)->select('SELECT at.arenaTeamId AS ARRAY_KEY, at.name, at.type, IF(at.captainGuid = atm.guid, 1, 0) AS captain, atm.* FROM arena_team at JOIN arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId WHERE atm.guid = ?d', $char['guid']);
foreach ($teams as $rGuid => $t)
{
$teamId = 0;
if (!($teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $rGuid)))
{
$team = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $rGuid,
'name' => $t['name'],
'nameUrl' => self::urlize($t['name']),
'type' => $t['type'],
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$teamId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($team), array_values($team));
}
$member = array(
'arenaTeamId' => $teamId,
'profileId' => $profileId,
'captain' => $t['captain'],
'weekGames' => $t['weekGames'],
'weekWins' => $t['weekWins'],
'seasonGames' => $t['seasonGames'],
'seasonWins' => $t['seasonWins'],
'personalRating' => $t['personalRating']
);
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES (?a) ON DUPLICATE KEY UPDATE ?a', array_keys($member), array_values($member), array_slice($member, 2));
}
CLI::write(' ..associated arena teams');
/*********************/
/* mark char as done */
/*********************/
if (DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE realm = ?d AND realmGUID = ?d', $data, $realmId, $charGuid) !== null)
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $profileId);
return true;
}
public static function getGuildFromRealm($realmId, $guildGuid)
{
$guild = DB::Characters($realmId)->selectRow('SELECT guildId, name, createDate, info, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM guild WHERE guildId = ?d', $guildGuid);
if (!$guild)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['guildId']);
CLI::write('fetching guild #'.$guildGuid.' from realm #'.$realmId);
CLI::write('writing...');
/**************/
/* Guild Data */
/**************/
unset($guild['guildId']);
$guild['nameUrl'] = self::urlize($guild['name']);
DB::Aowow()->query('UPDATE ?_profiler_guild SET ?a WHERE realm = ?d AND realmGUID = ?d', $guild, $realmId, $guildGuid);
// ranks
DB::Aowow()->query('DELETE FROM ?_profiler_guild_rank WHERE guildId = ?d', $guildId);
if ($ranks = DB::Characters($realmId)->select('SELECT ?d AS guildId, rid AS rank, rname AS name FROM guild_rank WHERE guildid = ?d', $guildId, $guildGuid))
foreach (Util::createSqlBatchInsert($ranks) as $r)
DB::Aowow()->query('INSERT INTO ?_profiler_guild_rank (?#) VALUES '.$r, array_keys(reset($ranks)));
CLI::write(' ..guild data');
/***************/
/* Member Data */
/***************/
$conditions = array(
['g.guildid', $guildGuid],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
// this here should all happen within ProfileList
$members = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$members->error)
$members->initializeLocalEntries();
else
return false;
CLI::write(' ..guild members');
/*********************/
/* mark guild as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_guild SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $guildId);
return true;
}
public static function getArenaTeamFromRealm($realmId, $teamGuid)
{
$team = DB::Characters($realmId)->selectRow('SELECT arenaTeamId, name, type, captainGuid, rating, seasonGames, seasonWins, weekGames, weekWins, rank, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM arena_team WHERE arenaTeamId = ?d', $teamGuid);
if (!$team)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $team['arenaTeamId']);
CLI::write('fetching arena team #'.$teamGuid.' from realm #'.$realmId);
CLI::write('writing...');
/*************/
/* Team Data */
/*************/
$captain = $team['captainGuid'];
unset($team['captainGuid']);
unset($team['arenaTeamId']);
$team['nameUrl'] = self::urlize($team['name']);
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET ?a WHERE realm = ?d AND realmGUID = ?d', $team, $realmId, $teamGuid);
CLI::write(' ..team data');
/***************/
/* Member Data */
/***************/
$members = DB::Characters($realmId)->select('
SELECT
atm.guid AS ARRAY_KEY, atm.arenaTeamId, atm.weekGames, atm.weekWins, atm.seasonGames, atm.seasonWins, atm.personalrating
FROM
arena_team_member atm
JOIN
characters c ON c.guid = atm.guid AND
c.deleteInfos_Account IS NULL AND
c.level <= ?d AND
(c.extra_flags & ?d) = 0
WHERE
arenaTeamId = ?d',
MAX_LEVEL,
self::CHAR_GMFLAGS,
$teamGuid
);
$conditions = array(
['c.guid', array_keys($members)],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
$mProfiles = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$mProfiles->error)
{
$mProfiles->initializeLocalEntries();
foreach ($mProfiles->iterate() as $__)
{
$mGuid = $mProfiles->getField('guid');
$members[$mGuid]['arenaTeamId'] = $teamId;
$members[$mGuid]['captain'] = (int)($mGuid == $captain);
$members[$mGuid]['profileId'] = $mProfiles->getField('id');
}
DB::Aowow()->query('DELETE FROM ?_profiler_arena_team_member WHERE arenaTeamId = ?d', $teamId);
foreach (Util::createSqlBatchInsert($members) as $m)
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES '.$m, array_keys(reset($members)));
}
else
return false;
CLI::write(' ..team members');
/*********************/
/* mark team as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $teamId);
return true;
}
}
?>

View File

@@ -0,0 +1,346 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenaTeamList extends BaseType
{
use profilerHelper, listviewHelper;
private $rankOrder = [];
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => $this->curTpl['name'],
'realm' => Profiler::urlize($this->curTpl['realmName']),
'realmname' => $this->curTpl['realmName'],
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
// 'battlegroupname' => $this->curTpl['battlegroup'],
'region' => Profiler::urlize($this->curTpl['region']),
'faction' => $this->curTpl['faction'],
'size' => $this->curTpl['type'],
'rank' => $this->curTpl['rank'],
'wins' => $this->curTpl['seasonWins'],
'games' => $this->curTpl['seasonGames'],
'rating' => $this->curTpl['rating'],
'members' => $this->curTpl['members']
);
}
return array_values($data);
}
public function renderTooltip() {}
public function getJSGlobals($addMask = 0) {}
}
class ArenaTeamListFilter extends Filter
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'sz' => [FILTER_V_LIST, [2, 3, 5], false], // tema size
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['at.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
// size [int]
if (!empty($_v['sz']))
$parts[] = ['at.type', $_v['sz']];
return $parts;
}
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
class RemoteArenaTeamList extends ArenaTeamList
{
protected $queryBase = 'SELECT `at`.*, `at`.`arenaTeamId` AS ARRAY_KEY FROM arena_team at';
protected $queryOpts = array(
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
'atm' => ['j' => 'arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId'],
'c' => ['j' => 'characters c ON c.guid = atm.guid AND c.deleteInfos_Account IS NULL AND c.level <= 80 AND (c.extra_flags & '.Profiler::CHAR_GMFLAGS.') = 0', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
);
private $members = [];
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
// ranks in DB are inaccurate. recalculate from rating (fetched as DESC from DB)
foreach ($this->dbNames as $rId => $__)
foreach ([2, 3, 5] as $type)
$this->rankOrder[$rId][$type] = DB::Characters($rId)->selectCol('SELECT arenaTeamId FROM arena_team WHERE `type` = ?d ORDER BY rating DESC', $type);
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$distrib = [];
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm, rank
$r = explode(':', $guid);
if (!empty($realms[$r[0]]))
{
$curTpl['realm'] = $r[0];
$curTpl['realmName'] = $realms[$r[0]]['name'];
$curTpl['region'] = $realms[$r[0]]['region'];
$curTpl['rank'] = array_search($curTpl['arenaTeamId'], $this->rankOrder[$r[0]][$curTpl['type']]) + 1;
}
else
{
trigger_error('arena team "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// team members
$this->members[$r[0]][$r[1]] = $r[1];
// equalize distribution
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
// get team members
foreach ($this->members as $realmId => &$teams)
$teams = DB::Characters($realmId)->select('
SELECT
at.arenaTeamId AS ARRAY_KEY, c.guid AS ARRAY_KEY2, c.name AS "0", c.class AS "1", IF(at.captainguid = c.guid, 1, 0) AS "2"
FROM
arena_team at
JOIN
arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON c.guid = atm.guid
WHERE
at.arenaTeamId IN (?a) AND
c.deleteInfos_Account IS NULL AND
c.level <= ?d AND
(c.extra_flags & ?d) = 0',
$teams,
MAX_LEVEL,
Profiler::CHAR_GMFLAGS
);
// equalize subject distribution across realms
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$r = explode(':', $guid);
if (isset($this->members[$r[0]][$r[1]]))
$curTpl['members'] = array_values($this->members[$r[0]][$r[1]]); // [name, classId, isCaptain]
$distrib[$curTpl['realm']]--;
$limit--;
}
}
public function initializeLocalEntries()
{
$profiles = [];
// init members for tooltips
foreach ($this->members as $realmId => $teams)
{
$gladiators = [];
foreach ($teams as $team)
$gladiators = array_merge($gladiators, array_keys($team));
$profiles[$realmId] = new RemoteProfileList(array(['c.guid', $gladiators], CFG_SQL_LIMIT_NONE), ['sv' => $realmId]);
if (!$profiles[$realmId]->error)
$profiles[$realmId]->initializeLocalEntries();
}
$data = [];
foreach ($this->iterate() as $guid => $__)
{
$data[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('arenaTeamId'),
'name' => $this->getField('name'),
'nameUrl' => Profiler::urlize($this->getField('name')),
'type' => $this->getField('type'),
'rating' => $this->getField('rating'),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic arena team data
foreach (Util::createSqlBatchInsert($data) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES '.$ins, array_keys(reset($data)));
// merge back local ids
$localIds = DB::Aowow()->selectCol(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_arena_team WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($data, 'realm'),
array_column($data, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl['id'] = $localIds[$guid];
// profiler_arena_team_member requires profiles and arena teams to be filled
foreach ($this->members as $realmId => $teams)
{
if (empty($profiles[$realmId]))
continue;
$memberData = [];
foreach ($teams as $teamId => $team)
foreach ($team as $memberId => $member)
$memberData[] = array(
'arenaTeamId' => $localIds[$realmId.':'.$teamId],
'profileId' => $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'],
'captain' => $member[2]
);
foreach (Util::createSqlBatchInsert($memberData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team_member (?#) VALUES '.$ins, array_keys(reset($memberData)));
}
}
}
class LocalArenaTeamList extends ArenaTeamList
{
protected $queryBase = 'SELECT at.*, at.id AS ARRAY_KEY FROM ?_profiler_arena_team at';
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$members = DB::Aowow()->selectCol('SELECT *, arenaTeamId AS ARRAY_KEY, profileId AS ARRAY_KEY2 FROM ?_profiler_arena_team_member WHERE arenaTeamId IN (?a)', $this->getFoundIDs());
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$curTpl['members'] = $members[$id];
}
}
public function getProfileUrl()
{
$url = '?arena-team=';
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
Profiler::urlize($this->getField('name'))
));
}
}
?>

View File

@@ -0,0 +1,307 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuildList extends BaseType
{
use profilerHelper, listviewHelper;
public function getListviewData()
{
$this->getGuildScores();
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => "$'".$this->curTpl['name']."'", // MUST be a string
'members' => $this->curTpl['members'],
'faction' => $this->curTpl['faction'],
'achievementpoints' => $this->getField('achievementpoints'),
'gearscore' => $this->getField('gearscore'),
'realm' => Profiler::urlize($this->curTpl['realmName']),
'realmname' => $this->curTpl['realmName'],
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
// 'battlegroupname' => $this->curTpl['battlegroup'],
'region' => Profiler::urlize($this->curTpl['region'])
);
}
return array_values($data);
}
private function getGuildScores()
{
/*
Guild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild.
Guilds with at least 25 level 80 players receive full benefit of the top 25 characters' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty,
at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. [...]
Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.
*/
$guilds = array_column($this->templates, 'id');
if (!$guilds)
return;
$stats = DB::Aowow()->select('SELECT guild AS ARRAY_KEY, id AS ARRAY_KEY2, level, gearscore, achievementpoints, IF(cuFlags & ?d, 0, 1) AS synced FROM ?_profiler_profiles WHERE guild IN (?a) ORDER BY gearscore DESC', PROFILER_CU_NEEDS_RESYNC, $guilds);
foreach ($this->iterate() as &$_curTpl)
{
$id = $_curTpl['id'];
if (empty($stats[$id]))
continue;
$guildStats = array_filter($stats[$id], function ($x) { return $x['synced']; } );
if (!$guildStats)
continue;
$nMaxLevel = count(array_filter($stats[$id], function ($x) { return $x['level'] >= MAX_LEVEL; } ));
$levelMod = 1.0;
if ($nMaxLevel < 25)
$levelMod = 0.85;
if ($nMaxLevel < 10)
$levelMod = 0.66;
if ($nMaxLevel < 1)
$levelMod = 0.20;
$totalGS = $totalAP = $nMembers = 0;
foreach ($guildStats as $gs)
{
$totalGS += $gs['gearscore'] * $levelMod * min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
$totalAP += $gs['achievementpoints'] * $levelMod * min($gs['achievementpoints'], 1500) / 1500;
$nMembers += min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
}
$_curTpl['gearscore'] = intval($totalGS / $nMembers);
$_curTpl['achievementpoints'] = intval($totalAP / $nMembers);
}
}
public function renderTooltip() {}
public function getJSGlobals($addMask = 0) {}
}
class GuildListFilter extends Filter
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['g.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
return $parts;
}
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
class RemoteGuildList extends GuildList
{
protected $queryBase = 'SELECT `g`.*, `g`.`guildid` AS ARRAY_KEY FROM guild g';
protected $queryOpts = array(
'g' => [['gm', 'c'], 'g' => 'ARRAY_KEY'],
'gm' => ['j' => 'guild_member gm ON gm.guildid = g.guildid', 's' => ', COUNT(1) AS members'],
'c' => ['j' => 'characters c ON c.guid = gm.guid', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
);
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$distrib = [];
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$r = explode(':', $guid)[0];
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
$curTpl['realmName'] = $realms[$r]['name'];
$curTpl['region'] = $realms[$r]['region'];
}
else
{
trigger_error('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// equalize distribution
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$distrib[$curTpl['realm']]--;
$limit--;
}
}
public function initializeLocalEntries()
{
$data = [];
foreach ($this->iterate() as $guid => $__)
{
$data[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guildid'),
'name' => $this->getField('name'),
'nameUrl' => Profiler::urlize($this->getField('name')),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic guild data
foreach (Util::createSqlBatchInsert($data) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, array_keys(reset($data)));
// merge back local ids
$localIds = DB::Aowow()->selectCol(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($data, 'realm'),
array_column($data, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl['id'] = $localIds[$guid];
}
}
class LocalGuildList extends GuildList
{
protected $queryBase = 'SELECT g.*, g.id AS ARRAY_KEY FROM ?_profiler_guild g';
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
}
}
public function getProfileUrl()
{
$url = '?guild=';
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
Profiler::urlize($this->getField('name'))
));
}
}
?>

View File

@@ -2311,7 +2311,7 @@ class ItemListFilter extends Filter
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false; return false;
foreach (Util::getRealms() as $rId => $__) foreach (Profiler::getRealms() as $rId => $__)
{ {
// todo: do something sensible.. // todo: do something sensible..
// // todo (med): get the avgbuyout into the listview // // todo (med): get the avgbuyout into the listview

View File

@@ -4,145 +4,81 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
// class CharacterList extends BaseType // new profiler-related parent: ProfilerType?; maybe a trait is enough => use ProfileHelper;
// class GuildList extends BaseType
// class ArenaTeamList extends BaseType
class ProfileList extends BaseType class ProfileList extends BaseType
{ {
public static $type = 0; // profiles dont actually have one use profilerHelper, listviewHelper;
public static $brickFile = 'profile';
public static $dataTable = ''; // doesn't have community content
protected $queryBase = ''; // SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiles p'; public function getListviewData($addInfo = 0, array $reqCols = [])
protected $queryOpts = array(
'p' => [['pa', 'pg']],
'pam' => [['?_profiles_arenateam_member pam ON pam.memberId = p.id', true], 's' => ', pam.status'],
'pa' => ['?_profiles_arenateam pa ON pa.id = pam.teamId', 's' => ', pa.mode, pa.name'],
'pgm' => [['?_profiles_guid_member pgm ON pgm.memberId = p.Id', true], 's' => ', pgm.rankId'],
'pg' => ['?_profiles_guild pg ON pg.if = pgm.guildId', 's' => ', pg.name']
);
public function __construct($conditions = [], $miscData = null)
{
$character = array(
'id' => 2,
'name' => 'CharName',
'region' => ['eu', 'Europe'],
'battlegroup' => ['pure-pwnage', 'Pure Pwnage'],
'realm' => ['dafuque', 'da\'Fuqúe'],
'level' => 80,
'classs' => 11,
'race' => 6,
'faction' => 1, // 0:alliance; 1:horde
'gender' => 1, // 0:male, 1:female
'skincolor' => 0, // playerbytes % 256
'hairstyle' => 0, // (playerbytes >> 16) % 256
'haircolor' => 0, // (playerbytes >> 24) % 256
'facetype' => 0, // (playerbytes >> 8) % 256 [maybe features]
'features' => 0, // playerBytes2 % 256 [maybe facetype]
'source' => 2, // source: used if you create a profile from a genuine character. It inherites region, realm and bGroup
'sourcename' => 'SourceCharName', // > if these three are false we get a 'genuine' profile [0 for genuine characters..?]
'user' => 1, // > 'genuine' is the parameter for _isArmoryProfile(allowCustoms) ['' for genuine characters..?]
'username' => 'TestUser', // > also, if 'source' <> 0, the char-icon is requestet via profile.php?avatar
'published' => 1, // public / private
'pinned' => 1, // usable for some utility funcs on site
'nomodel' => 0x0, // unchecks DisplayOnCharacter by (1 << slotId - 1)
'title' => 0, // titleId currently in use or null
'guild' => 'GuildName', // only on chars; id or null
'description' => 'this is a profile', // only on custom profiles
'arenateams' => [], // [size(2|3|5) => DisplayName]; DisplayName gets urlized to use as link
'playedtime' => 0, // exact to the day
'lastupdated' => 0, // timestamp in ms
'achievementpoints' => 0, // max you have
'talents' => array(
'builds' => array(
['talents' => '', 'glyphs' => ''], // talents:string of 0-5 points; glyphs: itemIds.join(':')
),
'active' => 1 // 1|2
),
'customs' => [], // custom profiles created from this char; profileId => [name, ownerId, iconString(optional)]
'skills' => [], // skillId => [curVal, maxVal]; can contain anything, should be limited to prim/sec professions
'inventory' => [], // slotId => [itemId, subItemId, permEnchantId, tempEnchantId, gemItemId1, gemItemId2, gemItemId3, gemItemId4]
'auras' => [], // custom list of buffs, debuffs [spellId]
// completion lists: [subjectId => amount/timestamp/1]
'reputation' => [], // factionId => amount
'titles' => [], // titleId => 1
'spells' => [], // spellId => 1; recipes, pets, mounts
'achievements' => [], // achievementId => timestamp
'quests' => [], // questId => 1
// UNKNOWN
'bookmarks' => [2], // UNK pinned or claimed userId => profileIds..?
'statistics' => [], // UNK all statistics? [achievementId => killCount]
'activity' => [], // UNK recent achievements? [achievementId => killCount]
'glyphs' => [], // not really used .. i guess..?
'pets' => array( // UNK
[], // one array per pet, structure UNK
),
);
// parent::__construct($conditions, $miscData);
@include('datasets/ProfilerExampleChar'); // tmp char data
$this->templates[2] = $character;
$this->curTpl = $character;
if ($this->error)
return;
// post processing
// foreach ($this->iterate() as $_id => &$curTpl)
// {
// }
}
public function getListviewData()
{ {
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
$tDistrib = $this->getTalentDistribution(); if ($this->getField('user') && User::$id != $this->getField('user') && !($this->getField('cuFlags') & PROFILER_CU_PUBLISHED))
continue;
if (($addInfo & PROFILEINFO_PROFILE) && !$this->isCustom())
continue;
if (($addInfo & PROFILEINFO_CHARACTER) && $this->isCustom())
continue;
$data[$this->id] = array( $data[$this->id] = array(
'id' => 0, 'id' => $this->getField('id'),
'name' => $this->curTpl['name'], 'name' => $this->getField('name'),
'achievementpoints' => $this->curTpl['achievementpoints'], 'race' => $this->getField('race'),
'guild' => $this->curTpl['guild'], // 0 if none 'classs' => $this->getField('class'),
'guildRank' => -1, 'gender' => $this->getField('gender'),
'realm' => $this->curTpl['realm'][0], 'level' => $this->getField('level'),
'realmname' => $this->curTpl['realm'][1], 'faction' => (1 << ($this->getField('race') - 1)) & RACE_MASK_ALLIANCE ? 0 : 1,
'battlegroup' => $this->curTpl['battlegroup'][0], 'talenttree1' => $this->getField('talenttree1'),
'battlegroupname' => $this->curTpl['battlegroup'][0], 'talenttree2' => $this->getField('talenttree2'),
'region' => $this->curTpl['region'][0], 'talenttree3' => $this->getField('talenttree3'),
'level' => $this->curTpl['level'], 'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2
'race' => $this->curTpl['race'], 'achievementpoints' => $this->getField('achievementpoints'),
'gender' => $this->curTpl['gender'], 'guild' => '$"'.$this->getField('guildname').'"', // force this to be a string
'classs' => $this->curTpl['classs'], 'guildrank' => $this->getField('guildrank'),
'faction' => $this->curTpl['faction'], 'realm' => Profiler::urlize($this->getField('realmName')),
'talenttree1' => $tDistrib[0], 'realmname' => $this->getField('realmName'),
'talenttree2' => $tDistrib[1], // 'battlegroup' => Profiler::urlize($this->getField('battlegroup')), // was renamed to subregion somewhere around cata release
'talenttree3' => $tDistrib[2], // 'battlegroupname' => $this->getField('battlegroup'),
'talentspec' => $this->curTpl['talents']['active'] 'gearscore' => $this->getField('gearscore')
); );
if (!empty($this->curTpl['description']))
$data[$this->id]['description'] = $this->curTpl['description'];
if (!empty($this->curTpl['icon'])) // for the lv this determins if the link is profile=<id> or profile=<region>.<realm>.<name>
$data[$this->id]['icon'] = $this->curTpl['icon']; if ($this->isCustom())
$data[$this->id]['published'] = (int)!!($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
else
$data[$this->id]['region'] = Profiler::urlize($this->getField('region'));
if ($this->curTpl['cuFlags'] & PROFILE_CU_PUBLISHED) if ($addInfo & PROFILEINFO_ARENA)
$data[$this->id]['published'] = 1; {
$data[$this->id]['rating'] = $this->getField('rating');
$data[$this->id]['captain'] = $this->getField('captain');
$data[$this->id]['games'] = $this->getField('seasonGames');
$data[$this->id]['wins'] = $this->getField('seasonWins');
}
if ($this->curTpl['cuFlags'] & PROFILE_CU_PINNED) // Filter asked for skills - add them
foreach ($reqCols as $col)
$data[$this->id][$col] = $this->getField($col);
if ($addInfo & PROFILEINFO_PROFILE)
if ($_ = $this->getField('description'))
$data[$this->id]['description'] = $_;
if ($addInfo & PROFILEINFO_PROFILE)
if ($_ = $this->getField('icon'))
$data[$this->id]['icon'] = $_;
if ($this->getField('cuFlags') & PROFILER_CU_PINNED)
$data[$this->id]['pinned'] = 1; $data[$this->id]['pinned'] = 1;
if ($this->curTpl['cuFlags'] & PROFILE_CU_DELETED) if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
$data[$this->id]['deleted'] = 1; $data[$this->id]['deleted'] = 1;
} }
return $data; return array_values($data);
} }
public function renderTooltip($interactive = false) public function renderTooltip($interactive = false)
@@ -150,65 +86,178 @@ class ProfileList extends BaseType
if (!$this->curTpl) if (!$this->curTpl)
return []; return [];
$title = '';
$name = $this->getField('name');
if ($_ = $this->getField('chosenTitle'))
$title = (new TitleList(array(['bitIdx', $_])))->getField($this->getField('gender') ? 'female' : 'male', true);
if ($this->isCustom())
$name .= ' (Custom Profile)';
else if ($title)
$name = sprintf($title, $name);
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name').'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$name.'</b></td></tr>';
if ($g = $this->getField('name')) if ($g = $this->getField('guildname'))
$x .= '<tr><td>&lt;'.$g.'&gt; ('.$this->getField('guildrank').')</td></tr>'; $x .= '<tr><td>&lt;'.$g.'&gt;</td></tr>';
else if ($d = $this->getField('description')) else if ($d = $this->getField('description'))
$x .= '<tr><td>'.$d.'</td></tr>'; $x .= '<tr><td>'.$d.'</td></tr>';
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->curTpl['race']).' '.Lang::game('cl', $this->curTpl['classs']).'</td></tr>'; $x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->getField('race')).' '.Lang::game('cl', $this->getField('class')).'</td></tr>';
$x .= '</table>'; $x .= '</table>';
return $x; return $x;
} }
public function getJSGlobals($addMask = 0) {} public function getJSGlobals($addMask = 0)
private function getTalentDistribution()
{ {
if (!empty($this->tDistribution)) $data = [];
$this->tDistribution[$this->curTpl['classId']] = DB::Aowow()->selectCol('SELECT COUNT(t.id) FROM dbc_talent t JOIN dbc_talenttab tt ON t.tabId = tt.id WHERE tt.classMask & ?d GROUP BY tt.id ORDER BY tt.tabNumber ASC', 1 << ($this->curTpl['classId'] - 1)); $realms = Profiler::getRealms();
$result = []; foreach ($this->iterate() as $id => $__)
$start = 0;
foreach ($this->tDistribution[$this->curTpl['classId']] as $len)
{ {
$result[] = array_sum(str_split(substr($this->curTpl['talentString'], $start, $len))); if (($addMask & PROFILEINFO_PROFILE) && ($this->getField('cuFlags') & PROFILER_CU_PROFILE))
$start += $len; {
$profile = array(
'id' => $this->getField('id'),
'name' => $this->getField('name'),
'race' => $this->getField('race'),
'classs' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender')
);
if ($_ = $this->getField('icon'))
$profile['icon'] = $_;
$data[] = $profile;
continue;
}
if ($addMask & PROFILEINFO_CHARACTER && !($this->getField('cuFlags') & PROFILER_CU_PROFILE))
{
if (!isset($realms[$this->getField('realm')]))
continue;
$data[] = array(
'id' => $this->getField('id'),
'name' => $this->getField('name'),
'realmname' => $realms[$this->getField('realm')]['name'],
'region' => $realms[$this->getField('realm')]['region'],
'realm' => Profiler::urlize($realms[$this->getField('realm')]['name']),
'race' => $this->getField('race'),
'classs' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender'),
'pinned' => $this->getField('cuFlags') & PROFILER_CU_PINNED ? 1 : 0
);
}
} }
return $result; return $data;
}
public function isCustom()
{
return $this->getField('cuFlags') & PROFILER_CU_PROFILE;
} }
} }
class ProfileListFilter extends Filter class ProfileListFilter extends Filter
{ {
public $extraOpts = []; public $useLocalList = false;
public $extraOpts = [];
protected $genericFilter = array( private $realms = [];
protected $enums = array(
-1 => array( // arena team sizes
// by name by rating by contrib
12 => 2, 13 => 2, 14 => 2,
15 => 3, 16 => 3, 17 => 3,
18 => 5, 19 => 5, 20 => 5
)
); );
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
3 => [FILTER_CR_NUMERIC, 'achievementpoints', NUM_CAST_INT ], // achievementpoints [num]
5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
9 => [FILTER_CR_STRING, 'g.name', ], // guildname
10 => [FILTER_CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
12 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname2v2
15 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname3v3
18 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname5v5
13 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng2v2
16 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng3v3
19 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng5v5
14 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib2v2 [num]
17 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib3v3 [num]
20 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib5v5 [num]
21 => [FILTER_CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
23 => [FILTER_CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
25 => [FILTER_CR_CALLBACK, 'cbProfession', 171, null], // alchemy [num]
26 => [FILTER_CR_CALLBACK, 'cbProfession', 164, null], // blacksmithing [num]
27 => [FILTER_CR_CALLBACK, 'cbProfession', 333, null], // enchanting [num]
28 => [FILTER_CR_CALLBACK, 'cbProfession', 202, null], // engineering [num]
29 => [FILTER_CR_CALLBACK, 'cbProfession', 182, null], // herbalism [num]
30 => [FILTER_CR_CALLBACK, 'cbProfession', 773, null], // inscription [num]
31 => [FILTER_CR_CALLBACK, 'cbProfession', 755, null], // jewelcrafting [num]
32 => [FILTER_CR_CALLBACK, 'cbProfession', 165, null], // leatherworking [num]
33 => [FILTER_CR_CALLBACK, 'cbProfession', 186, null], // mining [num]
34 => [FILTER_CR_CALLBACK, 'cbProfession', 393, null], // skinning [num]
35 => [FILTER_CR_CALLBACK, 'cbProfession', 197, null], // tailoring [num]
36 => [FILTER_CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [1, 36], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};]/ui', true ], // criteria values
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'ra' => [FILTER_V_LIST, [[1, 8], 10, 11], true ], // race
'cl' => [FILTER_V_LIST, [[1, 9], 11], true ], // class
'minle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min level
'maxle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max level
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
/* heads up!
a couple of filters are too complex to be run against the characters database
if they are selected, force useage of LocalProfileList
*/
public function __construct($fromPOST = false, $opts = [])
{
if (!empty($opts['realms']))
$this->realms = $opts['realms'];
else
$this->realms = array_keys(Profiler::getRealms());
parent::__construct($fromPOST, $opts);
if (!empty($this->fiData['c']['cr']))
if (array_intersect($this->fiData['c']['cr'], [2, 3, 5, 6, 7, 21]))
$this->useLocalList = true;
}
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
default:
break;
}
unset($cr); unset($cr);
$this->error = 1; $this->error = true;
return [1]; return [1];
} }
@@ -217,13 +266,446 @@ class ProfileListFilter extends Filter
$parts = []; $parts = [];
$_v = $this->fiData['v']; $_v = $this->fiData['v'];
// name // region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name_loc'.User::$localeId])) // table key differs between remote and local :<
$parts[] = $_; $k = $this->useLocalList ? 'p' : 'c';
// name [str] - the table is case sensitive. Since i down't want to destroy indizes, lets alter the search terms
if (!empty($_v['na']))
{
$lower = $this->modularizeString([$k.'.name'], Util::lower($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on');
$proper = $this->modularizeString([$k.'.name'], Util::ucWords($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on');
$parts[] = ['OR', $lower, $proper];
}
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = [$k.'.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = [$k.'.race', [2, 5, 6, 8, 10]];
}
// race [list]
if (!empty($_v['ra']))
$parts[] = [$k.'.race', $_v['ra']];
// class [list]
if (!empty($_v['cl']))
$parts[] = [$k.'.class', $_v['cl']];
// min level [int]
if (isset($_v['minle']))
$parts[] = [$k.'.level', $_v['minle'], '>='];
// max level [int]
if (isset($_v['maxle']))
$parts[] = [$k.'.level', $_v['maxle'], '<='];
return $parts; return $parts;
} }
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbProfession($cr, $skillId)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return;
$k = 'sk_'.Util::createHash(12);
$col = 'skill'.$skillId;
$this->formData['extraCols'][$skillId] = $col;
if ($this->useLocalList)
{
$this->extraOpts[$k] = array(
'j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.TYPE_SKILL.' AND '.$k.'.typeId = '.$skillId.' AND '.$k.'.cur '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.cur AS '.$col]
);
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = array(
'j' => ['character_skills '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.skill = '.$skillId.' AND '.$k.'.value '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.value AS '.$col]
);
return [$k.'.skill', null, '!'];
}
}
protected function cbCompletedAcv($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_achievement WHERE id = ?d', $cr[2]))
return false;
$k = 'acv_'.Util::createHash(12);
if ($this->useLocalList)
{
$this->extraOpts[$k] = ['j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.TYPE_ACHIEVEMENT.' AND '.$k.'.typeId = '.$cr[2], true]];
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = ['j' => ['character_achievement '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.achievement = '.$cr[2], true]];
return [$k.'.achievement', null, '!'];
}
}
protected function cbWearsItems($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_items WHERE id = ?d', $cr[2]))
return false;
$k = 'i_'.Util::createHash(12);
$this->extraOpts[$k] = ['j' => ['?_profiler_items '.$k.' ON '.$k.'.id = p.id AND '.$k.'.item = '.$cr[2], true]];
return [$k.'.item', null, '!'];
}
protected function cbHasGuild($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guild', null, $cr[1] ? '!' : null];
else
return ['gm.guildId', null, $cr[1] ? '!' : null];
}
protected function cbHasGuildRank($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guildrank', $cr[2], $cr[1]];
else
return ['gm.rank', $cr[2], $cr[1]];
}
protected function cbTeamName($cr)
{
if ($_ = $this->modularizeString(['at.name'], $cr[2]))
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], $_];
return false;
}
protected function cbTeamRating($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], ['at.rating', $cr[2], $cr[1]]];
}
} }
class RemoteProfileList extends ProfileList
{
protected $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
protected $queryOpts = array(
'c' => [['gm', 'g', 'ca', 'ct'], 'g' => 'ARRAY_KEY', 'o' => 'level DESC, name ASC'],
'ca' => ['j' => ['character_achievement ca ON ca.guid = c.guid', true], 's' => ', GROUP_CONCAT(DISTINCT ca.achievement SEPARATOR " ") AS _acvs'],
'ct' => ['j' => ['character_talent ct ON ct.guid = c.guid AND ct.spec = c.activespec', true], 's' => ', GROUP_CONCAT(DISTINCT ct.spell SEPARATOR " ") AS _talents'],
'gm' => ['j' => ['guild_member gm ON gm.guid = c.guid', true], 's' => ', gm.rank AS guildrank'],
'g' => ['j' => ['guild g ON g.guildid = gm.guildid', true], 's' => ', g.guildid AS guild, g.name AS guildname'],
'atm' => ['j' => ['arena_team_member atm ON atm.guid = c.guid', true], 's' => ', atm.personalRating AS rating'],
'at' => [['atm'], 'j' => 'arena_team at ON atm.arenaTeamId = at.arenaTeamId', 's' => ', at.name AS arenateam, IF(at.captainGuid = c.guid, 1, 0) AS captain']
);
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$acvCache = [];
$talentCache = [];
$atCache = [];
$distrib = null;
$talentData = [];
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm
$r = explode(':', $guid)[0];
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
$curTpl['realmName'] = $realms[$r]['name'];
$curTpl['region'] = $realms[$r]['region'];
}
else
{
trigger_error('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// temp id
$curTpl['id'] = 0;
// achievement points pre
if ($acvs = explode(' ', $curTpl['_acvs']))
foreach ($acvs as $a)
if ($a && !isset($acvCache[$a]))
$acvCache[$a] = $a;
// talent points pre
if ($talents = explode(' ', $curTpl['_talents']))
foreach ($talents as $t)
if ($t && !isset($talentCache[$t]))
$talentCache[$t] = $t;
// equalize distribution
if ($limit != CFG_SQL_LIMIT_NONE)
{
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
$curTpl['cuFlags'] = 0;
}
if ($talentCache)
$talentData = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, rank FROM ?_talents WHERE spell IN (?a)', $talentCache);
if ($distrib !== null)
{
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
}
if ($acvCache)
$acvCache = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, points FROM ?_achievement WHERE id IN (?a)', $acvCache);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($distrib !== null)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$distrib[$curTpl['realm']]--;
$limit--;
}
$a = explode(' ', $curTpl['_acvs']);
$t = explode(' ', $curTpl['_talents']);
unset($curTpl['_acvs']);
unset($curTpl['_talents']);
// achievement points post
$curTpl['achievementpoints'] = array_sum(array_intersect_key($acvCache, array_combine($a, $a)));
// talent points post
$curTpl['talenttree1'] = 0;
$curTpl['talenttree2'] = 0;
$curTpl['talenttree3'] = 0;
foreach ($talentData as $spell => $data)
if (in_array($spell, $t))
$curTpl['talenttree'.($data['tab'] + 1)] += $data['rank'];
}
}
public function getListviewData($addInfoMask = 0, array $reqCols = [])
{
$data = parent::getListviewData($addInfoMask, $reqCols);
// not wanted on server list
foreach ($data as &$d)
unset($d['published']);
return $data;
}
public function initializeLocalEntries()
{
$baseData = $guildData = [];
foreach ($this->iterate() as $guid => $__)
{
$baseData[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guid'),
'name' => $this->getField('name'),
'race' => $this->getField('race'),
'class' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender'),
'guild' => $this->getField('guild') ?: null,
'guildrank' => $this->getField('guild') ? $this->getField('guildrank') : null,
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
if ($this->getField('guild'))
$guildData[] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guild'),
'name' => $this->getField('guildname'),
'nameUrl' => Profiler::urlize($this->getField('guildname')),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic guild data (satisfying table constraints)
if ($guildData)
{
foreach (Util::createSqlBatchInsert($guildData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, array_keys(reset($guildData)));
// merge back local ids
$localGuilds = DB::Aowow()->selectCol('SELECT realm AS ARRAY_KEY, realmGUID AS ARRAY_KEY2, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($guildData, 'realm'), array_column($guildData, 'realmGUID')
);
foreach ($baseData as &$bd)
if ($bd['guild'])
$bd['guild'] = $localGuilds[$bd['realm']][$bd['guild']];
}
// basic char data (enough for tooltips)
if ($baseData)
{
foreach (Util::createSqlBatchInsert($baseData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_profiles (?#) VALUES '.$ins, array_keys(reset($baseData)));
// merge back local ids
$localIds = DB::Aowow()->select(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id, gearscore FROM ?_profiler_profiles WHERE (cuFlags & ?d) = 0 AND realm IN (?a) AND realmGUID IN (?a)',
PROFILER_CU_PROFILE,
array_column($baseData, 'realm'),
array_column($baseData, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl = array_merge($_curTpl, $localIds[$guid]);
}
}
}
class LocalProfileList extends ProfileList
{
protected $queryBase = 'SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiler_profiles p';
protected $queryOpts = array(
'p' => [['g'], 'g' => 'p.id'],
'ap' => ['j' => ['?_account_profiles ap ON ap.profileId = p.id', true], 's' => ', (IFNULL(ap.ExtraFlags, 0) | p.cuFlags) AS cuFlags'],
'atm' => ['j' => ['?_profiler_arena_team_member atm ON atm.profileId = p.id', true], 's' => ', atm.captain, atm.personalRating AS rating, atm.seasonGames, atm.seasonWins'],
'at' => [['atm'], 'j' => ['?_profiler_arena_team at ON at.id = atm.arenaTeamId', true], 's' => ', at.type'],
'g' => ['j' => ['?_profiler_guild g ON g.id = p.guild', true], 's' => ', g.name AS guildname']
);
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$acvPoints = DB::Aowow()->selectCol('SELECT pc.id AS ARRAY_KEY, SUM(a.points) FROM ?_profiler_completion pc LEFT JOIN ?_achievement a ON a.id = pc.typeId WHERE pc.`type` = ?d AND pc.id IN (?a) GROUP BY pc.id', TYPE_ACHIEVEMENT, $this->getFoundIDs());
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$curTpl['achievementpoints'] = isset($acvPoints[$id]) ? $acvPoints[$id] : 0;
}
}
public function getProfileUrl()
{
$url = '?profile=';
if ($this->isCustom())
return $url.$this->getField('id');
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
urlencode($this->getField('name'))
));
}
}
?> ?>

View File

@@ -340,33 +340,226 @@ class SpellList extends BaseType
public function getProfilerMods() public function getProfilerMods()
{ {
// weapon hand check: param: slot, class, subclass, value
$whCheck = '$function() { var j, w = _inventory.getInventory()[%d]; if (!w[0] || !g_items[w[0]]) { return 0; } j = g_items[w[0]].jsonequip; return (j.classs == %d && (%d & (1 << (j.subclass)))) ? %d : 0; }';
$data = $this->getStatGain(); // flat gains $data = $this->getStatGain(); // flat gains
foreach ($data as $id => &$spellData)
{
foreach ($spellData as $modId => $val)
{
if (!isset(Game::$itemMods[$modId]))
continue;
if ($modId == ITEM_MOD_EXPERTISE_RATING) // not a rating .. pure expertise
$spellData['exp'] = $val;
else
$spellData[Game::$itemMods[$modId]] = $val;
unset($spellData[$modId]);
}
// apply weapon restrictions
$this->getEntry($id);
$class = $this->getField('equippedItemClass');
$subClass = $this->getField('equippedItemSubClassMask');
$slot = $subClass & 0x5000C ? 18 : 16;
if ($class != ITEM_CLASS_WEAPON || !$subClass)
continue;
foreach ($spellData as $json => $pts)
$spellData[$json] = [1, 'functionOf', sprintf($whCheck, $slot, $class, $subClass, $pts)];
}
// 4 possible modifiers found
// <statistic> => [0.15, 'functionOf', <funcName:int>]
// <statistic> => [0.33, 'percentOf', <statistic>]
// <statistic> => [123, 'add']
// <statistic> => <value> ... as from getStatGain()
$modXByStat = function (&$arr, $stat, $pts) use (&$mv)
{
if ($mv == STAT_STRENGTH)
$arr[$stat ?: 'str'] = [$pts / 100, 'percentOf', 'str'];
else if ($mv == STAT_AGILITY)
$arr[$stat ?: 'agi'] = [$pts / 100, 'percentOf', 'agi'];
else if ($mv == STAT_STAMINA)
$arr[$stat ?: 'sta'] = [$pts / 100, 'percentOf', 'sta'];
else if ($mv == STAT_INTELLECT)
$arr[$stat ?: 'int'] = [$pts / 100, 'percentOf', 'int'];
else if ($mv == STAT_SPIRIT)
$arr[$stat ?: 'spi'] = [$pts / 100, 'percentOf', 'spi'];
};
$modXBySchool = function (&$arr, $stat, $val, $mask = null) use (&$mv)
{
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_HOLY))
$arr['hol'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'hol'.$stat];
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_FIRE))
$arr['fir'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'fir'.$stat];
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_NATURE))
$arr['nat'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'nat'.$stat];
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_FROST))
$arr['fro'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'fro'.$stat];
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_SHADOW))
$arr['sha'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'sha'.$stat];
if (($mask ?: $mv) & (1 << SPELL_SCHOOL_ARCANE))
$arr['arc'.$stat] = is_array($val) ? $val : [$val / 100, 'percentOf', 'arc'.$stat];
};
$jsonStat = function ($stat)
{
if ($stat == STAT_STRENGTH)
return 'str';
if ($stat == STAT_AGILITY)
return 'agi';
if ($stat == STAT_STAMINA)
return 'sta';
if ($stat == STAT_INTELLECT)
return 'int';
if ($stat == STAT_SPIRIT)
return 'spi';
};
foreach ($this->iterate() as $id => $__) foreach ($this->iterate() as $id => $__)
{ {
// Priest: Spirit of Redemption is a spell but also a passive. *yaaayyyy*
if (($this->getField('cuFlags') & SPELL_CU_TALENTSPELL) && $id != 20711)
continue;
// curious cases of OH MY FUCKING GOD WHY?!
if ($id == 16268) // Shaman - Spirit Weapons (parry is normaly stored in g_statistics)
{
$data[$id]['parrypct'] = [5, 'add'];
continue;
}
if ($id == 20550) // Tauren - Endurance (dependant on base health) ... if you are looking for something elegant, look away!
{
$data[$id]['health'] = [0.05, 'functionOf', '$function(p) { return g_statistics.combo[p.classs][p.level][5]; }'];
continue;
}
for ($i = 1; $i < 4; $i++) for ($i = 1; $i < 4; $i++)
{ {
$pts = $this->calculateAmountForCurrent($i)[1]; $pts = $this->calculateAmountForCurrent($i)[1];
$mv = $this->curTpl['effect'.$i.'MiscValue']; $mv = $this->getField('effect'.$i.'MiscValue');
$au = $this->curTpl['effect'.$i.'AuraId']; $mvB = $this->getField('effect'.$i.'MiscValueB');
$au = $this->getField('effect'.$i.'AuraId');
$class = $this->getField('equippedItemClass');
$subClass = $this->getField('equippedItemSubClassMask');
/* ISSUE! /* ISSUE!
mods formated like ['<statName>' => [<points>, 'percentOf', '<statName>']] are applied as multiplier and not mods formated like ['<statName>' => [<points>, 'percentOf', '<statName>']] are applied as multiplier and not
as a flat value (that is equal to the percentage, like they should be). So the stats-table won't show the actual deficit as a flat value (that is equal to the percentage, like they should be). So the stats-table won't show the actual deficit
*/ */
switch ($this->curTpl['effect'.$i.'AuraId']) switch ($au)
{ {
case 101: case 101: // Mod Resistance Percent
$data[$id][] = ['armor' => [$pts / 100, 'percentOf', 'armor']]; case 142: // Mod Base Resistance Percent
if ($mv == 1) // Armor only if explicitly specified only affects armor from equippment
$data[$id]['armor'] = [$pts / 100, 'percentOf', ['armor', 0]];
else if ($mv)
$modXBySchool($data[$id], 'res', $pts);
break; break;
case 13: // damage done flat case 182: // Mod Resistance Of Stat Percent
// per magic school, omit physical if ($mv == 1) // Armor only if explicitly specified
$data[$id]['armor'] = [$pts / 100, 'percentOf', $jsonStat($mvB)];
else if ($mv)
$modXBySchool($data[$id], 'res', [$pts / 100, 'percentOf', $jsonStat($mvB)]);
break; break;
case 30: // mod skill case 137: // mod stat percent
// diff between character skills and trade skills if ($mv > -1) // one stat
$modXByStat($data[$id], null, $pts);
else if ($mv < 0) // all stats
for ($iMod = ITEM_MOD_AGILITY; $iMod <= ITEM_MOD_STAMINA; $iMod++)
$data[$id][Game::$itemMods[$iMod]] = [$pts / 100, 'percentOf', Game::$itemMods[$iMod]];
break;
case 174: // Mod Spell Damage Of Stat Percent
$mv = $mv ?: SPELL_MAGIC_SCHOOLS;
$modXBySchool($data[$id], 'spldmg', [$pts / 100, 'percentOf', $jsonStat($mvB)]);
break;
case 212: // Mod Ranged Attack Power Of Stat Percent
$modXByStat($data[$id], 'rgdatkpwr', $pts);
break;
case 268: // Mod Attack Power Of Stat Percent
$modXByStat($data[$id], 'mleatkpwr', $pts);
break;
case 175: // Mod Spell Healing Of Stat Percent
$modXByStat($data[$id], 'splheal', $pts);
break;
case 219: // Mod Mana Regeneration from Stat
$modXByStat($data[$id], 'manargn', $pts);
break;
case 134: // Mod Mana Regeneration Interrupt
$data[$id]['icmanargn'] = [$pts / 100, 'percentOf', 'oocmanargn'];
break;
case 57: // Mod Spell Crit Chance
case 71: // Mod Spell Crit Chance School
$mv = $mv ?: SPELL_MAGIC_SCHOOLS;
$modXBySchool($data[$id], 'splcritstrkpct', [$pts, 'add']);
if (($mv & SPELL_MAGIC_SCHOOLS) == SPELL_MAGIC_SCHOOLS)
$data[$id]['splcritstrkpct'] = [$pts, 'add'];
break;
case 285: // Mod Attack Power Of Armor
$data[$id]['mleatkpwr'] = [1 / $pts, 'percentOf', 'fullarmor'];
$data[$id]['rgdatkpwr'] = [1 / $pts, 'percentOf', 'fullarmor'];
break;
case 52: // Mod Physical Crit Percent
if ($class < 1 || ($class == ITEM_CLASS_WEAPON && ($subClass & 0x5000C)))
$data[$id]['rgdcritstrkpct'] = [1, 'functionOf', sprintf($whCheck, 18, $class, $subClass, $pts)];
// $data[$id]['rgdcritstrkpct'] = [$pts, 'add'];
if ($class < 1 || ($class == ITEM_CLASS_WEAPON && ($subClass & 0xA5F3)))
$data[$id]['mlecritstrkpct'] = [1, 'functionOf', sprintf($whCheck, 16, $class, $subClass, $pts)];
// $data[$id]['mlecritstrkpct'] = [$pts, 'add'];
break;
case 47: // Mod Parry Percent
$data[$id]['parrypct'] = [$pts, 'add'];
break;
case 49: // Mod Dodge Percent
$data[$id]['dodgepct'] = [$pts, 'add'];
break;
case 51: // Mod Block Percent
$data[$id]['blockpct'] = [$pts, 'add'];
break;
case 132: // Mod Increase Energy Percent
if ($mv == POWER_HEALTH)
$data[$id]['health'] = [$pts / 100, 'percentOf', 'health'];
else if ($mv == POWER_ENERGY)
$data[$id]['energy'] = [$pts / 100, 'percentOf', 'energy'];
else if ($mv == POWER_MANA)
$data[$id]['mana'] = [$pts / 100, 'percentOf', 'mana'];
else if ($mv == POWER_RAGE)
$data[$id]['rage'] = [$pts / 100, 'percentOf', 'rage'];
else if ($mv == POWER_RUNIC_POWER)
$data[$id]['runic'] = [$pts / 100, 'percentOf', 'runic'];
break;
case 133: // Mod Increase Health Percent
$data[$id]['health'] = [$pts / 100, 'percentOf', 'health'];
break;
case 150: // Mod Shield Blockvalue Percent
$data[$id]['block'] = [$pts / 100, 'percentOf', 'block'];
break;
case 290: // Mod Crit Percent
$data[$id]['mlecritstrkpct'] = [$pts, 'add'];
$data[$id]['rgdcritstrkpct'] = [$pts, 'add'];
$data[$id]['splcritstrkpct'] = [$pts, 'add'];
break;
case 237: // Mod Spell Damage Of Attack Power
$mv = $mv ?: SPELL_MAGIC_SCHOOLS;
$modXBySchool($data[$id], 'spldmg', [$pts / 100, 'percentOf', 'mleatkpwr']);
break;
case 238: // Mod Spell Healing Of Attack Power
$data[$id]['splheal'] = [$pts / 100, 'percentOf', 'mleatkpwr'];
break;
case 166: // Mod Attack Power Percent [ingmae only melee..?]
$data[$id]['mleatkpwr'] = [$pts / 100, 'percentOf', 'mleatkpwr'];
break;
case 88: // Mod Health Regeneration Percent
$data[$id]['healthrgn'] = [$pts / 100, 'percentOf', 'healthrgn'];
break; break;
case 36: // shapeshift
} }
} }
} }

View File

@@ -17,10 +17,12 @@ class User
public static $dailyVotes = 0; public static $dailyVotes = 0;
public static $ip = null; public static $ip = null;
private static $reputation = 0; private static $reputation = 0;
private static $dataKey = ''; private static $dataKey = '';
private static $expires = false; private static $expires = false;
private static $passHash = ''; private static $passHash = '';
private static $excludeGroups = 1;
private static $profiles = null;
public static function init() public static function init()
{ {
@@ -54,7 +56,7 @@ class User
return false; return false;
$query = DB::Aowow()->SelectRow(' $query = DB::Aowow()->SelectRow('
SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes, a.excludeGroups
FROM ?_account a FROM ?_account a
LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP() LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP()
LEFT JOIN ?_account_reputation r ON a.id = r.userId LEFT JOIN ?_account_reputation r ON a.id = r.userId
@@ -73,15 +75,26 @@ class User
return false; return false;
} }
self::$id = intval($query['id']); self::$id = intval($query['id']);
self::$displayName = $query['displayName']; self::$displayName = $query['displayName'];
self::$passHash = $query['passHash']; self::$passHash = $query['passHash'];
self::$expires = (bool)$query['allowExpire']; self::$expires = (bool)$query['allowExpire'];
self::$reputation = $query['reputation']; self::$reputation = $query['reputation'];
self::$banStatus = $query['bans']; self::$banStatus = $query['bans'];
self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']); self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']);
self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']); self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']);
self::$dailyVotes = $query['dailyVotes']; self::$dailyVotes = $query['dailyVotes'];
self::$excludeGroups = $query['excludeGroups'];
$conditions = array(
[['cuFlags', PROFILER_CU_DELETED, '&'], 0],
['OR', ['user', self::$id], ['ap.accountId', self::$id]]
);
if (self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
array_shift($conditions);
self::$profiles = (new LocalProfileList($conditions));
if ($query['avatar']) if ($query['avatar'])
self::$avatar = $query['avatar']; self::$avatar = $query['avatar'];
@@ -140,11 +153,11 @@ class User
$rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2] $rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2]
// check IPv4 // check IPv4
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
break; break;
// check IPv6 // check IPv6
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
break; break;
} }
} }
@@ -565,6 +578,11 @@ class User
$gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE; $gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE;
$gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE; $gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE;
$gUser['characters'] = self::getCharacters(); $gUser['characters'] = self::getCharacters();
$gUser['excludegroups'] = self::$excludeGroups;
$gUser['settings'] = (new StdClass); // profiler requires this to be set; has property premiumborder (NYI)
if ($_ = self::getProfilerExclusions())
$gUser = array_merge($gUser, $_);
if ($_ = self::getProfiles()) if ($_ = self::getProfiles())
$gUser['profiles'] = $_; $gUser['profiles'] = $_;
@@ -593,36 +611,32 @@ class User
return $result; return $result;
} }
public static function getProfilerExclusions()
{
$result = [];
$modes = [1 => 'excludes', 2 => 'includes'];
foreach ($modes as $mode => $field)
if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, typeId AS ARRAY_KEY2, typeId FROM ?_account_excludes WHERE mode = ?d AND userId = ?d', $mode, self::$id))
foreach ($ex as $type => $ids)
$result[$field][$type] = array_values($ids);
return $result;
}
public static function getCharacters() public static function getCharacters()
{ {
// existing chars on realm(s) if (!self::$profiles)
$characters = array( return [];
// array(
// 'id' => 22,
// 'name' => 'Example Char',
// 'realmname' => 'Example Realm',
// 'region' => 'eu',
// 'realm' => 'example-realm',
// 'race' => 6,
// 'classs' => 11,
// 'level' => 80,
// 'gender' => 1,
// 'pinned' => 1
// )
);
return $characters; return self::$profiles->getJSGlobals(PROFILEINFO_CHARACTER);
} }
public static function getProfiles() public static function getProfiles()
{ {
// chars build in profiler if (!self::$profiles)
$profiles = array( return [];
// array('id' => 21, 'name' => 'Example Profile 1', 'race' => 4, 'classs' => 5, 'level' => 72, 'gender' => 1, 'icon' => 'inv_axe_04'),
// array('id' => 23, 'name' => 'Example Profile 2', 'race' => 11, 'classs' => 3, 'level' => 17, 'gender' => 0)
);
return $profiles; return self::$profiles->getJSGlobals(PROFILEINFO_PROFILE);
} }
public static function getCookies() public static function getCookies()

View File

@@ -370,7 +370,7 @@ class Util
); );
public static $configCats = array( public static $configCats = array(
'Other', 'Site', 'Caching', 'Account', 'Session', 'Site Reputation', 'Google Analytics' 'Other', 'Site', 'Caching', 'Account', 'Session', 'Site Reputation', 'Google Analytics', 'Profiler'
); );
public static $tcEncoding = '0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789'; public static $tcEncoding = '0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789';
@@ -413,16 +413,6 @@ class Util
return self::formatTime($tDiff * 1000, true); return self::formatTime($tDiff * 1000, true);
} }
public static function getBuyoutForItem($itemId)
{
if (!$itemId)
return 0;
// try, when having filled char-DB at hand
// return DB::Characters()->selectCell('SELECT SUM(a.buyoutprice) / SUM(ii.count) FROM auctionhouse a JOIN item_instance ii ON ii.guid = a.itemguid WHERE ii.itemEntry = ?d', $itemId);
return 0;
}
public static function formatMoney($qty) public static function formatMoney($qty)
{ {
$money = ''; $money = '';
@@ -809,42 +799,6 @@ class Util
} }
} }
public static function urlize($str)
{
$search = ['<', '>', ' / ', "'", '(', ')'];
$replace = ['&lt;', '&gt;', '-', '', '', ''];
$str = str_replace($search, $replace, $str);
$accents = array(
"ß" => "ss",
"á" => "a", "ä" => "a", "à" => "a", "â" => "a",
"è" => "e", "ê" => "e", "é" => "e", "ë" => "e",
"í" => "i", "î" => "i", "ì" => "i", "ï" => "i",
"ñ" => "n",
"ò" => "o", "ó" => "o", "ö" => "o", "ô" => "o",
"ú" => "u", "ü" => "u", "û" => "u", "ù" => "u",
"œ" => "oe",
"Á" => "A", "Ä" => "A", "À" => "A", "Â" => "A",
"È" => "E", "Ê" => "E", "É" => "E", "Ë" => "E",
"Í" => "I", "Î" => "I", "Ì" => "I", "Ï" => "I",
"Ñ" => "N",
"Ò" => "O", "Ó" => "O", "Ö" => "O", "Ô" => "O",
"Ú" => "U", "Ü" => "U", "Û" => "U", "Ù" => "U",
"œ" => "Oe"
);
$str = strtr($str, $accents);
$str = trim($str);
$str = preg_replace('/[^a-z0-9]/i', '-', $str);
$str = str_replace('--', '-', $str);
$str = str_replace('--', '-', $str);
$str = rtrim($str, '-');
$str = strtolower($str);
return $str;
}
public static function isValidEmail($email) public static function isValidEmail($email)
{ {
return preg_match('/^([a-z0-9._-]+)(\+[a-z0-9._-]+)?(@[a-z0-9.-]+\.[a-z]{2,4})$/i', $email); return preg_match('/^([a-z0-9._-]+)(\+[a-z0-9._-]+)?(@[a-z0-9.-]+\.[a-z]{2,4})$/i', $email);
@@ -1168,38 +1122,86 @@ class Util
return $json; return $json;
} }
public static function checkOrCreateDirectory($path) public static function createSqlBatchInsert(array $data)
{ {
// remove multiple slashes $nRows = 100;
$path = preg_replace('|/+|', '/', $path); $nItems = count(reset($data));
$result = [];
$buff = [];
if (!is_dir($path) && !@mkdir($path, self::FILE_ACCESS, true)) if (!count($data))
trigger_error('Could not create directory: '.$path, E_USER_ERROR); return [];
else if (!is_writable($path) && !@chmod($path, self::FILE_ACCESS))
trigger_error('Cannot write into directory: '.$path, E_USER_ERROR);
else
return true;
return false; foreach ($data as $d)
}
private static $realms = [];
public static function getRealms()
{
if (DB::isConnectable(DB_AUTH) && !self::$realms)
{ {
self::$realms = DB::Auth()->select('SELECT id AS ARRAY_KEY, name, IF(timezone IN (8, 9, 10, 11, 12), "eu", "us") AS region FROM realmlist WHERE allowedSecurityLevel = 0 AND gamebuild = ?d', WOW_BUILD); if (count($d) != $nItems)
foreach (self::$realms as $rId => $rData) return [];
{
if (DB::isConnectable(DB_CHARACTERS . $rId))
continue;
unset(self::$realms[$rId]); $d = array_map(function ($x) {
trigger_error('Realm #'.$rId.' ('.$rData['name'].') has no connection info set.', E_USER_NOTICE); if ($x === null)
return 'NULL';
return DB::Aowow()->escape($x);
}, $d);
$buff[] = implode(',', $d);
if (count($buff) >= $nRows)
{
$result[] = '('.implode('),(', $buff).')';
$buff = [];
} }
} }
return self::$realms; if ($buff)
$result[] = '('.implode('),(', $buff).')';
return $result;
}
/*****************/
/* file handling */
/*****************/
public static function writeFile($file, $content)
{
$success = false;
if ($handle = @fOpen($file, "w"))
{
if (fWrite($handle, $content))
$success = true;
else
trigger_error('could not write to file', E_USER_ERROR);
fClose($handle);
}
else
trigger_error('could not create file', E_USER_ERROR);
if ($success)
@chmod($file, Util::FILE_ACCESS);
return $success;
}
public static function writeDir($dir)
{
// remove multiple slashes
$dir = preg_replace('|/+|', '/', $dir);
if (is_dir($dir))
{
if (!is_writable($dir) && !@chmod($dir, Util::FILE_ACCESS))
trigger_error('cannot write into directory', E_USER_ERROR);
return is_writable($dir);
}
if (@mkdir($dir, Util::FILE_ACCESS, true))
return true;
trigger_error('could not create directory', E_USER_ERROR);
return false;
} }

View File

@@ -9,6 +9,7 @@ class Lang
private static $mail; private static $mail;
private static $game; private static $game;
private static $maps; private static $maps;
private static $profiler;
private static $screenshot; private static $screenshot;
private static $privileges; private static $privileges;
@@ -48,6 +49,13 @@ class Lang
// *cough* .. reuse-hacks (because copy-pastaing text for 5 locales sucks) // *cough* .. reuse-hacks (because copy-pastaing text for 5 locales sucks)
self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']]; self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']];
self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')'; self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')';
// not localized .. for whatever reason
self::$profiler['regions'] = array(
'eu' => "Europe",
'us' => "US & Oceanic"
);
self::$main['moreTitles']['privilege'] = self::$privileges['_privileges']; self::$main['moreTitles']['privilege'] = self::$privileges['_privileges'];
} }
@@ -252,7 +260,12 @@ class Lang
if ($mask & (1 << $k) && $str) if ($mask & (1 << $k) && $str)
$tmp[] = $str; $tmp[] = $str;
return implode(', ', $tmp); if (!$tmp && $class == ITEM_CLASS_ARMOR)
return self::spell('cat', -11, 8);
else if (!$tmp && $class == ITEM_CLASS_WEAPON)
return self::spell('cat', -11, 6);
else
return implode(', ', $tmp);
} }
public static function getStances($stanceMask) public static function getStances($stanceMask)

View File

@@ -103,7 +103,7 @@ class AchievementPage extends GenericPage
if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH)) if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH))
{ {
$avlb = []; $avlb = [];
foreach (Util::getRealms() AS $rId => $rData) foreach (Profiler::getRealms() AS $rId => $rData)
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId)) if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId))
$avlb[] = Util::ucWords($rData['name']); $avlb[] = Util::ucWords($rData['name']);

View File

@@ -104,14 +104,14 @@ class AdminPage extends GenericPage
$this->lvTabs[] = [null, array( $this->lvTabs[] = [null, array(
'data' => $t, 'data' => $t,
'name' => $n, 'name' => $n,
'id' => Util::urlize($n) 'id' => Profiler::urlize($n)
)]; )];
foreach ($miscTab as $n => $t) foreach ($miscTab as $n => $t)
$this->lvTabs[] = [null, array( $this->lvTabs[] = [null, array(
'data' => $t, 'data' => $t,
'name' => $n, 'name' => $n,
'id' => Util::urlize($n) 'id' => Profiler::urlize($n)
)]; )];
} }

View File

@@ -73,6 +73,88 @@ trait ListPage
} }
} }
trait TrProfiler
{
protected $region = '';
protected $realm = '';
protected $realmId = 0;
protected $battlegroup = ''; // not implemented, since no pserver supports it
protected $subjectName = '';
protected $subjectGUID = 0;
protected $sumSubjects = 0;
protected $doResync = null;
protected function getSubjectFromUrl($str)
{
if (!$str)
return;
// cat[0] is always region
// cat[1] is realm or bGroup (must be realm if cat[2] is set)
// cat[2] is arena-team, guild or player
$cat = explode('.', $str, 3);
$cat = array_map('urldecode', $cat);
if (count($cat) > 3)
return;
if ($cat[0] !== 'eu' && $cat[0] !== 'us')
return;
$this->region = $cat[0];
// if ($cat[1] == Profiler::urlize(CFG_BATTLEGROUP))
// $this->battlegroup = CFG_BATTLEGROUP;
if (isset($cat[1]))
{
foreach (Profiler::getRealms() as $rId => $r)
{
if (Profiler::urlize($r['name']) == $cat[1])
{
$this->realm = $r['name'];
$this->realmId = $rId;
if (isset($cat[2]) && mb_strlen($cat[2]) >= 3)
$this->subjectName = $cat[2]; // cannot reconstruct original name from urlized form; match against special name field
break;
}
}
}
}
protected function initialSync()
{
$this->prepareContent();
$this->notFound = array(
'title' => sprintf(Lang::profiler('firstUseTitle'), $this->subjectName, $this->realm),
'msg' => ''
);
$this->hasComContent = false;
Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics());
if (isset($this->tabId))
$this->pageTemplate['activeTab'] = $this->tabId;
$this->display('text-page-generic');
exit();
}
protected function generatePath()
{
if ($this->region)
{
$this->path[] = $this->region;
if ($this->realm)
$this->path[] = Profiler::urlize($this->realm);
// else
// $this->path[] = Profiler::urlize(CFG_BATTLEGROUP);
}
}
}
class GenericPage class GenericPage
{ {
@@ -148,7 +230,7 @@ class GenericPage
if ($pageParam) if ($pageParam)
$this->fullParams .= '='.$pageParam; $this->fullParams .= '='.$pageParam;
if (CFG_CACHE_DIR && Util::checkOrCreateDirectory(CFG_CACHE_DIR)) if (CFG_CACHE_DIR && Util::writeDir(CFG_CACHE_DIR))
$this->cacheDir = mb_substr(CFG_CACHE_DIR, -1) != '/' ? CFG_CACHE_DIR.'/' : CFG_CACHE_DIR; $this->cacheDir = mb_substr(CFG_CACHE_DIR, -1) != '/' ? CFG_CACHE_DIR.'/' : CFG_CACHE_DIR;
// force page refresh // force page refresh

View File

@@ -261,7 +261,7 @@ class ItemPage extends genericPage
// avg auction buyout // avg auction buyout
if (in_array($this->subject->getField('bonding'), [0, 2, 3])) if (in_array($this->subject->getField('bonding'), [0, 2, 3]))
if ($_ = Util::getBuyoutForItem($this->typeId)) if ($_ = Profiler::getBuyoutForItem($this->typeId))
$infobox[] = '[tooltip=tooltip_buyoutprice]'.Lang::item('buyout.').'[/tooltip]'.Lang::main('colon').'[money='.$_.']'.$each; $infobox[] = '[tooltip=tooltip_buyoutprice]'.Lang::item('buyout.').'[/tooltip]'.Lang::main('colon').'[money='.$_.']'.$each;
// avg money contained // avg money contained

116
prQueue Executable file
View File

@@ -0,0 +1,116 @@
<?php
require 'includes/shared.php';
/* todo (med):
* tidy this file
* make win-safe
*/
if (!CLI)
die("this script must be run from CLI\n");
if (CLI && getcwd().DIRECTORY_SEPARATOR.'prQueue' != __FILE__)
die("this script must be run from root directory\n");
if ($_ = getopt('', ['log::']))
if (!empty($_['log']))
CLI::initLogFile(trim($_['log']));
// check if we already have a queue running
if (!Profiler::queueLock(getmypid()))
exit();
CLI::write('profiler queue started', CLI::LOG_OK);
set_time_limit(0);
$tCycle = microtime(true);
$error = function ($type, $typeId, $realmId)
{
$what = '';
if ($type == TYPE_PROFILE)
$what = 'char';
if ($type == TYPE_GUILD)
$what = 'guild';
if ($type == TYPE_ARENA_TEAM)
$what = 'arena team';
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_CHAR, $realmId, $type, $typeId);
trigger_error('prQueue - unknown '.$what.' guid #'.$typeId.' on realm #'.$realmId.' to sync into profiler.', E_USER_WARNING);
CLI::write('unknown '.$what.' guid #'.$typeId.' on realm #'.$realmId.' to sync into profiler.', CLI::LOG_WARN);
};
// if (CFG_PROFILER_QUEUE) - wont work because it is not redefined if changed in config
while (DB::Aowow()->selectCell('SELECT value FROM ?_config WHERE `key` = "profiler_queue"'))
{
if (($tDiff = (microtime(true) - $tCycle)) < (CFG_PROFILER_QUEUE_DELAY / 1000))
{
$wait = (CFG_PROFILER_QUEUE_DELAY / 1000) - $tDiff;
CLI::write('sleeping '.Lang::nf($wait, 2).'s..');
usleep($wait * 1000 * 1000);
}
$tCycle = microtime(true);
$row = DB::Aowow()->selectRow('SELECT * FROM ?_profiler_sync WHERE status = ?d ORDER BY requestTime ASC', PR_QUEUE_STATUS_WAITING);
if (!$row)
{
// nothing more to do
CLI::write('profiler queue empty - process halted!', CLI::LOG_INFO);
Profiler::queueFree();
exit();
}
// scheduled for future date
if ($row['requestTime'] > time())
continue;
if (empty(Profiler::getRealms()[$row['realm']]))
{
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_ARMORY, $row['realm'], $row['type'], $row['typeId']);
CLI::write('realm #'.$row['realm'].' for subject guid '.$row['realmGUID'].' is undefined', CLI::LOG_WARN);
continue;
}
else
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d WHERE requestTime = ?d AND realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_WORKING, time(), $row['realm'], $row['type'], $row['typeId']);
switch ($row['type'])
{
case TYPE_PROFILE:
if (!Profiler::getCharFromRealm($row['realm'], $row['realmGUID']))
{
$error(TYPE_PROFILE, $row['realmGUID'], $row['realm']);
continue 2;
}
break;
case TYPE_GUILD:
if (!Profiler::getGuildFromRealm($row['realm'], $row['realmGUID']))
{
$error(TYPE_ARENA_GUILD, $row['realmGUID'], $row['realm']);
continue 2;
}
break;
case TYPE_ARENA_TEAM:
if (!Profiler::getArenaTeamFromRealm($row['realm'], $row['realmGUID']))
{
$error(TYPE_ARENA_TEAM, $row['realmGUID'], $row['realm']);
continue 2;
}
break;
default:
DB::Aowow()->query('DELETE FROM ?_profiler_sync WHERE realm = ?d AND type = ?d AND typeId = ?d', $row['realm'], $row['type'], $row['typeId']);
trigger_error('prQueue - unknown type #'.$row['type'].' to sync into profiler. Removing from queue...', E_USER_ERROR);
CLI::write('unknown type #'.$row['type'].' to sync into profiler. Removing from queue...', CLI::LOG_ERROR);
}
// mark as ready
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = 0 WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_READY, $row['realm'], $row['type'], $row['typeId']);
}
Profiler::queueFree();
CLI::write('profiler queue halted!', CLI::LOG_INFO);
?>

File diff suppressed because one or more lines are too long

View File

@@ -233,11 +233,20 @@ class FileGen
CLI::write('Also, expected include setup/tools/filegen/'.$name.'.func.php was not found.'); CLI::write('Also, expected include setup/tools/filegen/'.$name.'.func.php was not found.');
} }
} }
}
if ($content && $funcOK) if (fWrite($dest, $content))
if (CLISetup::writeFile($destPath.$file, $content)) {
$success = true; CLI::write(sprintf(ERR_NONE, CLI::bold($destPath.$file)), CLI::LOG_OK);
if ($content && $funcOK)
$success = true;
}
else
CLI::write(sprintf(ERR_WRITE_FILE, CLI::bold($destPath.$file)), CLI::LOG_ERROR);
fClose($dest);
}
else
CLI::write(sprintf(ERR_CREATE_FILE, CLI::bold($destPath.$file)), CLI::LOG_ERROR);
} }
else else
CLI::write(sprintf(ERR_READ_FILE, CLI::bold(FileGen::$tplPath.$file.'.in')), CLI::LOG_ERROR); CLI::write(sprintf(ERR_READ_FILE, CLI::bold(FileGen::$tplPath.$file.'.in')), CLI::LOG_ERROR);

View File

@@ -14,11 +14,27 @@ if (!CLI)
{ {
$success = true; $success = true;
$scripts = []; $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;
}
};
/**********/ /**********/
/* Quests */ /* Quests */
/**********/ /**********/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
$success = true; $success = true;
$condition = [ $condition = [
@@ -30,6 +46,23 @@ if (!CLI)
]; ];
$questz = new QuestList($condition); $questz = new QuestList($condition);
// get quests for exclusion
foreach ($questz->iterate() as $id => $__)
{
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); $currencies = array_column($questz->rewards, TYPE_CURRENCY);
foreach ($currencies as $curr) foreach ($currencies as $curr)
@@ -62,50 +95,10 @@ if (!CLI)
return $success; return $success;
}; };
/****************/
/* Achievements */
/****************/
$scripts[] = function()
{
$success = true;
$condition = array(
CFG_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(Util::$localeStrings[$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.'/p-achievements', $buff))
$success = false;
}
return $success;
};
/**********/ /**********/
/* Titles */ /* Titles */
/**********/ /**********/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
$success = true; $success = true;
$condition = array( $condition = array(
@@ -114,6 +107,11 @@ if (!CLI)
); );
$titlez = new TitleList($condition); $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) foreach (CLISetup::$localeIds as $l)
{ {
set_time_limit(5); set_time_limit(5);
@@ -142,7 +140,7 @@ if (!CLI)
/**********/ /**********/
/* Mounts */ /* Mounts */
/**********/ /**********/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
$success = true; $success = true;
$condition = array( $condition = array(
@@ -177,7 +175,7 @@ if (!CLI)
/**************/ /**************/
/* Companions */ /* Companions */
/**************/ /**************/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
$success = true; $success = true;
$condition = array( $condition = array(
@@ -212,7 +210,7 @@ if (!CLI)
/************/ /************/
/* Factions */ /* Factions */
/************/ /************/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
$success = true; $success = true;
$condition = array( // todo (med): exclude non-gaining reputation-header $condition = array( // todo (med): exclude non-gaining reputation-header
@@ -244,7 +242,7 @@ if (!CLI)
/***********/ /***********/
/* Recipes */ /* Recipes */
/***********/ /***********/
$scripts[] = function() $scripts[] = function() use ($exAdd)
{ {
// special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order // special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order
$skills = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, [185, 129, 356]]; $skills = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, [185, 129, 356]];
@@ -303,6 +301,81 @@ if (!CLI)
return $success; return $success;
}; };
/****************/
/* Achievements */
/****************/
$scripts[] = function() use ($exAdd)
{
$success = true;
$condition = array(
CFG_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(Util::$localeStrings[$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)
{
$s = count($exclusions);
$i = $n = 0;
CLI::write('applying '.$s.' 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));
if ($i >= 500)
{
$i = 0;
CLI::write(' * '.$n.' / '.$s.' ('.Lang::nf(100 * $n / $s, 1).'%)');
}
$i++;
$n++;
}
// 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 // check directory-structure
foreach (Util::$localeStrings as $dir) foreach (Util::$localeStrings as $dir)
if (!CLISetup::writeDir('datasets/'.$dir)) if (!CLISetup::writeDir('datasets/'.$dir))

View File

@@ -42,21 +42,24 @@ if (!CLI)
$subUS = []; $subUS = [];
$set = 0x0; $set = 0x0;
$menu = [ $menu = [
['us', 'US & Oceanic', null,[[Util::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP, null, &$subUS]]], // skip usage of battlegroup
['eu', 'Europe', null,[[Util::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP, null, &$subEU]]] // ['us', Lang::profiler('regions', 'us'), null,[[Profiler::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP, null, &$subUS]]],
// ['eu', Lang::profiler('regions', 'eu'), null,[[Profiler::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP, null, &$subEU]]]
['us', Lang::profiler('regions', 'us'), null, &$subUS],
['eu', Lang::profiler('regions', 'eu'), null, &$subEU]
]; ];
foreach (Util::getRealms() as $row) foreach (Profiler::getRealms() as $row)
{ {
if ($row['region'] == 'eu') if ($row['region'] == 'eu')
{ {
$set |= 0x1; $set |= 0x1;
$subEU[] = [Util::urlize($row['name']), $row['name']]; $subEU[] = [Profiler::urlize($row['name']), $row['name']];
} }
else if ($row['region'] == 'us') else if ($row['region'] == 'us')
{ {
$set |= 0x2; $set |= 0x2;
$subUS[] = [Util::urlize($row['name']), $row['name']]; $subUS[] = [Profiler::urlize($row['name']), $row['name']];
} }
} }

View File

@@ -28,12 +28,12 @@ if (!CLI)
function realms() function realms()
{ {
$realms = Util::getRealms(); $realms = Profiler::getRealms();
if (!$realms) if (!$realms)
CLI::write(' - realms: Auth-DB not set up .. static data g_realms will be empty', CLI::LOG_WARN); CLI::write(' - realms: Auth-DB not set up .. static data g_realms will be empty', CLI::LOG_WARN);
else // else
foreach ($realms as &$r) // foreach ($realms as &$r)
$r['battlegroup'] = CFG_BATTLEGROUP; // $r['battlegroup'] = CFG_BATTLEGROUP;
$toFile = "var g_realms = ".Util::toJSON($realms).";"; $toFile = "var g_realms = ".Util::toJSON($realms).";";
$file = 'datasets/realms'; $file = 'datasets/realms';

View File

@@ -32,21 +32,21 @@ if (!CLI)
baseParryPct baseParryPct
ParryCap ParryCap
baseBlockPct baseBlockPct
directMod1 applies mod directly only one class having something worth mentioning: DK classMod1 applies mod directly only one class having something worth mentioning: DK
directMod2 applies mod directly so what were they originally used for..? classMod2 applies mod directly so what were they originally used for..?
*/ */
$dataz = array( $dataz = array(
1 => [[-20, 2, 0, 3], [-10, 0, 1, 1], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 5, 0, 0], 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, 0, 0], 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, 2, 2], null, 0.9880, -4.0873, 145.560408, 5, 145.560408, 0, 0, 0], 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, 0, 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, 0, 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, 0, ['parryrtng' => [0.25, 'percentOf', 'str']]], // Forcefull Deflection (49410) 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, 0, 0], 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, 0, 0], 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, 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, 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) foreach ($dataz as $class => &$data)
@@ -57,20 +57,43 @@ if (!CLI)
$race = function() $race = function()
{ {
// { str, agi, sta, int, spi, hp, mana, directMod1, directMod2 } // where did i get this data again..?
// { str, agi, sta, int, spi, raceMod1, raceMod2 }
return array( $raceData = array(
1 => [20, 20, 20, 20, 20, 0, ['spi' => [0.05, 'percentOf', 'spi']]], // The Human Spirit (20598) 1 => [20, 20, 20, 20, 20, [], []],
2 => [23, 17, 22, 17, 23, 0, 0], 2 => [23, 17, 22, 17, 23, [], []],
3 => [22, 16, 23, 19, 19, 0, 0], 3 => [22, 16, 23, 19, 19, [], []],
4 => [17, 25, 19, 20, 20, 0, 0], 4 => [17, 25, 19, 20, 20, [], []],
5 => [19, 18, 21, 18, 25, 0, 0], 5 => [19, 18, 21, 18, 25, [], []],
6 => [25, 15, 22, 15, 22, 0, ['health' => [0.05, 'functionOf', '$function(p) { return g_statistics.combo[p.classs][p.level][5]; }']]], // Endurance (20550) ... if you are looking for something elegant, look away! 6 => [25, 15, 22, 15, 22, [], []],
7 => [15, 23, 19, 24, 20, 0, ['int' => [0.05, 'percentOf', 'int']]], // Expansive Mind (20591) 7 => [15, 23, 19, 24, 20, [], []],
8 => [21, 22, 21, 16, 21, 0, ['healthrgn' => [0.1, 'percentOf', 'healthrgn']]], // Regeneration (20555) 8 => [21, 22, 21, 16, 21, [], []],
10 => [17, 22, 18, 24, 19, 0, 0], 10 => [17, 22, 18, 24, 19, [], []],
11 => [21, 17, 19, 21, 22, 0, 0] // ['mlehitpct' => [1, 'add'], 'splhitpct' => [1, 'add'], 'rgdhitpct' => [1, 'add']] // Heroic Presence (6562, 28878) (not actually shown..?) 11 => [21, 17, 19, 21, 22, [], []]
); );
$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() $combo = function()
@@ -143,12 +166,27 @@ if (!CLI)
$skills = function() $skills = function()
{ {
// profession perks (skinning => +crit, mining => +stam) and maybe some others; skillId:{rankNo:someJSON, ..}? // profession perks ... too lazy to formulate a search algorithm for two occurences
return array(
return []; 186 => array( // mining / toughness
75 => ['sta' => 3],
150 => ['sta' => 5],
225 => ['sta' => 7],
300 => ['sta' => 10],
375 => ['sta' => 30],
450 => ['sta' => 60],
),
393 => array( // skinning / master of anatomy
75 => ['critstrkrtng' => 3],
150 => ['critstrkrtng' => 6],
225 => ['critstrkrtng' => 9],
300 => ['critstrkrtng' => 12],
375 => ['critstrkrtng' => 20],
450 => ['critstrkrtng' => 40],
)
);
}; };
// todo: x
$sub = ['classs', 'race', 'combo', 'level', 'skills']; $sub = ['classs', 'race', 'combo', 'level', 'skills'];
$out = []; $out = [];
$success = true; $success = true;

View File

@@ -31,7 +31,9 @@ if (!CLI)
function talentCalc() function talentCalc()
{ {
$success = true; $success = true;
$buildTree = function ($class) use (&$petFamIcons, &$tSpells) $spellMods = (new SpellList(array(['typeCat', -2], CFG_SQL_LIMIT_NONE)))->getProfilerMods();
$buildTree = function ($class) use (&$petFamIcons, &$tSpells, $spellMods)
{ {
$petCategories = []; $petCategories = [];
@@ -41,43 +43,44 @@ if (!CLI)
$tabs = DB::Aowow()->select('SELECT * FROM dbc_talenttab WHERE classMask = ?d ORDER BY `tabNumber`, `creatureFamilyMask`', $mask); $tabs = DB::Aowow()->select('SELECT * FROM dbc_talenttab WHERE classMask = ?d ORDER BY `tabNumber`, `creatureFamilyMask`', $mask);
$result = []; $result = [];
for ($l = 0; $l < count($tabs); $l++) 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_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[$l]['id']); $talents = DB::Aowow()->select('SELECT t.id AS tId, t.*, s.name_loc0, s.name_loc2, s.name_loc3, 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[$l] = array( $result[$tabIdx] = array(
'n' => Util::localizedString($tabs[$l], 'name'), 'n' => Util::localizedString($tabs[$tabIdx], 'name'),
't' => [] 't' => []
); );
if (!$class) if (!$class)
{ {
$petFamId = log($tabs[$l]['creatureFamilyMask'], 2); $petFamId = log($tabs[$tabIdx]['creatureFamilyMask'], 2);
$result[$l]['icon'] = $petFamIcons[$petFamId]; $result[$tabIdx]['icon'] = $petFamIcons[$petFamId];
$petCategories = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, categoryEnumID FROM dbc_creaturefamily WHERE petTalentType = ?d', $petFamId); $petCategories = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, categoryEnumID FROM dbc_creaturefamily WHERE petTalentType = ?d', $petFamId);
$result[$l]['f'] = array_keys($petCategories); $result[$tabIdx]['f'] = array_keys($petCategories);
} }
// talent dependencies go here // talent dependencies go here
$depLinks = []; $depLinks = [];
$tNums = []; $tNums = [];
for ($j = 0; $j < count($talents); $j++) for ($talentIdx = 0; $talentIdx < count($talents); $talentIdx++)
{ {
$tNums[$talents[$j]['tId']] = $j; $tNums[$talents[$talentIdx]['tId']] = $talentIdx;
$d = []; $d = [];
$s = []; $s = [];
$i = $talents[$j]['tId']; $i = $talents[$talentIdx]['tId'];
$n = Util::localizedString($talents[$j], 'name'); $n = Util::localizedString($talents[$talentIdx], 'name');
$x = $talents[$j]['column']; $x = $talents[$talentIdx]['column'];
$y = $talents[$j]['row']; $y = $talents[$talentIdx]['row'];
$r = null; $r = null;
$t = []; $t = [];
$icon = $talents[$j]['iconString']; $j = [];
$m = $talents[$j]['rank2'] == 0 ? 1 : ( $icon = $talents[$talentIdx]['iconString'];
$talents[$j]['rank3'] == 0 ? 2 : ( $m = $talents[$talentIdx]['rank2'] == 0 ? 1 : (
$talents[$j]['rank4'] == 0 ? 3 : ( $talents[$talentIdx]['rank3'] == 0 ? 2 : (
$talents[$j]['rank5'] == 0 ? 4 : 5 $talents[$talentIdx]['rank4'] == 0 ? 3 : (
$talents[$talentIdx]['rank5'] == 0 ? 4 : 5
) )
) )
); );
@@ -87,34 +90,38 @@ if (!CLI)
foreach ($petCategories as $k => $v) foreach ($petCategories as $k => $v)
{ {
// cant handle 64bit integer .. split // cant handle 64bit integer .. split
if ($v >= 32 && ((1 << ($v - 32)) & $talents[$j]['petCategory2'])) if ($v >= 32 && ((1 << ($v - 32)) & $talents[$talentIdx]['petCategory2']))
$f[] = $k; $f[] = $k;
else if ($v < 32 && ((1 << $v) & $talents[$j]['petCategory1'])) else if ($v < 32 && ((1 << $v) & $talents[$talentIdx]['petCategory1']))
$f[] = $k; $f[] = $k;
} }
for ($k = 0; $k <= ($m - 1); $k++) for ($itr = 0; $itr <= ($m - 1); $itr++)
{ {
if (!$tSpells->getEntry($talents[$j]['rank'.($k + 1)])) if (!$tSpells->getEntry($talents[$talentIdx]['rank'.($itr + 1)]))
continue; continue;
$d[] = $tSpells->parseText()[0]; $d[] = $tSpells->parseText()[0];
$s[] = $talents[$j]['rank'.($k + 1)]; $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[$j]['talentSpell']) if ($talents[$talentIdx]['talentSpell'])
$t[] = $tSpells->getTalentHeadForCurrent(); $t[] = $tSpells->getTalentHeadForCurrent();
} }
if ($talents[$j]['reqTalent']) if ($talents[$talentIdx]['reqTalent'])
{ {
// we didn't encounter the required talent yet => create reference // we didn't encounter the required talent yet => create reference
if (!isset($tNums[$talents[$j]['reqTalent']])) if (!isset($tNums[$talents[$talentIdx]['reqTalent']]))
$depLinks[$talents[$j]['reqTalent']] = $j; $depLinks[$talents[$talentIdx]['reqTalent']] = $talentIdx;
$r = @[$tNums[$talents[$j]['reqTalent']], $talents[$j]['reqRank'] + 1]; $r = @[$tNums[$talents[$talentIdx]['reqTalent']], $talents[$talentIdx]['reqRank'] + 1];
} }
$result[$l]['t'][$j] = array( $result[$tabIdx]['t'][$talentIdx] = array(
'i' => $i, 'i' => $i,
'n' => $n, 'n' => $n,
'm' => $m, 'm' => $m,
@@ -122,31 +129,32 @@ if (!CLI)
's' => $s, 's' => $s,
'x' => $x, 'x' => $x,
'y' => $y, 'y' => $y,
'j' => $j
); );
if (isset($r)) if (isset($r))
$result[$l]['t'][$j]['r'] = $r; $result[$tabIdx]['t'][$talentIdx]['r'] = $r;
if (!empty($t)) if (!empty($t))
$result[$l]['t'][$j]['t'] = $t; $result[$tabIdx]['t'][$talentIdx]['t'] = $t;
if (!empty($f)) if (!empty($f))
$result[$l]['t'][$j]['f'] = $f; $result[$tabIdx]['t'][$talentIdx]['f'] = $f;
if ($class) if ($class)
$result[$l]['t'][$j]['iconname'] = $icon; $result[$tabIdx]['t'][$talentIdx]['iconname'] = $icon;
// If this talent is a reference, add it to the array of talent dependencies // If this talent is a reference, add it to the array of talent dependencies
if (isset($depLinks[$talents[$j]['tId']])) if (isset($depLinks[$talents[$talentIdx]['tId']]))
{ {
$result[$l]['t'][$depLinks[$talents[$j]['tId']]]['r'][0] = $j; $result[$tabIdx]['t'][$depLinks[$talents[$talentIdx]['tId']]]['r'][0] = $talentIdx;
unset($depLinks[$talents[$j]['tId']]); unset($depLinks[$talents[$talentIdx]['tId']]);
} }
} }
// Remove all dependencies for which the talent has not been found // Remove all dependencies for which the talent has not been found
foreach ($depLinks as $dep_link) foreach ($depLinks as $dep_link)
unset($result[$l]['t'][$dep_link]['r']); unset($result[$tabIdx]['t'][$dep_link]['r']);
} }
return $result; return $result;

View File

@@ -0,0 +1,371 @@
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `aowow_profiler_sync`;
CREATE TABLE `aowow_profiler_sync` (
`realm` tinyint(3) unsigned NOT NULL,
`realmGUID` int(10) unsigned NOT NULL,
`type` smallint(5) unsigned NOT NULL,
`typeId` int(10) unsigned NOT NULL,
`requestTime` int(10) unsigned NOT NULL,
`status` tinyint(3) unsigned NOT NULL,
`errorCode` tinyint(3) unsigned NOT NULL DEFAULT '0',
UNIQUE KEY `realm_realmGUID_type_typeId` (`realm`,`realmGUID`,`type`),
UNIQUE KEY `type_typeId` (`type`,`typeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_guild`;
CREATE TABLE `aowow_profiler_guild` (
`id` int(10) unsigned NOT NULL,
`realm` int(10) unsigned NOT NULL DEFAULT '0',
`realmGUID` int(10) unsigned NOT NULL DEFAULT '0',
`cuFlags` int(10) unsigned NOT NULL DEFAULT '0',
`name` varchar(26) NOT NULL DEFAULT '',
`nameUrl` varchar(26) NOT NULL DEFAULT '',
`emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT '0',
`emblemColor` tinyint(3) unsigned NOT NULL DEFAULT '0',
`borderStyle` tinyint(3) unsigned NOT NULL DEFAULT '0',
`borderColor` tinyint(3) unsigned NOT NULL DEFAULT '0',
`backgroundColor` tinyint(3) unsigned NOT NULL DEFAULT '0',
`info` varchar(500) NOT NULL DEFAULT '',
`createDate` int(10) unsigned NOT NULL DEFAULT '0',
INDEX `name` (`name`),
INDEX `guild` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_guild_rank`;
CREATE TABLE `aowow_profiler_guild_rank` (
`guildId` int(10) unsigned NOT NULL DEFAULT '0',
`rank` tinyint(3) unsigned NOT NULL,
`name` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`guildId`,`rank`),
INDEX `rank` (`rank`),
CONSTRAINT `FK_aowow_profiler_guild_rank_aowow_profiler_guild` FOREIGN KEY (`guildId`) REFERENCES `aowow_profiler_guild` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_profiles`;
CREATE TABLE `aowow_profiler_profiles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`realm` tinyint(3) unsigned DEFAULT NULL,
`realmGUID` int(11) unsigned DEFAULT NULL,
`cuFlags` int(11) unsigned NOT NULL DEFAULT '0',
`sourceId` int(11) unsigned DEFAULT NULL,
`sourceName` varchar(50) DEFAULT NULL,
`copy` int(10) unsigned DEFAULT NULL,
`icon` varchar(50) DEFAULT NULL,
`user` int(11) unsigned DEFAULT NULL,
`name` varchar(50) NOT NULL,
`race` tinyint(3) unsigned NOT NULL,
`class` tinyint(3) unsigned NOT NULL,
`level` tinyint(3) unsigned NOT NULL,
`gender` tinyint(3) unsigned NOT NULL,
`guild` int(10) unsigned NULL,
`guildrank` tinyint(3) unsigned DEFAULT NULL COMMENT '0: guild master',
`skincolor` tinyint(3) unsigned NOT NULL,
`hairstyle` tinyint(3) unsigned NOT NULL,
`haircolor` tinyint(3) unsigned NOT NULL,
`facetype` tinyint(3) unsigned NOT NULL,
`features` tinyint(3) unsigned NOT NULL,
`nomodelMask` int(11) unsigned NOT NULL DEFAULT '0',
`title` tinyint(3) unsigned NOT NULL,
`description` text NULL,
`playedtime` int(11) unsigned NOT NULL,
`gearscore` smallint(5) unsigned NOT NULL,
`achievementpoints` smallint(5) unsigned NOT NULL,
`lastupdated` int(11) NOT NULL,
`talenttree1` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 1st tree',
`talenttree2` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 2nd tree',
`talenttree3` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 3rd tree',
`talentbuild1` varchar(105) NOT NULL,
`talentbuild2` varchar(105) NOT NULL,
`glyphs1` varchar(45) NOT NULL,
`glyphs2` varchar(45) NOT NULL,
`activespec` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `realm_realmGUID_name` (`realm`,`realmGUID`,`name`),
INDEX `user` (`user`),
INDEX `guild` (`guild`),
CONSTRAINT `FK_aowow_profiler_profiles_aowow_profiler_guild` FOREIGN KEY (`guild`) REFERENCES `aowow_profiler_guild` (`id`) ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_items`;
CREATE TABLE `aowow_profiler_items` (
`id` int(11) unsigned DEFAULT NULL,
`slot` tinyint(3) unsigned DEFAULT NULL,
`item` mediumint(8) unsigned DEFAULT NULL,
`subItem` smallint(6) DEFAULT NULL,
`permEnchant` mediumint(8) unsigned DEFAULT NULL,
`tempEnchant` mediumint(8) unsigned DEFAULT NULL,
`extraSocket` tinyint(3) unsigned DEFAULT NULL COMMENT 'not used .. the appropriate gem slot is set to -1 instead',
`gem1` mediumint(8) DEFAULT NULL,
`gem2` mediumint(8) DEFAULT NULL,
`gem3` mediumint(8) DEFAULT NULL,
`gem4` mediumint(8) DEFAULT NULL,
UNIQUE KEY `id_slot` (`id`,`slot`),
INDEX `id` (`id`),
INDEX `item` (`item`),
CONSTRAINT `FK_pr_items` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_arena_team`;
CREATE TABLE `aowow_profiler_arena_team` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`realm` tinyint(3) unsigned NOT NULL,
`realmGUID` int(10) unsigned NOT NULL,
`name` varchar(24) NOT NULL,
`nameUrl` varchar(24) NOT NULL,
`type` tinyint(3) unsigned NOT NULL DEFAULT '0',
`cuFlags` int(11) unsigned NOT NULL,
`rating` smallint(5) unsigned NOT NULL DEFAULT '0',
`seasonGames` smallint(5) unsigned NOT NULL DEFAULT '0',
`seasonWins` smallint(5) unsigned NOT NULL DEFAULT '0',
`weekGames` smallint(5) unsigned NOT NULL DEFAULT '0',
`weekWins` smallint(5) unsigned NOT NULL DEFAULT '0',
`rank` int(10) unsigned NOT NULL DEFAULT '0',
`backgroundColor` int(10) unsigned NOT NULL DEFAULT '0',
`emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT '0',
`emblemColor` int(10) unsigned NOT NULL DEFAULT '0',
`borderStyle` tinyint(3) unsigned NOT NULL DEFAULT '0',
`borderColor` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE INDEX `realm_realmGUID` (`realm`,`realmGUID`),
INDEX `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_arena_team_member`;
CREATE TABLE `aowow_profiler_arena_team_member` (
`arenaTeamId` int(10) unsigned NOT NULL DEFAULT '0',
`profileId` int(10) unsigned NOT NULL DEFAULT '0',
`captain` tinyint(1) unsigned NOT NULL DEFAULT '0',
`weekGames` smallint(5) unsigned NOT NULL DEFAULT '0',
`weekWins` smallint(5) unsigned NOT NULL DEFAULT '0',
`seasonGames` smallint(5) unsigned NOT NULL DEFAULT '0',
`seasonWins` smallint(5) unsigned NOT NULL DEFAULT '0',
`personalRating` smallint(5) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`arenaTeamId`,`profileId`),
INDEX `guid` (`profileId`),
CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_arena_team` FOREIGN KEY (`arenaTeamId`) REFERENCES `aowow_profiler_arena_team` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_profiles` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_completion`;
CREATE TABLE `aowow_profiler_completion` (
`id` int(11) unsigned NOT NULL,
`type` smallint(6) unsigned NOT NULL,
`typeId` mediumint(9) NOT NULL,
`cur` int(11) DEFAULT NULL,
`max` int(11) DEFAULT NULL,
INDEX `id` (`id`),
INDEX `type` (`type`),
INDEX `typeId` (`typeId`),
CONSTRAINT `FK_pr_completion` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_pets`;
CREATE TABLE `aowow_profiler_pets` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`owner` int(10) unsigned DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`family` tinyint(3) unsigned DEFAULT NULL,
`npc` smallint(5) unsigned DEFAULT NULL,
`displayId` smallint(5) unsigned DEFAULT NULL,
`talents` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `owner` (`owner`),
CONSTRAINT `FK_pr_pets` FOREIGN KEY (`owner`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `aowow_profiler_excludes`;
CREATE TABLE `aowow_profiler_excludes` (
`type` smallint(5) unsigned NOT NULL,
`typeId` mediumint(8) unsigned NOT NULL,
`groups` smallint(5) unsigned NOT NULL COMMENT 'see exclude group defines',
`comment` varchar(50) NOT NULL COMMENT 'rebuilding profiler files will delete everything without a comment',
PRIMARY KEY (`type`,`typeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `aowow_profiler_excludes` (`type`, `typeId`, `groups`, `comment`) VALUES
(6, 459, 1, 'Gray Wolf'),
(6, 468, 1, 'White Stallion'),
(6, 471, 1, 'Palamino'),
(6, 472, 1, 'Pinto'),
(6, 578, 1, 'Black Wolf'),
(6, 579, 1, 'Red Wolf'),
(6, 581, 1, 'Winter Wolf'),
(6, 3363, 1, 'Nether Drake'),
(6, 6896, 1, 'Black Ram'),
(6, 6897, 1, 'Blue Ram'),
(6, 8980, 1, 'Skeletal Horse'),
(6, 10681, 1, 'Summon Cockatoo'),
(6, 10686, 1, 'Summon Prairie Chicken'),
(6, 10687, 1, 'Summon White Plymouth Rock'),
(6, 10699, 1, 'Summon Bronze Whelpling'),
(6, 10700, 1, 'Summon Faeling'),
(6, 10701, 1, 'Summon Dart Frog'),
(6, 10702, 1, 'Summon Island Frog'),
(6, 10705, 1, 'Summon Eagle Owl'),
(6, 10708, 1, 'Summon Snowy Owl'),
(6, 10710, 1, 'Summon Cottontail Rabbit'),
(6, 10712, 1, 'Summon Spotted Rabbit'),
(6, 10715, 1, 'Summon Blue Racer'),
(6, 10718, 1, 'Green Water Snake'),
(6, 10719, 1, 'Ribbon Snake'),
(6, 10720, 1, 'Scarlet Snake'),
(6, 10721, 1, 'Summon Elven Wisp'),
(6, 10795, 1, 'Ivory Raptor'),
(6, 10798, 1, 'Obsidian Raptor'),
(6, 15648, 1, 'Corrupted Kitten'),
(6, 15779, 1, 'White Mechanostrider Mod B'),
(6, 15780, 1, 'Green Mechanostrider'),
(6, 15781, 1, 'Steel Mechanostrider'),
(6, 16055, 1, 'Black Nightsaber'),
(6, 16056, 1, 'Ancient Frostsaber'),
(6, 16058, 1, 'Primal Leopard'),
(6, 16059, 1, 'Tawny Sabercat'),
(6, 16060, 1, 'Golden Sabercat'),
(6, 16080, 1, 'Red Wolf'),
(6, 16081, 1, 'Winter Wolf'),
(6, 16082, 1, 'Palomino'),
(6, 16083, 1, 'White Stallion'),
(6, 16084, 1, 'Mottled Red Raptor'),
(6, 17450, 1, 'Ivory Raptor'),
(6, 17455, 1, 'Purple Mechanostrider'),
(6, 17456, 1, 'Red and Blue Mechanostrider'),
(6, 17458, 1, 'Fluorescent Green Mechanostrider'),
(6, 17459, 1, 'Icy Blue Mechanostrider Mod A'),
(6, 17460, 1, 'Frost Ram'),
(6, 17461, 1, 'Black Ram'),
(6, 17468, 1, 'Pet Fish'),
(6, 17469, 1, 'Pet Stone'),
(6, 18363, 1, 'Riding Kodo'),
(6, 18991, 1, 'Green Kodo'),
(6, 18992, 1, 'Teal Kodo'),
(6, 19363, 1, 'Summon Mechanical Yeti'),
(6, 23220, 1, 'Swift Dawnsaber'),
(6, 23428, 1, 'Albino Snapjaw'),
(6, 23429, 1, 'Loggerhead Snapjaw'),
(6, 23430, 1, 'Olive Snapjaw'),
(6, 23431, 1, 'Leatherback Snapjaw'),
(6, 23432, 1, 'Hawksbill Snapjaw'),
(6, 23530, 16, 'Tiny Red Dragon'),
(6, 23531, 16, 'Tiny Green Dragon'),
(6, 24985, 1, 'Summon Baby Murloc (Blue)'),
(6, 24986, 1, 'Summon Baby Murloc (Green)'),
(6, 24987, 1, 'Summon Baby Murloc (Orange)'),
(6, 24988, 4, 'Lurky'),
(6, 24989, 1, 'Summon Baby Murloc (Pink)'),
(6, 24990, 1, 'Summon Baby Murloc (Purple)'),
(6, 25849, 1, 'Baby Shark'),
(6, 26067, 1, 'Summon Mechanical Greench'),
(6, 26391, 1, 'Tentacle Call'),
(6, 28828, 1, 'Nether Drake'),
(6, 29059, 1, 'Naxxramas Deathcharger'),
(6, 30152, 1, 'White Tiger Cub'),
(6, 30156, 2, 'Hippogryph Hatchling'),
(6, 30174, 2, 'Riding Turtle'),
(6, 32298, 4, 'Netherwhelp'),
(6, 32345, 1, 'Peep the Phoenix Mount'),
(6, 33050, 128, 'Magical Crawdad'),
(6, 33057, 1, 'Summon Mighty Mr. Pinchy'),
(6, 33630, 1, 'Blue Mechanostrider'),
(6, 34407, 1, 'Great Elite Elekk'),
(6, 35157, 1, 'Summon Spotted Rabbit'),
(6, 37015, 1, 'Swift Nether Drake'),
(6, 40319, 16, 'Lucky'),
(6, 40405, 16, 'Lucky'),
(6, 43688, 1, 'Amani War Bear'),
(6, 43810, 1, 'Frost Wyrm'),
(6, 44317, 1, 'Merciless Nether Drake'),
(6, 44744, 1, 'Merciless Nether Drake'),
(6, 45125, 2, 'Rocket Chicken'),
(6, 45174, 16, 'Golden Pig'),
(6, 45175, 16, 'Silver Pig'),
(6, 45890, 1, 'Scorchling'),
(6, 47037, 1, 'Swift War Elekk'),
(6, 48406, 16, 'Essence of Competition'),
(6, 48408, 1, 'Essence of Competition'),
(6, 48954, 8, 'Swift Zhevra'),
(6, 49322, 8, 'Swift Zhevra'),
(6, 49378, 1, 'Brewfest Riding Kodo'),
(6, 50869, 1, 'Brewfest Kodo'),
(6, 50870, 1, 'Brewfest Ram'),
(6, 51851, 1, 'Vampiric Batling'),
(6, 51960, 1, 'Frost Wyrm Mount'),
(6, 52615, 4, 'Frosty'),
(6, 53082, 8, 'Mini Tyrael'),
(6, 53768, 1, 'Haunted'),
(6, 54187, 1, 'Clockwork Rocket Bot'),
(6, 55068, 1, 'Mr. Chilly'),
(6, 58983, 8, 'Big Blizzard Bear'),
(6, 59572, 1, 'Black Polar Bear'),
(6, 59573, 1, 'Brown Polar Bear'),
(6, 59802, 1, 'Grand Ice Mammoth'),
(6, 59804, 1, 'Grand Ice Mammoth'),
(6, 59976, 1, 'Black Proto-Drake'),
(6, 60021, 1, 'Plagued Proto-Drake'),
(6, 60136, 1, 'Grand Caravan Mammoth'),
(6, 60140, 1, 'Grand Caravan Mammoth'),
(6, 61309, 512, 'Magnificent Flying Carpet'),
(6, 61442, 1, 'Swift Mooncloth Carpet'),
(6, 61444, 1, 'Swift Shadoweave Carpet'),
(6, 61446, 1, 'Swift Spellfire Carpete'),
(6, 61451, 512, 'Flying Carpet'),
(6, 61855, 1, 'Baby Blizzard Bear'),
(6, 62048, 1, 'Black Dragonhawk Mount'),
(6, 62514, 1, 'Alarming Clockbot'),
(6, 63318, 8, 'Murkimus the Gladiator'),
(6, 64351, 1, 'XS-001 Constructor Bot'),
(6, 64656, 1, 'Blue Skeletal Warhorse'),
(6, 64731, 128, 'Sea Turtle'),
(6, 65682, 1, 'Warbot'),
(6, 65917, 2, 'Magic Rooster'),
(6, 66030, 8, 'Grunty'),
(6, 66122, 1, 'Magic Rooster - dummy spell'),
(6, 66123, 1, 'Magic Rooster - dummy spell'),
(6, 66124, 1, 'Magic Rooster - dummy spell'),
(6, 66520, 1, 'Jade Tiger'),
(6, 66907, 1, 'Argent Warhorse'),
(6, 67527, 16, 'Onyx Panther'),
(6, 68767, 2, 'Tuskarr Kite'),
(6, 68810, 2, 'Spectral Tiger Cub'),
(6, 69002, 1, 'Onyxian Whelpling'),
(6, 69452, 8, 'Core Hound Pup'),
(6, 69535, 4, 'Gryphon Hatchling'),
(6, 69536, 4, 'Wind Rider Cub'),
(6, 69539, 1, 'Zipao Tiger'),
(6, 69541, 4, 'Pandaren Monk'),
(6, 69677, 4, 'Lil\' K.T.'),
(6, 74856, 2, 'Blazing Hippogryph'),
(6, 74918, 2, 'Wooly White Rhino'),
(6, 75596, 512, 'Frosty Flying Carpet'),
(6, 75613, 1, 'Celestial Dragon'),
(6, 75614, 16, 'Celestial Steed'),
(6, 75906, 4, 'Lil\' XT'),
(6, 75936, 1, 'Murkimus the Gladiator'),
(6, 75973, 8, 'X-53 Touring Rocket'),
(6, 78381, 8, 'Mini Thor'),
(8, 87, 1024, 'Bloodsail Buccaneers - max rank is honored'),
(8, 92, 1024, 'Gelkis Clan Centaur - max rank is friendly'),
(8, 93, 1024, 'Magram Clan Centaur - max rank is friendly');
CREATE TABLE `aowow_account_profiles` (
`accountId` INT(10) UNSIGNED NOT NULL,
`profileId` INT(10) UNSIGNED NOT NULL,
`extraFlags` INT(10) UNSIGNED NOT NULL,
UNIQUE INDEX `accountId_profileId` (`accountId`, `profileId`),
INDEX `accountId` (`accountId`),
INDEX `profileId` (`profileId`),
CONSTRAINT `FK_account_id` FOREIGN KEY (`accountId`) REFERENCES `aowow_account` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_profile_id` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
) COLLATE='utf8_general_ci' ENGINE=InnoDB;
CREATE TABLE `aowow_account_excludes` (
`userId` INT(11) UNSIGNED NOT NULL,
`type` SMALLINT(5) UNSIGNED NOT NULL,
`typeId` MEDIUMINT(8) UNSIGNED NOT NULL,
`mode` TINYINT(2) UNSIGNED NOT NULL COMMENT '1: exclude; 2: include',
UNIQUE INDEX `userId_type_typeId` (`userId`, `type`, `typeId`),
INDEX `userId` (`userId`),
CONSTRAINT `FK_acc_excludes` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
) COLLATE='utf8_general_ci' ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=1;

View File

@@ -0,0 +1,16 @@
DELETE FROM aowow_config WHERE `key` LIKE 'profiler_%';
INSERT INTO aowow_config (`key`, `value`, `cat`, `flags`, `comment`) VALUES
('profiler_queue', 0, 7, 0x84, 'default: 0 - enable/disable profiler queue'),
('profiler_queue_delay', 3000, 7, 0x81, 'default: 3000 - min. delay between queue cycles (in ms)'),
('profiler_resync_ping', 5000, 7, 0x81, 'default: 5000 - how often the javascript asks for for updates, when queued (in ms)'),
('profiler_resync_delay', 1*60*60, 7, 0x81, 'default: 1*60*60 - how often a character can be refreshed (in sec)');
DROP TABLE IF EXISTS `aowow_characters`;
ALTER TABLE `aowow_talents`
ADD COLUMN `petTypeMask` TINYINT(3) UNSIGNED NOT NULL AFTER `class`;
ALTER TABLE `aowow_account`
ADD COLUMN `excludeGroups` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '1' COMMENT 'profiler - completion exclude bitmask' AFTER `description`;
UPDATE aowow_dbversion SET `sql` = CONCAT(IFNULL(`sql`, ''), ' talents');