Skip to content

Commit

Permalink
Implemented 'Inventory Snapshot' feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Uleat committed Sep 26, 2015
1 parent 41d19c4 commit a1089fc
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 6 deletions.
11 changes: 11 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 09/25/2015 ==
Uleat: Implemented 'Inventory Snapshot' feature to track online player inventories at timed intervals.
rules:
'Character:ActiveInvSnapshots' - active (true) or inactive (false - default)
'Character:InvSnapshotMinIntervalM' - minimum time between snapshots (in minutes)
'Character:InvSnapshotMinRetryM' - minimum time to attempt a retry after a failed snapshot (in minutes)
'Character:InvSnapshotHistoryD' - minimum time to keep snapshot entries (in days)
commands:
'#invsnapshot' - Takes a snapshot of target client's inventory (feature active or inactive)
'#clearinvsnapshots [use rule]' - Clears snapshot entries based on bool argument ([true] - honors the 'InvSnapshotHistoryD' rule, [false] - erases all)

== 08/02/2015 ==
Shendare: VS2013 query StringFormat glitches when "%f" is passed for the int GetRunSpeed().
Shendare: In CreateNewNPCCommand(), the npc_type_id and spawngroupid are created in the database, but never set in the spawn class, so later it can't delete them with #npcspawn remove or #npcspawn delete.
Expand Down
9 changes: 9 additions & 0 deletions common/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2173,3 +2173,12 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings)
}
}
}

void Database::ClearInvSnapshots(bool use_rule)
{
uint32 del_time = time(nullptr);
if (use_rule) { del_time -= RuleI(Character, InvSnapshotHistoryD) * 86400; }

std::string query = StringFormat("DELETE FROM inventory_snapshots WHERE time_index <= %lu", (unsigned long)del_time);
QueryDatabase(query);
}
2 changes: 2 additions & 0 deletions common/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ class Database : public DBcore {
void SetLFP(uint32 CharID, bool LFP);
void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon);

void ClearInvSnapshots(bool use_rule = true);

/* EQEmuLogSys */
void LoadLogSettings(EQEmuLogSys::LogSettings* log_settings);

Expand Down
2 changes: 2 additions & 0 deletions common/extprofile.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ struct ExtendedProfile_Struct {
uint32 mercTimerRemaining; /* Not Used */
uint8 mercGender; /* Not Used */
int32 mercState; /* Not Used */
uint32 last_invsnapshot_time; /* Used */
uint32 next_invsnapshot_time; /* Used */
};

#pragma pack()
Expand Down
4 changes: 4 additions & 0 deletions common/ruletypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of
RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?)
RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?)
RULE_INT(Character, TrollCommonTongue, 95) // 95 By default (live-like?)
RULE_BOOL(Character, ActiveInvSnapshots, false) // Takes a periodic snapshot of inventory contents from online players
RULE_INT(Character, InvSnapshotMinIntervalM, 180) // Minimum time (in minutes) between inventory snapshots
RULE_INT(Character, InvSnapshotMinRetryM, 30) // Time (in minutes) to re-attempt an inventory snapshot after a failure
RULE_INT(Character, InvSnapshotHistoryD, 30) // Time (in days) to keep snapshot entries
RULE_CATEGORY_END()

RULE_CATEGORY(Mercs)
Expand Down
2 changes: 1 addition & 1 deletion common/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/

#define CURRENT_BINARY_DATABASE_VERSION 9086
#define CURRENT_BINARY_DATABASE_VERSION 9087
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
#ifndef WIN32
Expand Down
1 change: 1 addition & 0 deletions utils/sql/db_update_manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@
9084|2015_06_30_runspeed_adjustments.sql|SELECT `runspeed` FROM `npc_types` WHERE `runspeed` > 3|not_empty|
9085|2015_07_01_Marquee_Rule.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Character:MarqueeHPUpdates%'|empty|
9086|2015_07_02_aa_rework.sql|SHOW TABLES LIKE 'aa_ranks'|empty|
9087|2015_09_25_inventory_snapshots.sql|SHOW TABLES LIKE 'inventory_snapshots'|empty|

# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not
Expand Down
46 changes: 46 additions & 0 deletions utils/sql/git/required/2015_09_25_inventory_snapshots.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
CREATE TABLE `inventory_snapshots` (
`time_index` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`charid` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`slotid` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0',
`itemid` INT(11) UNSIGNED NULL DEFAULT '0',
`charges` SMALLINT(3) UNSIGNED NULL DEFAULT '0',
`color` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0',
`augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0',
`augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0',
`augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT '0',
`augslot5` MEDIUMINT(7) UNSIGNED NULL DEFAULT '0',
`augslot6` MEDIUMINT(7) NOT NULL DEFAULT '0',
`instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`custom_data` TEXT NULL,
`ornamenticon` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`ornamentidfile` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`ornament_hero_model` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`time_index`, `charid`, `slotid`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;

ALTER TABLE `character_data` ADD COLUMN `e_last_invsnapshot` INT(11) UNSIGNED NOT NULL DEFAULT '0';

INSERT INTO `rule_values` VALUES
(1, 'Character:ActiveInvSnapshots', 'false', 'Takes a periodic snapshot of inventory contents from online players'),
(2, 'Character:ActiveInvSnapshots', 'false', 'Takes a periodic snapshot of inventory contents from online players'),
(4, 'Character:ActiveInvSnapshots', 'false', 'Takes a periodic snapshot of inventory contents from online players'),
(5, 'Character:ActiveInvSnapshots', 'false', 'Takes a periodic snapshot of inventory contents from online players'),
(10, 'Character:ActiveInvSnapshots', 'false', 'Takes a periodic snapshot of inventory contents from online players'),
(1, 'Character:InvSnapshotMinIntervalM', '180', 'Minimum time (in minutes) between inventory snapshots'),
(2, 'Character:InvSnapshotMinIntervalM', '180', 'Minimum time (in minutes) between inventory snapshots'),
(4, 'Character:InvSnapshotMinIntervalM', '180', 'Minimum time (in minutes) between inventory snapshots'),
(5, 'Character:InvSnapshotMinIntervalM', '180', 'Minimum time (in minutes) between inventory snapshots'),
(10, 'Character:InvSnapshotMinIntervalM', '180', 'Minimum time (in minutes) between inventory snapshots'),
(1, 'Character:InvSnapshotMinRetryM', '30', 'Time (in minutes) to re-attempt an inventory snapshot after a failure'),
(2, 'Character:InvSnapshotMinRetryM', '30', 'Time (in minutes) to re-attempt an inventory snapshot after a failure'),
(4, 'Character:InvSnapshotMinRetryM', '30', 'Time (in minutes) to re-attempt an inventory snapshot after a failure'),
(5, 'Character:InvSnapshotMinRetryM', '30', 'Time (in minutes) to re-attempt an inventory snapshot after a failure'),
(10, 'Character:InvSnapshotMinRetryM', '30', 'Time (in minutes) to re-attempt an inventory snapshot after a failure'),
(1, 'Character:InvSnapshotHistoryD', '30', 'Time (in days) to keep snapshot entries'),
(2, 'Character:InvSnapshotHistoryD', '30', 'Time (in days) to keep snapshot entries'),
(4, 'Character:InvSnapshotHistoryD', '30', 'Time (in days) to keep snapshot entries'),
(5, 'Character:InvSnapshotHistoryD', '30', 'Time (in days) to keep snapshot entries'),
(10, 'Character:InvSnapshotHistoryD', '30', 'Time (in days) to keep snapshot entries');
2 changes: 2 additions & 0 deletions world/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ int main(int argc, char** argv) {
database.ClearRaid();
database.ClearRaidDetails();
database.ClearRaidLeader();
Log.Out(Logs::General, Logs::World_Server, "Clearing inventory snapshots..");
database.ClearInvSnapshots();
Log.Out(Logs::General, Logs::World_Server, "Loading items..");
if(!database.LoadItems(hotfix_name))
Log.Out(Logs::General, Logs::World_Server, "Error: Could not load item data. But ignoring");
Expand Down
11 changes: 11 additions & 0 deletions zone/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,17 @@ bool Client::Save(uint8 iCommitNow) {

m_pp.hunger_level = EQEmu::Clamp(m_pp.hunger_level, 0, 50000);
m_pp.thirst_level = EQEmu::Clamp(m_pp.thirst_level, 0, 50000);

// perform snapshot before SaveCharacterData() so that m_epp will contain the updated time
if (RuleB(Character, ActiveInvSnapshots) && time(nullptr) >= GetNextInvSnapshotTime()) {
if (database.SaveCharacterInventorySnapshot(CharacterID())) {
SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM));
}
else {
SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM));
}
}

database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */

return true;
Expand Down
7 changes: 7 additions & 0 deletions zone/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,13 @@ class Client : public Mob

bool InterrogateInventory(Client* requester, bool log, bool silent, bool allowtrip, bool& error, bool autolog = true);

void SetNextInvSnapshot(uint32 interval_in_min) {
m_epp.last_invsnapshot_time = time(nullptr);
m_epp.next_invsnapshot_time = m_epp.last_invsnapshot_time + (interval_in_min * 60);
}
uint32 GetLastInvSnapshotTime() { return m_epp.last_invsnapshot_time; }
uint32 GetNextInvSnapshotTime() { return m_epp.next_invsnapshot_time; }

//Command #Tune functions
virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false);
Expand Down
32 changes: 32 additions & 0 deletions zone/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ int command_init(void) {
command_add("castspell", "[spellid] - Cast a spell", 50, command_castspell) ||
command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) ||
command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) ||
command_add("clearinvsnapshots", "[use rule] - Clear inventory snapshot history (true - elapsed entries, false - all entries)", 200, command_clearinvsnapshots) ||
command_add("close_shop", nullptr, 100, command_merchantcloseshop) ||
command_add("connectworld", nullptr,0, command_connectworldserver) ||
command_add("connectworldserver", "- Make zone attempt to connect to worldserver", 200, command_connectworldserver) ||
Expand Down Expand Up @@ -260,6 +261,7 @@ int command_init(void) {
command_add("instance", "- Modify Instances", 200, command_instance) ||
command_add("interrogateinv", "- use [help] argument for available options", 0, command_interrogateinv) ||
command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", 50, command_interrupt) ||
command_add("invsnapshot", "- Takes an inventory snapshot of your current target", 80, command_invsnapshot) ||
command_add("invul", nullptr,0, command_invul) ||
command_add("invulnerable", "[on/off] - Turn player target's or your invulnerable flag on or off", 80, command_invul) ||
command_add("ipban", "[IP address] - Ban IP by character name", 200, command_ipban) ||
Expand Down Expand Up @@ -2836,6 +2838,36 @@ void command_interrogateinv(Client *c, const Seperator *sep)
c->Message(13, "An unknown error occurred while processing Client::InterrogateInventory()");
}

void command_invsnapshot(Client *c, const Seperator *sep)
{
auto t = c->GetTarget();
if (!t || !t->IsClient()) {
c->Message(0, "Target must be a client");
return;
}

if (database.SaveCharacterInventorySnapshot(((Client*)t)->CharacterID())) {
c->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM));
c->Message(0, "Successful inventory snapshot taken of %s", t->GetName());
}
else {
c->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM));
c->Message(0, "Failed to take inventory snapshot of %s", t->GetName());
}
}

void command_clearinvsnapshots(Client *c, const Seperator *sep)
{
if (strcmp(sep->arg[1], "false") == 0) {
database.ClearInvSnapshots(false);
c->Message(0, "Inventory snapshots cleared using current time");
}
else {
database.ClearInvSnapshots();
c->Message(0, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i days)", RuleI(Character, InvSnapshotHistoryD));
}
}

void command_findnpctype(Client *c, const Seperator *sep)
{
if(sep->arg[1][0] == 0) {
Expand Down
2 changes: 2 additions & 0 deletions zone/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ void command_appearance(Client *c, const Seperator *sep);
void command_nukeitem(Client *c, const Seperator *sep);
void command_peekinv(Client *c, const Seperator *sep);
void command_interrogateinv(Client *c, const Seperator *sep);
void command_invsnapshot(Client *c, const Seperator *sep);
void command_clearinvsnapshots(Client *c, const Seperator *sep);
void command_findnpctype(Client *c, const Seperator *sep);
void command_findzone(Client *c, const Seperator *sep);
void command_viewnpctype(Client *c, const Seperator *sep);
Expand Down
66 changes: 61 additions & 5 deletions zone/zonedb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,8 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct*
"RestTimer, "
"`e_aa_effects`, "
"`e_percent_to_aa`, "
"`e_expended_aa_spent` "
"`e_expended_aa_spent`, "
"`e_last_invsnapshot` "
"FROM "
"character_data "
"WHERE `id` = %i ", character_id);
Expand Down Expand Up @@ -983,7 +984,9 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct*
pp->RestTimer = atoi(row[r]); r++; // "RestTimer, "
m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, "
m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, "
m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent` "
m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent`, "
m_epp->last_invsnapshot_time = atoi(row[r]); r++; // "`e_last_invsnapshot` "
m_epp->next_invsnapshot_time = m_epp->last_invsnapshot_time + (RuleI(Character, InvSnapshotMinIntervalM) * 60);
}
return true;
}
Expand Down Expand Up @@ -1377,6 +1380,56 @@ bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_
return true;
}

bool ZoneDatabase::SaveCharacterInventorySnapshot(uint32 character_id){
uint32 time_index = time(nullptr);
std::string query = StringFormat(
"INSERT INTO inventory_snapshots ("
" time_index,"
" charid,"
" slotid,"
" itemid,"
" charges,"
" color,"
" augslot1,"
" augslot2,"
" augslot3,"
" augslot4,"
" augslot5,"
" augslot6,"
" instnodrop,"
" custom_data,"
" ornamenticon,"
" ornamentidfile,"
" ornament_hero_model"
")"
" SELECT"
" %u,"
" charid,"
" slotid,"
" itemid,"
" charges,"
" color,"
" augslot1,"
" augslot2,"
" augslot3,"
" augslot4,"
" augslot5,"
" augslot6,"
" instnodrop,"
" custom_data,"
" ornamenticon,"
" ornamentidfile,"
" ornament_hero_model"
" FROM inventory"
" WHERE charid = %u",
time_index,
character_id
);
auto results = database.QueryDatabase(query);
Log.Out(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterInventorySnapshot %i (%s)", character_id, (results.Success() ? "pass" : "fail"));
return results.Success();
}

bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){
clock_t t = std::clock(); /* Function timer start */
std::string query = StringFormat(
Expand Down Expand Up @@ -1473,7 +1526,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
" RestTimer, "
" e_aa_effects, "
" e_percent_to_aa, "
" e_expended_aa_spent "
" e_expended_aa_spent, "
" e_last_invsnapshot "
") "
"VALUES ("
"%u," // id " id, "
Expand Down Expand Up @@ -1568,7 +1622,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
"%u," // RestTimer pp->RestTimer, " RestTimer) "
"%u," // e_aa_effects
"%u," // e_percent_to_aa
"%u" // e_expended_aa_spent
"%u," // e_expended_aa_spent
"%u" // e_last_invsnapshot
")",
character_id, // " id, "
account_id, // " account_id, "
Expand Down Expand Up @@ -1662,7 +1717,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
pp->RestTimer, // " RestTimer) "
m_epp->aa_effects,
m_epp->perAA,
m_epp->expended_aa
m_epp->expended_aa,
m_epp->last_invsnapshot_time
);
auto results = database.QueryDatabase(query);
Log.Out(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC);
Expand Down
1 change: 1 addition & 0 deletions zone/zonedb.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ class ZoneDatabase : public SharedDatabase {
bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name);
bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon);
bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterInventorySnapshot(uint32 character_id);

/* Character Data Deletes */
bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
Expand Down

0 comments on commit a1089fc

Please sign in to comment.