Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ SMAUG ➜ SMAUG coding ➜ Bug fix: Timed do-function recreating timer

Bug fix: Timed do-function recreating timer

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by David Haley   USA  (3,881 posts)  Bio
Date Mon 08 Aug 2005 08:11 PM (UTC)

Amended on Mon 08 Aug 2005 08:12 PM (UTC) by David Haley

Message
Hi all,

While debugging Samryn's problem - see http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=5776&page=999999 - we came across an apparent problem in SMAUG. It would seem that a timed do-function cannot recreate a do-function timer. This means that you cannot have a do-function timer "beat" at regular intervals.

The fix is really quite simple. I've included my reply to Samryn with problem diagnosis + a solution that he said fixes his problem.

Maybe this'll help somebody out there, and if it's actually a reliable fix, maybe should go up for inclusion in SMAUGFUSS.

(Disclaimer: I haven't tested this myself and don't know if it breaks something that depends on this somewhat odd behavior of SMAUG's)

Quote:
I think I found the source of your problem. On my copy of SMAUG - which, admittedly, is based off of SMAUG 1.0 - I have this in the 'violence_update' function:
        for ( timer = ch->first_timer; timer; timer = timer_next )
        {
            timer_next = timer->next;
            if ( --timer->count <= 0 )
            {
                if ( timer->type == TIMER_DO_FUN )
                {
                    int tempsub;

                    tempsub = ch->substate;
                    ch->substate = timer->value;
                    (timer->do_fun)( ch, "" );
                    if ( char_died(ch) )
                        break;
                    ch->substate = tempsub; 
                }
                extract_timer( ch, timer );
            }
        }


What does this mean? Well, it means that after executing a timer, it is removed. However, if we look at the 'add_timer' function...
void add_timer( CHAR_DATA *ch, sh_int type, sh_int count, DO_FUN *fun, int value )
{
    TIMER *timer;
    
    for ( timer = ch->first_timer; timer; timer = timer->next )
    {
        if ( timer->type == type )
        {
           timer->count  = count;
           timer->do_fun = fun;
           timer->value  = value;
           break;
        }
    }

    if ( !timer )
    {
        CREATE( timer, TIMER, 1 );
        timer->count    = count;
        timer->type = type;
        timer->do_fun   = fun;
        timer->value    = value;
        LINK( timer, ch->first_timer, ch->last_timer, next, prev );
    }
}
Here, we see that when we add a timer, we first loop through the existing timers, seeing if we already have one of that type.

In your case, we do have a timer, because we're in the middle of executing one, and we haven't removed it yet. So, we'll edit it, and then we'll remove it right after running it!

To fix this, we'll have to make sure that the timer is not extracted in violence_update if it has count left. This should be as simple as adding:
if ( timer->count == 0 )
before the 'extract_timer' call.

EDIT: it would probably be safer to have the ifcheck read <=, not ==...

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Samryn   United Kingdom  (60 posts)  Bio
Date Reply #1 on Tue 09 Aug 2005 12:03 AM (UTC)
Message
seeing as i've already done it, thought i'd tell you how its doing...

There doesn't seem to be any leaks or any issues with it, its working great and im sure everyone can thing of a few things they can do now just using add_timer instead having to go the long way round and add it all the update.c etc...

Anyway just thought i'd say its something that should be in everyones code from now on!

Thanks again for helping me out.

-- Samryn

Samryn Medri
Top

Posted by Gohan_TheDragonball   USA  (183 posts)  Bio
Date Reply #2 on Wed 10 Aug 2005 03:49 AM (UTC)

Amended on Wed 10 Aug 2005 03:51 AM (UTC) by Gohan_TheDragonball

Message
actually, you can have a function beat at regular intervals:


do_function () {
 switch ( ch->substate ) {
  default: // first use of the function
   add_timer( ch, TIMER_DO_FUN, 2, __FUNCTION__, 1 );
   break;
  case 1:
   // first loop
   add_timer( ch, TIMER_DO_FUN, 2, __FUNCTION__, 2 );
  case 2:
   // second loop
   add_timer( ch, TIMER_DO_FUN, 2, __FUNCTION__, 1 );
  case SUB_TIMER_DO_ABORT:
   // they typed something!!! run
   return;
 }
}


that is the most basic way to loop a function, I use it for stuff like meditation.
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #3 on Wed 10 Aug 2005 07:07 AM (UTC)
Message
That will not work without the bugfix (or an alternate fix, of course). It is possible that your code has a fix already. But if you look at the code I posted - which, again, was from an older version of SMAUG but seemed to fix Samryn's problem - adding a timer when acting on a timer doesn't do anything.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Gohan_TheDragonball   USA  (183 posts)  Bio
Date Reply #4 on Wed 10 Aug 2005 10:49 PM (UTC)
Message
yeah, i guess i fixed this a long time ago. lol, i did it a different way, don't know if its worse or better than what you did.


                if ( timer->type == TIMER_DO_FUN )
                {
                    int tempsub;

                    tempsub = ch->substate;
                    ch->substate = timer->value;
                    (timer->do_fun)( ch, "" );
                    if ( char_died(ch) )
                        break;
                    if (ch->substate != timer->value)
                        repeat = TRUE;
                    ch->substate = tempsub;
                }

                if (!repeat)
                    extract_timer( ch, timer );
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #5 on Wed 10 Aug 2005 11:14 PM (UTC)
Message
Your solution works in that it will let you loop commands, but what I propose is better (more general) in that it will allow timers to repeat even if they send the character into the same substate. I'm not sure why you'd ever want to prevent a timer from explicitly repeating itself, even if it goes into the same substate.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Gohan_TheDragonball   USA  (183 posts)  Bio
Date Reply #6 on Wed 10 Aug 2005 11:30 PM (UTC)
Message
Yeah, i must say i do like your idea better. I think this was a fix posted on a forum a long time ago. Truthfully don't think i would have said to myself "hmm, i think i can fix this by going into different substates", and i say this because i am not that imaginative. Plus it will cut down on code, not having to repeat everything twice to loop it.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


20,994 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.