Index: zone/client_process.cpp =================================================================== --- zone/client_process.cpp (revision 103) +++ zone/client_process.cpp (working copy) @@ -5116,7 +5116,13 @@ m_pp.expansion = atoi(val); else m_pp.expansion = 0xFF; - + + p_timers.SetCharID(CharacterID()); + if(!p_timers.Load()) { + //report it... + } + + CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); outapp = new APPLAYER(OP_PlayerProfile,sizeof(PlayerProfile_Struct)); #ifdef SOLAR Index: zone/client.cpp =================================================================== --- zone/client.cpp (revision 103) +++ zone/client.cpp (working copy) @@ -678,6 +678,10 @@ dbasync->CancelWork(pQueuedSaveWorkID); pQueuedSaveWorkID = 0; } + + //FatherNitwit: I dont know if there is a better place for this: + p_timers.Store(); + if (iCommitNow <= 1) { char* query = 0; uint32_breakdown workpt; Index: zone/makefile =================================================================== --- zone/makefile (revision 103) +++ zone/makefile (working copy) @@ -8,7 +8,7 @@ groups.o ../common/classes.o ../common/races.o ../common/TCPConnection.o forage.o \ ../common/crc32.o ../common/guilds.o ../common/md5.o ../common/dbcore.o \ ../common/dbasync.o zonedbasync.o parser.o beacon.o\ - ../common/EMuShareMem.o ../common/EQEMuError.o \ + ../common/EMuShareMem.o ../common/EQEMuError.o ../common/ptimer.o \ .obj/debug.o .obj/database.o .obj/Item.o .obj/misc.o \ doors.o command.o Index: zone/command.cpp =================================================================== --- zone/command.cpp (revision 103) +++ zone/command.cpp (working copy) @@ -42,6 +42,7 @@ #endif #include "../common/debug.h" +#include "../common/ptimer.h" #include "../common/version.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" @@ -371,4 +377,5 @@ command_add("npcshout","[message] - Make your NPC target shout a message.",150,command_npcshout) || + command_add("timers","- show target's timers.",200,command_timers) || command_add("npcemote","[message] - Make your NPC target emote a message.",150,command_npcemote) ) { @@ -5775,3 +5786,22 @@ } } +void command_timers(Client *c, const Seperator *sep) { + if(!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(0,"Need a player target for timers."); + return; + } + Client *them = c->GetTarget()->CastToClient(); + + vector< pair > res; + them->GetPTimers().ToVector(res); + + c->Message(0,"Timers for target:"); + + int r; + int l = res.size(); + for(r = 0; r < l; r++) { + c->Message(0,"Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); + } +} + Index: zone/client.h =================================================================== --- zone/client.h (revision 103) +++ zone/client.h (working copy) @@ -20,6 +20,7 @@ class Client; #include "../common/timer.h" +#include "../common/ptimer.h" #include "../common/eq_opcodes.h" #include "../common/eq_packet_structs.h" #include "../common/EQNetwork.h" @@ -390,6 +391,8 @@ void SetTradeskillObject(Object* object) { m_tradeskill_object = object; } Object* GetTradeskillObject() { return m_tradeskill_object; } + inline PTimerList &GetPTimers() { return(p_timers); } + PlayerAA_Struct *GetAAStruct(void) { return &aa; } uint16 GetAA(uint8 aa_id); bool SetAA(uint8 aa_id, uint8 new_value); @@ -503,6 +506,7 @@ Timer* position_timer; int8 position_timer_counter; + PTimerList p_timers; //persistent timers Timer* hpregen_timer; Timer* camp_timer; Timer* process_timer; Index: zone/command.h =================================================================== --- zone/command.h (revision 102) +++ zone/command.h (working copy) @@ -223,5 +223,6 @@ void command_npcshout(Client *c, const Seperator *sep); void command_npcemote(Client *c, const Seperator *sep); void command_npcedit(Client *c, const Seperator *sep); +void command_timers(Client *c, const Seperator *sep); #ifdef GUILDWARS Index: common/ptimer.cpp =================================================================== --- common/ptimer.cpp (revision 0) +++ common/ptimer.cpp (revision 108) @@ -0,0 +1,463 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "../common/debug.h" + +#include "../common/database.h" +extern Database database; + +#include "../common/timer.h" +#include "../common/ptimer.h" +#include + +#ifdef WIN32 + #include + #include + int gettimeofday (timeval *tp, ...); +#else + #include +#endif + +#if EQDEBUG > 10 + #define DEBUG_PTIMERS +#endif + + +/* +Persistent timers, By Father Nitwit + +The idea of persistent timers is timers which follow a player +through zoning. You use operations on them much like regular +timers: Start, Check, Clear but they also provide methods +to store them in the DB: Load and Store. + +Each persistent timer is attached to a character, and given +a specific type. A given character can only have one timer +of each type. While the type is just an arbitrary number, +please record what you are using it for in the enum for +pTimerType at the top of ptimer.h, and give it a UNIQUE number. + +There should be little need to directly access ptimers. Each +client has a facility called p_timers which should handle +most of what you need. The idea is that instead of making +your own PersistentTimer, you use the methods on p_timers: +Start, Check, Clear, GetRemainingTime to access them. You +starting a timer which does not exist will create it. If +you need to do more than that with your timer, you should +still use p_timers, just use the Get() method to get direct +access to the PersistentTimer. All timers in the p_timers +list will automatically be loaded and stored to the database. +You should never need to call any Load or Store methods. + +To get to p_timers when you are not in the Client:: scope, +use client->GetPTimers(). to access timers. + +To use ptimers, you need to create the table below in your DB: + +Schema: + +CREATE TABLE timers ( + char_id INT(11) NOT NULL, + type MEDIUMINT UNSIGNED NOT NULL, + start INT UNSIGNED NOT NULL, + duration INT UNSIGNED NOT NULL, + enable TINYINT NOT NULL, + PRIMARY KEY(char_id, type) +); + + +*/ + + +//#define DEBUG_PTIMERS + + +PersistentTimer *PersistentTimer::LoadTimer(int32 char_id, pTimerType type) { + PersistentTimer *p; + p = new PersistentTimer(char_id, type, 0); + if(p->Load()) + return(p); + delete p; + return(NULL); +} + +PersistentTimer::PersistentTimer(int32 char_id, pTimerType type, int32 in_timer_time) { + _char_id = char_id; + _type = type; + + timer_time = in_timer_time; + start_time = get_current_time(); + if (timer_time == 0) { + enabled = false; + } else { + enabled = true; + } +#ifdef DEBUG_PTIMERS + printf("New timer: char %lu of type %u at %lu for %lu seconds.\n", _char_id, _type, start_time, timer_time); +#endif +} + +PersistentTimer::PersistentTimer(int32 char_id, pTimerType type, int32 in_start_time, int32 in_timer_time, bool in_enable) { + _char_id = char_id; + _type = type; + + timer_time = in_timer_time; + start_time = in_start_time; + enabled = in_enable; +#ifdef DEBUG_PTIMERS + printf("New stored timer: char %lu of type %u at %lu for %lu seconds.\n", _char_id, _type, start_time, timer_time); +#endif +} + +bool PersistentTimer::Load() { + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + MYSQL_ROW row; + char *query = 0; + uint32 qlen = 0; + uint32 qcount = 0; + + qlen = MakeAnyLenString(&query, "SELECT start,duration,enable " + " FROM timers WHERE char_id=%lu AND type=%u", _char_id, _type); + +#ifdef DEBUG_PTIMERS + printf("Loading timer: char %lu of type %u\n", _char_id, _type); +#endif + + if (!database.RunQuery(query, qlen, errbuf, &result)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + bool res = false; + qcount = mysql_num_rows(result); + if(qcount == 1 && (row = mysql_fetch_row(result)) ) { + start_time = strtoul(row[0], NULL, 10); + timer_time = strtoul(row[1], NULL, 10); + enabled = (row[2][0] == '1'); + + res = true; + } + mysql_free_result(result); + + return(res); +} + +bool PersistentTimer::Store() { + if(Check(false)) //dont need to store expired timers. + return(true); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 qlen = 0; + + qlen = MakeAnyLenString(&query, "REPLACE INTO timers " + " (char_id,type,start,duration,enable) " + " VALUES(%lu,%u,%lu,%lu,%d)", + _char_id, _type, start_time, timer_time, enabled?1:0); + + +#ifdef DEBUG_PTIMERS + printf("Storing timer: char %lu of type %u: '%s'\n", _char_id, _type, query); +#endif + + if (!database.RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Store, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); +} + +bool PersistentTimer::Clear() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 qlen = 0; + + qlen = MakeAnyLenString(&query, "DELETE FROM timers " + " WHERE char_id=%lu AND type=%u ", + _char_id, _type); + +#ifdef DEBUG_PTIMERS + printf("Clearing timer: char %lu of type %u: '%s'\n", _char_id, _type, query); +#endif + + if (!database.RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); + +} + +/* This function checks if the timer triggered */ +bool PersistentTimer::Check(bool iReset) { + if (this == NULL) { + LogFile->write(EQEMuLog::Error, "Null timer during ->Check()!?\n"); + return(true); + } + int32 current_time = get_current_time(); + if (enabled && current_time-start_time > timer_time) { + if (iReset) { + start_time = current_time; // Reset timer + } else { + Clear(); //remove it from DB too + } + return(true); + } + + return(false); +} + +/* This function set the timer and restart it */ +void PersistentTimer::Start(int32 set_timer_time) { + start_time = get_current_time(); + enabled = true; + if (set_timer_time != 0) { + timer_time = set_timer_time; + } +#ifdef DEBUG_PTIMERS + printf("Starting timer: char %lu of type %u at %lu for %lu seconds.\n", _char_id, _type, start_time, timer_time); +#endif +} + +// This timer updates the timer without restarting it +void PersistentTimer::SetTimer(int32 set_timer_time) { + // If we were disabled before => restart the timer + timer_time = set_timer_time; + if (!enabled) { + start_time = get_current_time(); + enabled = true; + } +#ifdef DEBUG_PTIMERS + printf("Setting timer: char %lu of type %u at %lu for %lu seconds.\n", _char_id, _type, start_time, timer_time); +#endif +} + +int32 PersistentTimer::GetRemainingTime() { + if (enabled) { + int32 current_time = get_current_time(); + if (current_time-start_time > timer_time) + return 0; + else + return (start_time + timer_time) - current_time; + } + else { + return 0xFFFFFFFF; + } +} + + +int32 PersistentTimer::get_current_time() { + timeval tv; + gettimeofday(&tv, NULL); + return(tv.tv_sec); +} + +PTimerList::PTimerList(int32 char_id) { + _char_id = char_id; +} + +PTimerList::~PTimerList() { + map::iterator s; + s = _list.begin(); + while(s != _list.end()) { + if(s->second != NULL) + delete s->second; + s++; + } +} + + +bool PTimerList::Load() { + _list.clear(); + + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + MYSQL_ROW row; + char *query = 0; + uint32 qlen = 0; + uint32 qcount = 0; + + qlen = MakeAnyLenString(&query, "SELECT type,start,duration,enable " + " FROM timers WHERE char_id=%lu", _char_id); + +#ifdef DEBUG_PTIMERS + printf("Loading all timers for char %lu\n", _char_id); +#endif + + if (!database.RunQuery(query, qlen, errbuf, &result)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + pTimerType type; + int32 start_time, timer_time; + bool enabled; + + PersistentTimer *cur; + qcount = mysql_num_rows(result); + while((row = mysql_fetch_row(result)) ) { + type = atoi(row[0]); + start_time = strtoul(row[1], NULL, 10); + timer_time = strtoul(row[2], NULL, 10); + enabled = (row[3][0] == '1'); + + //if it expired allready, dont bother. + + cur = new PersistentTimer(_char_id, type, start_time, timer_time, enabled); + if(! cur->Check(false)) + _list[type] = cur; + else + delete cur; + } + mysql_free_result(result); + + return(true); +} + +bool PTimerList::Store() { +#ifdef DEBUG_PTIMERS + printf("Storing all timers for char %lu\n", _char_id); +#endif + + map::iterator s; + s = _list.begin(); + bool res = true; + while(s != _list.end()) { + if(s->second != NULL) { +#ifdef DEBUG_PTIMERS + printf("Storing timer %u for char %lu\n", s->first, _char_id); +#endif + if(!s->second->Store()) + res = false; + } + s++; + } + return(res); +} + +bool PTimerList::Clear() { + _list.clear(); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 qlen = 0; + + qlen = MakeAnyLenString(&query, "DELETE FROM timers " + " WHERE char_id=%lu ", _char_id); + +#ifdef DEBUG_PTIMERS + printf("Storing all timers for char %lu: '%s'\n", _char_id, query); +#endif + + if (!database.RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); +} + +void PTimerList::Start(pTimerType type, int32 duration) { + if(_list.count(type) == 1 && _list[type] != NULL) { + _list[type]->Start(duration); + } else { + _list[type] = new PersistentTimer(_char_id, type, duration); + } +} + +void PTimerList::Clear(pTimerType type) { + if(_list.count(type) == 1) { + if(_list[type] != NULL) { + _list[type]->Clear(); + delete _list[type]; + } + _list.erase(type); + } +} + +bool PTimerList::Check(pTimerType type, bool reset) { + if(_list.count(type) != 1) + return(true); + if(_list[type] == NULL) + return(true); + return(_list[type]->Check(reset)); +} + +void PTimerList::Enable(pTimerType type) { + if(_list.count(type) == 1 && _list[type] != NULL) + _list[type]->Enable(); +} + +void PTimerList::Disable(pTimerType type) { + if(_list.count(type) == 1 && _list[type] != NULL) + _list[type]->Disable(); +} + +int32 PTimerList::GetRemainingTime(pTimerType type) { + if(_list.count(type) != 1) + return(0); + if(_list[type] == NULL) + return(0); + return(_list[type]->GetRemainingTime()); +} + +PersistentTimer *PTimerList::Get(pTimerType type) { + if(_list.count(type) != 1) + return(NULL); + return(_list[type]); +} + +void PTimerList::ToVector(vector< pair > &out) { + + pair p; + + map::iterator s; + s = _list.begin(); + while(s != _list.end()) { + if(s->second != NULL) { + p.first = s->first; + p.second = s->second; + out.push_back(p); + } + s++; + } +} + + Index: common/ptimer.h =================================================================== --- common/ptimer.h (revision 0) +++ common/ptimer.h (revision 108) @@ -0,0 +1,99 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef PTIMER_H +#define PTIMER_H + +#include "types.h" +#include +#include +using namespace std; + +enum { //values for pTimerType + pTimerStartAdventureTimer = 1, + pTimerAdventureTimer = 2, + pTimerAAStart = 10, + pTimerAAEnd = 130 +}; + +typedef uint16 pTimerType; + +class PersistentTimer { +public: + static PersistentTimer *LoadTimer(int32 char_id, pTimerType type); + + PersistentTimer(int32 char_id, pTimerType type, int32 duration); + PersistentTimer(int32 char_id, pTimerType type, int32 start_time, int32 duration, bool enable); + + bool Check(bool iReset = true); + void Start(int32 set_timer_time=0); + + void SetTimer(int32 set_timer_time=0); + int32 GetRemainingTime(); + inline void Enable() { enabled = true; } + inline void Disable() { enabled = false; } + inline const int32 GetTimerTime() { return timer_time; } + + inline bool Enabled() { return enabled; } + + bool Load(); + bool Store(); + bool Clear(); + +protected: + int32 get_current_time(); + + int32 start_time; + int32 timer_time; + bool enabled; + + int32 _char_id; + pTimerType _type; +}; + +//a list of persistent timers for a specific character +class PTimerList { +public: + PTimerList(int32 char_id = 0); + + ~PTimerList(); + + bool Load(); + bool Store(); + bool Clear(); + + void Start(pTimerType type, int32 duration); + bool Check(pTimerType type, bool reset = true); + void Clear(pTimerType type); + void Enable(pTimerType type); + void Disable(pTimerType type); + int32 GetRemainingTime(pTimerType type); + PersistentTimer *Get(pTimerType type); + + inline void SetCharID(int32 char_id) { _char_id = char_id; } + + void ToVector(vector< pair > &out); + +protected: + int32 _char_id; + + map _list; +}; + + + +#endif