One Time

One Time 1.0.0.14

No permission to download

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
GoldDraco13 submitted a new resource:

One Time - One Timer to Rule them all!

This is a single timer to add flexibility for adding timers to your creations without using a timer!

This timer will raise events on the basic time intervals (Millisecond, Second, Minute and Hour)

Add a event handler,,

Code:
//millisecond
OneTimeEvent.MilliTimerTick += MilliTimerTick;
//second
OneTimeEvent.SecTimerTick += SecTimerTick;
//minute
OneTimeEvent.MinTimerTick += MinTimerTick;
//hour
OneTimeEvent.HourTimerTick += HourTimerTick;
and a related method.

Code:
...
Read more about this resource...
 

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
Been playing around with the wavy grass, came up with sticky grass, use [tile wavygrass to cover a area, then try to walk over it, the grass will notice the player and try to hinder movement!
 

Attachments

  • Like
Reactions: Tasanar

Vorspire

Vita-Nex: Core
Admin
ServUO Developer
Jul 29, 2013
1,165
270
83
33
United Kingdom
core.vita-nex.com
Shard Name
Ultima Shards: Multiverse
Donate
Donate money to this user
The concept is neat and the example implementation looks great.
It's easy to use and implement for any dev, which means it's going to be a popular download.

A lot of features have been coded to use one timer *per instance*, where they would benefit from a single timer for all instances of that type.
There are still a few instances of such features in the ServUO repository - they can be found when running a [TimerDump (or [DumpTimers) and reviewing the logs to see what this One Time API can be used to improve on.
Mostly they are items, like the Fountain Of Life, or mobiles, like the Player Vendor payment timers.

So having said that, I think sharing information regarding the implementation of events is important...

Events are backed by arrays.
Every event handler subscribed to an event adds an entry to the backing array.
The += and -= operators are syntax-sugar replacements for add and remove operations, respectively.

In a static context, it's rare that you'd need to unload or dispose of a handler.
However, in the non-static context usage presented in this implementation, that can be dangerous.
Deleting one of the example grass tiles with an event subscription assigned to it will make it impossible for the item to be cleaned up in memory by the garbage collector while the shard is running.
The reason a permanent reference is held in memory is because the delegate created to hold the event requires the original object reference in order to invoke the non-static method on it.
Event definitions that are in a static context never need to be disposed by garbage collection unless they're completely unassigned (assigned to null, which de-references the event backing array).

The solution to this problem is simply to use the -= operator to unsubscribe your handlers in OnDelete or OnAfterDelete.

OK, with that explained, it'll be easier to explain why accuracy for shorter timers can - and will- start drifting over time.

Timers have a priority assignment for a reason, this ensures that they are processed in a certain way that ensures their accuracy to within 10ms (timers cannot be much more accurate than this due to CPU processing overhead on each TimerThread cycle.
The "EveryTick" timer priority, for example, doesn't mean "one millisecond", but rather "as soon as possible, as often as possible".
So, if you have a single timer now serving multiple event handlers that require different time intervals, you have to make a choice between trading off accuracy or performance.
It comes back to the backing arrays, and those arrays need to be iterated whenever your event is invoked, and that takes time.
That affects how your other events are invoked, too.
I'll assume this is why you've added async capability to the system - but I think that's going to cause more headaches than it will solve.
I wouldn't expose that feature to an average developer without implementing it in such a way that it basically has the equivalent to a child locking cap on a bleach bottle.
 
  • Like
Reactions: GoldDraco13

zerodowned

Moderator
ServUO Developer
Jun 28, 2014
1,825
248
63
Deleting one of the example grass tiles with an event subscription assigned to it will make it impossible for the item to be cleaned up in memory by the garbage collector while the shard is running.
I'm self taught and never had any formal teaching for programming or computer science; but I think this sums up what you are saying?
if for instance someone were to use the timer on a champion spawner so that say their hue or bodyvalue cycled per second then each mobile that is deleted by corpse decay or loot/claim systems will effectively clog up the server memory and continue building until the shard is restarted ?
effectively turning servuo into a memory hog like internet explorer
 

Vorspire

Vita-Nex: Core
Admin
ServUO Developer
Jul 29, 2013
1,165
270
83
33
United Kingdom
core.vita-nex.com
Shard Name
Ultima Shards: Multiverse
Donate
Donate money to this user
I'm self taught and never had any formal teaching for programming or computer science; but I think this sums up what you are saying?
if for instance someone were to use the timer on a champion spawner so that say their hue or bodyvalue cycled per second then each mobile that is deleted by corpse decay or loot/claim systems will effectively clog up the server memory and continue building until the shard is restarted ?
effectively turning servuo into a memory hog like internet explorer
If the event handler is a non-static method of the actual champion spawner itself, then it would only be an issue if the spawner itself was deleted.
If the event handler is a non-static method of each mobile spawn, then yes, that would be an issue for each mobile that was killed and deleted (the corpse and the contents won't be affected, though).

In summary, in a *non-static* context, it's best to de-reference the event handler when the handler is a member of the object that is being disposed.
 
  • Like
Reactions: GoldDraco13

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
I'm 100% self taught too, I would even go further that over the last 14 years, people like you and others taught me through these forums with great responses like this!

In all honesty I get an idea in my head and I keep working on it till it works in game as intended, though the lack of knowing how everything works under the hood makes me stumble, like the first attempt at Async in this resource, I was changing the item values directly in the task and found that processdelta and/or update netstate caused crashes, so to solve this I decided to do some computing in the task but to temp variables that would update the item on the main thread next pass!

So with that said, I do want to ask if the only issue is that when using these eventhandlers, will someone need to add a -= event to the ondelete method of the object?

Also on server restart, do the handlers get removed from the backend array? When testing I found that without adding a += handler in the load, the item would not be hooked up to a handler!

I value any information that would help me make this better, or if this is not a good idea overall and should be scrapped and go back to using timers like we always have?

- My goal is to help shards, not destroy them unintentionally!
 

Vorspire

Vita-Nex: Core
Admin
ServUO Developer
Jul 29, 2013
1,165
270
83
33
United Kingdom
core.vita-nex.com
Shard Name
Ultima Shards: Multiverse
Donate
Donate money to this user
I'm 100% self taught too, I would even go further that over the last 14 years, people like you and others taught me through these forums with great responses like this!
Looks like that makes 3 of us haha.

So with that said, I do want to ask if the only issue is that when using these eventhandlers, will someone need to add a -= event to the ondelete method of the object?
Yes, it's definitely recommended in cases where you know the item can be deleted, or has the potential to be removed from the world.
In contexts outside of ServUO, Mobiles, and Items - this practise would be recommended when dealing with IDisposable implementations and such.

Also on server restart, do the handlers get removed from the backend array? When testing I found that without adding a += handler in the load, the item would not be hooked up to a handler!
Yes, they're not serialized in any way, so they can't be persisted without some effort.
Fortunately, there's an example you can pull from for making delegates persistent, if you're up for it!
- The ServUO Config class is capable of reading and parsing strings as delegates - it's specifically used to assign a custom currency consumer for the Ultima Store feature, but it's designed to be extensible for use out of that context.
You could use this to persist events, but it's not really necessary - the += syntax is way more reliable and easy to implement.

You can imagine the event to be *like* a List<Delegate>, where += and -= are proxies for Add and Remove, and it uses a Delegate[] array that gets resized for capacity when needed.
When you define an event, you can actually specify the accessors like so;
Code:
public static event Action MyEvent { add; remove; }
I value any information that would help me make this better, or if this is not a good idea overall and should be scrapped and go back to using timers like we always have?
I think it's a good idea - it's a good tool, just dangerous in the hands of an inexperienced developer.
Perhaps dangerous is a bit over-dramatic, hehe.

Overall, I think events are easier to use for people, they look cleaner and require less code.
The examples you provided are clear and concise, and remove the doubt and worry of having to spin up a timer and the issues even that alone can cause.
I think the danger discussed here is probably about equal to users implementing timers as they are now - with events, the solution to the problems is more elegant than having to stop and nullify a timer directly - by simply unsubscribing the event handler.

If I had a tip going forward, it would be to try to maintain the timer priority feature.
Under the hood, you'd have a single timer per event, rather than a single timer handling them all at once.
IIRC there are 9 timer priorities, so you'd have 9 timers each at the relevant interval for the event that it invokes.
 

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
Thanks so much for the feedback, you have clarified the events for me as I was a little confused of how they were handled in ServUO vs what I know about Delegates and Events in general!

I will amend my main post with the inclusion of the Disposal information we discussed and a pointer to these discussions to help people understand the ins/outs of using this! I will also update the core timer, to the suggested 9, TBH I was thinking of doing this but for some crazy reason I thought it would be better if I used only one, I did not know about the priority with timers, I am now a bit smarter!

Thanks @Vorspire : I guess we have all been learning from each other!
Post automatically merged:

Another quick question, you mention 9 types of timers but I seem to only see 6, am I missing something?

1. Tick
2. Millisecond
3. Second
4. Minute
5. Hour
6. Day
 
Last edited:
  • Like
Reactions: Milva and Vorspire

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
GoldDraco13 updated One Time with a new update entry:

Update - Major

This new version includes solutions to the discussion and recommendations from @Vorspire!

*The system now uses 7 timers, one for each type of interval found in the distro timers
*Each time interval has its own event, see item folder for examples of how to use events
*Added a custom way to get rid of events without having to use -= which causes a huge overhead see delete() in examples
*Added a check for deletion on load <Bug Fix

+I'll update the main post with updated...
Read the rest of this update entry...
Post automatically merged:

Version 1.0.0.8 => Stress Test [Coming Soon]

OT1008_Test.png
 
Last edited:
  • Like
Reactions: Vorspire

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
GoldDraco13 updated One Time with a new update entry:

Update

*Add NewStickyGrass.cs and Help.cs (This example uses one event to control all sticky grass)
*Refracted some code and fix some help text
*added a OnAfterDelete() example to TimeTest, use instead of my Deletion work around!

The new sticky grass stays invisible to players until they are within range then it comes alive to grab them!
Read the rest of this update entry...
 

GoldDraco13

Squire
Aug 1, 2014
366
239
43
48
Canada
mycy4.com
Donate
Donate money to this user
GoldDraco13 updated One Time with a new update entry:

Update

*Added check to timers to verify the time is correct, ie, make sure the second only fires every second!
*Added new debug support to check UOTime vs ticks/Milli and over all second count vs cpu clock second!
*Added Async code to NewStickyGrass method, check Helper.cs for example!
*Cleaned up some code, removed debug from Min/Hr and Day, not needed!
Read the rest of this update entry...