Q and A.</> <SECT1> <TITLE>What's a kernel function (kfun)?</> <PARA> A kernel function is a function provided by &dgd; itself. Documentation for these functions can be found in the doc/kfun directory of the &dgd; distribution and <ULINK url="/cgi-bin/man.cgi">online</> (unofficially). These functions are available to every object (unless redefined by an inherited object -- the auto object often does redefine them). </> </> <SECT1 id="qanda-auto"> <TITLE>What's the auto object?</> <PARA> The auto object is one of two special objects in &dgd;. It is (automatically) inherited by every other object except the driver object. In this way, any function in the auto object that is declared static will act much the same as a kfun. Functions like this are alternatively called efuns (since they act as efuns do in other &lpc; drivers) or afuns. </> <PARA> It should be noted that even if a function that is static in the auto object isn't explicitly defined as nomask it will always act as if it is in the context of the auto object. That is, although it may be masked in an object, calls to it from within the auto object will always use the version in the auto object. This differs from the standard behaviour of functions which are static but not nomask in other objects, which will use the overriden version of the function. To quote Dworkin: </> <BLOCKQUOTE><PARA>To be yet more precise: <EMPHASIS role="bold">any</> calls made to static functions in the auto object can never be redirected to a new function by masking the static function, no matter from where the calls are made. In C++ terminology, static functions in the auto object are not 'virtual'.</></> </> <SECT1> <TITLE>Is it bad to have a large auto object?</> <PARA> This question has been asked a number of times on the &dgd; mailing list, where large refers to a <EMPHASIS role="bold">lot</> of functions. Dworkin has replied to this and stated that the function lookup time is constant and independent of the number of functions in the auto object. However, he has also said: </> <BLOCKQUOTE><PARA> There is an obscure cost associated with functions in the auto object that are neither static nor private: all such functions have a cost of two bytes a piece in <every> inheriting object's program. This is also true for non-private functions in all other (i.e. non-auto) objects. </><PARA> Normally, this is not much of a problem. However, if you were to have 200 such functions in the auto object, every other program in the game but that of the driver object would become 400 bytes larger. </></> <PARA> It is not unreasonable to assume that <EMPHASIS role="italic">functions in the auto object that are neither static nor private</> are uncommon. </> <PARA> On the related question of using an inherited module rather than placing the code in the auto object Dworkin made the following statements: </> <BLOCKQUOTE> <PARA> - A call to a function in the auto object uses the special CALL_AFUNC instruction, which is 3 bytes in size. Exactly the same functionality would be available with the CALL_DFUNC instruction, which takes 4 bytes. Thus, programs that call functions in the auto object will be slightly smaller. In the most extreme case, a function that consists of nothing but function calls will be almost 25% smaller if CALL_AFUNC can be used for every call. There will be a barely measurable performance difference. </> <PARA> Note that programs larger than 2K are compressed before being saved in the swap file, where the compression factor depends on the redundancy of the byte code, so the size advantage in the swap file would be much less than 25% if the function mentioned above is large enough. </> <PARA> - Inheriting a utility object, which itself does not inherit anything, increases the size of the inheriting program with about 16 bytes. </> <PARA> - Putting everything into the auto object increases the size of the working set. For a single object the difference won't amount to much, but a mudlib wholly designed with the idea of keeping the working set small will have a performance advantage. </> </> <PARA> With this in mind, it would seem that whether to have a large auto object or not is an issue which has pros and cons on both sides, that is, there is no canonical answer to this question. </> </> <SECT1 id="qanda-driver"> <TITLE>What's the driver object?</> <PARA> The driver object is the interface between &dgd; and the mudlib. It is loaded before any other object and is the only object not to inherit the auto object. Various <EMPHASIS role="italic">applies</> (see the LPMud &faq; -- these are functions called by &dgd; itself, not another &lpc; object) are called in the driver object by &dgd; in response to various events and are expected to perform various tasks such as dealing with compile or runtime errors, calling, compiling and inheriting objects, etc. </> </> <SECT1> <TITLE>What does atomic mean or do?</> <PARA>A function can be declared to be <EMPHASIS role="italic">atomic</>:</> <EXAMPLE> <TITLE>An atomic function</> <PROGRAMLISTING><![CDATA[ atomic void foo() { ... } ]]></> </> <PARA> Atomic functions treat errors in a special way. If an error occurs, every change made by this function is undone, including changes made in functions called by this function. </> <PARA> It is illegal to use file or directory changing kfuns from atomically executed code: </> <ITEMIZEDLIST> <LISTITEM><PARA>write_file()</></> <LISTITEM><PARA>remove_file()</></> <LISTITEM><PARA>make_directory()</></> <LISTITEM><PARA>remove_directory()</></> <LISTITEM><PARA>save_object()</></> <LISTITEM><PARA>editor()</></> </> <PARA> Atomically executed code uses twice as many ticks as normal code. </> <PARA> An example given by Dworkin on the mailing-list: </> <BLOCKQUOTE> <PARA> The 2.4.5 mudlib for &dgd; contains the following code in /dgd/lib/inventory.c: </> <PROGRAMLISTING><![CDATA[ private void move(object obj, object from, object dest) { int light; light = query_light(obj); if (from != 0) { from->_F_rm_inv(obj, light); } obj->_F_move(dest); dest->_F_add_inv(obj, light); }]]></> <PARA> This code is called from the move_object() efun to do the actual movement. If it were to fail halfway through, this would result in an inconsistency. For example, if the inventory array of `dest' already has the maximum size, adding the object `obj' to the inventory of `dest' will result in an error; this will leave the environment of `obj' set to `dest' while `obj' is not actually in the inventory of `dest'. </> <PARA> Furthermore, there are some errors that can happen in almost any code: running out of ticks, or out of stack space for nested function calls. (It so happens that this particular code snippet is safe from those errors.) </> <PARA> Now if the function were made atomic, it would either succeed normally, or fail without making any change at all. The object is either moved or not moved -- it cannot get stuck in an intermediate state where it is half moved. </> <PARA> Using atomic is not the only solution. It may be possible to check for all possible error conditions in advance, and only execute the code if it's safe. Alternatively, an error occurring halfway through could be caught, and the already-performed actions could be undone explicitly. However, using atomic is always the most simple solution, often the cheapest, and sometimes the only possible one. </> <PARA> There is a lot of code out there that doesn't check for errors at all, of course. The atomic function feature could be used to guard such code from inconsistencies with only minimal rewriting. </> </> </> <SECT1> <TITLE>What are <acronym>LWOs</>?</> <PARA> <acronym>LWO</> is an abbreviation of <EMPHASIS role="italic">light-weight object</> and it refers to a new feature introduced in the experimental branch of &dgd; 1.2 at version 1.2.18. Dworkin described them as follows: </> <BLOCKQUOTE> <PARA> Like clones, they are created from a master object, which is a normal, persistent object. Light-weight objects do have some restrictions: </> <ITEMIZEDLIST> <LISTITEM><PARA>they cannot be explicitly destruct</></> <LISTITEM><PARA>they cannot be used as an editor, user or parser object</></> <LISTITEM><PARA>they cannot have callouts</></> <LISTITEM><PARA>destruct a master object will also instantly destruct all light-weight objects made therefrom (!)</></> </> <PARA> Furthermore, like arrays, they are local to the dataspace of some particular (persistent) object. This means that if a light-weight object is exported to some other object's dataspace, it will become a <copy> there at the end of the &lpc; thread, just as currently happens with arrays and mappings. </> </> </> <SECT1> <TITLE>Why isn't my create() function called when I compile an object?</> <PARA> This is mentioned in the &dgd; documentation. The <EMPHASIS>create()</> function is only called just before the object is first accessed by a <EMPHASIS>call_other</>. To change this behaviour one can simply mask <EMPHASIS>compile_object</> in the auto object with a version which accesses the object straight after it is compiled and hence provokes the calling of the <EMPHASIS>create()</> function by the driver. </> </> <SECT1> <TITLE>Are there any function pointer/lambda closure equivalents?</> <PARA> No, but you can simulate this sort of functionality with &lpc; should you require it. Indeed, Dworkin at one point wrote a simulation for the lambda closures of the 3.2 &lpc; (Amylaar) driver. </> </> <SECT1> <TITLE>Can I compile my &lpc; to C</> <PARA> Yes, you can. This must be done at the time the driver is compiled though, one cannot (quite) do it on the fly. Look in the <FILENAME>dgd/src/lpc</> directory for more information concerning this. The 2.4.5 mudlib simulation contains a good example of how to do this. </> <PARA> To perform this operation almost on the fly, compile the new version of the driver with the C version of the new &lpc; objects included then perform a state dump of the game before shutting down and restarting with the new driver. </> </> <SECT1> <TITLE>Multiple inheritance with &dgd;?</> <PARA> Multiple inheritance in &dgd; is different from other &lpc; drivers. To inherit multiple files one must label the inherited files (other &lpc; drivers automatically label the inherited objects with the file name -- which is a problem if there are two files of the same name in different directories which you wish to inherit). The best way to illustrate the &dgd; method is via an example. In this example two objects A and B are inherited and given the labels foo and bar. These labels are used to call the inherited create functions. </> <EXAMPLE> <TITLE>Multiple inheritance with &dgd;</> <PROGRAMLISTING role="C"><![CDATA[ /* * Inherit program "A" and label it as foo. * Inherit program "B" and label it as bar. */ inherit foo "A"; inherit bar "B"; void create() { /* * Call the create functions in programs "A" and "B" via their inheritance * labels. */ foo::create(); bar::create(); }]]> </> </> </> <SECT1> <TITLE>ANSI colours?</> <PARA> A good start would be to apply the ANSI patch that is listed in the <LINK linkend="section-packages">list of packages for &dgd;</>. If it doesn't apply correctly you can quite likely figure out from the patch itself what the original intention was, since you only need to change one line in each file. The package also contains some documentation about vt100 escape sequences, which (as far as colours go) is probably what you want. </> </> <SECT1> <TITLE>What is parse_string()?</> <PARA> If you're starting from scratch, I'd recommend checking <ULINK url="http://www.mindspring.com/~zeppo1/parse_string.html">this introduction</> that Steve Foley put together. It explains some of the basics. </> </> <SECT1> <TITLE>What is an ASN?</> <PARA> ASN are a relatively recent addition to the experimental branch of &dgd; 1.2 (at 1.2.51), this is what Dworkin wrote at their introduction: <BLOCKQUOTE> <PARA> ASN stands for "arbitrary size number". In &lpc;, such numbers are represented by big-endian strings. If the highest bit of the first character of the string is set, the number is assumed to be negative, and in two's complement format. If you want to represent a positive number that starts with a byte that would make the string representation negative, simply precede it with a \0 character. </> <PARA> For example, <BLOCKQUOTE><PARA> "\0\xff" </></> represents the positive number 0xff. </> <PARA> All of the ASN functions except for asn_mod() take 3 arguments, the third argument being the modulus, which must be a positive number greater than zero. </> <PARA> To compile &dgd;1.2.51 with MSVC, make sure to add the new source file, <FILENAME>asn.c</>, to the project. </> </> </> <PARA> These kfuns were added to manipulate ASNs: <ITEMIZEDLIST> <LISTITEM><PARA>asn_add()</></> <LISTITEM><PARA>asn_and() (added in 1.2.55)</></> <LISTITEM><PARA>asn_cmp() (added in 1.2.52)</></> <LISTITEM><PARA>asn_div()</></> <LISTITEM><PARA>asn_lshift() (added in 1.2.55)</></> <LISTITEM><PARA>asn_mod()</></> <LISTITEM><PARA>asn_mult()</></> <LISTITEM><PARA>asn_or() (added in 1.2.55)</></> <LISTITEM><PARA>asn_pow() (added in 1.2.53)</></> <LISTITEM><PARA>asn_rshift() (added in 1.2.55)</></> <LISTITEM><PARA>asn_sub()</></> <LISTITEM><PARA>asn_xor() (added in 1.2.55)</></> </> It is recommended to upgrade at least to 1.2.55 to use this new feature. </PARA> </SECT1> <SECT1> <TITLE>Does &dgd; have encryption/decryption support?</> <PARA> Yes. From the very start there was the <EMPHASIS role=bold>crypt()</> kfun which does the <EMPHASIS>traditional</> DES encryption commonly used for passwords. </> <PARA> More recently Dworkin added the following kfuns for encryption and encryption related work: <ITEMIZEDLIST> <LISTITEM><PARA>hash_crc16() (added in 1.1.90)</></> <LISTITEM><PARA>hash_crc32() (added in 1.2.51)</></> <LISTITEM><PARA>hash_md5() (added in 1.1.90)</></> <LISTITEM><PARA>hash_sha1() (added in 1.2.51)</></> <LISTITEM><PARA>decrypt() (added in 1.2.55)</></> <LISTITEM><PARA>encrypt() (added in 1.2.55)</></> </> </> <PARA> Dworkin wrote the following about this: <BLOCKQUOTE> <PARA> I'm going to start with providing basic DES encryption as a kfun. That should suffice to implement all the different DES modes (ECB, CBC, 3DES CBC, etc.) in &lpc;. I intend to wrap it in a general kfun, like this: <PROGRAMLISTING role="LPC"><![CDATA[ prepared_encrypt_key = encrypt("DES key", key); encrypted = encrypt("DES", message, prepared_encrypt_key); prepared_decrypt_key = decrypt("DES key", key); message = decrypt("DES", encrypted, prepared_decrypt_key);]]></> with <PROGRAMLISTING role="LPC"><![CDATA[ string encrypt(string cipher, string mesg_or_key, string keys...) string decrypt(string cipher, string mesg_or_key, string keys...)]]></> For unsupported ciphers, the kfuns would return nil. This way, it would be easy to make optional encryption packages that could be added or removed without breaking dumpfile compatibility. The kfuns could also be masked, for example in the implementation of 3DES CBC: <PROGRAMLISTING role="LPC"><![CDATA[ string encrypt(string cipher, string mesg, string keys...) { switch (cipher) { case "3DES CBC key": return ::encrypt("DES key", mesg) + ::decrypt("DES key", keys[0]) + ::encrypt("DES key", keys[1])); case "3DES CBC": encrypted = ""; keylen = strlen(keys[0]) / 3; while (strlen(mesg) >= 8) { keys[1] = str = asn_xor(mesg[.. 7], keys[1]); str = ::encrypt("DES", str, keys[0][.. keylen - 1]); str = ::decrypt("DES", str, keys[0][keylen .. 2 * keylen - 1]); str = ::encrypt("DES", str, keys[0][2 * keylen ..]; mesg = mesg[8 ..]; encrypted += str; } if (strlen(mesg) != 0) { keys[1] = str = asn_xor(mesg, keys[1]); str = ::encrypt("DES", str, keys[0][.. keylen - 1]); str = ::decrypt("DES", str, keys[0][keylen .. 2 * keylen - 1]); str = ::encrypt("DES", str, keys[0][2 * keylen ..]; keys[1] = str = asn_xor(str, keys[1]); encrypted += str; } return encrypted; default: return ::encrypt(cipher, mesg, keys...); } }]]></> </> </> </> </> </>