Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > DGD LPC Reference > DGD LPC Diffs

Differences Between DGD's LPC and LPMUD 3.1.2

While the DGD code is unrelated to any other LPMUD of any kind, the LPC dialect that DGD implements is loosely based on Amylaar version 3.1.2. This document describes some differences between the LPC dialects, and between the capabilities of the two servers. Not everything is listed — DGD has a lot of features that most LPMUDs, and specifically version 3.1.2, don't have.

Quite a lot of this file is taken from the Changelog that comes with DGD, and the rest comes from the mailing list. It may be rearranged or rephrased, but the content is from those two sources.


Mappings are similar to, but different from, Amylaar (and other LPC) mappings. In DGD, the standard way to delete an element is to assign nil to that index.

Typechecking in DGD is unusually strict. DGD actually has several typechecking modes of varying strictness, but in general, DGD enforces type safety to about the extent possible.

Array-of-array types like "int****" and "string ***" are possible.

Initializers are not supported.

Indexing beyond the end of the array, or with a negative index, are not supported. DGD gives an error rather than returning anything meaningful. If you think this should change, please check the mailing list archives before suggesting it — this is an old topic, and unless you've got a new take on it, you're just going to feel bad when we dismiss your opinion as old hat.

The call_other() kfun does not require a cast. If the type is not clear from the context, a cast is recommended. The construct foo->bar(baz) maps directly to call_other(foo, bar, baz). You can thus override its behavior by declaring a new call_other() function appropriately.

Indexed string assignments such as foo = "bar"; foo[0] = 'B'; are possible. Note that there is no character type, so a string is effectively an array of small integers.

The zero size array and zero size mapping are not globally shared, so comparisions such as array == ({ }) will fail.

It is illegal to declare any functions, function prototypes or variables before inheriting.

Inheritance is completely different. There are two kinds of inheritance, virtual and labeled. See documents elsewhere for more details.

There is an object, often called the Auto object, that is inherited by every object except itself and the Driver object. The driver object may choose to inherit it, but does not do so automatically.

Predefined functions are called kfuns (kernel functions); the kfuns and the non-private functions declared in the auto object together are called efuns. Kfuns are assumed to be static functions inherited in the auto object, with the added limitation that they cannot be called with this_object()->kfun(). All static functions declared in the auto object share this limitation.

There is no class_specifier inherit "file"; This did not work in LPmud 3.1.2 anyhow.

Function prototypes are always inherited together with the function.

Function calls are typechecked if the called function is typechecked. It is illegal to call a non-varargs function with the wrong number of arguments from a typechecked function, even if the called function is not typechecked itself.

Kfuns are not treated differently from other inherited functions, so destruct_object(this_object(), this_object()); will not cause a compile-time error in an untypechecked function.

Inherited functions may be redeclared with a different prototype. Redeclaring an inherited function with different parameters is allowed (runtime typechecking will handle calls from inherited objects to the new function), but the return type still has to match.

A call to an undeclared function from an untypechecked function implicitly declares a prototype for the undeclared function that matches anything.

this_object()->function(); can be used to call static functions, but not private ones.

Any kfun can be redefined, just as ordinary inherited functions can. catch() and rlimits() are not considered to be kfuns (they are not true functions because catch(foo, bar) does not "call catch with two arguments").

The built-in preprocessor conforms to the ANSI standard, except for the following:

  • No trigraphs
  • Successive string constants are not concatenated (use the + operator instead)
  • The suffixes U and L are not allowed on integer constants
  • No floating point constants (is this still true?)

There is a standard include file which is included automatically. The filename is configurable in the .dgd file.

All #pragma directives are ignored.

All integer constants may be in decimal (starting with 1-9), octal (starting with 0), hexadecimal (starting with 0x) or a character constant 'x'. The only escaped characters that are translated into something else in character constants and string constants are \t and \n.

Objects are pure programs. They do not have inventories, environments, are not alive, do not have actions (all of this can be simulated).

Pathnames without a leading slash are considered to be relative to the directory of the current object (i.e. /foo for the object compiled from /foo/bar.c).

create() is called if a function in an object is called for the first time. It is not called if an object is loaded because it is inherited.

reset() is never called by the driver. Both reset on reference and reset after a fixed period are easily simulated.

clean_up() is never called by the driver.

Text typed by a user is passed on by calling receive_message(text) in the interactive object. The kfun send_message(text) can be used in interactive objects to send text back to the user.

There is no "master object" as in LPmud 3.1.2, but a "driver object" instead. It is used only to translate pathnames for #include and inherit, and to supply the object that is used for interactive connections. It does not inherit the auto object, unless this is done explicitly.

There is no shadowing. (Note: if you want to argue the merits of this, check the mailing list archives first, and have a reasonable grasp of code security. Otherwise, you'll feel bad when we dismiss you as not knowing what you're talking about)

The default state of any object is swapped out. Strings and arrays are only shared inside objects. If an array is exported from one object to another, it will be copied into the new object as soon as execution of the current thread finishes.

Self-referential datastructures will be deallocated when no more outside references exist, because they do not belong in any object.

DGD removes many of the kfuns of LPmud 3.1.2. Specifically:

  • No kfuns such as environment(), this_player(), add_action(). Simulate them if you want them.
  • There is no exec(), heart_beat(), wizlist() (simulate them).
  • No parse_command(). It cannot be simulated, so people might want to port it to DGD, but it will never become part of "pure" DGD. (Note: parse_string now serves this purpose)
  • No alist kfuns. I prefer mappings, even if alists are more powerful.
  • All file kfuns are unprotected. To get either native or compat mode security, make the proper redefinitions in the auto object.
  • The built-in editor uses a temporary file and is very close to ex.
  • call_other() is typechecked if the called function is typechecked. Calling a function that does not exist results in nil being returned.
  • There are no optional flag arguments to any kfun.

Doing a call_other() from a destructed object is ignored.

DGD now automatically generates several files in the first include directory.

  • "float.h" gives information about floating-point numbers and their sizes and limits in DGD
  • "limits.h" describes the sizes of datatypes and resources
  • "status.h" describes the information returned by the status() kfun, and defines offsets for each piece
  • "type.h" gives the constants returned by the typeof() kfun

Destructing a destructed object gives an error, rather than a fatal error.

DGD has an ellipsis (...) operator which can be used to make function calls with an arbitrary number of arguments. It also has the ability to declare a function with an ellipsis afterward, which will allow the function to take an arbitrary number of arguments.

The LPMUD alarm/timeout interface has been replaced by call_out() statements.

The rlimits() construct exists. It allows for resource-constrained execution. Specifically, ticks (processor time) and stack depth may be constrained.