zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 1 | // (c) by Padreic (Padreic@mg.mud.de) |
| 2 | |
| 3 | /* 12 Juli 1998, Padreic |
| 4 | * |
| 5 | * Dieser Master dient zum einsparen von call_out Ketten bei Laufnpcs. |
| 6 | * Anmelden bei diesem Master geschieht ueber die Funktion RegisterWalker(). |
| 7 | * varargs void RegisterWalker(int time, int random, closure walk_closure) |
| 8 | * time - in welchen Abstaenden soll das Objekt bewegt werden |
| 9 | * rand - dieser Wert wird als random immer beim bewegen zu time addiert |
| 10 | * walk_closure - die Closure die immer aufgerufen werden muss, wenn dieser |
| 11 | * Parameter weggelassen wird, wird statdessen die Funktion |
| 12 | * Walk() im NPC aufgerufen. |
| 13 | * |
| 14 | * Abgemeldet wird der NPC sobald die Closure bzw. die Walk-Funktion 0 |
| 15 | * returned. Bei allen Werten !=0 bleibt der NPC aktiv. |
| 16 | * |
| 17 | * Hinweis: Der NPC muss sich mind. alle 180 sek. bewegen (time+random), |
| 18 | * ansonsten kann dieser Master nicht verwendet werden. |
| 19 | */ |
| 20 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 21 | #pragma strong_types,rtt_checks |
| 22 | #pragma no_clone,no_inherit |
| 23 | |
Zesstra | 23019b9 | 2019-12-09 20:56:05 +0100 | [diff] [blame] | 24 | #include "/p/service/padreic/mnpc/mnpc.h" |
| 25 | |
| 26 | #define MAX_DELAY_HBS (MAX_MASTER_TIME/__HEART_BEAT_INTERVAL__) |
| 27 | #define DEFAULT_WALK_DELAY MAX_MASTER_TIME /* ist der billigste Wert :) */ |
| 28 | |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 29 | /* Ab welcher Rest-Tickmenge wird die Verarbeitung von Walkers unterbrochen */ |
| 30 | #define MAX_JOB_COST 200000 |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 31 | |
| 32 | // Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten |
| 33 | // im Walkerarray |
| 34 | #define TIME(t) (t & 0x00ff) /* 8 Bit = 256 */ |
| 35 | #define RANDOM(r) ((r & 0xff00) >> 8) /* 8 Bit = 256 */ |
| 36 | #define WERT(t, r) ((t & 0x00ff)+((r << 8) & 0xff00)) /* 16 Bit */ |
| 37 | |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 38 | // Indizes fuer clients |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 39 | #define WALK_CLOSURE 0 |
| 40 | #define WALK_DELAY 1 |
| 41 | #define WALK_TIMESLOT 2 |
Zesstra | 1d54d2a | 2019-06-24 22:25:12 +0200 | [diff] [blame] | 42 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 43 | nosave int counter; // Markiert aktuellen Zeitslot im Array walker |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 44 | nosave < < closure >* >* walker; |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame] | 45 | // Mapping mit allen registrierten MNPC (als Objekte) als Key und deren |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 46 | // walk_closure (1. Wert), Zeitdaten (2. wert) und dem aktuellen |
| 47 | // Zeitslot in <walker> (3. Wert). |
| 48 | nosave mapping clients = m_allocate(0,3); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 49 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 50 | public int Registration(); |
Zesstra | bc85487 | 2019-06-24 22:51:54 +0200 | [diff] [blame] | 51 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 52 | protected void create() |
| 53 | { |
Zesstra | 23019b9 | 2019-12-09 20:56:05 +0100 | [diff] [blame] | 54 | walker=map(allocate(MAX_DELAY_HBS+1), #'allocate); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 55 | } |
| 56 | |
| 57 | #define ERROR(x) raise_error(sprintf(x, previous_object())); |
| 58 | |
Zesstra | b9a22a6 | 2019-09-26 19:36:52 +0200 | [diff] [blame] | 59 | // Am besten nicht direkt sondern nur ueber einen Standardnpc benutzen. |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 60 | public varargs void RegisterWalker(int time, int rand, closure walk_closure) |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 61 | { |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 62 | // pruefen ob die Paramter zulaessig sind... |
| 63 | if (time<0) ERROR("negative time to RegisterWalker() from %O.\n"); |
| 64 | if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n"); |
Zesstra | 260613d | 2019-12-09 21:02:58 +0100 | [diff] [blame] | 65 | // das max. Delay darf max. MAX_MASTER_TIME-1 sein, sonst landet der MNPC im |
| 66 | // HB evtl. wieder in dem Slot, der gerade bearbeitet wird und der direkt |
| 67 | // danach komplett genullt wird... |
| 68 | if ((time+rand) >= (MAX_MASTER_TIME)) |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 69 | ERROR("Too long delaytime from %s to RegisterWalker().\n"); |
| 70 | |
Zesstra | e7956f3 | 2019-06-24 22:32:04 +0200 | [diff] [blame] | 71 | if (Registration()) |
Zesstra | bc85487 | 2019-06-24 22:51:54 +0200 | [diff] [blame] | 72 | raise_error(sprintf("Mehrfachanmeldung nicht erlaubt. Objekt: %O\n", |
| 73 | previous_object())); |
Zesstra | e7956f3 | 2019-06-24 22:32:04 +0200 | [diff] [blame] | 74 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 75 | int wert=WERT(time, rand); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 76 | if (!wert && !rand) wert=DEFAULT_WALK_DELAY; |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 77 | |
| 78 | closure func = walk_closure; |
| 79 | if (!closurep(func)) |
| 80 | { |
Bugfix | 8ecb43e | 2021-01-23 19:30:43 +0100 | [diff] [blame] | 81 | func=symbol_function("CheckWalk", previous_object()); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 82 | if (!func) |
| 83 | raise_error("RegisterWalker() call from Object without Walk() function.\n"); |
| 84 | } |
Zesstra | b9a22a6 | 2019-09-26 19:36:52 +0200 | [diff] [blame] | 85 | else |
| 86 | { |
| 87 | // Closures auf fremde lfuns fuehren zu Inkonsistenzen/Fehlers und man |
| 88 | // kann sie auch nicht wieder abmelden. Daher abfangen. |
| 89 | if (get_type_info(func, 2) != previous_object()) |
| 90 | raise_error(sprintf("Anmeldung von Closures auf fremde lfuns ist nicht " |
| 91 | "erlaubt. Closure: %O\n",func)); |
| 92 | } |
Zesstra | 8b66d69 | 2019-10-17 22:19:53 +0200 | [diff] [blame] | 93 | // Erster Client? -> HB einschalten. |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 94 | if (!sizeof(clients)) { |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 95 | set_heart_beat(1); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 96 | } |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 97 | int next=counter; |
Zesstra | 23019b9 | 2019-12-09 20:56:05 +0100 | [diff] [blame] | 98 | //min. 1 Heartbeat delay erzwingen, ab jetzt in Heartbeats |
| 99 | next += max(1, (time+random(rand))/__HEART_BEAT_INTERVAL__); |
| 100 | if (next>MAX_DELAY_HBS) next-=MAX_DELAY_HBS; |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 101 | walker[next]+=({ func }); |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 102 | clients += ([ get_type_info(func, 2): func; wert; next ]); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 103 | } |
| 104 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 105 | // Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist |
| 106 | // es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk() |
| 107 | // 0 zu returnen. |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 108 | public void RemoveWalker() |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 109 | { |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame] | 110 | if (!member(clients, previous_object())) |
| 111 | return; |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 112 | // Naechster Zeitslot und index in dem Slotarrays ermitteln |
| 113 | int next = clients[previous_object(), WALK_TIMESLOT]; |
| 114 | closure func = clients[previous_object(), WALK_CLOSURE]; |
| 115 | int idx = member(walker[next], func); |
| 116 | // Durch 0 ersetzen. Aber wir koennten gerade im heart_beat stecken... In |
| 117 | // dem Fall den Eintrag ersetzen durch was, was beim Abarbeiten ausgetragen |
| 118 | // wird. |
| 119 | if (next==counter) |
| 120 | walker[next][idx]=function () {return 0;}; |
| 121 | else |
| 122 | walker[next][idx]=0; |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame] | 123 | |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 124 | // 0-Eintraege entfernen, aber nur, wenn wir gerade nicht evtl. in dem HB |
| 125 | // stecken und den Slot abarbeiten. |
| 126 | if (next!=counter) |
| 127 | walker[next]-=({ 0 }); |
| 128 | // und noch die Stammdaten entfernen |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame] | 129 | m_delete(clients, previous_object()); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 130 | } |
| 131 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 132 | public int Registration() |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 133 | { |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame] | 134 | return member(clients, previous_object()); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | void heart_beat() |
| 138 | { |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 139 | int i = sizeof(walker[counter]); |
| 140 | if (i) |
| 141 | { |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 142 | for (i--;i>=0;i--) |
| 143 | { |
| 144 | if (get_eval_cost() < MAX_JOB_COST) |
| 145 | { |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 146 | // nicht abgefertigte NPCs im naechsten heart_beat ausfuehren |
| 147 | walker[counter]=walker[counter][0..i]; |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 148 | return; |
| 149 | } |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 150 | else |
| 151 | { |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 152 | closure func = walker[counter][i]; |
| 153 | if (func) |
Zesstra | 1d54d2a | 2019-06-24 22:25:12 +0200 | [diff] [blame] | 154 | { |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 155 | object mnpc = get_type_info(func, 2); |
| 156 | mixed res; |
| 157 | if (!catch(res=funcall(func);publish) |
| 158 | && intp(res) && res) |
| 159 | { |
| 160 | // Es gab keinen Fehler und das Objekt will weiterlaufen. |
| 161 | // Naechsten Zeitslot bestimmen und dort die closure eintragen. |
| 162 | int delay = clients[mnpc, WALK_DELAY]; |
Zesstra | 9ad5b11 | 2019-10-17 22:54:48 +0200 | [diff] [blame] | 163 | // das delay muss min. 1 sein, sonst tragen wir den MNPC in |
| 164 | // diesen aktuellen zeitslot wieder ein (ja, Magier tun solche |
| 165 | // Dinge), der ja nach Abarbeiten des Slots genullt wird. |
| 166 | int next = counter |
Zesstra | 23019b9 | 2019-12-09 20:56:05 +0100 | [diff] [blame] | 167 | + max(1, (TIME(delay) + random(RANDOM(delay))) |
| 168 | /__HEART_BEAT_INTERVAL__); |
| 169 | if (next > MAX_DELAY_HBS) |
| 170 | next -= MAX_DELAY_HBS; |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 171 | walker[next] += ({ func }); |
Zesstra | bc91fa7 | 2019-07-21 22:01:58 +0200 | [diff] [blame] | 172 | clients[mnpc, WALK_TIMESLOT] = next; |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 173 | } |
| 174 | else // Fehler oder Objekt will abschalten |
| 175 | m_delete(clients, mnpc); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 176 | } |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 177 | // else: Falls die closure nicht mehr existiert, existiert das Objekt |
| 178 | // nicht mehr, dann ist es ohnehin auch schon aus clients raus und es |
| 179 | // muss nix gemacht werden. |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 180 | } |
| 181 | } |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 182 | walker[counter]=({}); // fertiger Zeitslot, komplett leeren |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 183 | } |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 184 | // Wrap-around am Ende des Arrays. |
Zesstra | 23019b9 | 2019-12-09 20:56:05 +0100 | [diff] [blame] | 185 | if (counter == MAX_DELAY_HBS) |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 186 | counter=0; |
Zesstra | 457693b | 2019-07-21 21:47:02 +0200 | [diff] [blame] | 187 | else |
| 188 | counter++; |
| 189 | |
| 190 | // wenn keine Clients mehr uebrig, kann pausiert werden. Es kann sein, dass |
| 191 | // noch Dummy-Eintraege in walker enthalten sind, die stoeren nicht, bis |
| 192 | // wir das naechste Mal drueber laufen. |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 193 | if (!sizeof(clients)) { |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 194 | set_heart_beat(0); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 195 | } |
| 196 | } |
| 197 | |
Zesstra | 8b66d69 | 2019-10-17 22:19:53 +0200 | [diff] [blame] | 198 | // im reset zur Sicherheit mal den heart_beat ggf. einschalten. |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 199 | // dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat() |
Zesstra | 8b66d69 | 2019-10-17 22:19:53 +0200 | [diff] [blame] | 200 | // wenn doch nix gemacht werden musss, schaltet sich der HB eh wieder aus. |
| 201 | void reset() |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 202 | { |
Zesstra | 8b66d69 | 2019-10-17 22:19:53 +0200 | [diff] [blame] | 203 | // set_heart_beat() wird als query_heart_beat() 'missbraucht' und daher muss |
| 204 | // im else der HB in jedem Fall auch wieder eingeschaltet werden. |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 205 | if (set_heart_beat(0)<=0) |
| 206 | { |
| 207 | if (sizeof(clients) > 0) |
| 208 | { |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 209 | write_file(object_name()+".err", sprintf( |
| 210 | "%s: Fehler im heart_beat(). %d aktive Prozesse.\n", |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 211 | dtime(time()), sizeof(clients))); |
Zesstra | 8b66d69 | 2019-10-17 22:19:53 +0200 | [diff] [blame] | 212 | set_heart_beat(1); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 213 | } |
| 214 | } |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 215 | else |
| 216 | set_heart_beat(1); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 217 | } |
| 218 | |
Zesstra | 9002688 | 2019-07-19 18:46:28 +0200 | [diff] [blame] | 219 | // Bemerkung: damit kann jeder die Closures ermitteln und dann selber rufen. |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 220 | mixed *WalkerList() // nur fuer Debugzwecke |
Zesstra | 9d250de | 2019-07-19 18:59:54 +0200 | [diff] [blame] | 221 | { return ({ clients, walker, counter }); } |