EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Server Code Submissions (https://www.eqemulator.org/forums/forumdisplay.php?f=669)
-   -   COMMITTED: Buff Timer Suspension (https://www.eqemulator.org/forums/showthread.php?t=31788)

wheeljack 08-05-2010 09:55 PM

COMMITTED: Buff Timer Suspension
 
This patch allows buff timers to not count down while in loiter zones (or any zone the server admin chooses to flag by setting the shouldsuspendbufftimers field to 1). It requires a schema change to allow zones to be flagged and I wasn't sure how you version the schema so I've included the alter and update statements here.

I have tested this with the Titanium client and everything seems to work quite well. One noticible artifact on the client is when in a loiter zone you'll see the buff timer appear to go up by 6 seconds and then immediately back down to the original amount each tick. This is similar to the behavior on live, but on live it appears to go down by 6 seconds and then immediately back up each tick. This is because the client is expecting the buff to tick down and the server keeps correcting it.

SQL changes:
Code:

ALTER TABLE zone ADD shouldsuspendbufftimers tinyint(1) unsigned zerofill NOT NULL DEFAULT '0';
UPDATE zone SET shouldsuspendbufftimers = 1 WHERE short_name IN ('guildlobby', 'guildhall');

Server changes:
Code:

Index: zone/spell_effects.cpp
===================================================================
--- zone/spell_effects.cpp        (revision 1617)
+++ zone/spell_effects.cpp        (working copy)
@@ -3061,7 +3061,18 @@
                if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
                        DoBuffTic(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel, entity_list.GetMob(buffs[buffs_i].casterid));
                        if (buffs[buffs_i].durationformula != 50) {
-                                buffs[buffs_i].ticsremaining--;
+                                if(spells[buffs[buffs_i].spellid].goodEffect == 0 || zone->ShouldSuspendBuffTimers() == false)
+                                {
+                                        // tick down the buff if it's detrimental or if not in a suspended buff timer zone
+                                        buffs[buffs_i].ticsremaining--;
+                                }
+                                else
+                                {
+                                        // refresh the buff timer on the client since it ticks down independently
+                                        // if you don't do this, the client will still show the buff ticking down even though it isn't on the server
+                                        buffs[buffs_i].UpdateClient = true;
+                                }
+
                                if (buffs[buffs_i].ticsremaining <= 0) {
                                        mlog(SPELLS__BUFFS, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i);
                                        BuffFadeBySlot(buffs_i);
Index: zone/zone.cpp
===================================================================
--- zone/zone.cpp        (revision 1617)
+++ zone/zone.cpp        (working copy)
@@ -1035,7 +1035,7 @@
        {
                map_name = NULL;
                if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                {
                        LogFile->write(EQEMuLog::Error, "Error loading the Zone Config.");
                        return false;
@@ -1046,11 +1046,11 @@
                //Fall back to base zone if we don't find the instance version.
                map_name = NULL;
                if(!database.GetZoneCFG(database.GetZoneID(filename), instance_id, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                {
                        safe_delete_array(map_name);
                        if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                        {
                                LogFile->write(EQEMuLog::Error, "Error loading the Zone Config.");
                                return false;
Index: zone/zone.h
===================================================================
--- zone/zone.h        (revision 1617)
+++ zone/zone.h        (working copy)
@@ -201,6 +201,7 @@
        bool        CanLevitate() const {return(can_levitate); } // Magoth78
        bool    CanCastOutdoor() const {return(can_castoutdoor);} //qadar
        bool        IsHotzone() const { return(is_hotzone); }
+        bool        ShouldSuspendBuffTimers() const { return shouldSuspendBuffTimers; }
       
        time_t        weather_timer;
        int8        weather_type;
@@ -247,6 +248,7 @@
        bool    can_castoutdoor;
        bool        can_levitate;
        bool        is_hotzone;
+        bool        shouldSuspendBuffTimers;
        int32        pgraveyard_id, pgraveyard_zoneid;
        float        pgraveyard_x, pgraveyard_y, pgraveyard_z, pgraveyard_heading;
        int    default_ruleset;
Index: zone/zonedb.cpp
===================================================================
--- zone/zonedb.cpp        (revision 1617)
+++ zone/zonedb.cpp        (working copy)
@@ -80,7 +80,7 @@
        return true;
 }
 
-bool ZoneDatabase::GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, int &ruleset, char **map_filename) {
+bool ZoneDatabase::GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &shouldSuspendBuffTimers, int &ruleset, char **map_filename) {
        char errbuf[MYSQL_ERRMSG_SIZE];
        char *query = 0;
        MYSQL_RES *result;
@@ -96,7 +96,7 @@
                "fog_red4,fog_green4,fog_blue4,fog_minclip4,fog_maxclip4,fog_density,"
                "sky,zone_exp_multiplier,safe_x,safe_y,safe_z,underworld,"
                "minclip,maxclip,time_type,canbind,cancombat,canlevitate,"
-                "castoutdoor,hotzone,ruleset,map_file_name,short_name"
+                "castoutdoor,hotzone,shouldsuspendbufftimers,ruleset,map_file_name,short_name"
                " from zone where zoneidnumber=%i and version=%i",zoneid, instance_id), errbuf, &result)) {
                safe_delete_array(query);
                row = mysql_fetch_row(result);
@@ -135,6 +135,7 @@
            can_levitate = atoi(row[r++])==0?false:true;
                        can_castoutdoor = atoi(row[r++])==0?false:true;
                        is_hotzone = atoi(row[r++])==0?false:true;
+                        shouldSuspendBuffTimers = atoi(row[r++])==0?false:true;
                        ruleset = atoi(row[r++]);
                        char *file = row[r++];
                        if(file)
Index: zone/zonedb.h
===================================================================
--- zone/zonedb.h        (revision 1617)
+++ zone/zonedb.h        (working copy)
@@ -192,7 +192,7 @@
        /*
          * Zone related
          */
-        bool        GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, int &ruleset, char **map_filename);
+        bool        GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &shouldSuspendBuffTimers, int &ruleset, char **map_filename);
        bool        SaveZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct* zd);
        bool        DumpZoneState();
        sint8        LoadZoneState(const char* zonename, LinkedList<Spawn2*>& spawn2_list);


steve 08-06-2010 11:37 PM

Nice addition!

wheeljack 08-07-2010 07:56 PM

One small correction to what I posted above. After getting more familiar with the codebase, I figured that this should use the IsDetrimentalSpell function rather than checking the goodEffect member directly. I moved the zone check to the beginning of the conditional as well to hopefully shortcircuit the function call into IsDetrimentalSpell when possible. I've also tested this with the SoD client since original posting as well and all is good. Anyhow, here's the updated version:

SQL:
Code:

ALTER TABLE zone ADD shouldsuspendbufftimers tinyint(1) unsigned zerofill NOT NULL DEFAULT '0';
UPDATE zone SET shouldsuspendbufftimers = 1 WHERE short_name IN ('guildlobby', 'guildhall');

Code:
Code:

Index: zone/spell_effects.cpp
===================================================================
--- zone/spell_effects.cpp        (revision 1617)
+++ zone/spell_effects.cpp        (working copy)
@@ -3061,7 +3061,18 @@
                if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
                        DoBuffTic(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel, entity_list.GetMob(buffs[buffs_i].casterid));
                        if (buffs[buffs_i].durationformula != 50) {
-                                buffs[buffs_i].ticsremaining--;
+                                if(!(zone->ShouldSuspendBuffTimers()) || IsDetrimentalSpell(buffs[buffs_i].spellid))
+                                {
+                                        // tick down the buff if it's detrimental or if not in a suspended buff timer zone
+                                        buffs[buffs_i].ticsremaining--;
+                                }
+                                else
+                                {
+                                        // refresh the buff timer on the client since it ticks down independently
+                                        // if you don't do this, the client will still show the buff ticking down even though it isn't on the server
+                                        buffs[buffs_i].UpdateClient = true;
+                                }
+
                                if (buffs[buffs_i].ticsremaining <= 0) {
                                        mlog(SPELLS__BUFFS, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i);
                                        BuffFadeBySlot(buffs_i);
Index: zone/zone.cpp
===================================================================
--- zone/zone.cpp        (revision 1617)
+++ zone/zone.cpp        (working copy)
@@ -1035,7 +1035,7 @@
        {
                map_name = NULL;
                if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                {
                        LogFile->write(EQEMuLog::Error, "Error loading the Zone Config.");
                        return false;
@@ -1046,11 +1046,11 @@
                //Fall back to base zone if we don't find the instance version.
                map_name = NULL;
                if(!database.GetZoneCFG(database.GetZoneID(filename), instance_id, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                {
                        safe_delete_array(map_name);
                        if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind,
-                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name))
+                        can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, shouldSuspendBuffTimers, default_ruleset, &map_name))
                        {
                                LogFile->write(EQEMuLog::Error, "Error loading the Zone Config.");
                                return false;
Index: zone/zone.h
===================================================================
--- zone/zone.h        (revision 1617)
+++ zone/zone.h        (working copy)
@@ -201,6 +201,7 @@
        bool        CanLevitate() const {return(can_levitate); } // Magoth78
        bool    CanCastOutdoor() const {return(can_castoutdoor);} //qadar
        bool        IsHotzone() const { return(is_hotzone); }
+        bool        ShouldSuspendBuffTimers() const { return shouldSuspendBuffTimers; }
       
        time_t        weather_timer;
        int8        weather_type;
@@ -247,6 +248,7 @@
        bool    can_castoutdoor;
        bool        can_levitate;
        bool        is_hotzone;
+        bool        shouldSuspendBuffTimers;
        int32        pgraveyard_id, pgraveyard_zoneid;
        float        pgraveyard_x, pgraveyard_y, pgraveyard_z, pgraveyard_heading;
        int    default_ruleset;
Index: zone/zonedb.cpp
===================================================================
--- zone/zonedb.cpp        (revision 1617)
+++ zone/zonedb.cpp        (working copy)
@@ -80,7 +80,7 @@
        return true;
 }
 
-bool ZoneDatabase::GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, int &ruleset, char **map_filename) {
+bool ZoneDatabase::GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &shouldSuspendBuffTimers, int &ruleset, char **map_filename) {
        char errbuf[MYSQL_ERRMSG_SIZE];
        char *query = 0;
        MYSQL_RES *result;
@@ -96,7 +96,7 @@
                "fog_red4,fog_green4,fog_blue4,fog_minclip4,fog_maxclip4,fog_density,"
                "sky,zone_exp_multiplier,safe_x,safe_y,safe_z,underworld,"
                "minclip,maxclip,time_type,canbind,cancombat,canlevitate,"
-                "castoutdoor,hotzone,ruleset,map_file_name,short_name"
+                "castoutdoor,hotzone,shouldsuspendbufftimers,ruleset,map_file_name,short_name"
                " from zone where zoneidnumber=%i and version=%i",zoneid, instance_id), errbuf, &result)) {
                safe_delete_array(query);
                row = mysql_fetch_row(result);
@@ -135,6 +135,7 @@
            can_levitate = atoi(row[r++])==0?false:true;
                        can_castoutdoor = atoi(row[r++])==0?false:true;
                        is_hotzone = atoi(row[r++])==0?false:true;
+                        shouldSuspendBuffTimers = atoi(row[r++])==0?false:true;
                        ruleset = atoi(row[r++]);
                        char *file = row[r++];
                        if(file)
Index: zone/zonedb.h
===================================================================
--- zone/zonedb.h        (revision 1617)
+++ zone/zonedb.h        (working copy)
@@ -192,7 +192,7 @@
        /*
          * Zone related
          */
-        bool        GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, int &ruleset, char **map_filename);
+        bool        GetZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &shouldSuspendBuffTimers, int &ruleset, char **map_filename);
        bool        SaveZoneCFG(int32 zoneid, uint16 instance_id, NewZone_Struct* zd);
        bool        DumpZoneState();
        sint8        LoadZoneState(const char* zonename, LinkedList<Spawn2*>& spawn2_list);


joligario 08-07-2010 08:10 PM

Regarding the client timer still ticking down, I bet there is an OPCODE in there that tells the client to pause/continue timers.

Derision 08-08-2010 10:09 AM

There is a field in the NewZone struct for SoF and later clients that disables buff timers. I don't know whether there is one for Titanium (or where it is in the struct).

Anyway, committed in Rev1618 with a few minor changes. Thanks :)


All times are GMT -4. The time now is 12:03 AM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.