197 lines
9.9 KiB
Plaintext
197 lines
9.9 KiB
Plaintext
chapter 2 "The LPC Program"
|
|
LPC Basics
|
|
Written by Descartes of Borg
|
|
first edition: 23 april 1993
|
|
second edition: 16 june 1993
|
|
|
|
CHAPTER 2: The LPC Program
|
|
|
|
2.1 About programs
|
|
The title of this chapter of the textbook is actually poorly named, since
|
|
one does not write programs in LPC. An LPC coder instead writes *objects*.
|
|
What is the difference? Well, for our purposes now, the difference is
|
|
in the way the file is executed. When you "run" a program, execution
|
|
begins at a definite place in the program. In other words, there
|
|
is a place in all programs that is noted as the beginning where program
|
|
execution starts. In addition, programs have definite end points,
|
|
so that when execution reaches that point, the execution of the program
|
|
terminates. So, in short, execution of a program runs from a definite
|
|
beginning point through to a definite end point. This is not so with
|
|
LPC objects.
|
|
|
|
With muds, LPC objects are simply distinct parts of the C program which
|
|
is running the game (the driver). In other words, execution of the mud
|
|
program begins and ends in the driver. But the driver in fact does
|
|
very little in the way of creating the world you know when you play
|
|
a mud. Instead, the driver relies heavily on the code created in LPC,
|
|
executing lines of the objects in the mud as needed. LPC objects thus
|
|
have no place that is necessarily the beginning point, nor do they
|
|
have a definite ending point.
|
|
|
|
Like other programming languages, an LPC "program" may be made up of
|
|
one or more files. For an LPC object to get executed, it simple
|
|
needs to be loaded into the driver's memory. The driver will call lines
|
|
from the object as it needs according to a structure which will be
|
|
defined throughout this textbook. The important thing you need to
|
|
understand at this point is that there is no "beginning" to an LPC
|
|
object in terms of execution, and there is no "end".
|
|
|
|
2.2 Driver-mudlib interaction
|
|
As I have mentioned earlier, the driver is the C program that runs on
|
|
the host machine. It connects you into the game and processes LPC code.
|
|
Note that this is one theory of mud programming, and not necessarily
|
|
better than others. It could be that the entire game is written in C.
|
|
Such a game would be much faster, but it would be less flexible in
|
|
that wizards could not add things to the game while it was running. This
|
|
is the theory behind DikuMUDs. Instead, LPMUDs run on the theory that
|
|
the driver should in no define the nature of the game, that the nature
|
|
of the game is to be decided by the individuals involved, and that
|
|
you should be able to add to the game *as it is being played*. This
|
|
is why LPMUDs make use of the LPC programming language. It allows
|
|
you to define the nature of the game in LPC for the driver to read and
|
|
execute as needed. It is also a much simpler language to understand
|
|
than C, thus making the process of world creation open to a greater
|
|
number of people.
|
|
|
|
Once you have written a file in LPC (assuming it is corrent LPC ), it justs
|
|
sits there on the host machine's hard drive until something in the game
|
|
makes reference to it. When something in the game finally does make
|
|
reference to the object, a copy of the file is loaded into memory and
|
|
a special *function* of that object is called in order to initialize
|
|
the values of the variables in the object. Now, do not be concerned
|
|
if that last sentence went right over your head, since someone brand
|
|
new to programming would not know what the hell a function or a variable
|
|
is. The important thing to understand right now is that a copy of the
|
|
object file is taken by the driver from the machine's hard drive and
|
|
stored into memory (since it is a copy, multiple versions of that
|
|
object may exist). You will later understand what a function is, what
|
|
a variable is, and exactly how it is something in the game made reference
|
|
to your object.
|
|
|
|
2.3 Loading an object into memory
|
|
Although there is no particular place in an object code that must exist
|
|
in order for the driver to begin executing it, there is a place for which
|
|
the driver will search in order to initialize the object. On compat
|
|
drivers, it is the function called reset(). On native muds it is the
|
|
function called create().
|
|
|
|
LPC objects are made up of variables (values which can change) and
|
|
functions which are used to manipulate those variables. Functions
|
|
manipulate variables through the use of LPC grammatical structures,
|
|
which include calling other functions, using externally defined
|
|
functions (efuns), and basic LPC expressions and flow control
|
|
mechanisms.
|
|
|
|
Does that sound convoluted? First lets start with a variable. A
|
|
variable might be something like: level. It can "vary" from sitation
|
|
to situation in value, and different things use the value of the player's
|
|
level to make different things happen. For instance, if you are a
|
|
level 19 player, the value of the variable level will be 19. Now
|
|
if your mud is on the old LPMud 2.4.5 system where levels 1-19 are
|
|
players and 20+ are wizards, things can ask for your level value to
|
|
see if you can perform wizard type actions. Basically, each object
|
|
in LPC is a pile of variables with values which change over time.
|
|
Things happen to these objects based on what values its variables
|
|
hold. Often, then things that happen cause the variables to change.
|
|
|
|
So, whenever an object in LPC is referenced by another object currently
|
|
in memory, the driver searches to see what places for values the
|
|
object has (but they have no values yet). Once that is done, the driver
|
|
calls a function in the object called reset() or create() (depending
|
|
on your driver) which will set up the starting values for the object's
|
|
variables. It is thus through *calls* to *functions* that variable
|
|
values get manipulated.
|
|
|
|
But create() or reset() is NOT the starting place of LPC code, although
|
|
it is where most LPC code execution does begin. The fact is, those
|
|
functions need not exist. If your object does just fine with its
|
|
starting values all being NULL pointers (meaning, for our purposes
|
|
here, 0), then you do not need a create() or reset() function. Thus
|
|
the first bit of execution of the object's code may begin somewhere
|
|
completely different.
|
|
|
|
Now we get to what this chapter is all about. The question: What
|
|
consists a complete LPC object? Well, an LPC object is simply
|
|
one or more functions grouped together manipulating 0 or more
|
|
variables. The order in which functions are placed in an object
|
|
relative to one another is irrelevant. In other words:
|
|
|
|
-----
|
|
void init() { add_action("smile", "smile"); }
|
|
|
|
void create() { return; }
|
|
|
|
int smile(string str) { return 0; }
|
|
-----
|
|
|
|
is exactly the same as:
|
|
|
|
-----
|
|
void create() { return; }
|
|
|
|
int smile(string str) { return 0; }
|
|
|
|
void init() { add_action("smile", "smile"); }
|
|
_____
|
|
|
|
Also important to note, the object containing only:
|
|
|
|
-----
|
|
void nonsense() {}
|
|
-----
|
|
|
|
is a valid, but trivial object, although it probably would not interact
|
|
properly with other objects on your mud since such an object has no
|
|
weight, is invisible, etc..
|
|
|
|
2.4 Chapter summary
|
|
LPC code has no beginning point or ending point, since LPC code is used
|
|
to create objects to be used by the driver program rather than create
|
|
individual programs. LPC objects consist of one or more functions whose
|
|
order in the code is irrelevant, as well as of zero or more variables whose
|
|
values are manipulated inside those functions. LPC objects simply sit
|
|
on the host machine's hard driver until referenced by another object in
|
|
the game (in other words, they do not really exist). Once the object
|
|
is referenced, it is loaded into the machine's memory with empty
|
|
values for the variables. The function reset() in compat muds or
|
|
create() in native muds is called in that object if it exists to allow
|
|
the variables to take on initial values. Other functions in the object
|
|
are used by the driver and other objects in the game to allow interaction
|
|
among objects and the manipulation of the LPC variables.
|
|
|
|
A note on reset() and create():
|
|
create() is only used by muds in native mode (see the textbook Introduction
|
|
for more information on native mode vs. compat mode). It is only used
|
|
to initialize newly referenced objects.
|
|
|
|
reset() is used by both muds in compat mode and native mode. In compat
|
|
mode, reset() performs two functions. First, it is used to initialize
|
|
newly referenced objects. In addition, however, compat mode muds use
|
|
reset() to "reset" the object. In other words, return it to its initial
|
|
state of affairs. This allows monsters to regenerate in a room and doors
|
|
to start back in the shut position, etc.. Native mode muds use reset()
|
|
to perform the second function (as its name implies).
|
|
|
|
So there are two important things which happen in LP style muds which
|
|
cause the driver to make calls to functions in objects. The first is
|
|
the creation of the object. At this time, the driver calls a function
|
|
to initalize the values in the object. For compat mode muds, this
|
|
is performed by the function named reset() (with an argument of 0,
|
|
more on this later though). For muds running in native mode, this is
|
|
performed by the function create().
|
|
|
|
The second is the returning of the room to some base state of affairs.
|
|
This base set of affairs may or may not be different from the initial
|
|
state of affairs, and certainly you would not want to take up time
|
|
doing redundant things (like resetting variables that never change).
|
|
Compat mode muds nevertheless use the same function that was used to
|
|
create the object to reset it, that being reset(). Native mode muds,
|
|
who use create() to create the room, instead use reset() to reset it.
|
|
All is not lost in compat mode though, as there is a way to tell the
|
|
difference between creation and resetting. For reset purposes, the
|
|
driver passes either 1 or the reset number as an argument to reset()
|
|
in compat mode. Now this is meaningless to you now, but just keep in
|
|
mind that you can in fact tell the difference in compat mode. Also
|
|
keep in mind that the argment in the creation use of reset is 0 and
|
|
the argument in the reset use is a nonzero number.
|