mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Updated HTML docs.
This commit is contained in:
parent
59e50f3fa5
commit
06bc3c8bcd
663 changed files with 2 additions and 61705 deletions
|
|
@ -1,136 +0,0 @@
|
|||
# TickerHandler
|
||||
|
||||
|
||||
One way to implement a dynamic MUD is by using "tickers", also known as "heartbeats". A ticker is a
|
||||
timer that fires ("ticks") at a given interval. The tick triggers updates in various game systems.
|
||||
|
||||
## About Tickers
|
||||
|
||||
Tickers are very common or even unavoidable in other mud code bases. Certain code bases are even
|
||||
hard-coded to rely on the concept of the global 'tick'. Evennia has no such notion - the decision to
|
||||
use tickers is very much up to the need of your game and which requirements you have. The "ticker
|
||||
recipe" is just one way of cranking the wheels.
|
||||
|
||||
The most fine-grained way to manage the flow of time is of course to use [Scripts](./Scripts.md). Many
|
||||
types of operations (weather being the classic example) are however done on multiple objects in the
|
||||
same way at regular intervals, and for this, storing separate Scripts on each object is inefficient.
|
||||
The way to do this is to use a ticker with a "subscription model" - let objects sign up to be
|
||||
triggered at the same interval, unsubscribing when the updating is no longer desired.
|
||||
|
||||
Evennia offers an optimized implementation of the subscription model - the *TickerHandler*. This is
|
||||
a singleton global handler reachable from `evennia.TICKER_HANDLER`. You can assign any *callable* (a
|
||||
function or, more commonly, a method on a database object) to this handler. The TickerHandler will
|
||||
then call this callable at an interval you specify, and with the arguments you supply when adding
|
||||
it. This continues until the callable un-subscribes from the ticker. The handler survives a reboot
|
||||
and is highly optimized in resource usage.
|
||||
|
||||
Here is an example of importing `TICKER_HANDLER` and using it:
|
||||
|
||||
```python
|
||||
# we assume that obj has a hook "at_tick" defined on itself
|
||||
from evennia import TICKER_HANDLER as tickerhandler
|
||||
|
||||
tickerhandler.add(20, obj.at_tick)
|
||||
```
|
||||
|
||||
That's it - from now on, `obj.at_tick()` will be called every 20 seconds.
|
||||
|
||||
You can also import function and tick that:
|
||||
|
||||
```python
|
||||
from evennia import TICKER_HANDLER as tickerhandler
|
||||
from mymodule import myfunc
|
||||
|
||||
tickerhandler.add(30, myfunc)
|
||||
```
|
||||
|
||||
Removing (stopping) the ticker works as expected:
|
||||
|
||||
```python
|
||||
tickerhandler.remove(20, obj.at_tick)
|
||||
tickerhandler.remove(30, myfunc)
|
||||
```
|
||||
|
||||
Note that you have to also supply `interval` to identify which subscription to remove. This is
|
||||
because the TickerHandler maintains a pool of tickers and a given callable can subscribe to be
|
||||
ticked at any number of different intervals.
|
||||
|
||||
The full definition of the `tickerhandler.add` method is
|
||||
|
||||
```python
|
||||
tickerhandler.add(interval, callback,
|
||||
idstring="", persistent=True, *args, **kwargs)
|
||||
```
|
||||
|
||||
Here `*args` and `**kwargs` will be passed to `callback` every `interval` seconds. If `persistent`
|
||||
is `False`, this subscription will not survive a server reload.
|
||||
|
||||
Tickers are identified and stored by making a key of the callable itself, the ticker-interval, the
|
||||
`persistent` flag and the `idstring` (the latter being an empty string when not given explicitly).
|
||||
|
||||
Since the arguments are not included in the ticker's identification, the `idstring` must be used to
|
||||
have a specific callback triggered multiple times on the same interval but with different arguments:
|
||||
|
||||
```python
|
||||
tickerhandler.add(10, obj.update, "ticker1", True, 1, 2, 3)
|
||||
tickerhandler.add(10, obj.update, "ticker2", True, 4, 5)
|
||||
```
|
||||
|
||||
> Note that, when we want to send arguments to our callback within a ticker handler, we need to
|
||||
specify `idstring` and `persistent` before, unless we call our arguments as keywords, which would
|
||||
often be more readable:
|
||||
|
||||
```python
|
||||
tickerhandler.add(10, obj.update, caller=self, value=118)
|
||||
```
|
||||
|
||||
If you add a ticker with exactly the same combination of callback, interval and idstring, it will
|
||||
overload the existing ticker. This identification is also crucial for later removing (stopping) the
|
||||
subscription:
|
||||
|
||||
```python
|
||||
tickerhandler.remove(10, obj.update, idstring="ticker1")
|
||||
tickerhandler.remove(10, obj.update, idstring="ticker2")
|
||||
```
|
||||
|
||||
The `callable` can be on any form as long as it accepts the arguments you give to send to it in
|
||||
`TickerHandler.add`.
|
||||
|
||||
> Note that everything you supply to the TickerHandler will need to be pickled at some point to be
|
||||
saved into the database. Most of the time the handler will correctly store things like database
|
||||
objects, but the same restrictions as for [Attributes](./Attributes.md) apply to what the TickerHandler
|
||||
may store.
|
||||
|
||||
When testing, you can stop all tickers in the entire game with `tickerhandler.clear()`. You can also
|
||||
view the currently subscribed objects with `tickerhandler.all()`.
|
||||
|
||||
See the [Weather Tutorial](../Howtos/Weather-Tutorial.md) for an example of using the TickerHandler.
|
||||
|
||||
### When *not* to use TickerHandler
|
||||
|
||||
Using the TickerHandler may sound very useful but it is important to consider when not to use it.
|
||||
Even if you are used to habitually relying on tickers for everything in other code bases, stop and
|
||||
think about what you really need it for. This is the main point:
|
||||
|
||||
> You should *never* use a ticker to catch *changes*.
|
||||
|
||||
Think about it - you might have to run the ticker every second to react to the change fast enough.
|
||||
Most likely nothing will have changed at a given moment. So you are doing pointless calls (since
|
||||
skipping the call gives the same result as doing it). Making sure nothing's changed might even be
|
||||
computationally expensive depending on the complexity of your system. Not to mention that you might
|
||||
need to run the check *on every object in the database*. Every second. Just to maintain status quo
|
||||
...
|
||||
|
||||
Rather than checking over and over on the off-chance that something changed, consider a more
|
||||
proactive approach. Could you implement your rarely changing system to *itself* report when its
|
||||
status changes? It's almost always much cheaper/efficient if you can do things "on demand". Evennia
|
||||
itself uses hook methods for this very reason.
|
||||
|
||||
So, if you consider a ticker that will fire very often but which you expect to have no effect 99% of
|
||||
the time, consider handling things things some other way. A self-reporting on-demand solution is
|
||||
usually cheaper also for fast-updating properties. Also remember that some things may not need to be
|
||||
updated until someone actually is examining or using them - any interim changes happening up to that
|
||||
moment are pointless waste of computing time.
|
||||
|
||||
The main reason for needing a ticker is when you want things to happen to multiple objects at the
|
||||
same time without input from something else.
|
||||
Loading…
Add table
Add a link
Reference in a new issue