RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

First In, Not last out. (Timer.DelayCall)

Talow

Sorceror
This is a tutorial for Timer.DelayCall. The reason most will find useful to use this is when you want something to happen but you need to wait a moment because some other scripts will be doing things that would interfere with what you are doing.

An example of this, you want a gump to display when a player dies. Well when a player dies there are many things happening but you can use the player death event to hook into it and send the gump. The gump will be sent but it will also most likely be closed too as the gumps are closed on player's death. Because of this waiting a moment or two for the server to be done closing your gumps is a wonderful way for you to be able to send it and keep it. There are plenty of other reasons to use this.

The delaycall is a way to call a method in your script once the timer has clicked without having to set up the timer class or the ontick ext. From my point of view it's a timer that easy to set up.

Code:
Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( method ));

Lets look this over. The timer tutorial that's already done, is well done and so I will not be going over the TimeSpan argument as he already spells it out well so reference that argument there. I will however tell you the argument is the length of time before your method is called. In this example we are waiting 2 seconds.

TimerStateCallback is where we define which method we want to call back to. I used "method" as the method I want to call back to for this tutorial.

In this form the DelayCall will call the method without any arguments and will wait 2 seconds before it makes this call.
 

Talow

Sorceror
For what we are doing with delay call we may want to call a function that includes arguments. This is where things get a good bit tricky.

Because the DelayCall could not know the type of arguments we are wanting to pass it only allows you to pass one argument to the method you are referencing. This type of argument is an object and will be passed as an object to your method.

Code:
int i = 0;
object[] arg = new object[] {i};

Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( method ), arg);

Things to note with this. The argument is an object but in the form of an array so we can pass as may object as we want.

We can create any form of object by casting, int string ext are all forms of object (in one form or another depending on who you ask) so we can send all the variable and information we want to in the object array. This is one extra argument to the DelayCall and it will in sence wait for the timer to tick (based on the timespan) and then make a call to your function/method kind of like this...

Code:
method(arg);

This is not what actually happens but for easy of understanding how to set up your method we want to think of it calling just like we would.

So we have timer set up to call our method and when it calls our method it will now pass our arg object... Again, it's important to remember it's not the data in usable format yet, but we can get it there.

we need to look at how we set up our argument and remember first in is first out for us so our int i in the object array is set to arg[0] as arrays start at 0. If we had added another object to the array arg it would be at arg[1].

Now we are starting to see how we can use this information with cast typing we can make the objects usable information...

Code:
public static void method(object state)
{
object[] states = (object[])state;
int i = (int)states[0];
}

So with this information we can see we have an object that comes in. This is how the delayed call sends the information to your method as mentioned before, we then recreate the array of objects we had by type casting the object into an array.

Next we use the array information to pull out the parts we need by addressing the same type to the index we put the type into. IE: type int was indexed to zero and thus when we assign index zero to a variable it needs to be cased as an int.

and that's the last out.
 

Talow

Sorceror
Now I just figured most of this out last night and I could be off with some of this. If this is the case please correct any information below and I will edit the above.

Thanks to Mark for showing me an example so I could understand how to use the object and type casting.
 

Vorspire

Knight
Good job Talow, I saw you talking about this in IRC the other night, I didn't think you'd get round to it this fast :)

I have a question.
Why doesn't DelayCall use (or have the option of) Generic Types when using the TimerStateCallback?

You can instantiate a Delegate as an anonymous Method in .NET 2.0 with DelayCall, something like this:
Rich (BB code):
int foo = 0;

Timer timer = Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerCallback( delegate( ) {
      //Anonymous Method Body
 
      foo++;

      //foo is 1
} ) );

//foo is 0 (At this point, the delegate will not have been called yet)
//You *could* block the thread so no more code is processed until the delegate has been called, but there isn't much sense in it if you build your DelayCall correctly.

while( foo != 1 )
{ /*Do nothing while we wait*/ }

//foo is 1
This means that you don't have to pass the TimerStateCallback and object (in example, 'foo') to a specific method, nor do you have to Type-Cast everything in the object.
TimerStateCallback would be useful in the event that the target Method was shared by other functions.
 

Talow

Sorceror
Vorspire, I've been reading your posts and comments for about 2 years now and you always manage to amaze me with the way you find clever ways to do something.

As for the Generic Types, Not my fault, the base code isn't there to support it. I guess if one wanted (vorspire ) one could use the TimerStateCallback and create further options that can be used. But I do not beleive it was created virtual so I would have to guess if you where to be creating an override, one would want to add a base to it as well.

My though though.
 

Vorspire

Knight
I think it would be worth creating a GenericTimer class...
In theory, editing the Timer class would be better, because we only need the one method:

Timer.DelayCall( TimeSpan delay, new TimerStateCallback<Mobile>( void( Mobile from ) );

Could be used like this too:

Timer.DelayCall( TimeSpan delay, new TimerStateCallback<List<string>>( void( List<string> list ) );
Timer.DelayCall( TimeSpan delay, new TimerStateCallback<Dictionary<string, Mobile>>( void( Dictionary<string, Mobile> list ) );

Maybe it's time to bring the RunUO 1.0 code for Timers up to date? I assume it's 1.0 code because it doesn't seem to have changed :D (Don't fix what isn't broken... smart devs)
 
Top