1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: events.pike,v 1.2 2009/05/06 19:23:10 astra Exp $
28 //! sTeam event support - uses Events from Event.pmod and supports
29 //! listeners. Each event is also triggered globally through the server
30 //! object (_Server constant).
41 #define DEBUG_EVENT(s, args...) werror(s+"\n", args)
43 #define DEBUG_EVENT(s, args...)
46 private mapping mEvents; // list of event listening objects
47 private mapping mListeners; // list of events this object listens to
49 object get_environment();
51 void require_save(void|string a, void|string b);
52 object get_annotating();
57 void notify_listener(object l, mixed args) {
58 if ( l->get_phase() == PHASE_NOTIFY ) {
59 mixed err = catch(::notify_listener(l, args));
61 FATAL("Error on Event: %s\n%s",
63 describe_backtrace(err[1]));
68 ::notify_listener(l, args);
71 mapping get_params() {
72 mapping p = ::get_params();
73 p->user = this_user();
78 mapping s = ([ "listeners": ({ }), "event": get_event(), ]);
79 array listeners = get_listeners();
81 foreach( listeners, object l ) {
83 if ( !functionp(l->save) )
84 FATAL("Cannot save listener: %s\n",
85 (l->describe?l->describe():""));
87 mixed data = l->save();
89 s->listeners += ({ data });
96 void load(mapping data) {
97 if ( !mappingp(data) || data->event == 0 )
99 set_event(data->event);
100 foreach( data->listeners, mapping ldata ) {
101 if ( !mappingp(ldata) ) continue;
103 SteamListener(ldata->id,
113 class SteamListener {
117 create(int|void eid,int|void phase,object|void obj,function|void callback, int|void oEvents)
119 ::create(eid, phase, obj, callback, 0);
128 "phase": get_phase(),
129 "callback": get_callback(),
130 "objEvents": getObjectEvents(),
134 void load(mapping data) {
135 set(data->id, data->phase, data->obj, data->callback);
136 if ( data->objEvents )
144 * init_events() need to be called by create() in the inheriting object.
145 * The function only initializes the event mappings.
160 * A function calls event() to define a new event. Other objects are then
161 * able to listen or block this event. Callback functions always include
162 * the event-type as first parameter, because it is possible to use
163 * one event function for several events. This function is to be used
164 * in own program code to allow other objects to block actions which
165 * are currently taking place.
166 * The try_event() call should be before the action actually took place,
167 * because there is no rollback functionality.
169 * @param event - the type of the event, all events are located in events.h
170 * @param args - number of arguments for that event
171 * @return ok or blocked
178 try_event(int event, mixed ... args)
180 if ( event == 0 ) return;
181 SteamEvent e = mEvents[event];
189 low_run_event(int event, array monitoring_stack, mixed ... args)
193 if ( event == 0 ) return;
194 SteamEvent e = mEvents[event];
196 FATAL("Error during notify Event: %O\n%O", err[0], err[1]);
199 DEBUG_EVENT("running event {%d} %s",
204 //let the environment be notified about the event,if not allready monitored
205 object env = get_environment();
208 // for our annotating object also monitor...
209 object annotates = this_object()->get_annotating();
210 if ( objectp(annotates) ) {
211 if ( this_object()->get_object_class() & CLASS_FACTORY )
217 * Call this function to run an event inside this object. The integer
218 * event type is the first argument and each event has a diffent number
219 * of arguments. The difference to try_event is that run_event cannot be
220 * blocked. This function is to be used in own program code. It makes
221 * add_event possible for other objects to be notified about the action
222 * which currently takes place.
224 * @param int event - the event to fire
225 * @param mixed ... args - a list of arguments for this individual event
230 final void run_event(int event, mixed ... args)
232 low_run_event(event, ({ }), @args);
238 * this functions monitors the attributes of the objects in
239 * the containers inventory and fires a EVENT_ATTRIBUTES|EVENTS_MONITORED
242 * @param obj - the monitored object
243 * @param caller - the object calling set_attribute in 'obj'
244 * @param args - some args, like key and value
246 void monitor_event(int event, array monitoring_stack, object obj, mixed ... args)
248 if ( !functionp(obj->get_object_id) ||
249 CALLER->get_object_id() != obj->get_object_id() )
251 return; // monitoring loop!
254 if ( event & EVENTS_MONITORED )
261 * Add a new event to this object. The listener object needs to define
262 * a callback function. The call will then include some parameters of which
263 * the first will always be the event-type.
264 * Do not call this function yourself. Call add_event instead, otherwise
265 * the data structure that connects listener object and event object will
268 * @param type - the event type to add
269 * @param callback - the function to call when event happens
270 * @return id of the event or FAIL (-1)
274 listen_event(Listener nlistener)
277 int event = nlistener->get_event();
279 try_event(EVENT_LISTEN_EVENT, CALLER, event, nlistener->get_phase());
281 DEBUG_EVENT("new event.... = "+ event + " on #"+get_object_id());
282 // check what events the listener listens to
283 array events = split_events(event);
285 foreach( events, event ) {
288 e = SteamEvent(event);
290 object nlist = e->add_listener(nlistener);
291 if ( nlist->get_listener_id() != nlistener->get_listener_id() ) {
296 require_save(STORE_EVENTS);
298 run_event(EVENT_LISTEN_EVENT, CALLER,
299 nlistener->get_event(), nlistener->get_phase());
304 * This is the most central function to be used for subscribing events.
305 * Add an event to object obj, the event will be stored in the local
306 * event list. The callback function will be called with the event-id
307 * (in case there is one callback function used for multiple events),
308 * then the object is provided where the event took place and a number
309 * of parameters are passed depending on the event.
310 * callback(event-id, object, params)
312 * @param obj - the object to listen to
313 * @param event - the event type
314 * @param phase - notify or block phase
315 * @param callback - the callback function
317 * @return event id or fail (-1), but usually will throw an exception
322 add_event(object obj, int event, int phase, function callback)
324 ASSERTINFO(_SECURITY->valid_proxy(obj), "No add event on non proxies !");
326 if ( phase == PHASE_BLOCK )
327 _SECURITY->access_write(0, this_object(), CALLER);
329 _SECURITY->access_read(0, this_object(), CALLER);
331 SteamListener nlistener =
332 SteamListener(event, phase, this_object(), callback);
334 /* the key for mListeners is event/obj/callback */
335 if ( !mappingp(mListeners[event]) )
336 mListeners[event] = ({ });
338 object nlisten = obj->listen_event(nlistener);
339 // this one has been added
340 if ( nlistener->get_listener_id() == nlisten->get_listener_id() )
341 mListeners[event] += ({ nlistener });
342 else if ( search(mListeners[event], nlisten) == -1 )
343 mListeners[event] += ({ nlisten });
351 object addEvent(object obj, int event, int phase, function callback)
353 object listener = add_event(obj, event, phase, callback);
354 if ( objectp(listener) )
355 listener->setObjectEvents();
362 void restore_listener(object listener)
364 int event = listener->get_event();
365 if ( !mappingp(mListeners[event]) )
366 mListeners[event] = ({ });
367 mListeners[event] += ({ listener });
373 * remove an event, it is removed from the object and this object
374 * only the local function for remove and add should be called.
375 * Event-type, function and object are the identifier for an object.
376 * No object should listen to an event through one callback function twice.
379 * @param obj - the object to listen to
380 * @param event - the type of event
381 * @param id - function or identifier
382 * @return true or false
386 final bool remove_event(object obj, int event, function|object id)
388 /* event does not exists */
389 if ( !mappingp(mListeners[event]) )
392 /* remove by function pointer, if no functionp is given search
393 * it by the given event-id */
394 if ( functionp(id) ) {
396 foreach(mListeners[event], Listener l) {
397 if ( l->event_function == id ) {
398 mListeners[event] -= ({ l });
399 obj->ignore_event(l);
405 require_save(STORE_EVENTS);
408 mListeners[event] -= ({ id });
409 obj->ignore_event(id);
410 require_save(STORE_EVENTS);
419 * Listener object removes an event. The event id is what add_event()
420 * returns. Usually the function shouldnt be called. It is called
421 * automatically, when the function remove_event() is called.
423 * @param event - the type of event
424 * @return true or false
428 final bool ignore_event(Listener l)
430 int event = l->event_id;
432 SteamEvent e = mEvents[event];
436 try_event(EVENT_IGNORE_EVENT, CALLER, event);
438 e->remove_listener(l);
440 require_save(STORE_EVENTS);
441 run_event(EVENT_IGNORE_EVENT, CALLER, event);
446 * Get a list of listening objects. The returned mapping is in the form
447 * event: array of listening objects.
449 * @return list of listening objects
452 final mapping get_events()
454 return copy_value(mEvents);
458 * Returns the mapping entry for a given event. For example the function
459 * could be called with get_event(EVENT_MOVE). Check the file include/events.h
460 * for a list of all events.
462 * @param int event - the event to return
463 * @return array of listening objects
466 final array get_event(int event)
468 return copy_value(mEvents[event]);
473 * Get a list of events this object listens to.
475 * @return mapping of array this objects listens to (listeners)
478 final mapping get_listeners()
480 foreach( indices(mListeners), int event )
481 mListeners[event] -= ({ 0 });
482 return copy_value(mListeners);
486 * Get the mapping of events subscribed on object 'where' or 0 if there is
487 * no entry of 'event'.
489 * @param int event - the event
490 * @param object where - The object to check for subscription
491 * @return the listener object.
493 final object get_listener(int event, object where)
495 if ( !mappingp(mListeners[event]) )
497 foreach( mListeners[event], object l )
498 if ( l->get_object() == where )
504 * restore the events of an object
506 * @param data - the event data for the object
508 * @see retrieve_events
512 restore_events(mixed data)
514 ASSERTINFO(CALLER == _Database, "Invalid call to restore_events()");
516 if ( !mappingp(data) || data->Events )
519 if ( equal(data, ([ ])) )
524 foreach(indices(data), int event) {
525 if ( !mappingp(data[event]) )
527 object e = SteamEvent(0);
528 e->load(data[event]);
530 foreach(e->get_listeners(), object l) {
531 object o = l->get_object();
533 o->restore_listener(l);
542 * retrieve the event data of the object
544 * @return the events of the object
546 * @see restore_events
552 ASSERTINFO(CALLER == _Database, "Invalid call to retrieve_events()");
553 mapping em = map(mEvents, save_events);
561 mapping save_events(SteamEvent event)
563 return event->save();
569 array save_listeners(array listeners)
571 array result = ({ });
572 foreach (listeners, object l) {
573 if ( objectp(l) && functionp(l->save) )
574 result += ({ l->save() });