blob: 51a4c3ce48b81c51600b2e79957d7dbbf7247d16 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001Intermediate LPC
2Descartes of Borg
3Novermber 1993
4
5 Chapter 2: The LPMud Driver
6
72.1 Review of Basic Driver/Mudlib Interaction
8In the LPC Basics textbook, you learned a lot about the way the mudlib
9works, specifically in relation to objects you code in order to build your
10realm. Not much was discussed about the interaction between the
11mudlib and the driver. You should know, however, that the driver
12does the following:
131) When an object is first loaded into memory, the driver will call
14create() in native muds and reset() in compat muds. A creator
15uses create() or reset() to give initial values to the object.
162) At an interval setup by the game administrator, the driver calls the
17function reset(). This allows the object to regenerate monsters and
18such. Notice that in a compat mud, the same function is used to set up
19initial values as is used to reset the room.
203) Any time a living object comes in contact with an object of any sort,
21the driver calls init() in the newly encountered object. This allows
22newly encountered objects to give living objects commands to execute
23through the add_action() efun, as well as perform other actions which
24should happen whenever a living thing encounters a given object.
254) The driver defines a set of functions known as efuns which are
26available to all objects in the game. Examples of commonly used efuns
27are: this_player(), this_object(), write(), say(), etc.
28
292.2 The Driver Cycle
30The driver is a C program which runs the game. Its basic functions are
31to accept connections from the outside world so people can login,
32interpret the LPC code which defines LPC objects and how they
33function in the game, and accept user input and call the appropriate LPC
34functions which match the event. In its most simplest essence, it is an
35unending loop.
36
37Once the game has booted up and is properly functioning (the boot up
38process will be discussed in a future, advanced LPC textbook), the
39driver enters a loop which does not terminate until the shutdown() efun
40is legally called or a bug causes the driver program to crash. First off,
41the driver handles any new incoming connections and passes control of
42the connection to a login object. After that, the driver puts together a
43table of commands which have been entered by users since the last cycle
44of the driver. After the command table is assembled, all messages
45scheduled to be sent to the connection from the last driver cycle are sent
46out to the user. At this point, the driver goes through the table of
47commands to be executed and executes each set of commands each
48object has stored there. The driver ends its cycle by calling the function
49heart_beat() in every object with a heart_beat() set and finally
50performing all pending call outs. This chapter will not deal with the
51handling of connections, but instead will focus on how the driver
52handles user commands and heartbeats and call outs.
53
542.3 User Commands
55As noted in section 1.2, the driver stores a list of commands for each
56user to be executed each cycle. The commands list has the name of the
57living object performing the command, the object which gave the living
58object that command, and the function which is to be executed in order
59to perform the command. The driver refers to the object which typed in
60the command as the command giver. It is the command giver which
61gets returned as this_player() in most cases.
62
63The driver starts at the top of the list of living objects with pending
64commands, and successively performs each command it typed by calling
65the function associated with the command and passing any arguments
66the command giver gave as arguments to the function. As the driver
67starts with the commands issued by a new living object, the command
68giver variable is changed to be equal to the new living object, so that
69during the sequence of functions initiated by that command, the efun
70this_player() returns the object which issued the command.
71
72Let's look at the command buffer for an example player. Since the
73execution of his last command, Bozo has typed "north" and "tell
74descartes when is the next reboot". The command "north" is associated
75with the function "Do_Move()" in the room Bozo is in (the command
76"north" is automatically setup by the set_exits() efun in that room). The
77command "tell" is not specifically listed as a command for the player,
78however, in the player object there is a function called "cmd_hook()"
79which is associated with the command "", which matches any possible
80user input.
81
82Once the driver gets down to Bozo, the command giver variable is set to
83the object which is Bozo. Then, seeing Bozo typed "north" and the
84function "north" is associated with, the driver calls Bozo's_Room-
85>Do_Move(0). An argument of 0 is passed to the function since Bozo
86only typed the command "north" with no arguments. The room
87naturally calls some functions it needs, all the while such that the efun
88this_player() returns the object which is Bozo. Eventually, the room
89object will call move_player() in Bozo, which in turn calls the
90move_object() efun. This efun is responsible for changing an object's
91environment.
92
93When the environment of an object changes, the commands available to
94it from objects in its previous environment as well as from its previous
95environment are removed from the object. Once that is done, the driver
96calls the efun init() in the new environment as well as in each object in
97the new environment. During each of these calls to init(), the object
98Bozo is still the command giver. Thus all add_action() efuns from this
99move will apply to Bozo. Once all those calls are done, control passes
100back from the move_object() efun to the move_player() lfun in Bozo.
101move_player() returns control back to Do_Move() in the old room,
102which returns 1 to signify to the driver that the command action was
103successful. If the Do_Move() function had returned 0 for some reason,
104the driver would have written "What?" (or whatever your driver's
105default bad command message is) to Bozo.
106
107Once the first command returns 1, the driver proceeds on to Bozo's
108second command, following much the same structure. Note that with
109"tell descartes when is the next reboot", the driver passes "descartes
110when is the next reboot" to the function associated with tell. That
111function in turn has to decide what to do with that argument. After that
112command returns either 1 or 0, the driver then proceeds on to the next
113living object with commands pending, and so on until all living objects
114with pending commands have had their commands performed.
115
1162.4 The Efuns set_heart_beat() and call_out()
117Once all commands are performed for objects with commands pending,
118the driver then proceeds to call the heart_beat() function in all objects
119listed with the driver as having heartbeats. Whenever an object calls the
120efun set_heart_beat() with a non-zero argument (depending on your
121driver, what non-zero number may be important, but in most cases you
122call it with the int 1). The efun set_heart_beat() adds the object which
123calls set_heart_beat() to the list of objects with heartbeats. If you call it
124with an argument of 0, then it removes the object from the list of objects
125with heartbeats.
126
127The most common use for heartbeats in the mudlib is to heal players and
128monsters and perform combat. Once the driver has finished dealing with
129the command list, it goes through the heartbeat list calling heart_beat() in
130each object in the list. So for a player, for example, the driver will call
131heart_beat() in the player which will:
1321) age the player
1332) heal the player according to a heal rate
1343) check to see if there are any hunted, hunting, or attacking objects
135around
1364) perform an attack if step 3 returns true.
1375) any other things which need to happen automatically roughly every
138second
139
140Note that the more objects which have heartbeats, the more processing
141which has to happen every cycle the mud is up. Objects with heartbeats
142are thus known as the major hog of CPU time on muds.
143
144The call_out() efun is used to perform timed function calls which do not
145need to happen as often as heartbeats, or which just happen once. Call
146outs let you specify the function in an object you want called. The
147general formula for call outs is:
148call_out(func, time, args);
149The third argument specifying arguments is optional. The first argument
150is a string representing the name of the function to be called. The second
151argument is how many seconds should pass before the function gets
152called.
153
154Practically speaking, when an object calls call_out(), it is added to a list
155of objects with pending call outs with the amount of time of the call out
156and the name of the function to be called. Each cycle of the driver, the
157time is counted down until it becomes time for the function to be called.
158When the time comes, the driver removes the object from the list of
159objects with pending call outs and performs the call to the call out
160function, passing any special args originally specified by the call out
161function.
162
163If you want a to remove a pending call before it occurs, you need to use
164the remove_call_out() efun, passing the name of the function being
165called out. The driver will remove the next pending call out to that
166function. This means you may have some ambiguity if more than one
167call out is pending for the same function.
168
169In order to make a call out cyclical, you must reissue the call_out() efun
170in the function you called out, since the driver automatically removes the
171function from the call out table when a call out is performed. Example:
172
173void foo() { call_out("hello", 10); }
174
175void hello() { call_out("hello", 10); }
176
177will set up hello() to be called every 10 seconds after foo() is first called.
178There are several things to be careful about here. First, you must watch
179to make sure you do not structure your call outs to be recursive in any
180unintended fashion. Second, compare what a set_heart_beat() does
181when compared directly to what call_out() does.
182
183set_heart_beat():
184a) Adds this_object() to a table listing objects with heartbeats.
185b) The function heart_beat() in this_object() gets called every single
186driver cycle.
187
188call_out():
189a) Adds this_object(), the name of a function in this_object(), a time
190delay, and a set of arguments to a table listing functions with pending
191call outs.
192b) The function named is called only once, and that call comes after the
193specified delay.
194
195As you can see, there is a much greater memory overhead associated
196with call outs for part (a), yet that there is a much greater CPU overhead
197associated with heartbeats as shown in part (b), assuming that the delay
198for the call out is greater than a single driver cycle.
199
200Clearly, you do not want to be issuing 1 second call outs, for then you
201get the worst of both worlds. Similarly, you do not want to be having
202heart beats in objects that can perform the same functions with call outs
203of a greater duration than 1 second. I personally have heard much talk
204about at what point you should use a call out over a heartbeat. What I
205have mostly heard is that for single calls or for cycles of a duration
206greater than 10 seconds, it is best to use a call out. For repetitive calls of
207durations less than 10 seconds, you are better off using heartbeats. I do
208not know if this is true, but I do not think following this can do any
209harm.
210
2112.5 Summary
212Basic to a more in depth understanding of LPC is and understanding of
213the way in which the driver interacts with the mudlib. You should now
214understand the order in which the driver performs functions, as well as a
215more detailed knowledge of the efuns this_player(), add_action(), and
216move_object() and the lfun init(). In addition to this building upon
217knowledge you got from the LPC Basics textbook, this chapter has
218introduced call outs and heartbeats and the manner in which the driver
219handles them. You should now have a basic understanding of call outs
220and heartbeats such that you can experiment with them in your realm
221code.
222
223Copyright (c) George Reese 1993