Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/hook_provider.c b/std/hook_provider.c
new file mode 100644
index 0000000..00b8ca3
--- /dev/null
+++ b/std/hook_provider.c
@@ -0,0 +1,493 @@
+// MorgenGrauen MUDlib
+//
+// /std/hook_provider.c - Hooksystem
+//
+// $Id: hook_provider.c 9453 2016-02-04 21:22:54Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#undef NEED_PROTOTYPES
+
+// get the object fur closure <cl> (where the code is!)
+#define GET_OBJECT(cl) get_type_info(cl,2)
+
+// Struct describing one hook consumer. <cl> will be called when the hook is
+// triggered. The lower <prio>, the earlier the consumer will be called.
+// <type> is a consumertype, e.g. surveyor, listener etc. (see hook.h)
+struct hook_entry_s {
+ closure cl;
+ int prio;
+ int endtime;
+ int type;
+};
+
+// Struct holding the consumers of one specific hook.
+// the value arrays are guarenteed to exist, but may be empty.
+struct hook_s {
+ struct hook_entry_s *surveyors;
+ struct hook_entry_s *hmods;
+ struct hook_entry_s *dmods;
+ struct hook_entry_s *listeners;
+};
+
+/* hook mapping
+ the list of all offered hooks in the following structure:
+ ([hookid: (<hook_s>), ...
+ )]
+*/
+private nosave mapping hookMapping=([]);
+
+protected void CleanHookMapping(int *hookids);
+
+// Debugging - ggf. ueberschreiben
+protected int h_dbg() {return 0;}
+
+void HookTestOffer(int id, int stat){
+ if(h_dbg()) {
+ offerHook(id,stat);
+ }
+}
+
+void HookTestTrigger(int id, mixed data){
+ if(h_dbg()) {
+ HookFlow(id,data);
+ }
+}
+
+// NOTE: if you have the closure, you can call the lfun, even if it is
+// private. Therefore access to this data should be restricted to
+// this_object().
+// These two functions should be used for debugging purposes.
+protected mapping HCopyHookMapping(){
+ return deep_copy(hookMapping);
+}
+
+protected mapping HCopyHookConsumers(int hookid){
+ if(member(hookMapping,hookid)) {
+ CleanHookMapping(({hookid}));
+ return deep_copy(hookMapping[hookid]);
+ }
+ return 0;
+}
+
+// Ggf. zum Ueberschreiben.
+int HConsumerTypeIsAllowed(int type, object consumer){
+ return 1;
+}
+
+int HPriorityIsAllowed(int prio, object consumer){
+ return 1;
+}
+
+// clean internal hook data structures of stale hook consumers.
+protected void CleanHookMapping(int *hookids){
+ // hooks enthaelt die aufzuraeumenden Hooks. Wenn kein Array -> alle Hooks
+ if (!pointerp(hookids))
+ hookids=m_indices(hookMapping);
+
+ foreach(int hookid : hookids) { // alle Hooks
+ struct hook_s hooks = hookMapping[hookid];
+ if (!structp(hooks))
+ continue;
+ // ueber alle Consumertypen laufen
+ foreach (string consumertype: H_CONSUMERNAMES)
+ {
+ // Yeah - compute struct lookup at runtime... ;-)
+ struct hook_entry_s *consumers = hooks->(consumertype);
+ // Hookeintraege / Consumer
+ foreach(struct hook_entry_s h : &consumers) {
+ // alle abgelaufenen Eintraege oder solche mit zerstoerten Objekten
+ // nullen
+ if (!h->cl || h->endtime < time() )
+ h = 0;
+ }
+ // 0 noch rauswerfen.
+ hooks->(consumertype) -= ({0});
+ }
+ }
+}
+
+protected void offerHook(int hookid, int offerstate)
+{
+ H_DMSG(sprintf("offerHook hookid %d offerstate %d\n",hookid,offerstate));
+ if (hookid>0)
+ {
+ if (offerstate) {
+ if (!member(hookMapping,hookid)) {
+ struct hook_s hook = (<hook_s>
+ surveyors: ({}),
+ hmods: ({}),
+ dmods: ({}),
+ listeners: ({}) );
+ hookMapping[hookid] = hook;
+ }
+ }
+ else {
+ if (member(hookMapping,hookid)) {
+ m_delete(hookMapping,hookid);
+ }
+ }
+ }
+ H_DMSG(sprintf(" result %O\n",hookMapping));
+}
+
+// hookConsumerInfo() liefert Array von hook_entry_s zurueck. D.h. bei Abfrage
+// von Objekten alle Closures dieses Objekts und jede davon erzeugt ein
+// Element hook_entry_s im Ergebnisarray. Bei Abfrage von Closures hat das
+// Array immer genau 1 oder kein Element.
+// WARNING: whoever has a hook_entry_s can change/delete the hook!
+// NEVER return the original to an external caller!
+// NOTE: whoever has the cl from hook_entry_s can call it, even if the lfun
+// is private (and this object the only one knowing it).
+protected mixed * hookConsumerInfo(int hookid, object|closure consumer)
+{
+ closure filter_cl;
+
+ if (!member(hookMapping,hookid))
+ return ({});
+
+ // Closure zum Filtern bestimmen - je nachdem, was gesucht wird.
+ if (closurep(consumer))
+ {
+ filter_cl = function int (struct hook_entry_s h)
+ { return h->cl == consumer
+ && h->endtime >= time(); };
+ }
+ else if (objectp(consumer))
+ {
+ filter_cl = function int (struct hook_entry_s h)
+ { return GET_OBJECT(h->cl) == consumer
+ && h->endtime >= time(); };
+ }
+ else
+ {
+ return ({});
+ }
+
+ struct hook_s hook = hookMapping[hookid];
+ struct hook_entry_s *result = ({});
+ foreach (string consumertype: H_CONSUMERNAMES )
+ {
+ result += filter(hook->(consumertype), filter_cl);
+ }
+ return result;
+}
+
+int HIsHookConsumer(int hookid, mixed consumer) {
+ return sizeof(hookConsumerInfo(hookid,consumer)) != 0;
+}
+
+int* HListHooks() {
+ return m_indices(hookMapping);
+}
+
+int HUnregisterFromHook(int hookid, mixed consumer) {
+
+ H_DMSG(sprintf("HUnregisterFromHook hookid %d consumer %O\n",hookid,consumer));
+ if (objectp(consumer))
+ consumer = symbol_function("HookCallback", consumer);
+ if (!closurep(consumer))
+ return 0;
+
+ struct hook_entry_s *info = hookConsumerInfo(hookid,consumer);
+
+ // it should never happen that a closure is registered more than once, i.e.
+ // the result contains more than one element.
+ if (sizeof(info)) {
+ struct hook_entry_s h = info[0];
+ h->cl = 0;
+ H_DMSG(sprintf(" result %O\n", hookMapping));
+ // the now invalid h will be cleaned up later.
+ return 1;
+ }
+ return 0;
+}
+
+// surveyors are asked for registration permittance
+protected int askSurveyorsForRegistrationAllowance(
+ struct hook_entry_s *surveyors, object consumer,int hookid,
+ int hookprio,int consumertype)
+{
+ H_DMSG(sprintf("askSurveyorsForRegistrationAllowance surveyors %O, "
+ "consumer %O, hookid %d, hookprio %d, consumertype %d\n",
+ surveyors,consumer,hookid,hookprio,consumertype));
+
+ foreach(struct hook_entry_s surveyor : surveyors) {
+ if (closurep(surveyor->cl) && surveyor->endtime >= time())
+ {
+ // surveyor hook gueltig.
+ object sob = GET_OBJECT(surveyor->cl);
+ if (!sob->HookRegistrationCallback(consumer, hookid,
+ this_object(), hookprio, consumertype))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int HRegisterToHook(int hookid, mixed consumer, int hookprio,
+ int consumertype, int timeInSeconds) {
+ int ret, regtime;
+
+ if (!closurep(consumer) && !objectp(consumer))
+ raise_error(sprintf("Wrong argument %.50O to HRegisterToHook(): consumer "
+ "must be closure or object.\n",consumer));
+
+ if (!member(hookMapping, hookid))
+ return -1;
+
+ if (objectp(consumer)) {
+ consumer = symbol_function("HookCallback", consumer);
+ if (!closurep(consumer))
+ return -2;
+ }
+
+ if (timeInSeconds > 0) {
+ regtime=time() + timeInSeconds;
+ }
+ else {
+ regtime=__INT_MAX__;
+ }
+
+ H_DMSG(sprintf("HRegisterToHook hookid %d consumer %O\n hookprio %d "
+ "consumertype %d\n",hookid,consumer,hookprio,consumertype));
+
+ CleanHookMapping(({hookid})); // entfernt ungueltige/abgelaufene Eintraege
+
+ // nur einmal pro closure registrieren!
+ if (HIsHookConsumer(hookid, consumer))
+ return -3;
+
+ // Consumertyp erlaubt?
+ if (H_CONSUMERCHECK(consumertype) == -1
+ || !HConsumerTypeIsAllowed(consumertype,GET_OBJECT(consumer)))
+ return -4;
+
+ // Prioritaet erlaubt?
+ if (H_HOOK_VALIDPRIO(hookprio) == -1
+ || !HPriorityIsAllowed(hookprio, GET_OBJECT(consumer)))
+ return -5;
+
+ struct hook_s hook = hookMapping[hookid];
+
+ // Und surveyors erlauben die Registierung?
+ if (!askSurveyorsForRegistrationAllowance(hook->surveyors,
+ GET_OBJECT(consumer),hookid,
+ hookprio,consumertype))
+ return -6;
+
+ string ctypename = H_CONSUMERNAMES[consumertype];
+
+ // get the consumer array
+ struct hook_entry_s *consumers = hook->(ctypename);
+
+ // assemble new hook consumer struct
+ struct hook_entry_s newconsumer = (<hook_entry_s>
+ cl : consumer,
+ prio : hookprio,
+ endtime : regtime,
+ type : consumertype );
+
+ // consumers enthaelt die Hookeintraege
+ if (sizeof(consumers) < MAX_HOOK_COUNTS[consumertype]) {
+ // max. Anzahl an Eintraegen fuer diesen Typ noch nicht
+ // erreicht, direkt anhaengen.
+ consumers += ({ newconsumer });
+ hook->(ctypename) = consumers;
+ ret=1;
+ }
+ else {
+ // gibt es einen Eintrag mit hoeherem Priowert (niedrigere
+ // Prioritaet), den man ersetzen koennte?
+ // Das Array ist sortiert, mit hoechsten Priowerten am
+ // Ende. Ersetzt werden soll der Eintrag mit dem hoechsten
+ // Zahlenwert, falls der neue Eintrag einen niedrigeren Wert
+ // hat, d.h. es muss nur er letzte Consumer im Array geprueft werden.
+ // Pruefung auf Closureexistenz, falls der Surveyor Objekte
+ // zerstoert (hat)...
+ struct hook_entry_s oh = consumers[<1];
+ if (!oh->cl || oh->prio > newconsumer->prio) {
+ // Found superseedable entry - replace it, but inform the object.
+ H_DMSG("Found superseedable entry\n");
+ consumers[<1] = newconsumer;
+ GET_OBJECT(oh->cl)->superseededHook(hookid, this_object());
+ ret = 1;
+ // nicht noetig, consumers wieder in sein hook_s zu haenngen wegen Array
+ // -> Referenz
+ }
+ }
+
+ // wenn ein Eintrag hinzugefuegt wurde, muss neu sortiert werden
+ if (ret) {
+ hook->(ctypename) = sort_array(consumers,
+ function int (struct hook_entry_s a, struct hook_entry_s b) {
+ return a->prio > b->prio; } );
+ }
+
+ H_DMSG(sprintf(" result %O\n",hookMapping));
+
+ // -7, wenn kein Eintrag mehr frei / zuviele Hooks
+ return (ret > 0 ? 1 : -7);
+}
+
+// Conveniences wrapper for simple listener hooks
+int HRegisterListener(int hookid, mixed consumer)
+{
+ return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2), H_LISTENER, 0);
+}
+
+// Cnveniences wrapper for simple modificator hooks
+int HRegisterModifier(int hookid, mixed consumer)
+{
+ return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2),
+ H_HOOK_MODIFICATOR, 0);
+}
+
+// surveyors are asked for cancellation permittance
+protected int askSurveyorsForCancelAllowance(mixed surveyors,
+ object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+ foreach(struct hook_entry_s surveyor : surveyors) {
+ if (closurep(surveyor->cl) && surveyor->endtime >= time())
+ {
+ // surveyor hook gueltig.
+ object sob = GET_OBJECT(surveyor->cl);
+ if (!sob->HookCancelAllowanceCallback(modifiyingOb, hookid,
+ hookOb, prio, data))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+// surveyors are asked for data change permittance
+protected int askSurveyorsForModificationAllowance(mixed surveyors,
+ object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+ foreach(struct hook_entry_s surveyor : surveyors) {
+ if (closurep(surveyor->cl) && surveyor->endtime >= time())
+ {
+ // surveyor hook gueltig.
+ object sob = GET_OBJECT(surveyor->cl);
+ if (!sob->HookModificationAllowanceCallback(modifiyingOb,
+ hookid, hookOb, prio, data))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+protected mixed HookFlow(int hookid, mixed hookdata){
+ mixed tmp, ret;
+
+ ret=({H_NO_MOD,hookdata});
+
+ H_DMSG(sprintf("HookFlow hookid %d hookdata %O\n",hookid,hookdata));
+
+ if (!member(hookMapping, hookid))
+ return ret;
+
+ struct hook_s hook = hookMapping[hookid];
+
+ // notify surveyors
+ foreach(struct hook_entry_s h : hook->surveyors) {
+ if (closurep(h->cl) && h->endtime >= time())
+ {
+ // Hook gueltig
+ tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+ if(tmp[H_RETCODE]==H_CANCELLED) {
+ ret[H_RETCODE]=H_CANCELLED;
+ return ret; // und weg...
+ }
+ else if(tmp[H_RETCODE]==H_ALTERED){
+ ret[H_RETCODE]=H_ALTERED;
+ ret[H_RETDATA]=tmp[H_RETDATA];
+ }
+ }
+ // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+ else if (find_call_out(#'CleanHookMapping) == -1) {
+ call_out(#'CleanHookMapping, 0, ({hookid}));
+ }
+ } // surveyors fertig
+
+ // notify hmods
+ foreach(struct hook_entry_s h : hook->hmods) {
+ if (closurep(h->cl) && h->endtime >= time())
+ {
+ // Hook gueltig
+ tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+ if(tmp[H_RETCODE]==H_CANCELLED) {
+ // ask allowance in surveyors
+ if(h->cl &&
+ askSurveyorsForCancelAllowance(hook->surveyors,
+ GET_OBJECT(h->cl), hookdata, hookid,
+ h->prio, this_object()))
+ {
+ ret[H_RETCODE] = H_CANCELLED;
+ return ret; // und raus...
+ }
+ }
+ else if(tmp[H_RETCODE]==H_ALTERED) {
+ // ask allowance in surveyors
+ if(h->cl &&
+ askSurveyorsForModificationAllowance(hook->surveyors,
+ GET_OBJECT(h->cl),hookdata, hookid,
+ h->prio,this_object()))
+ {
+ ret[H_RETCODE] = H_ALTERED;
+ ret[H_RETDATA] = tmp[H_RETDATA];
+ }
+ }
+ }
+ // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+ else if (find_call_out(#'CleanHookMapping) == -1) {
+ call_out(#'CleanHookMapping, 0, ({hookid}));
+ }
+ } // hmods fertig
+
+ // notify dmods
+ foreach(struct hook_entry_s h : hook->dmods) {
+ if (closurep(h->cl) && h->endtime >= time())
+ {
+ // Hook gueltig
+ tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+ if(tmp[H_RETCODE]==H_ALTERED) {
+ // ask allowance in surveyors
+ if (h->cl &&
+ askSurveyorsForModificationAllowance(hook->surveyors,
+ GET_OBJECT(h->cl),hookdata, hookid,
+ h->prio,this_object()))
+ {
+ ret[H_RETCODE] = H_ALTERED;
+ ret[H_RETDATA] = tmp[H_RETDATA];
+ }
+ }
+ }
+ // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+ else if (find_call_out(#'CleanHookMapping) == -1) {
+ call_out(#'CleanHookMapping, 0, ({hookid}));
+ }
+ } // dmods fertig
+
+ // notify listener
+ foreach(struct hook_entry_s h : hook->listeners) {
+ if (closurep(h->cl) && h->endtime >= time())
+ {
+ // Hook gueltig
+ funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+ }
+ // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+ else if (find_call_out(#'CleanHookMapping) == -1) {
+ call_out(#'CleanHookMapping, 0, ({hookid}));
+ }
+ } // listener fertig
+
+ return ret;
+}
+