events._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
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.
7  *
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.
12  *
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
16  *
17  * $Id: events.pike,v 1.2 2009/05/06 19:23:10 astra Exp $
18  */
19  inherit Event;
20  inherit Listener;
21 #include <macros.h>
22 #include <events.h>
23 #include <access.h>
24 #include <assert.h>
25 #include <database.h>
26 #include <classes.h>
27 #include <config.h>
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).
31 class events {
32 public:
33 
34 
35 
36 
37 
38 import Events;
39 
40 #ifdef EVENT_DEBUG
41 #define DEBUG_EVENT(s, args...) werror(s+"\n", args)
42 #else
43 #define DEBUG_EVENT(s, args...)
44 #endif
45 
46 private mapping mEvents; // list of event listening objects
47 private mapping mListeners; // list of events this object listens to
48 
49 object get_environment();
50 int get_object_id();
51  void require_save(void|string a, void|string b);
52 object get_annotating();
53 
54 class SteamEvent {
55 public:
56 
57  void notify_listener(object l, mixed args) {
58  if ( l->get_phase() == PHASE_NOTIFY ) {
59  mixed err = catch(::notify_listener(l, args));
60  if ( err != 0 ) {
61  FATAL("Error on Event: %s\n%s",
62  err[0],
63  describe_backtrace(err[1]));
64  remove_listener(l);
65  }
66  }
67  else {
68  ::notify_listener(l, args);
69  }
70  }
71  mapping get_params() {
72  mapping p = ::get_params();
73  p->user = this_user();
74  return p;
75  }
76  // add persistence
77  mapping save() {
78  mapping s = ([ "listeners": ({ }), "event": get_event(), ]);
79  array listeners = get_listeners();
80  mixed err = catch {
81  foreach( listeners, object l ) {
82  if ( objectp(l) ) {
83  if ( !functionp(l->save) )
84  FATAL("Cannot save listener: %s\n",
85  (l->describe?l->describe():""));
86  else {
87  mixed data = l->save();
88  if ( data )
89  s->listeners += ({ data });
90  }
91  }
92  }
93  };
94  return s;
95  }
96  void load(mapping data) {
97  if ( !mappingp(data) || data->event == 0 )
98  return;
99  set_event(data->event);
100  foreach( data->listeners, mapping ldata ) {
101  if ( !mappingp(ldata) ) continue;
102  SteamListener l =
103  SteamListener(ldata->id,
104  ldata->phase,
105  ldata->obj,
106  ldata->callback,
107  ldata->objEvents);
108  add_listener(l);
109  }
110  }
111 }
112 
113 class SteamListener {
114 public:
115 
116  void
117  create(int|void eid,int|void phase,object|void obj,function|void callback, int|void oEvents)
118  {
119  ::create(eid, phase, obj, callback, 0);
120  if ( oEvents )
121  setObjectEvents();
122  }
123 
124  mapping save() {
125  return ([
126  "id":get_event(),
127  "obj": get_object(),
128  "phase": get_phase(),
129  "callback": get_callback(),
130  "objEvents": getObjectEvents(),
131  ]);
132  }
133 
134  void load(mapping data) {
135  set(data->id, data->phase, data->obj, data->callback);
136  if ( data->objEvents )
137  setObjectEvents();
138  }
139 
140 }
141 
142 /**
143 private:
144  * init_events() need to be called by create() in the inheriting object.
145  * The function only initializes the event mappings.
146  *
147  */
148 protected:
149 final void
150 private:
151 init_events()
152 {
153  mEvents = ([ ]);
154  mListeners = ([ ]);
155 }
156 
157 public:
158 
159 /**
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.
168  *
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
172  * @see add_event
173  * @see run_events
174  * @see run_event
175  */
176 protected:
177 final void
178 try_event(int event, mixed ... args)
179 {
180  if ( event == 0 ) return;
181  SteamEvent e = mEvents[event];
182  if ( objectp(e) )
183 }
184 
185 public:
186 
187 protected:
188 final void
189 low_run_event(int event, array monitoring_stack, mixed ... args)
190 {
191  mixed err;
192 
193  if ( event == 0 ) return;
194  SteamEvent e = mEvents[event];
195  if (objectp(e) ) {
196  FATAL("Error during notify Event: %O\n%O", err[0], err[1]);
197  }
198  else
199  DEBUG_EVENT("running event {%d} %s",
200  }
201 
202 public:
203 
204  //let the environment be notified about the event,if not allready monitored
205  object env = get_environment();
206  if ( objectp(env) )
207 
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 )
212  return;
213  }
214 }
215 
216 /**
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.
223  *
224  * @param int event - the event to fire
225  * @param mixed ... args - a list of arguments for this individual event
226  * @see try_event
227  * @see run_events
228  */
229 protected:
230 final void run_event(int event, mixed ... args)
231 {
232  low_run_event(event, ({ }), @args);
233 }
234 
235 public:
236 
237 /**
238  * this functions monitors the attributes of the objects in
239  * the containers inventory and fires a EVENT_ATTRIBUTES|EVENTS_MONITORED
240  * event.
241  *
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
245  */
246 void monitor_event(int event, array monitoring_stack, object obj, mixed ... args)
247 {
248  if ( !functionp(obj->get_object_id) ||
249  CALLER->get_object_id() != obj->get_object_id() )
250  return;
251  return; // monitoring loop!
252  }
253 
254  if ( event & EVENTS_MONITORED )
255  else
256 }
257 
258 
259 
260 /**
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
266  * be invalid.
267  *
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)
271  * @see remove_event
272  */
273 final object
274 listen_event(Listener nlistener)
275 {
276  SteamEvent e;
277  int event = nlistener->get_event();
278 
279  try_event(EVENT_LISTEN_EVENT, CALLER, event, nlistener->get_phase());
280 
281  DEBUG_EVENT("new event.... = "+ event + " on #"+get_object_id());
282  // check what events the listener listens to
283  array events = split_events(event);
284 
285  foreach( events, event ) {
286  e = mEvents[event];
287  if ( !objectp(e) ) {
288  e = SteamEvent(event);
289  }
290  object nlist = e->add_listener(nlistener);
291  if ( nlist->get_listener_id() != nlistener->get_listener_id() ) {
292  return nlist;
293  }
294  mEvents[event] = e;
295  }
296  require_save(STORE_EVENTS);
297 
298  run_event(EVENT_LISTEN_EVENT, CALLER,
299  nlistener->get_event(), nlistener->get_phase());
300  return nlistener;
301 }
302 
303 /**
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)
311  *
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
316  *
317  * @return event id or fail (-1), but usually will throw an exception
318  * @see listen_event
319  */
320 protected:
321  object
322 add_event(object obj, int event, int phase, function callback)
323 {
324  ASSERTINFO(_SECURITY->valid_proxy(obj), "No add event on non proxies !");
325 
326  if ( phase == PHASE_BLOCK )
327  _SECURITY->access_write(0, this_object(), CALLER);
328  else
329  _SECURITY->access_read(0, this_object(), CALLER);
330 
331  SteamListener nlistener =
332  SteamListener(event, phase, this_object(), callback);
333 
334  /* the key for mListeners is event/obj/callback */
335  if ( !mappingp(mListeners[event]) )
336  mListeners[event] = ({ });
337 
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 });
344 
345  return nlisten;
346 }
347 
348 public:
349 
350 protected:
351  object addEvent(object obj, int event, int phase, function callback)
352 {
353  object listener = add_event(obj, event, phase, callback);
354  if ( objectp(listener) )
355  listener->setObjectEvents();
356  return listener;
357 }
358 
359 public:
360 
361 private:
362 void restore_listener(object listener)
363 {
364  int event = listener->get_event();
365  if ( !mappingp(mListeners[event]) )
366  mListeners[event] = ({ });
367  mListeners[event] += ({ listener });
368 }
369 
370 public:
371 
372 /**
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.
377  *
378  *
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
383  * @see ignore_event
384  */
385 protected:
386 final bool remove_event(object obj, int event, function|object id)
387 {
388  /* event does not exists */
389  if ( !mappingp(mListeners[event]) )
390  return false;
391 
392  /* remove by function pointer, if no functionp is given search
393  * it by the given event-id */
394  if ( functionp(id) ) {
395  int save=0;
396  foreach(mListeners[event], Listener l) {
397  if ( l->event_function == id ) {
398  mListeners[event] -= ({ l });
399  obj->ignore_event(l);
400  save=1;
401  return true;
402  }
403  }
404  if (save)
405  require_save(STORE_EVENTS);
406  }
407  else {
408  mListeners[event] -= ({ id });
409  obj->ignore_event(id);
410  require_save(STORE_EVENTS);
411  return true;
412  }
413  return false;
414 }
415 
416 public:
417 
418 /**
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.
422  *
423  * @param event - the type of event
424  * @return true or false
425  * @see add_event
426  * @see remove_event
427  */
428 final bool ignore_event(Listener l)
429 {
430  int event = l->event_id;
431 
432  SteamEvent e = mEvents[event];
433  if ( !objectp(e) )
434  return false;
435 
436  try_event(EVENT_IGNORE_EVENT, CALLER, event);
437 
438  e->remove_listener(l);
439  destruct(l);
440  require_save(STORE_EVENTS);
441  run_event(EVENT_IGNORE_EVENT, CALLER, event);
442  return true;
443 }
444 
445 /**
446  * Get a list of listening objects. The returned mapping is in the form
447  * event: array of listening objects.
448  *
449  * @return list of listening objects
450  * @see get_my_events
451  */
452 final mapping get_events()
453 {
454  return copy_value(mEvents);
455 }
456 
457 /**
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.
461  *
462  * @param int event - the event to return
463  * @return array of listening objects
464  * @see get_events
465  */
466 final array get_event(int event)
467 {
468  return copy_value(mEvents[event]);
469 }
470 
471 
472 /**
473  * Get a list of events this object listens to.
474  *
475  * @return mapping of array this objects listens to (listeners)
476  * @see get_events
477  */
478 final mapping get_listeners()
479 {
480  foreach( indices(mListeners), int event )
481  mListeners[event] -= ({ 0 });
482  return copy_value(mListeners);
483 }
484 
485 /**
486  * Get the mapping of events subscribed on object 'where' or 0 if there is
487  * no entry of 'event'.
488  *
489  * @param int event - the event
490  * @param object where - The object to check for subscription
491  * @return the listener object.
492  */
493 final object get_listener(int event, object where)
494 {
495  if ( !mappingp(mListeners[event]) )
496  return 0;
497  foreach( mListeners[event], object l )
498  if ( l->get_object() == where )
499  return l;
500  return 0;
501 }
502 
503 /**
504  * restore the events of an object
505  *
506  * @param data - the event data for the object
507 private:
508  * @see retrieve_events
509  */
510 final void
511 private:
512 restore_events(mixed data)
513 {
514  ASSERTINFO(CALLER == _Database, "Invalid call to restore_events()");
515 
516  if ( !mappingp(data) || data->Events )
517  return;
518 
519  if ( equal(data, ([ ])) )
520  return;
521 
522  mEvents = ([ ]);
523 
524  foreach(indices(data), int event) {
525  if ( !mappingp(data[event]) )
526  continue;
527  object e = SteamEvent(0);
528  e->load(data[event]);
529  mEvents[event] = e;
530  foreach(e->get_listeners(), object l) {
531  object o = l->get_object();
532  if ( objectp(o) )
533  o->restore_listener(l);
534  }
535  }
536 }
537 
538 public:
539 
540 
541 /**
542  * retrieve the event data of the object
543  *
544  * @return the events of the object
545 private:
546  * @see restore_events
547  */
548 final mapping
549 private:
550 retrieve_events()
551 {
552  ASSERTINFO(CALLER == _Database, "Invalid call to retrieve_events()");
553  mapping em = map(mEvents, save_events);
554 
555  return em;
556 }
557 
558 public:
559 
560 protected:
561  mapping save_events(SteamEvent event)
562 {
563  return event->save();
564 }
565 
566 public:
567 
568 protected:
569  array save_listeners(array listeners)
570 {
571  array result = ({ });
572  foreach (listeners, object l) {
573  if ( objectp(l) && functionp(l->save) )
574  result += ({ l->save() });
575  }
576  return result;
577 }
578 
579 public:
580 
581 
582 };