blob: d289de12263d6f15acb2ff46a0432dd668a5291c [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// thing/properties.c -- most general class (property handling)
4//
5// $Id: properties.c 6951 2008-08-09 23:08:31Z Zesstra $
6
7// Properties.c -- Propertyverwaltung
8// (c) 1993 Hate@MorgenGrauen, Mateese@NightFall
9// Idea and Code Flames and Destructions
10// -> *grin* thats the point actually :)
11//
12// Ueberarbeitet von Jof am 12.06.1994
13// Ueberarbeitet von Mandragon am 11.05.2003
14
15#pragma strict_types
16#pragma save_types
17#pragma range_check
18#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020019
Zesstra830b2af2021-12-29 16:44:09 +010020#include <driver_info.h>
21
MG Mud User88f12472016-06-24 23:31:02 +020022#define NEED_PROTOTYPES
23
24#include "/sys/thing/properties.h"
25#include "/secure/wizlevels.h"
26
MG Mud User88f12472016-06-24 23:31:02 +020027// the mapping where the actual properties are stored. Direct initialization.
Zesstra375df1e2021-04-21 11:09:06 +020028// Indexed with F_VALUE, F_MODE, F_SET_METHOD and F_QUERY_METHOD
29// F_MODE, F_SET_METHOD and F_QUERY_METHOD are usually 'sparse' (i.e. there is
30// no entry for most properties), therefore it is more memory-efficient to
31// store them like this than in one mapping, although it requires more
32// mapping lookups.
MG Mud User88f12472016-06-24 23:31:02 +020033private nosave mapping *prop = ({ ([]), ([]), ([]), ([]) });
34
Zesstra375df1e2021-04-21 11:09:06 +020035// the mapping that is used for saving. During save_object/restore_object it
36// contains the properties with SAVE flag.
37// This is empty outside of a save_object() or restore_object() call!
MG Mud User88f12472016-06-24 23:31:02 +020038private mapping properties;
39
Zesstra830b2af2021-12-29 16:44:09 +010040// security-flag: wenn gesetzt und es gleich der aktuellen DI_EVAL_NUMBER ist,
41// ist dieser Ausfuehrungsthread in einer Auswertung von F_QUERY/SET_METHOD
42// (ggf. rekursiv) und es wird jeder Aufruf als extern gewertet bzgl.
43// PROTECTED/SECURED.
MG Mud User88f12472016-06-24 23:31:02 +020044private nosave int closure_call;
45
MG Mud User88f12472016-06-24 23:31:02 +020046
Zesstraf971f3a2021-04-13 11:48:37 +020047// Z.Zt. nur Abschalten des Resets noetig.
MG Mud User88f12472016-06-24 23:31:02 +020048protected void create() {
49 // Blueprints in /std benoetigenkeinen Reset ....
50 if (object_name()=="/std/thing/properties")
51 set_next_reset(-1);
52}
53
54protected void create_super() {
55 set_next_reset(-1);
56}
57
58// Welche externen Objekte duerfen zugreifen?
59nomask private int allowed()
60{
61 if ( (previous_object() && IS_ARCH(getuid(previous_object())) &&
62 this_interactive() && IS_ARCH(this_interactive())) ||
63 (previous_object() && getuid(previous_object()) == ROOTID &&
64 geteuid(previous_object()) == ROOTID) )
65 return 1;
66 return 0;
67}
68
69
70// Set() -- provides direct access to a property, no filters
Zesstra5c0c4092021-09-08 23:27:54 +020071// Type=F_VALUE und drop_priv=extern_call() by default
72public mixed Set(string name, mixed Value, int Type, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +020073{
MG Mud User88f12472016-06-24 23:31:02 +020074 if (!objectp(this_object()))
75 return 0;
76
77 // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
Zesstra5c0c4092021-09-08 23:27:54 +020078 // selber, EM+ oder ROOT veraendert werden.
Zesstra830b2af2021-12-29 16:44:09 +010079 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv' zu
80 // ersetzen, weil extern_call() das default-argument fuer <drop_priv> ist.
81 // Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des Aufrufers
82 // ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist, muessen wir
83 // dennoch selber pruefen. Wir glauben aber immer, wenn es 1 ist, der
84 // Aufrufer hat nichts davon und das eigene Objekt darf Privilegien abgeben.
85 drop_priv = drop_priv||extern_call()||closure_call==driver_info(DI_EVAL_NUMBER);
86 if ((prop[F_MODE][name]&(PROTECTED|SECURED))
87 && drop_priv && previous_object() != this_object() && !allowed())
MG Mud User88f12472016-06-24 23:31:02 +020088 return -1;
Zesstra5c0c4092021-09-08 23:27:54 +020089
MG Mud User88f12472016-06-24 23:31:02 +020090 // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
91 if ((prop[F_MODE][name]&SECURED)&&
92 (Type==F_MODE||Type==F_MODE_AD)&&(Value & SECURED))
93 return -2;
Zesstra5c0c4092021-09-08 23:27:54 +020094
MG Mud User88f12472016-06-24 23:31:02 +020095 // Setzen duerfen das SECURED-Flag nur das Objekt selber, EM+ oder ROOT
Zesstra830b2af2021-12-29 16:44:09 +010096 if ((Type==F_MODE||Type==F_MODE_AS) && (Value&SECURED)
97 && drop_priv && previous_object() != this_object() && !allowed() )
MG Mud User88f12472016-06-24 23:31:02 +020098 return -3;
99
100 switch(Type)
101 {
102 // Je nach Modus Flags veraendern
103 case F_MODE_AS: prop[F_MODE][name]|= Value;
104 return prop[F_MODE][name];
105 case F_MODE_AD: prop[F_MODE][name]&= ~Value;
106 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
107 return prop[F_MODE][name];
108 case F_MODE: prop[F_MODE][name]^= Value;
109 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
110 return prop[F_MODE][name];
Zesstraf54f03a2021-12-29 19:55:08 +0100111
112 // Query- und Setmethoden laufen ab hier durch.
MG Mud User88f12472016-06-24 23:31:02 +0200113 case F_SET_METHOD:
Zesstraf54f03a2021-12-29 19:55:08 +0100114 case F_SET_MAPPER:
115 // SETMAPPED passend setzen
116 if (Type==F_SET_METHOD)
117 prop[F_MODE][name] &= ~SETMAPPED;
118 else
119 prop[F_MODE][name] |= SETMAPPED;
120 // Ab hier alles gleich
MG Mud User88f12472016-06-24 23:31:02 +0200121 // -1 als Setz-Methode: Nosetmethod setzen
122 if (Value == -1)
123 {
124 prop[F_SET_METHOD]-=([name]);
125 prop[F_MODE][name] |= NOSETMETHOD;
126 return 0;
127 }
Zesstraf54f03a2021-12-29 19:55:08 +0100128 // Kein break! Rest wie Querymethod
MG Mud User88f12472016-06-24 23:31:02 +0200129 case F_QUERY_METHOD:
Zesstra746046c2018-11-04 11:44:44 +0100130 // Ungebundene Lambda_Closure? Heutzutage ein Fehler.
131 if (closurep(Value) && !query_closure_object(Value))
MG Mud User88f12472016-06-24 23:31:02 +0200132 {
Zesstra746046c2018-11-04 11:44:44 +0100133 raise_error("Ungebundene Lambdas sind als Querymethoden "
134 "nicht mehr unterstuetzt.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200135 }
136 // Kein break!
137 default:
138 if (!Value) prop[Type]-=([name]);
139 else prop[Type][name] = Value;
140 }
141
142 // Gesamtwert zurueckgeben
143 return prop[Type][name];
144}
145
146
147// Direktes Auslesen der Property ohne Filter ...
Zesstra5c0c4092021-09-08 23:27:54 +0200148// Type = F_VALUE by default
149public mixed Query( string name, int Type )
MG Mud User88f12472016-06-24 23:31:02 +0200150{
151 if (pointerp(prop)) return prop[Type][name];
152 return 0;
153}
154
155// Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen
Zesstraffcd0fe2021-09-08 23:33:17 +0200156public mixed SetProp( string name, mixed Value, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +0200157{
MG Mud User88f12472016-06-24 23:31:02 +0200158 mixed result;
Zesstra10412ed2019-11-23 15:54:09 +0100159
Zesstraf54f03a2021-12-29 19:55:08 +0100160 int mode = prop[F_MODE][name];
MG Mud User88f12472016-06-24 23:31:02 +0200161 // NOSETMETHOD: Darf nicht gesetzt werden
Zesstraf54f03a2021-12-29 19:55:08 +0100162 if (mode & NOSETMETHOD ) return -1;
MG Mud User88f12472016-06-24 23:31:02 +0200163
164 // Set-Method abfragen, so vorhanden
Zesstra10412ed2019-11-23 15:54:09 +0100165 // TODO: nachdem alle moeglichen Werte als Set-Methode illegal sind, auf
166 // closure aendern.
167 mixed func = prop[F_SET_METHOD][name];
168 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200169 {
MG Mud User88f12472016-06-24 23:31:02 +0200170 // Wert als Set-Method? gleich zurueckgeben
171 if (!closurep(func)) return func;
172
173 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
174 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
175 // Objekt existiert auch noch (sonst waere func == 0).
176
177 // closure_call setzen, falls noch nicht gesetzt
Zesstra830b2af2021-12-29 16:44:09 +0100178 int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
179 if (set_closure_call)
180 closure_call = driver_info(DI_EVAL_NUMBER);
MG Mud User88f12472016-06-24 23:31:02 +0200181
182 // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
183 if (catch(result=funcall(func, Value, name);publish))
184 {
185 prop[F_SET_METHOD]-=([name]);
186 }
Zesstraf54f03a2021-12-29 19:55:08 +0100187 else // Erfolgreicher call
188 {
189 if (mode & SETMAPPED) // SM hat noch nicht selber gesetzt
190 result = Set( name, result, F_VALUE, extern||extern_call() );
191 }
Zesstraffcd0fe2021-09-08 23:33:17 +0200192
Zesstra830b2af2021-12-29 16:44:09 +0100193 // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
194 // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
195 // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
196 if (set_closure_call) --closure_call;
MG Mud User88f12472016-06-24 23:31:02 +0200197
198 // Und zurueckgeben
Zesstraf54f03a2021-12-29 19:55:08 +0100199 return result;
MG Mud User88f12472016-06-24 23:31:02 +0200200 }
201
Zesstraffcd0fe2021-09-08 23:33:17 +0200202 // _set_*-Methode vorhanden? falls ja, aufrufen.
MG Mud User88f12472016-06-24 23:31:02 +0200203 if (call_resolved(&result,this_object(),"_set_"+name,Value ))
204 return result;
205
206 // Letzte Moeglichkeit: Muss eine 'normale' Property sein
Zesstraffcd0fe2021-09-08 23:33:17 +0200207 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv'
208 // zu ersetzen, weil extern_call() das default-argument fuer <drop_priv>
209 // ist. Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des
210 // Aufrufers ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist,
211 // muessen wir dennoch selber pruefen. Wir glauben aber immer, wenn es 1
212 // ist und der Aufrufer Privilegien abgeben will.
213 return Set( name, Value, F_VALUE, drop_priv||extern_call() );
MG Mud User88f12472016-06-24 23:31:02 +0200214}
215
216
217// Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen
218public mixed QueryProp( string name )
219{
MG Mud User88f12472016-06-24 23:31:02 +0200220 mixed result;
MG Mud User88f12472016-06-24 23:31:02 +0200221 // Query-Methode vorhanden?
Zesstra10412ed2019-11-23 15:54:09 +0100222 mixed func = prop[F_QUERY_METHOD][name];
223 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200224 {
MG Mud User88f12472016-06-24 23:31:02 +0200225 // Wert als Query-Method? Gleich zurueckgeben ...
226 if (!closurep(func)) return func;
227
228 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
229 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
230 // Objekt existiert auch noch (sonst waere func == 0).
231
232 // closure_call setzen, falls noch nicht gesetzt
Zesstra830b2af2021-12-29 16:44:09 +0100233 int set_closure_call = closure_call != driver_info(DI_EVAL_NUMBER);
234 if (set_closure_call)
235 closure_call = driver_info(DI_EVAL_NUMBER);
MG Mud User88f12472016-06-24 23:31:02 +0200236
237 // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
Zesstraa81eb292021-12-29 19:46:25 +0100238 if (catch(result=funcall(func, prop[F_VALUE][name]);publish))
MG Mud User88f12472016-06-24 23:31:02 +0200239 {
240 prop[F_QUERY_METHOD]-=([name]);
241 }
Zesstra830b2af2021-12-29 16:44:09 +0100242 // Wenn dieser Aufruf closure_call gesetzt hat, wieder loeschen.
243 // Da 0 ein gueltiger Wert ist, wird um eins dekrementiert, damit das Flag
244 // fuer diese Top-Level-Ausfuehrung wieder freigegeben.
245 if (set_closure_call) --closure_call;
MG Mud User88f12472016-06-24 23:31:02 +0200246
247 // Und zurueckgeben
248 return result;
249 }
250
251 // _query_*-Methode vorhanden? falls ja, aufrufen.
MG Mud User88f12472016-06-24 23:31:02 +0200252 if (call_resolved(&result,this_object(),"_query_"+name))
253 return result;
254
255 // Hilft alles nichts. Es ist eine 'normale' Property ...
256 return prop[F_VALUE][name];
257}
258
Zesstrafaebc152021-07-06 22:21:47 +0200259// Addiert einen Wert zu einer Prop. Eigentlich ist es nur ein Short-Cut fuer
260// QueryProp und += und SetProp. Dementsprechend gehen auch hier nur
261// Typ-Kombinationen, die += auch kann.
262public mixed AddToProp(string pname,
263 <int|float|string|mixed*|mapping|bytes> add_value)
264{
265 mixed value = QueryProp(pname);
266 string err = catch(value += add_value; nolog);
267 if (err)
268 raise_error(sprintf("Nicht unterstuetzter Typ des Summanden: %s\n",
269 err[strstr(err,":")+2..]));
270
271 return SetProp(pname, value, extern_call());
272}
273
274// "subtrahiert" einen Wert von einer Prop. Eigentlich ist es nur ein
275// Short-Cut fuer QueryProp und -= und SetProp. Dementsprechend gehen auch
276// hier nur Typ-Kombinationen, die -= auch kann.
277public mixed SubFromProp(string pname,
278 <int|float|string|mixed*|mapping|bytes> sub_value)
279{
280 mixed value = QueryProp(pname);
281 string err = catch(value -= sub_value; nolog);
282 if (err)
283 raise_error(sprintf("Nicht unterstuetzter Typ des Subtrahenden: %s\n",
284 err[strstr(err,":")+2..]));
285
286 return SetProp(pname, value, extern_call());
287}
MG Mud User88f12472016-06-24 23:31:02 +0200288
Zesstra375df1e2021-04-21 11:09:06 +0200289// Viele Properties auf einen Schlag setzen.
290// genutzt von simul_efun zum Setzen aller Properties, welche im
291// restore_object() eingelesen wurden.
292// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
MG Mud User88f12472016-06-24 23:31:02 +0200293public void SetProperties( mapping props )
294{
295 string *names;
296 int i, j, same_object;
297
298 // Kein Mapping? Schlecht ...
299 if(!mappingp(props)) return;
300
301 // Setzen wir selber?
Zesstra830b2af2021-12-29 16:44:09 +0100302 same_object = (closure_call!=driver_info(DI_EVAL_NUMBER) &&
MG Mud User88f12472016-06-24 23:31:02 +0200303 (!extern_call()||previous_object()==this_object()||
304 allowed()));
305 names = m_indices(props);
306
307 // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden:
308 // Alle SECURED-Flags aus props loeschen
309 if (!same_object)
310 {
311 j=sizeof(names);
312 while(j--) props[names[j], F_MODE] &= ~SECURED;
313 }
314
315 j=sizeof(names);
316 while(j--)
317 {
318 // Properties, die schon SECURED oder PROTECTED sind, duerfen
319 // nur vom Objekt selber manipuliert werden
320 if (same_object||!(prop[F_MODE][names[j]] & (PROTECTED|SECURED)) )
321 {
322 i=4;
323 while(i--)
324 {
325 if(props[names[j],i])
326 prop[i][names[j]] = props[names[j], i];
327 else
328 prop[i]-=([names[j]]);
329 }
330 }
331 }
332 return;
333}
334
335
336// Ein Mapping mit allen Properties zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200337// genutzt von simul_efun im save_object() zur Abfrage aller Properties, um
338// hieraus die gespeicherten zu bestimmen.
339// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
MG Mud User88f12472016-06-24 23:31:02 +0200340public mapping QueryProperties()
341{
342 mapping props;
343 int i, j;
344 string *names;
345
346 props = m_allocate( 0, 4 );
347
348 if (pointerp(prop))
349 {
350 i=4;
351 while(i--)
352 {
353 names = m_indices(prop[i]);
354 j=sizeof(names);
355 while(j--) props[names[j], i] = prop[i][names[j]];
356 }
357 }
358 return props;
359}
360
361// Die Properties als urspruengliches Array zurueckgeben
362public mixed *__query_properties()
363{
364 if ( pointerp(prop) )
365 return(deep_copy(prop));
366 else
367 return ({ ([]),([]),([]),([]) });
368}
369
370
371// mapping Properties setzen zum Speichern (per save_object())
Zesstra375df1e2021-04-21 11:09:06 +0200372// Aufruf nur aus simul_efun heraus (sinnvoll). Diese fragt alle Properties
373// via QueryProperties() ab, filtert alle nicht-gespeicherten Properties aus
374// und setzt ueber diese Funktion die gespeicherten in der tatsaechlich von
375// save_object() gespeicherten Variable <properties>.
MG Mud User88f12472016-06-24 23:31:02 +0200376public void _set_save_data(mixed data) { properties = data; }
377
378// mapping Properties zum Restoren zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200379// Aufruf nur aus simul_efun heraus (sinnvoll). Diese ruft nach dem
380// restore_object die restaurierten Properties hiermit ab und schreibt sie
381// nach Konditionierung via SetProperties() zurueck, damit die Daten (wieder)
382// in <prop> zur Verfuegung steheh.
MG Mud User88f12472016-06-24 23:31:02 +0200383public mixed _get_save_data() { return properties; }
384