LPC (short for Lars Pensjö C) is an object-oriented programming language derived from C and developed originally by Lars Pensjö to facilitate MUD building on LPMuds. Though designed for game development, its flexibility has led to it being used for a variety of purposes, and to its evolution into the language Pike. LPC syntax places it in the family of C-like languages, with C and C++ its strongest influences.
Almost everything in LPC is an object. However, LPC does not precisely use the concept of a class (MudOS has something called a class, but it is really a struct). Instead, LPC objects are blueprint objects and clones of blueprint objects, in a prototype-based programming model. One can treat a blueprint object much as a class in other object-oriented languages.
Each object has variables (attributes) and functions (methods). The variables store the object's state; the functions are executable routines that can be called in the object. An object is uniquely defined by the name of the file in which it comes from, plus, if a clone, a numeric identifier. In a typical implementation, a clone of the file /lib/weapon.c
which is the third clone created in the current run session will be /lib/weapon#3
. The blueprint object corresponding to /lib/weapon.c
is simply /lib/weapon
. In a MUD game, common objects include rooms, weapons, armor, and non-player character (NPCs). Most mudlibs define inheritable objects for such common things. In the LPMud 2.4.5 mudlib, for example, the parent object for all rooms is /lib/room
.
In LPC, it is not possible to syntactically declare class-only or instance-only methods and attributes; all functions and variables may be accessed identically in blueprints and in clones. For example, "/lib/user"->number_of_users()
would call the number_of_users()
function directly in the blueprint instance of /lib/user.c
. (However, a controversial mechanism in many drivers called "shadowing" allows instance methods to be emulated to some extent by permitting one object to "overlay" its function set onto another, in effect interposing itself between external function calls and the shadowed object.) Direct external access to variables is also not supported; all interaction between objects is carried out through function calls (notwithstanding that data structures pointed to by variables are independent of objects and changes to them are visible in all objects referencing those data structures simultaneously, and that some drivers have privileged functions allowing a cumbersome form of external variable access that permits inspection but not mutation).
Because LPC is generally used to code MUDs, 'rooms' are often created as objects that store information describing a particular scene along with exits that point to other room objects. This is the most common usage of LPC. Other uses that are not game related are possible.
The following example shows a very simple traditional room that leverages functions defined in the mudlib object /lib/room
. However, not all mudlibs define or determine rooms in the same way, so this is not the only method of defining a room.
inherit "/lib/room"; void create() { ::create(); set_short("a simple room"); set_long("A simple room in a simple building."); set_description("This is a simple room in a simple building. It is very nice."); add_exit("north", "/realms/descartes/north_room"); }
The first line tells the object to inherit functionality from the /lib/room
object. This example assumes the /lib/room
object defines functions for ::create(), set_short(), set_long(), set_description(),
and add_exit()
.
This example contains a single function, create()
. Most drivers call, or can be set to call, create()
to allow an object to initialize itself with startup values; it is the standard constructor. In this case, the example calls functions that set up the basic room attributes in the inherited /lib/room
. The functions called here are highly dependent on the mudlib in use, since the mudlib defines the actual room parent.
object obj = clone_object("/std/weapon");
int number = 23;
string text = "Hello, world!";
*
modifier, as in object *, string *, int *
. Typically, only one *
can be applied to a declaration, so if one wanted an array of arrays, the declaration would be mixed *
. LPC arrays are generally "rubberband" arrays that can have their length modified after allocation. Arrays are almost universally available, though the preferred declaration syntax differs in MudOS. Usual literal form is the list of values, separated by commas (optional after the last value), enclosed in ({
and })
boundary markers.int * numbers = ({ 1, 2, 3 }); string * words = ({ "foo", "bar", "baz" }); mixed * stuff = ({ 1, "green", 48.2 });
([
boundary marker, followed by zero or more key-value pairs with a colon between the key and value and a comma following the value (optional on the last key-value pair), and a ])
boundary marker.mapping map = ([ "hello" : 1, "world" : 2, ]);
float number = 3.1415;
mixed value = random(2) ? "hello" : 1;
status flag = 1;
closure func = #'some_local_function;
symbol var = 'x;
mixed quoted_array = '({ 1, 2, 3 });
<type> *
form is usually also available.int array numbers = ({ 1, 2, 3 });
class example { int number; string name; }; class example instance = new(class example); instance->number = 23; instance->name = "Bob";
struct example { int number; string name; }; struct example instance = (<example>); instance->number = 23; instance->name = "Bob";
(: :)
boundaries, using $1, $2,
etc. for arguments. LDMud also supports a variant of this syntax for its closure values.function op = (: return sqrt($1 * $1 + $2 * $2); :);
Most drivers also support applying type modifiers to inherit statements, causing the inherited object to behave with respect to its inheritor as if the type modifier were applied to its functions and/or variables, as appropriate.
Where the term "object variable" is used above, this means a variable which is an element of an object (i.e. an attribute), as opposed to a local variable (exists only within a function or block) or a global variable (nonexistent in LPC — if someone speaks of a global variable in reference to LPC, they probably mean an object variable).
Primitive LPC types (int, string, status, float, etc.) are passed by value. Data structure types (object, array, mapping, class, struct) are passed by reference.
This feature can be powerful, but it can also lead to security holes. In most MUDs, the people building the world are generally less trusted than the staff running the game. If an object passes a mapping with sensitive values like access control information, an author of another object can modify that and thus increase their access rights. Mudlib developers and server administrators should thus be careful when passing complex types to lower access objects.
LPC environments generally categorize functions into several major types according to how they are implemented:
function_name()
, while functions in other objects are usually called with the syntax object->function_name()
. Overloaded lfuns defined in objects one is inheriting can be called with the syntax ::function_name()
or object_name::function_name()
.this_player()
can be called with the syntax this_player()
, or efun::this_player()
if one needs to bypass an lfun or simul_efun.some_function()
is available as some_function()
in the namespace of all LPC functions.LPC implementations generally have a "master object", which is a specific LPC object that is loaded first by the LPC driver and which essentially controls what will happen past that point. The master object will typically tell the driver where the simul_efun object is, preload any objects which need to be present at startup, define what functions will be called when an object is loaded, and otherwise configure the driver's operation. The driver will refer back to the master object when it needs to interact with a central point of reference for the running environment, such as for accepting network connections, handling errors, and validating attempts to perform privileged operations.
The content is sourced from: https://handwiki.org/wiki/LPC_(programming_language)