找回密码
 注册
搜索
查看: 6504|回复: 8

LPC教程连载

[复制链接]
门派:不明
发表于 2008-4-30 13:02:46 | 显示全部楼层 |阅读模式
下面开始LPC教程连载
门派:不明
 楼主| 发表于 2008-4-30 13:03:27 | 显示全部楼层
CHAPTER 1: Introduction to the Coding Environment1.1 UNIX file structure
LPMuds use basic UNIX commands and its file structure.  If you know
UNIX commands already, then note (with a few exceptions) options are
not available to the commands.  Like DOS, UNIX is heirarchical.  The
root directory of which all directories are sub-directories is called
root(/).  And from those sub-directories you may have further
sub-directories.  A directory may be referred to in two different ways:
1) by its full name, or absolute name, or 2) by its relative name.
Absolute name refers to the directory's full path starting from / winding
down the directory tree until you name the directory in question.  For
example:

    /players/descartes/obj/monster

refers to the directory monster which is a sub-directory of obj which
is a sub-directory of descartes which is a sub-directory of players
which is a sudirectory of /.

The relative name refers to the name relative to another directory.
The above example is called monster relative to /players/descartes/obj,
but it is also called obj/monster relative to /players/descartes,
descartes/obj/monster relative to /players, and finally
players/descartes/obj/monster relative to /.  You can tell the
difference between absolute names and relative names because absolute
names always start with /.  In order to know exactly which directory
is being named by a relative name, you naturally must know what
directory it is relative to.

A directory contains sub-directories and files.  LPMuds only use text files
inside the mudlib.  Like directories, files have both absolute and
relative names.  The most basic relative name is often referred to as the file
name, with the rest of the absolute name being referred to as the path.  So,
for the file: /players/descartes/castle.c, castle.c is the file name, and
/players/descartes is the path.

On some muds, a file with a file name beginning with a . (like .plan) is
not visible when you list files with the regular file listing command.

1.2 UNIX Commands
Along with the UNIX file structure, LPMuds use many UNIX commands.  Typical
UNIX commands on most muds are:
pwd, cd, ls, rm, mv, cp, mkdir, rmdir, more, head, cat, ed
If you have never before seen UNIX commands, you probably are thinking this
is all nonsense.  Well, it is, but you got to use them.  Before getting
into what they mean though, first a discussion of current directory.
If you know DOS, then you know what a current working directory is.
At any given point, you are considered to be "in" some directory.  This
means that any relative file or directory names you give in UNIX commands
are relative to that directory.  For example, if my current directory is
/players/descartes and I type "ed castle.c" (ed is the command to edit),
then it assumes I mean the file /players/descartes/castle.c

pwd: shows you your current working directory
cd: changes your current working directory.  You may give either relative
    or absolute path names.  With no arguments, it changes to your home
    directory.
ls: lists all files in the directory named.  If no directory is named,
    it lists the files of the current working directory
rm: deletes the file named
mv: renames the file named
cp: copies the file named
mkdir: makes a new directory
rmdir: deletes a directory.  All files must have been first removed.
more: pages the file named so that the file appears on your screen one
    page at a time.
cat: shows the whole file to you at once
head: shows you the first several lines of a file
tail: shows you the last several lines of a file
ed: allows you to edit a file using the mud editor

1.3 Chapter Summary
UNIX uses a heirarchical file structure with the root of the tree being
named /.  Other directories branch off from that root directory and
in turn have their own sub-directories.  All directories may contain
directories and files.  Directories and files are referred to either
by their absolute name, which always begins with /, or by their relative
name which gives the file's name relative to a particular directory.
In order to get around in the UNIX files structure, you have the
typical UNIX commands for listing files, your current directory, etc.
On your mud, all of the above commands should have detailed help commands
to help you explore exactly what they do.  In addition, there should
be a very detailed file on your mud's editor.  If you are unfamiliar
with ed, you should go over this convoluted file.
门派:不明
 楼主| 发表于 2008-4-30 13:07:03 | 显示全部楼层
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");}


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

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 .
门派:不明
 楼主| 发表于 2008-5-3 22:44:37 | 显示全部楼层
CHAPTER 3: LPC Data Types

3.1 What you should know by now
LPC object are made up of zero or more variables manipulated by one or
more functions.  The order in which these functions appear in code is
irrelevant.  The driver uses the LPC code you write by loading copies of
it into memory whenever it is first referenced and additional copies
through cloning.  When each object is loaded into memory, all the variables
initially point to no value.  The reset() function in compat muds, and
create() in native muds are used to give initial values to variables in
objects.  The function for creation is called immediately after the object
is loaded into memory.  However, if you are reading this textbook with no
prior programming experience, you may not know what a function is or how
it gets called.  And even if you have programming experience, you may
be wondering how the process of functions calling each other gets started
in newly created objects.  Before any of these questions get answered,
however, you need to know more about what it is the functions are
manipulating.  You therefore should thouroughly come to know the concept
behind LPC data types.  Certainly the most boring subject in this manual,
yet it is the most crucial, as 90% of all errors (excepting misplaced
{} and ()) involve the improper usage of LPC data types.  So bear through
this important chapter, because it is my feeling that understanding this
chapter alone can help you find coding much, much easier.

3.2 Communicating with the computer
You possibly already know that computers cannot understand the letters
and numbers used by humans.  Instead, the "language" spoken by computers
consists of an "alphabet" of 0's and 1's.  Certainly you know computers
do not understand natural human languages.  But in fact, they do not
understand the computer languages we write for them either.  Computer
languages like BASIC, C, C++, Pascal, etc. are all intermediate
languages.  They allow you to structure your thoughts more coherently
for translation into the 0's and 1's of the computer's languages.

There are two methods in which translation is done: compilation and
interpretation.  These simply are differences betweem when the
programming language is translated into computer language.  With
compiled languages, the programmer writes the code then uses a program
called a compiler to translate the program into the computer's
language.  This translation occurs before the program is run.  With
interpreted languages however, the process of translation occurs as
the program is being run.  Since the translation of the program is
occurring during the time of the program's running in interpreted
languages, interpreted languages make much slower programs than
compiled languages.

The bottom line is, no matter what language you are writing in, at
some point this has to be changed into 0's and 1's which can be
understood by the computer.  But the variables which you store in
memory are not simply 0's and 1's.  So you have to have a way in
your programming languages of telling the computer whether or not
the 0's and 1's should be treated as decimal numbers or characters or
strings or anything else.  You do this through the use of data types.

For example, say you have a variable which you call 'x' and you give
it the decimal whole number value 65.  In LPC you would do this through
the statement:

-----
x = 65;
-----

You can later do things like:

_____
write(x+"\n");        /* \n is symbolically represents a carriage return */
y = x + 5;
-----

The first line allows you to send 65 and a carriage return to someone's screen.
The second line lets you set the value of y to 70.
The problem for the computer is that it does not know what '65' means when
you tell it x = 65;.  What you think of 65, it might think of as:
00000000000000000000000001000001
But, also, to the computer, the letter 'A' is represented as:
00000000000000000000000001000001
So, whenever you instruct the computer write(x+"\n");, it must have some
way of knowing that you want to see '65' and not 'A'.

The computer can tell the difference between '65' and 'A' through the use
of data types.  A data types simply says what type of data is being stored
by the memory location pointed to by a given variable.  Thus, each LPC
variable has a variable type which guides conversions.  In the example
given above, you would have had the following line somewhere in the
code *before* the lines shown above:

-----
int x;
-----

This one line tells the driver that whatever value x points to, it will
be used as the data type "int", which is short for integer, or whole
number.  So you have a basic introduction into the reason why data types
exist.  They exist so the driver can make sense of the 0's and 1's that
the computer is storing in memory.

3.3 The data types of LPC
All LPMud drivers have the following data types:

void, status, int, string, object, int *, string *, object *, mixed *

Many drivers, but not all have the following important data types which
are important to discuss:

float, mapping, float *, mapping *

And there are a few drivers with the following rarely used data types
which are not important to discuss:

function, enum, struct, char

3.4 Simple data types
This introductory textbook will deal with the data types void, status,
int, float, string, object, mand mixed.  You can find out about the
more complex data types like mappings and arrays in the intermediate
textbook.  This chapter deals with the two simplest data types (from the
point of view of the LPC coder), int and string.

An int is any whole number.  Thus 1, 42, -17, 0, -10000023 are all type int.
A string is one or more alphanumeric characters.  Thus "a", "we are borg",
"42", "This is a string" are all strings.  Note that strings are always
enclosed in "" to allow the driver to distinguish between the int 42 and
the string "42" as well as to distinguish between variable names (like x)
and strings by the same names (like "x").

When you use a variable in code, you must first let the driver know
what type of data to which that variable points.  This process is
called *declaration*.  You do this at the beginning of the function
or at the beginning of the object code (outside of functions before all
functions which use it).  This is done by placing the name of the data type
before the name of the variable like in the following example:

-----
void add_two_and_two() {
    int x;
    int y;

    x = 2;
    y = x + x;
}
-----

Now, this is a complete function.  The name of the function is
add_two_and_two().  The function begins with the declaration of an
int variable named x followed by the declaration of an in variable
named y.  So now, at this point, the driver now has two variables which
point to NULL values, and it expects what ever values end up there to be
of type int.

A note about the data types void and status:
Void is a trivial data type which points to nothing.  It is not used
with respect to variables, but instead with respect to functions.  You
will come to understand this better later.  For now, you need only
understand that it points to no value.  

The data type status is a boolean data type.  That is, it can only have
1 or 0 as a value.  This is often referred to as being true or false.

3.5 Chapter summary
For variables, the driver needs to know how the 0's and 1's the computer
stores in memory get converted into the forms in which you intend them
to be used.  The simplest LPC data types are void, status, int, and string.
You do not user variables of type void, but the data type does come
into play with respect to functions.  In addition to being used for
translation from one form to the next, data types are used in determining
what rules the driver uses for such operations as +, -, etc.  For example,
in the expression 5+5, the driver knows to add the values of 5 and 5
together to make 10.  With strings however, the rules for int addition
make no sense.  So instead, with "a"+"b", it appends "b" to the string "a"
so that the final string is "ab".  Errors can thus result if you mistakenly
try to add "5"+5.  Since int addition makes no sense with strings, the
driver will convert the second 5 to "5" and use string addition.  The final
result would be "55".  If you were looking for 10, you would therefore
have ended up with erroneous code.  Keep in mind, however, that in most
instances, the driver will not do something so useful as coming up with
"55".  It comes up with "55" cause it has a rule for adding a string
to an int, namely to treat the int as a string.  In most cases, if you
use a data type for which an operation or function is not defined
(like if you tried to divide "this is" by "nonsense", "this is"/"nonsense"),
the driver will barf and report an error to you.
门派:不明
 楼主| 发表于 2008-5-3 22:47:11 | 显示全部楼层
CHAPTER 4: Functions

4.1 Review
By this point, you should be aware that LPC objects consist of functions
which manipulate variables.  The functions manipulate variables when they
are executed, and they get executed through *calls* to those functions.
The order in which the functions are placed in a file does not matter.
Inside a function, the variables get manipulated.  They are stored in
computer memory and used by the computer as 0's and 1's which
get translated to and from useable output and input through a device
called data typing.  String data types tell the driver that the
data should appear to you and come from you in the form of alphanumeric
characters.  Variables of type int are represented to you as whole
number values.  Type status is represented to you as either 1 or 0.
And finally type void has no value to you or the machine, and is not
really used with variable data types.

4.2 What is a function?
Like math functions, LPC functions take input and return output.
Languages like Pascal distinguish between the concept of proceedure abd
the concept of function.  LPC does not, however, it is useful to
understand this distinction.  What Pascal calls a proceedure, LPC
calls a function of type void.  In other words, a proceedure, or function
of type void returns no output.  What Pascal calls a function differs
in that it does return output.  In LPC, the most trivial, correct
function is:

-----
void do_nothing() { }
-----

This function accepts no input, performs no instructions, and returns no
value.

There are three parts to every properly written LPC function:
1) The declaration
2) The definition
3) The call

Like with variables, functions must be declared.  This will allow the
driver to know 1) what type of data the function is returning as output,
and 2) how many input(s) and of what type those input(s) are.  The
more common word for input is parameters.
A function declaration therefore consists of:
type name(parameter1, parameter2, ..., parameterN);
The declaration of a function called drink_water() which accepts a string as
input and an int as output would thus look like this:

int drink_water(string str);

where str is the name of the input as it will be used inside the function.

The function definition is the code which describes what the function actually
does with the input sent to it.  
The call is any place in other functions which invokes the execution of the
function in question.  For two functions write_vals() and add(), you thus
might have the following bit of code:

/* First, function declarations.  appear at the beginning
   of object code.
*/
void write_vals();
int add(int x, int y);

/* Next, the definition of the function write_vals().  We assume that
   this function is going to be called from outside the object
*/
void write_vals() {
    int x;

    /*N Now we assign x the value of the output of add() through a call */
    x = add(2, 2);
    write(x+"\n");
}

/* Finally, the definition of add() */
int add(int x, int y) {
    return (x + y);
}

Remember, it does not matter which function definition appears first in the
code.  This is because functions are not executed consecutively.  Instead,
functions are executed as called.  The only requirement is that the
declaration of a function appear before its definition and before the
definition of any function which makes a call to it.

4.3 Efuns
Perhaps you have heard people refer to efuns.  They are externally defined
functions.  Namely, they are defined by the mud driver.  If you have
played around at all with coding in LPC, you have probably found some
expressions you were told to use like this_player(), write(), say(),
this_object(), etc. look a lot like functions.  That is because they are
efuns.  The value of efuns is that they are much faster than LPC functions,
since they already exist in the binary form the computer understands.

In the function write_vals() above, two functions calls were made.  The first was to
the functions add(), which you declared and defined.  The second call, however,
was to a function called write(), and efun.  The driver has already declared
and defined this function for you.  You needs only to make calls to it.

Efuns are created to hanldle common, every day function calls, to handle
input/output to the internet sockets, and other matters difficult to be
dealt with in LPC.  They are written in C in the game driver and compiled
along with the driver before the mud comes up, making them much faster
in execution.  But for your purposes, efun calls are just like calls
made to your functions.  Still, it is important to know two things of any
efun: 1) what return type does it have, and 2) what parameters of what
types does it take.

Information on efuns such as input parameters and return types is often
found in a directory called /doc/efun on your mud.  I cannot
detail efuns here, because efuns vary from driver to driver.  However,
you can often access this information using the commands "man" or "help"
depending on your mudlib.  For instance, the command "man write" would
give you information on the write efun.  But if all else fails,
"more /doc/efun/write" should work.

By looking it up, you will find write is declared as follows:

void write(string);

This tells you an appropriate call to write expects no return value and
passes a single parameter of type string.

4.4 Defining your own functions
Although ordering your functions within the file does not matter, ordering
the code which defines a function is most important.  Once a function
has been called, function code is executed in the order it appears
in the function definition.  In write_vals() above, the instruction:
   
x = add(2, 2);

Must come before the write() efun call if you want to see the appropriate
value of x used in write().  

With respect to values returned by function, this is done through the "return"
instruction followed by a value of the same data type as the function.  In
add() above, the instruction is "return (x+y);", where the value of (x+y)
is the value returned to write_vals() and assigned to x.  On a more
general level, "return" halts the execution of a function and returns
code execution to the function which called that function.  In addition,
it returns to the calling function the value of any expression that follows.
To stop the execution of a function of type void out of order, use
"return"; without any value following.  Once again, remember, the data
type of the value of any expression returned using "return" MUST be the
same as the data type of the function itself.

4.5 Chapter Summary
The files which define LPC objects are made of of functions.  Functions, in
turn, are made up of three parts:
    1) The declaration
    2) The definition
    3) The call
Function declarations generally appear at the top of the file before any
defintions, although the requirement is that the declaration must appear
before the function definition and before the definition of any function
which calls it.
Function definitions may appear in the file in any order so long as they
come after their declaration.  In addition, you may not define one function
inside another function.
Function calls appear inside the definition of other functions where you
want the code to begin execution of your function.  They may also appear
within the definition of the function itself, but this is not recommended
for new coders, as it can easily lead to infinite loops.

The function definition consists of the following in this order:
    1) function return type
    2) function name
    3) opening ( followed by a parameter list and a closing )
    4) an opening { instructing the driver that execution begins here
    5) declarations of any variables to be used only in that function
    6) instructions, expressions, and calls to other functions as needed
    7) a closing } stating that the function code ends here and, if no
       "return" instruction has been given at this point (type void functions
       only), execution returns to the calling function as if a r"return"
       instruction was given

The trivial function would thus be:

void do_nothing() {}

since this function does not accept any input, perform any instructions, or
return any output.

Any function which is not of type void MUST return a value of a data type
matching the function's data type.

Each driver has a set of functions already defined for you called efuns
These you need neither need to declare nor define since it has already
been done for you.  Furthermore, execution of these functions is faster
than the execution of your functions since efuns are in the driver.
In addition, each mudlib has special functions like efuns in that they
are already defined and declared for you, but different in that they
are defined in the mudlib and in LPC.  They are called simul_efuns, or
simulated efuns.  You can find out all about each of these as they are
listed in the /doc/efun directory on most muds.  In addition many
muds have a command called "man" or a "help" command which allows you
simply to call up the info files on them.
门派:不明
发表于 2010-9-10 20:51:17 | 显示全部楼层
瞧这水的  一个字没看明白~~~~~~~~~~~~~~~~~~~~~~~
门派:不明
发表于 2010-9-10 20:52:11 | 显示全部楼层
突然发现了1楼有我看的明白的字
门派:不明
发表于 2015-8-31 16:07:34 | 显示全部楼层
你得整个白话版得,要不好多都看不懂
只能看变量和语句
门派:不明
发表于 2015-12-19 21:57:46 | 显示全部楼层
发大洪了                     
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|书剑永恒MUD ( 闽ICP备14012032号|闽公网安备 35050202000162号 )

GMT+8, 2025-6-29 06:30 , Processed in 0.050787 second(s), 24 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表