| 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(). [ MorgenGrauen is native - Boing ] |
| |
| 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. |