blob: 3934736bf912af28974b40f6088997a290242967 [file] [log] [blame]
zesstra42fd8bf2016-06-27 22:04:14 +02001// (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
Zesstra90026882019-07-19 18:46:28 +020021#pragma strong_types,rtt_checks
22#pragma no_clone,no_inherit
23
zesstra42fd8bf2016-06-27 22:04:14 +020024#define MAX_DELAYTIME 90 /* max. delay ist 2*MAX_DELAYTIME */
25#define DEFAULT_WALK_DELAY 180 /* ist der billigste Wert :) */
Zesstra457693b2019-07-21 21:47:02 +020026/* Ab welcher Rest-Tickmenge wird die Verarbeitung von Walkers unterbrochen */
27#define MAX_JOB_COST 200000
zesstra42fd8bf2016-06-27 22:04:14 +020028
29// Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten
30// im Walkerarray
31#define TIME(t) (t & 0x00ff) /* 8 Bit = 256 */
32#define RANDOM(r) ((r & 0xff00) >> 8) /* 8 Bit = 256 */
33#define WERT(t, r) ((t & 0x00ff)+((r << 8) & 0xff00)) /* 16 Bit */
34
Zesstra457693b2019-07-21 21:47:02 +020035// Indizes fuer clients
36#define WALK_DELAY 1
37#define WALK_CLOSURE 0
Zesstra1d54d2a2019-06-24 22:25:12 +020038
Zesstra90026882019-07-19 18:46:28 +020039nosave int counter; // Markiert aktuellen Zeitslot im Array walker
Zesstra457693b2019-07-21 21:47:02 +020040nosave < < closure >* >* walker;
Zesstrad41e6712019-07-19 18:35:37 +020041// Mapping mit allen registrierten MNPC (als Objekte) als Key und deren
Zesstra457693b2019-07-21 21:47:02 +020042// Zeitdaten (2. wert) und deren walk_closure (1. Wert).
Zesstrad41e6712019-07-19 18:35:37 +020043nosave mapping clients = m_allocate(0,2);
zesstra42fd8bf2016-06-27 22:04:14 +020044
Zesstra90026882019-07-19 18:46:28 +020045public int Registration();
Zesstrabc854872019-06-24 22:51:54 +020046
zesstra42fd8bf2016-06-27 22:04:14 +020047protected void create()
48{
Zesstraeeddeeb2019-07-19 18:44:46 +020049 walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
zesstra42fd8bf2016-06-27 22:04:14 +020050 enable_commands(); // ohne das, kein heart_beat()
51}
52
53#define ERROR(x) raise_error(sprintf(x, previous_object()));
54
55// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
56// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
57// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
Zesstra90026882019-07-19 18:46:28 +020058// Bemerkung: man kann hiermit andere Objekt registrieren. Aber nur das Objekt
59// selber kann spaeter seine Registrierung pruefen oder sich abmelden...
60// (Fraglich, ob das so gewollt ist.)
61public varargs void RegisterWalker(int time, int rand, closure walk_closure)
zesstra42fd8bf2016-06-27 22:04:14 +020062{
zesstra42fd8bf2016-06-27 22:04:14 +020063 // pruefen ob die Paramter zulaessig sind...
64 if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
65 if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
66 if ((time+rand) > (2*MAX_DELAYTIME))
67 ERROR("Too long delaytime from %s to RegisterWalker().\n");
68
Zesstrae7956f32019-06-24 22:32:04 +020069 if (Registration())
Zesstrabc854872019-06-24 22:51:54 +020070 raise_error(sprintf("Mehrfachanmeldung nicht erlaubt. Objekt: %O\n",
71 previous_object()));
Zesstrae7956f32019-06-24 22:32:04 +020072
Zesstra90026882019-07-19 18:46:28 +020073 int wert=WERT(time, rand);
zesstra42fd8bf2016-06-27 22:04:14 +020074 if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
Zesstra90026882019-07-19 18:46:28 +020075
76 closure func = walk_closure;
77 if (!closurep(func))
78 {
zesstra42fd8bf2016-06-27 22:04:14 +020079 func=symbol_function("Walk", previous_object());
80 if (!func)
81 raise_error("RegisterWalker() call from Object without Walk() function.\n");
82 }
Zesstra9d250de2019-07-19 18:59:54 +020083 if (!sizeof(clients)) {
zesstra42fd8bf2016-06-27 22:04:14 +020084 set_heart_beat(1);
zesstra42fd8bf2016-06-27 22:04:14 +020085 }
Zesstra90026882019-07-19 18:46:28 +020086 int next=counter;
zesstra42fd8bf2016-06-27 22:04:14 +020087 next+=(time+random(rand))/2;
88 if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
Zesstra457693b2019-07-21 21:47:02 +020089 walker[next]+=({ func });
90 clients += ([ get_type_info(func, 2): func; wert ]);
zesstra42fd8bf2016-06-27 22:04:14 +020091}
92
zesstra42fd8bf2016-06-27 22:04:14 +020093// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
94// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk()
95// 0 zu returnen.
Zesstra90026882019-07-19 18:46:28 +020096public void RemoveWalker()
zesstra42fd8bf2016-06-27 22:04:14 +020097{
Zesstrad41e6712019-07-19 18:35:37 +020098 if (!member(clients, previous_object()))
99 return;
100
101 for (int i=MAX_DELAYTIME; i>=0; i--) {
102 for (int j=sizeof(walker[i])-1; j>=0; j--)
zesstra42fd8bf2016-06-27 22:04:14 +0200103 {
Zesstra457693b2019-07-21 21:47:02 +0200104 if (get_type_info(walker[i][j], 2)==previous_object())
zesstra42fd8bf2016-06-27 22:04:14 +0200105 {
Zesstra457693b2019-07-21 21:47:02 +0200106 // koennte gerade im heart_beat stecken... Eintrag ersetzen durch was,
107 // was beim Abarbeiten ausgetragen wird.
108 if (i==counter)
109 walker[i][j]=function () {return 0;};
zesstra42fd8bf2016-06-27 22:04:14 +0200110 else
111 walker[i][j]=0;
zesstra42fd8bf2016-06-27 22:04:14 +0200112 }
113 }
114 if (i!=counter) // koennte gerade im heart_beat stecken...
115 walker[i]-=({ 0 });
116 }
Zesstrad41e6712019-07-19 18:35:37 +0200117 m_delete(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200118}
119
Zesstra90026882019-07-19 18:46:28 +0200120public int Registration()
zesstra42fd8bf2016-06-27 22:04:14 +0200121{
Zesstrad41e6712019-07-19 18:35:37 +0200122 return member(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200123}
124
125void heart_beat()
126{
Zesstra9d250de2019-07-19 18:59:54 +0200127 int i = sizeof(walker[counter]);
128 if (i)
129 {
Zesstra9d250de2019-07-19 18:59:54 +0200130 for (i--;i>=0;i--)
131 {
132 if (get_eval_cost() < MAX_JOB_COST)
133 {
zesstra42fd8bf2016-06-27 22:04:14 +0200134 // nicht abgefertigte NPCs im naechsten heart_beat ausfuehren
135 walker[counter]=walker[counter][0..i];
zesstra42fd8bf2016-06-27 22:04:14 +0200136 return;
137 }
Zesstra9d250de2019-07-19 18:59:54 +0200138 else
139 {
Zesstra457693b2019-07-21 21:47:02 +0200140 closure func = walker[counter][i];
141 if (func)
Zesstra1d54d2a2019-06-24 22:25:12 +0200142 {
Zesstra457693b2019-07-21 21:47:02 +0200143 object mnpc = get_type_info(func, 2);
144 mixed res;
145 if (!catch(res=funcall(func);publish)
146 && intp(res) && res)
147 {
148 // Es gab keinen Fehler und das Objekt will weiterlaufen.
149 // Naechsten Zeitslot bestimmen und dort die closure eintragen.
150 int delay = clients[mnpc, WALK_DELAY];
151 int next = counter + (TIME(delay) + random(RANDOM(delay)))/2;
152 if (next > MAX_DELAYTIME)
153 next -= MAX_DELAYTIME;
154 walker[next] += ({ func });
155 }
156 else // Fehler oder Objekt will abschalten
157 m_delete(clients, mnpc);
zesstra42fd8bf2016-06-27 22:04:14 +0200158 }
Zesstra457693b2019-07-21 21:47:02 +0200159 // else: Falls die closure nicht mehr existiert, existiert das Objekt
160 // nicht mehr, dann ist es ohnehin auch schon aus clients raus und es
161 // muss nix gemacht werden.
zesstra42fd8bf2016-06-27 22:04:14 +0200162 }
163 }
Zesstra457693b2019-07-21 21:47:02 +0200164 walker[counter]=({}); // fertiger Zeitslot, komplett leeren
zesstra42fd8bf2016-06-27 22:04:14 +0200165 }
Zesstra457693b2019-07-21 21:47:02 +0200166 // Wrap-around am Ende des Arrays.
zesstra42fd8bf2016-06-27 22:04:14 +0200167 if (counter == MAX_DELAYTIME)
168 counter=0;
Zesstra457693b2019-07-21 21:47:02 +0200169 else
170 counter++;
171
172 // wenn keine Clients mehr uebrig, kann pausiert werden. Es kann sein, dass
173 // noch Dummy-Eintraege in walker enthalten sind, die stoeren nicht, bis
174 // wir das naechste Mal drueber laufen.
Zesstra9d250de2019-07-19 18:59:54 +0200175 if (!sizeof(clients)) {
zesstra42fd8bf2016-06-27 22:04:14 +0200176 set_heart_beat(0);
zesstra42fd8bf2016-06-27 22:04:14 +0200177 }
178}
179
180void reset()
181// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
182// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
183{
Zesstra9d250de2019-07-19 18:59:54 +0200184 if (set_heart_beat(0)<=0)
185 {
186 if (sizeof(clients) > 0)
187 {
zesstra42fd8bf2016-06-27 22:04:14 +0200188 write_file(object_name()+".err", sprintf(
189 "%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
Zesstra9d250de2019-07-19 18:59:54 +0200190 dtime(time()), sizeof(clients)));
zesstra42fd8bf2016-06-27 22:04:14 +0200191 enable_commands();
192 set_heart_beat(1);
193 }
194 }
Zesstra9d250de2019-07-19 18:59:54 +0200195 else
196 set_heart_beat(1);
zesstra42fd8bf2016-06-27 22:04:14 +0200197}
198
Zesstra90026882019-07-19 18:46:28 +0200199// Bemerkung: damit kann jeder die Closures ermitteln und dann selber rufen.
zesstra42fd8bf2016-06-27 22:04:14 +0200200mixed *WalkerList() // nur fuer Debugzwecke
Zesstra9d250de2019-07-19 18:59:54 +0200201{ return ({ clients, walker, counter }); }