Phantasmal MUD Lib for DGD
|
Phantasmal Site > DGD > DGD LPC Reference > Mappings Mappings in DGDDate: Thu, 22 Jul 1999 16:05:28 +0200 (CEST) From: "Felix A. Croes" Subject: Re: [DGD]Global/Local Variables On Thu, 22 Jul 1999, Neil McBride wrote: >[...] > Now, my problem is that this variable with the same name is behaving as a global > variable under certain situations. Ie, I enter function 1 and the variable x is a > mapping with two keys. I run function 2, passing the x as the argument and modify > the data in function 2. Function 2 then finishes and function 1 continues on. > However, the value's within the mapping have now changed according to anything that > was done to function 2's x variable. > > Now, as I understand how it's supposed to work, this should _not_ happen unless I'm > passing pointers around, which I'm clearly not (can you even do that under DGD??) > Is this the expected behaviour or is there something amiss here? Not so. Both mappings and arrays are shared when passed as function arguments. If you want to pass a copy, and the mapping is just one level deep, the following will work: foo( map[..] ); Regards, Dworkin Date: Wed, 25 Aug 1999 14:23:32 +0200 (CEST) From: "E. Harte" Subject: Re: [DGD]A complicated mapping. On Tue, 24 Aug 1999, Seer Asmodean wrote: [...] > mapping table_s ; > > table_s = ([ "Title1": ([ "Subtitle1": ({ 1, > "Short bit of text.\n", > "A much longer bit of text.\n" }), > "Subtitle2": ({ 2, > "Short bit of text.\n", > "A much longer bit of text.\n" }) > ]), > "Title2": ([ "Subtitle1": ({ 1, > "Short bit of text.\n", > "A much longer bit of text.\n" }) > ]) > ]); > > (That IS a mapping mapped to another mapping mapped to an array.) [...] > I'm having several problems with this: > 1) Where do I declare it, and where do I assign up the values? It's > intended to be a constant. If you want to have one unique mapping like this in the whole 'mud', you'll want to put it in an object of its own, and query the data from that one object. If you put it in a .h file and and #include that .h file in various objects, you'll be creating different instances of the same data, there will be no sharing. > I've tried putting it in a separate .h file as above (but on one long > line for the compiler) and including it into player.c. This generates a > 'Syntax Error' when I run the mud. Probably because you made a typo in the data, I've corrected two or three in the quoted text above, one '([' was deleted and a '.' replaced with a ',', compare and see. :) > I've tried turning it into a #define, the mud will run but any line of code > added to the lib to access it will cause the same unhelpful error message. Same reason, probably. :) > 2) Assuming I'm not making an impossible request and it can be made to work, > how do I access the various elements? > My current asumption would be something like > table_s["Title1"]["Subtitle2"](0) to return 2, or > table_s["Title1"]["Subtitle2"](1) for "Short bit of text.\n". You'll want to use table_s["Title1"]["Subtitle2"][0] and [1], respectively. > 3) Is it possible to lay such code out like above, i.e split across several > lines in a nice coder-readable table? When coding it I put the whole thing on > one line, not knowing how to tell the compiler that it was all one line. Sure, if you're using a #define then you can use backslashes to continue on the next line, like this: table_s = ([ "Title1": ([ "Subtitle1": ({ 1, \ "Short bit of text.\n", \ "A much longer bit of text.\n" }), \ "Subtitle2": ({ 2, \ "Short bit of text.\n", \ "A much longer bit of text.\n" }) \ ]), \ "Title2": ([ "Subtitle1": ({ 1, \ "Short bit of text.\n", \ "A much longer bit of text.\n" }) \ ]) \ ]); \ Hope that helps. :) Erwin. Date: Fri, 15 Dec 2000 00:15:51 +0100 From: Erwin Harte Subject: Re: [DGD]removing elements from mappings On Thu, Dec 14, 2000 at 11:35:25PM +0100, Boris J wrote: > Hello ! > > I wondering what is the proper way of removing elements (indices/values) > from a mapping in DGD ? Several ways. > Shouldn't this work: > > foo_mapping -= bar_indices; > > which would remove 'bar_indices' indice and it's value from the > 'foo_mapping' mapping ? Yes: mapping m; m = ([ 1: 2 ]); m = m - ({ 1 }); m is now ([ ]) This does the same thing: m -= ({ 1 }); > I discovered also that setting value of an indice to 'nil', e.g. > > foo_mapping[bar_indices] = nil; > > makes the 'bar_indices' entry to disappear from the mapping. Does this mean > that it's impossible to store nil values in DGD mappings, or is it a > configurable option ? ( Like changing 'typechecking' in driver config file > or something... ) Assigning a nil value to an index is another way of removing an index:value pair from a mapping. Correct. If you want to use nil anyway, you could for instance use 1-sized arrays instead of the actual values. The only effect the typechecking option has is that for values 0 and 1 you can accomplish the same thing using 0 instead of nil (more to the point, nil _is_ 0 in those situations). Hope that helps, Erwin. -- Erwin Harte : `Don't mind Erwin, he gets crabby. :)' : -- Par Winzell From: dgd at list.imaginary.com (Erwin Harte) Date: Wed Oct 10 10:03:01 2001 Subject: [DGD] mapping question On Wed, Oct 10, 2001 at 04:44:39PM +0200, Pete wrote: > Is there a way how to access mapping elements by sequential > index? Something other, faster, then using map_indices() and then > iterating them and use them as index for mapping. Doesn't get any faster than this: int i, sz; mixed *ind, *val; sz = map_sizeof(map); ind = map_indices(map); val = map_values(map); for (i = 0; i < sz; i++) { /* Do whatever you want with the ind[i] and val[i] pair. */ } Hope that helps, Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Erwin Harte) Date: Wed Feb 6 09:06:01 2002 Subject: [DGD] [Melville] non-static mappings On Wed, Feb 06, 2002 at 08:40:30AM -0600, Erwin Harte wrote: > On Wed, Feb 06, 2002 at 12:15:05PM +0000, Shevek wrote: > > > > >I do not get your point. In DGD, setting map[key] = nil will remove > > >the key-value pair from the mapping. This is just as safe as map -= > > >({ key }). > > > > > >// Mikael / Elemel > > > > Are you sure about that? > > I am, my code would fall apart if DGD didn't behave like that. > [...blah...] Ok, that was totally unrelated to what you asked, but let me assure you that 'map[key] = nil' has the same effect of 'map -= ({ key })'. Please, don't let me post before having had my coffee, next time? Erwin. ;-) -- Erwin Harte From: dgd at list.imaginary.com (Erwin Harte) Date: Wed Feb 6 11:41:00 2002 Subject: [DGD] [Melville] non-static mappings On Wed, Feb 06, 2002 at 04:57:59PM +0000, Shevek wrote: [...] > > testmap=([ ]); > testarr=({"test1","test2","test3","test4","test5","test6","test7","test8","test9",}); > testkey=({"key1","key2","key3"}); > > /* Populate mapping */ > for(i=0;i<sizeof(testkey);i++){ > testmap[testkey[i]]=testarr[i*3..i*3+2]; > } > [...] > > /* testmap-=({testkey[1]});*/ > testmap[testkey[1]]=nil; > [...] > > And from the save file: > testmap ([2|"key1":({3|"test1","test2","test3",}),"key3":({3|"test7","test8","test9",}),]) > > Both methods produce exactly the same results. > Ie Both remove all values associated under the key and the key from the > mapping. Neither produced anything unexpected in the save file. :-) > Now if I could just figure out why I thought differently :> Dunno, but I think you would benefit greatly from writing some sort of 'string dump_value(mixed m)' type function, would save you a few dozen lines in this test alone. Also, you do know you can easily populate that mapping by just doing: testmap = ([ "key1": ({ "test1", "test2", "test3" }), "key2": ({ "test4", "test5", "test6" }), "key3": ({ "test7", "test8", "test9" }) ]); Right? That at least would make the program easier to understand, for me. Your mileage, etc. Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Erwin Harte) Date: Wed Feb 6 17:38:01 2002 Subject: [DGD] [Melville] non-static mappings On Thu, Feb 07, 2002 at 12:07:43AM +0100, Mikael Lind wrote: [...] > > map = ([ ob1: 1, > > 2: ob2 ]); > > > > Now, if either ob1 or ob2 gets destroyed, the relevant index:value > > pair in the mapping will disappear. > > Is this really true for ob1, though? I would have thought that when > ob1 is destructed, it would be replaced by nil, which is a valid > mapping index. So, with both ob1 and ob2 destructed, map would be > ([ nil: 1 ]). It's true for ob1 as well. You could use this feature to easily keep track of the online players by having a map from <connection-object> to '1' and only update it when someone (re)enters the game. If someone logs out or loses his/her/its link the mapping will automatically be cleaned out, so less effort there. Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Par Winzell) Date: Tue Mar 19 16:44:01 2002 Subject: [DGD] Memory management >> To expand on this: for persistent objects (which is all you'll see in >> the kernel library) the references to the object are of no >> consequence; when the object is destructed, any existing reference to >> the object will evaluate to 'nil'. > > > Does this include references in mappings and arrays? The kernel userd > object uses a logout method to specifically remove a user from the array > and mapping it maintains. Seems as if this wouldn't be needed, since > the user object itself is destructed soon after. It is not required for the mapping -- destruct objects in a mapping are removed from it -- but if you did not remove the user object from the array, the array would contain an explicit nil. That's tolerable from time to time, of course; any access to this array could just make sure to wipe 'nil' values from it. However, in this case, the logout is the more well-structured approach, eh? Zell From: dgd at list.imaginary.com (Erwin Harte) Date: Tue Mar 19 16:55:01 2002 Subject: [DGD] Memory management On Tue, Mar 19, 2002 at 05:07:45PM -0500, Jay Shaffstall wrote: > > >To expand on this: for persistent objects (which is all you'll see in the > >kernel library) the references to the object are of no consequence; when > >the object is destructed, any existing reference to the object will > >evaluate to 'nil'. > > Does this include references in mappings and arrays? The kernel userd > object uses a logout method to specifically remove a user from the array > and mapping it maintains. Seems as if this wouldn't be needed, since the > user object itself is destructed soon after. In arrays they turn to nil as well. Mappings are special, in the sense that if an entry in the mapping (index or value or both) refer to a destructed object, the entry is cleaned out before you see it in LPC space (i.e: before DGD returns the result of one of the map_* kfuns or an index into a mapping. So if you had: mapping m; m = ([ <someobject>: 1 ]); And destruct <someobject>, then m will be ([ ]) the next time you look at it. Same for this: mapping m; m = ([ 100: <someobject> ]); Hope that helps, Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Erwin Harte) Date: Thu Jan 8 07:35:01 2004 Subject: [DGD] Re: another question about clones On Thu, Jan 08, 2004 at 12:29:54PM +0100, Bart van Leeuwen wrote: > On Wed, 7 Jan 2004, Par Winzell wrote: [...] > > My suggestion is that you let the master copy of the object keep track > > of all its clones, possibly using a mapping of mappings. > > This is workable and is what I use right now. > Actually, each 'master' only has to have an array of all its clones, you > can track all the masters with some kind of daemon still, the same daemon > can be used to track inheritance of course. Beware of what will happen when you have more clones of one particular master object than fits in status()[OST_ARRAYSIZE], which is why Par mentioned the mapping of mappings. :) Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Par Winzell) Date: Thu Jan 8 09:54:01 2004 Subject: [DGD] Re: another question about clones On Thu, 2004-01-08 at 09:04, Bart van Leeuwen wrote: > The choice of mapping or array is not very relevant for this unless you > are going to use the mapping for other things then tracking clones (one > could think of using it for keeping track of how a clone was created for > example). Somehow I assumed an array to be cheaper if all I want is a > collection of clones (or alternatively a collection of collections of > clones if it no longer fits in a single array) If you mean that you use array addition and subtraction whenever you create a new clone or destruct an old one, then that's not really an option. Both cloning and destructing N objects then becomes pretty much a O(N^2) operation. You really want to stay away from those. Alternately, perhaps you have an array of arrays that you index by the 'clone number' of the clone? That'd solve the time complexity problem, but use a hell of a lot of space. Zell From: dgd at list.imaginary.com (Bart van Leeuwen) Date: Thu Jan 8 10:46:01 2004 Subject: [DGD] Re: another question about clones On Thu, 8 Jan 2004, Par Winzell wrote: > On Thu, 2004-01-08 at 09:04, Bart van Leeuwen wrote: > > The choice of mapping or array is not very relevant for this unless you > > are going to use the mapping for other things then tracking clones (one > > could think of using it for keeping track of how a clone was created for > > example). Somehow I assumed an array to be cheaper if all I want is a > > collection of clones (or alternatively a collection of collections of > > clones if it no longer fits in a single array) > > If you mean that you use array addition and subtraction whenever you > create a new clone or destruct an old one, then that's not really an > option. Both cloning and destructing N objects then becomes pretty much > a O(N^2) operation. You really want to stay away from those. Not really. When an object is destroyed, its value in the array becomes 0, subtracting 0 from an array before use is still far from O(N^2) and isn't done at destruction time at all. As a result, the code actually has an O(1) behavior on destructing objects (for as far as the lpc code is concerned... cost of the different opperations on driver level is a different matter) The only problem is with adding clones, which you can solve in a few ways, but will come down to finding an array with < MAX elements. You can assume this to be the array with the highest number and if you start there and look back before creating a new array, you will get a result that is in most cases no more then setting an index to a value, and as such hardly more expensive then when you'd be using a mapping. (note that you do not have to find the holes in an array, there are none) If you'd want, you can delay locating such arrays untill they are completely empty, and just remove 0 size arrays only, but that may not work out very well in some specific situations (you may end up with a lot of arrays with 1-2 elements each for example so some sort of merging is needed to make this one work, also it makes things more expensive elsewhere) In my experience, in the end the most important performance issue is however the time it takes to create a list of all clones and the complexity of that is way lower with an array of arrays. All other things can be spread out between when you register/remove a clone and when you use the data, and in the end it is nice to save a few cycles, but nicer to spend a few more, but never need too many of them at once. With mappings you make a few different choices obviously, and I assume that you wouldn't go for the most expensive approach there either, the 'shortcuts' are just different. > > Alternately, perhaps you have an array of arrays that you index by the > 'clone number' of the clone? That'd solve the time complexity problem, > but use a hell of a lot of space. > You'd be better off implementing that with mappings, and seeing how clone numbers can rise above the size of an array, it means first doing a bit of math to derive the indeces Anyway, regardless of if its more or less efficient, it has been used for many years and seems to do its job very well. From: dgd at list.imaginary.com (Par Winzell) Date: Thu Jan 8 11:02:01 2004 Subject: [DGD] Re: another question about clones Bart, >>If you mean that you use array addition and subtraction whenever you >>create a new clone or destruct an old one, then that's not really an >>option. Both cloning and destructing N objects then becomes pretty much >>a O(N^2) operation. You really want to stay away from those. > > Not really. When an object is destroyed, its value in the array becomes 0, > subtracting 0 from an array before use is still far from O(N^2) and isn't > done at destruction time at all. As a result, the code actually has an > O(1) behavior on destructing objects (for as far as the lpc code is > concerned... cost of the different opperations on driver level is a > different matter) I guess this works if you can rely on destruction to remove the object (or, rather, to nil it). Skotos uses this kind of indexing system for other lists where a clone may need to leave one chain and enter another without the convenience of destruction. In that case, you do need to search through O(N) elements to remove an object, and while this cost is at the driver level, yes, complexity considerations remain similar. > Anyway, regardless of if its more or less efficient, it has been used for > many years and seems to do its job very well. If your solution works, great. Marrach has something like 200k clones of /base/obj/thing and I suspect the array solution could result in a pretty significant performance problem for us. Whatever. :) It wasn't meant as an attack. Zell From: dgd at list.imaginary.com (Noah Gibbs) Date: Sun Jan 25 03:13:01 2004 Subject: [DGD] copy() or lack thereof in Melville --- Michael McKiel wrote: > there is a function called copy: [...] > which is used, according to the header, as a > non-recursive copy function for mappings, arrays > and other variables represented by pointers. Yup. A fine and useful function. That is, in fact, just what it does. > It also > states, that if the variable itself contains > pointers, > that they are _not_ copied. Yes. So it's a shallow copy, or one-level copy, as opposed to a full recursive copy. > Now in various files we will see this function > finding its way into the code, like users_d >.c whenever a > map_indices() or map_values() is returned. I'll restate what Steve Schmidt said, but in a slightly different way. In DGD, when you return a mapping, an array or a lightweight object (LWO), that object is passed by reference, not by value. So if you modify elements of the array or mapping, or call functions on the LWO, you can modify the original copy. However, you can't share data that way for long, because at the end of the DGD thread of execution the mapping, array or LWO will be copied over into the object that has a reference to it. An object with security needs should be careful returning its arrays, mappings or LWOs, because an unscrupulous caller might change them. So if you store a room's exits as an array, don't just return the array -- if the caller reassigns an element of the array, he can change what the set of exits is! However, it seems odd to me that he tends to call copy when he returns a map_values or map_indices, because I think that returns a copy anyway. So if you modify it, it won't do anything to the original array or mapping. > Places we dont see it: > ---------------------- > in /system/users_d.c: > object find_user(string name) { > if (!users) users = ([ ]) ; > if (member_array(name,map_indices(users))==-1) > return 0 ; > return users[name] ; > } Yeah. In this case, returning a single user won't do anything to the users array. However, if users[array] were an array or mapping that could be modified, we'd want to copy it. I don't remember Melville well enough to know whether that's the case. If it's an object, we still might want to worry about security since functions could be called on it, but we'd handle that kind of security in a different place, and in a different way. > in /inherit/object.c: > string *query_id() { return id ; } > which returns an array of id names. Yeah, copy() should probably be called here to avoid the caller being able to modify 'id'. > So my question is, is the copy() function > needed? That functionality is needed, yeah. Phantasmal, for instance, would return "id[..]" rather than "id", which does the same thing that copy() would. It's a matter of how you prefer to write it, but it does the same thing either way. > is it redundant now in dgd 1.2.7# ? since > melville was released back in dgd 1.0.# days? Nope. That particular situation hasn't changed any, except that there are now LWOs, which are more interesting to copy. Melville doesn't use them, so that's not an issue. > And if it's not > redundant, then shouldn't everything that > returns a mapping/array need it? Only if the called object retains a reference to that mapping or array. For instance, Phantasmal will sometimes build a new array in response to a function call. It hands it back to the caller. That's fine, because Phantasmal no longer has a reference to it, so it no longer cares what happens to it. > Sorry for this long ramble, not sure how to > be more concise and still explain. No, it's a good question. Say, have you found my documentation web site? It's at "http://phantasmal.sf.net/DGD". The answer to this question is there, though you'd have to read pretty carefully to find it. I'll have to add a better answer to this question while doing the overhaul. From: dgd at list.imaginary.com (Michael McKiel) Date: Thu Feb 12 14:08:01 2004 Subject: [DGD] Objectd.c mappings of arrays I believe Par Winzell metioned mappings of arrays in January during a discussion of object managers, and another mentioned arrays of arrays. There was some debate about the operational efficiency of both: O(N) O(N^2) or such. And mention of Marrach having some 200k+ clones of 'thing.c' So would it be something like this? mapping object_list; object *arr1; object *arr2; object *arr3; object_list[0] == ({ arr1 }); object_list[1] == ({ arr2 }); object_list[2] == ({ arr3 }); If so, wouldn't you have to have some idea how many of a given object you will have so you can make sure your arrays/mappings don't grow beyond MAX_ARR_SIZE? Like in my example above, if my MAX_ARR_SIZE was 2000. Then if the number of clones of a given item became 6000, the object manager would fail. So I would have to have a bunch of empty arrays initialized just in case? Or am I missing something... From: dgd at list.imaginary.com (Par Winzell) Date: Thu Feb 12 15:19:01 2004 Subject: [DGD] Objectd.c mappings of arrays Michael, > So would it be something like this? > mapping object_list; > object *arr1; > object *arr2; > object *arr3; > > object_list[0] == ({ arr1 }); > object_list[1] == ({ arr2 }); > object_list[2] == ({ arr3 }); > > If so, wouldn't you have to have some idea how many of a given object you > will have so you can make sure your arrays/mappings don't grow beyond > MAX_ARR_SIZE? Like in my example above, if my MAX_ARR_SIZE was 2000. Then if > the number of clones of a given item became 6000, the object manager would > fail. So I would have to have a bunch of empty arrays initialized just in > case? Or am I missing something... You can do it lots of ways. I believe we use an array of mappings at some point in the Skotos library. Having a few empty initialized arrays in an object that already stores several megabytes of meta-information is so irrelevant it should not even be considered sloppy. If the objects really are sparse, you could just do a mapping of mappings instead -- e.g. mapping bigmap; void store_object(ob) { int ix; ix = status(ob)[O_INDEX]; if (!bigmap[ix/1024]) { bigmap[ix/1024] = ([ ]); } bigmap[ix/1024][ob] = ob; } which would suffice for a good while, and still have excellent computational complexity. Zell From: dgd at list.imaginary.com (Erwin Harte) Date: Fri Feb 20 19:35:01 2004 Subject: [DGD] Re: map_indices guaranteed to be alphabetical? On Fri, Feb 20, 2004 at 05:11:51PM -0800, Noah Gibbs wrote: > I've hit a point of contention with another programmer. I thought > that map_indices() wasn't guaranteed to return the indices in ASCII > order. The other programmer claims it is. I know it usually does, but > will it always? I've built many a program on the assumption that it does. Rest assured that a hitman will be hired if Dworkin ever changes that behaviour. ;-) Yes, the indices as returned by map_indices() will always be sorted appropriately within each type. Erwin. -- Erwin Harte From: dgd at list.imaginary.com (Erwin Harte) Date: Thu Dec 13 17:35:01 2001 Subject: [DGD] Mappings On Thu, Dec 13, 2001 at 03:12:56PM -0800, Chooser Fireman wrote: > I was wondering if there is a function similar to sscanf that I can > find a match for partial input in a mapping? > > ie. match attack with the user input of atta with a mapping? Not in the general case but for that particular one you could use mapping-subranges: string verb, verb0, *matches; mapping verbs; verb0 = verb; verb0[strlen(verb0) - 1]++; matches = map_indices(verbs[verb..verb0]) - ({ verb0 }); This is untested code, so the usual disclaimers apply. ;-) Erwin. -- Erwin Harte |