blob: fb709132913948af69d3f0fda4aa4a7873dbce58 [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
20#define NEED_PROTOTYPES
21
22#include "/sys/thing/properties.h"
23#include "/secure/wizlevels.h"
24
25
26// the mapping where the actual properties are stored. Direct initialization.
Zesstra375df1e2021-04-21 11:09:06 +020027// Indexed with F_VALUE, F_MODE, F_SET_METHOD and F_QUERY_METHOD
28// F_MODE, F_SET_METHOD and F_QUERY_METHOD are usually 'sparse' (i.e. there is
29// no entry for most properties), therefore it is more memory-efficient to
30// store them like this than in one mapping, although it requires more
31// mapping lookups.
MG Mud User88f12472016-06-24 23:31:02 +020032private nosave mapping *prop = ({ ([]), ([]), ([]), ([]) });
33
Zesstra375df1e2021-04-21 11:09:06 +020034// the mapping that is used for saving. During save_object/restore_object it
35// contains the properties with SAVE flag.
36// This is empty outside of a save_object() or restore_object() call!
MG Mud User88f12472016-06-24 23:31:02 +020037private mapping properties;
38
39// security-flag
40private nosave int closure_call;
41
MG Mud User88f12472016-06-24 23:31:02 +020042
Zesstraf971f3a2021-04-13 11:48:37 +020043// Z.Zt. nur Abschalten des Resets noetig.
MG Mud User88f12472016-06-24 23:31:02 +020044protected void create() {
45 // Blueprints in /std benoetigenkeinen Reset ....
46 if (object_name()=="/std/thing/properties")
47 set_next_reset(-1);
48}
49
50protected void create_super() {
51 set_next_reset(-1);
52}
53
54// Welche externen Objekte duerfen zugreifen?
55nomask private int allowed()
56{
57 if ( (previous_object() && IS_ARCH(getuid(previous_object())) &&
58 this_interactive() && IS_ARCH(this_interactive())) ||
59 (previous_object() && getuid(previous_object()) == ROOTID &&
60 geteuid(previous_object()) == ROOTID) )
61 return 1;
62 return 0;
63}
64
65
66// Set() -- provides direct access to a property, no filters
Zesstra5c0c4092021-09-08 23:27:54 +020067// Type=F_VALUE und drop_priv=extern_call() by default
68public mixed Set(string name, mixed Value, int Type, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +020069{
MG Mud User88f12472016-06-24 23:31:02 +020070 if (!objectp(this_object()))
71 return 0;
72
73 // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
Zesstra5c0c4092021-09-08 23:27:54 +020074 // selber, EM+ oder ROOT veraendert werden.
75 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv'
76 // zu ersetzen, weil extern_call() das default-argument fuer <drop_priv>
77 // ist. Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des
78 // Aufrufers ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist,
79 // muessen wir dennoch selber pruefen. Wir glauben aber immer, wenn es 1
80 // ist und der Aufrufer Privilegien abgeben will.
MG Mud User88f12472016-06-24 23:31:02 +020081 if ((prop[F_MODE][name]&(PROTECTED|SECURED))&&
Zesstra5c0c4092021-09-08 23:27:54 +020082 (closure_call||drop_priv||extern_call()) &&
MG Mud User88f12472016-06-24 23:31:02 +020083 previous_object() != this_object() && !allowed())
84 return -1;
Zesstra5c0c4092021-09-08 23:27:54 +020085
MG Mud User88f12472016-06-24 23:31:02 +020086 // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
87 if ((prop[F_MODE][name]&SECURED)&&
88 (Type==F_MODE||Type==F_MODE_AD)&&(Value & SECURED))
89 return -2;
Zesstra5c0c4092021-09-08 23:27:54 +020090
MG Mud User88f12472016-06-24 23:31:02 +020091 // Setzen duerfen das SECURED-Flag nur das Objekt selber, EM+ oder ROOT
Zesstra5c0c4092021-09-08 23:27:54 +020092 // 'drop_priv||extern_call()' ist Absicht, s.o.
MG Mud User88f12472016-06-24 23:31:02 +020093 if ((Type==F_MODE||Type==F_MODE_AS)&&(Value&SECURED)&&
Zesstra5c0c4092021-09-08 23:27:54 +020094 (closure_call || drop_priv || extern_call()) &&
MG Mud User88f12472016-06-24 23:31:02 +020095 previous_object() != this_object() && !allowed() )
96 return -3;
97
98 switch(Type)
99 {
100 // Je nach Modus Flags veraendern
101 case F_MODE_AS: prop[F_MODE][name]|= Value;
102 return prop[F_MODE][name];
103 case F_MODE_AD: prop[F_MODE][name]&= ~Value;
104 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
105 return prop[F_MODE][name];
106 case F_MODE: prop[F_MODE][name]^= Value;
107 if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
108 return prop[F_MODE][name];
109 case F_SET_METHOD:
110 // -1 als Setz-Methode: Nosetmethod setzen
111 if (Value == -1)
112 {
113 prop[F_SET_METHOD]-=([name]);
114 prop[F_MODE][name] |= NOSETMETHOD;
115 return 0;
116 }
117 // Kein break!
118 case F_QUERY_METHOD:
Zesstra746046c2018-11-04 11:44:44 +0100119 // Ungebundene Lambda_Closure? Heutzutage ein Fehler.
120 if (closurep(Value) && !query_closure_object(Value))
MG Mud User88f12472016-06-24 23:31:02 +0200121 {
Zesstra746046c2018-11-04 11:44:44 +0100122 raise_error("Ungebundene Lambdas sind als Querymethoden "
123 "nicht mehr unterstuetzt.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200124 }
125 // Kein break!
126 default:
127 if (!Value) prop[Type]-=([name]);
128 else prop[Type][name] = Value;
129 }
130
131 // Gesamtwert zurueckgeben
132 return prop[Type][name];
133}
134
135
136// Direktes Auslesen der Property ohne Filter ...
Zesstra5c0c4092021-09-08 23:27:54 +0200137// Type = F_VALUE by default
138public mixed Query( string name, int Type )
MG Mud User88f12472016-06-24 23:31:02 +0200139{
140 if (pointerp(prop)) return prop[Type][name];
141 return 0;
142}
143
144// Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen
Zesstraffcd0fe2021-09-08 23:33:17 +0200145public mixed SetProp( string name, mixed Value, int drop_priv)
MG Mud User88f12472016-06-24 23:31:02 +0200146{
MG Mud User88f12472016-06-24 23:31:02 +0200147 mixed result;
Zesstra10412ed2019-11-23 15:54:09 +0100148
MG Mud User88f12472016-06-24 23:31:02 +0200149 // NOSETMETHOD: Darf nicht gesetzt werden
150 if (prop[F_MODE][name] & NOSETMETHOD ) return -1;
151
152 // Set-Method abfragen, so vorhanden
Zesstra10412ed2019-11-23 15:54:09 +0100153 // TODO: nachdem alle moeglichen Werte als Set-Methode illegal sind, auf
154 // closure aendern.
155 mixed func = prop[F_SET_METHOD][name];
156 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200157 {
158 int flag;
159
160 // Wert als Set-Method? gleich zurueckgeben
161 if (!closurep(func)) return func;
162
163 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
164 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
165 // Objekt existiert auch noch (sonst waere func == 0).
166
167 // closure_call setzen, falls noch nicht gesetzt
168 if ((flag=closure_call<time()))
169 closure_call = time()+59;
170
171 // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
172 if (catch(result=funcall(func, Value, name);publish))
173 {
174 prop[F_SET_METHOD]-=([name]);
175 }
Zesstraffcd0fe2021-09-08 23:33:17 +0200176
MG Mud User88f12472016-06-24 23:31:02 +0200177 // Wenn closure_call gesetzt wurde, wieder loeschen
178 if (flag) closure_call = 0;
179
180 // Und zurueckgeben
181 return result;
182 }
183
Zesstraffcd0fe2021-09-08 23:33:17 +0200184 // _set_*-Methode vorhanden? falls ja, aufrufen.
MG Mud User88f12472016-06-24 23:31:02 +0200185 if (call_resolved(&result,this_object(),"_set_"+name,Value ))
186 return result;
187
188 // Letzte Moeglichkeit: Muss eine 'normale' Property sein
Zesstraffcd0fe2021-09-08 23:33:17 +0200189 // Es ist verfuehrerisch, das 'drop_priv||extern_call()' durch 'drop_priv'
190 // zu ersetzen, weil extern_call() das default-argument fuer <drop_priv>
191 // ist. Das ist keine gute Idee, weil <drop_priv> unter der Kontrolle des
192 // Aufrufers ist und dieser 0 uebergeben kann. Sprich: wenn es 0 ist,
193 // muessen wir dennoch selber pruefen. Wir glauben aber immer, wenn es 1
194 // ist und der Aufrufer Privilegien abgeben will.
195 return Set( name, Value, F_VALUE, drop_priv||extern_call() );
MG Mud User88f12472016-06-24 23:31:02 +0200196}
197
198
199// Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen
200public mixed QueryProp( string name )
201{
MG Mud User88f12472016-06-24 23:31:02 +0200202 mixed result;
MG Mud User88f12472016-06-24 23:31:02 +0200203 // Query-Methode vorhanden?
Zesstra10412ed2019-11-23 15:54:09 +0100204 mixed func = prop[F_QUERY_METHOD][name];
205 if (func)
MG Mud User88f12472016-06-24 23:31:02 +0200206 {
207 int flag;
208
209 // Wert als Query-Method? Gleich zurueckgeben ...
210 if (!closurep(func)) return func;
211
212 // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
213 // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
214 // Objekt existiert auch noch (sonst waere func == 0).
215
216 // closure_call setzen, falls noch nicht gesetzt
217 if ((flag=closure_call<time()))
218 closure_call = time()+59;
219
220 // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
221 if (catch(result=funcall(func);publish))
222 {
223 prop[F_QUERY_METHOD]-=([name]);
224 }
225 // Wenn closure_call gesetzt wurde, wieder loeschen
226 if (flag) closure_call = 0;
227
228 // Und zurueckgeben
229 return result;
230 }
231
232 // _query_*-Methode vorhanden? falls ja, aufrufen.
MG Mud User88f12472016-06-24 23:31:02 +0200233 if (call_resolved(&result,this_object(),"_query_"+name))
234 return result;
235
236 // Hilft alles nichts. Es ist eine 'normale' Property ...
237 return prop[F_VALUE][name];
238}
239
240
Zesstra375df1e2021-04-21 11:09:06 +0200241// Viele Properties auf einen Schlag setzen.
242// genutzt von simul_efun zum Setzen aller Properties, welche im
243// restore_object() eingelesen wurden.
244// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
MG Mud User88f12472016-06-24 23:31:02 +0200245public void SetProperties( mapping props )
246{
247 string *names;
248 int i, j, same_object;
249
250 // Kein Mapping? Schlecht ...
251 if(!mappingp(props)) return;
252
253 // Setzen wir selber?
254 same_object = (!closure_call &&
255 (!extern_call()||previous_object()==this_object()||
256 allowed()));
257 names = m_indices(props);
258
259 // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden:
260 // Alle SECURED-Flags aus props loeschen
261 if (!same_object)
262 {
263 j=sizeof(names);
264 while(j--) props[names[j], F_MODE] &= ~SECURED;
265 }
266
267 j=sizeof(names);
268 while(j--)
269 {
270 // Properties, die schon SECURED oder PROTECTED sind, duerfen
271 // nur vom Objekt selber manipuliert werden
272 if (same_object||!(prop[F_MODE][names[j]] & (PROTECTED|SECURED)) )
273 {
274 i=4;
275 while(i--)
276 {
277 if(props[names[j],i])
278 prop[i][names[j]] = props[names[j], i];
279 else
280 prop[i]-=([names[j]]);
281 }
282 }
283 }
284 return;
285}
286
287
288// Ein Mapping mit allen Properties zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200289// genutzt von simul_efun im save_object() zur Abfrage aller Properties, um
290// hieraus die gespeicherten zu bestimmen.
291// Andere objekt-externe Nutzung ausdruecklich **NICHT** supportet!
MG Mud User88f12472016-06-24 23:31:02 +0200292public mapping QueryProperties()
293{
294 mapping props;
295 int i, j;
296 string *names;
297
298 props = m_allocate( 0, 4 );
299
300 if (pointerp(prop))
301 {
302 i=4;
303 while(i--)
304 {
305 names = m_indices(prop[i]);
306 j=sizeof(names);
307 while(j--) props[names[j], i] = prop[i][names[j]];
308 }
309 }
310 return props;
311}
312
313// Die Properties als urspruengliches Array zurueckgeben
314public mixed *__query_properties()
315{
316 if ( pointerp(prop) )
317 return(deep_copy(prop));
318 else
319 return ({ ([]),([]),([]),([]) });
320}
321
322
323// mapping Properties setzen zum Speichern (per save_object())
Zesstra375df1e2021-04-21 11:09:06 +0200324// Aufruf nur aus simul_efun heraus (sinnvoll). Diese fragt alle Properties
325// via QueryProperties() ab, filtert alle nicht-gespeicherten Properties aus
326// und setzt ueber diese Funktion die gespeicherten in der tatsaechlich von
327// save_object() gespeicherten Variable <properties>.
MG Mud User88f12472016-06-24 23:31:02 +0200328public void _set_save_data(mixed data) { properties = data; }
329
330// mapping Properties zum Restoren zurueckgeben
Zesstra375df1e2021-04-21 11:09:06 +0200331// Aufruf nur aus simul_efun heraus (sinnvoll). Diese ruft nach dem
332// restore_object die restaurierten Properties hiermit ab und schreibt sie
333// nach Konditionierung via SetProperties() zurueck, damit die Daten (wieder)
334// in <prop> zur Verfuegung steheh.
MG Mud User88f12472016-06-24 23:31:02 +0200335public mixed _get_save_data() { return properties; }
336