blob: 98b35525eb621e0050d1a869428fcf1a6135d2e3 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001Intermediate LPC
2Descartes of Borg
3November 1993
4
5 Chapter 6: Intermediate Inheritance
6
76.1 Basics of Inheritance
8In the textbook LPC Basics, you learned how it is the mudlib maintains
9consistency amoung mud objects through inheritance. Inheritance
10allows the mud administrators to code the basic functions and such that
11all mudlib objects, or all mudlib objects of a certain type must have so
12that you can concentrate on creating the functions which make these
13objects different. When you build a room, or a weapon, or a monster,
14you are taking a set of functions already written for you and inheriting
15them into your object. In this way, all objects on the mud can count on
16other objects to behave in a certain manner. For instance, player objects
17can rely on the fact that all room objects will have a function in them
18called query_long() which describes the room. Inheritance thus keeps
19you from having to worry about what the function query_long() should
20look like.
21
22Naturally, this textbook tries to go beyond this fundamental knowledge
23of inheritance to give the coder a better undertstanding of how
24inheritance works in LPC programming. Without getting into detail that
25the advanced domain coder/beginner mudlib coder simply does not yet
26need, this chapter will try to explain exactly what happens when you
27inherit an object.
28
296.2 Cloning and Inheritance
30Whenever a file is referenced for the first time as an object (as opposed
31to reading the contents of the file), the game tries to load the file into
32memory and create an object. If the object is successfully loaded into
33memory, it becomes as master copy. Master copies of objects may be
34cloned but not used as actual game objects. The master copy is used to
35support any clone objects in the game.
36
37The master copy is the source of one of the controversies of mud LPC
38coding, that is whether to clone or inherit. With rooms, there is no
39question of what you wish to do, since there should only be one instance
40of each room object in the game. So you generally use inheritance in
41creating rooms. Many mud administrators, including myself, however
42encourage creators to clone the standard monster object and configure it
43from inside room objects instead of keeping monsters in separate files
44which inherit the standard monster object.
45
46As I stated above, each time a file is referenced to create an object, a
47master copy is loaded into memory. When you do something like:
48void reset() {
49 object ob;
50 ob = new("/std/monster");
51 /* clone_object("/std/monster") some places */
52 ob->set_name("foo monster");
53 ... rest of monster config code followed by moving
54it to the room ...
55}
56the driver searches to see if their is a master object called "/std/monster".
57If not, it creates one. If it does exist, or after it has been created, the
58driver then creates a clone object called "/std/monster#<number>". If
59this is the first time "/std/monster" is being referenced, in effect, two
60objects are being created: the master object and the cloned instance.
61
62On the other hand, let's say you did all your configuring in the create()
63of a special monster file which inherits "/std/monster". Instead of
64cloning the standard monster object from your room, you clone your
65monster file. If the standard monster has not been loaded, it gets loaded
66since your monster inherits it. In addition, a master copy of your file
67gets loaded into memory. Finally, a clone of your monster is created
68and moved into the room, for a total of three objects added to the game.
69Note that you cannot make use of the master copy easily to get around
70this. If, for example, you were to do:
71 "/wizards/descartes/my_monster"->move(this_object());
72instead of
73 new("/wizards/descartes/my_monster")->move(this_object());
74you would not be able to modify the file "my_monster.c" and update it,
75since the update command destroys the current master version of an
76object. On some mudlibs it also loads the new version into memory.
77Imagine the look on a player's face when their monster disappears in
78mid-combat cause you updated the file!
79
80Cloning is therefore a useful too when you plan on doing just that-
81cloning. If you are doing nothing special to a monster which cannot be
82done through a few call others, then you will save the mud from getting
83loaded with useless master copies. Inheritance, however, is useful if
84you plan to add functionality to an object (write your own functions) or
85if you have a single configuration that gets used over and over again
86(you have an army of orc guards all the same, so you write a special orc
87file and clone it).
88
896.3 Inside Inheritance
90When objects A and B inherit object C, all three objects have their own
91set of data sharing one set of function definitions from object C. In
92addition, A and B will have separate functions definitions which were
93entered separately into their code. For the sake of example throughout
94the rest of the chapter, we will use the following code. Do not be
95disturbed if, at this point, some of the code makes no sense:
96
97OBJECT C
98private string name, cap_name, short, long;
99private int setup;
100
101void set_name(string str)
102nomask string query_name();
103private int query_setup();
104static void unsetup();
105void set_short(string str);
106string query_short();
107void set_long(string str);
108string query_long();
109
110
111void set_name(string str) {
112 if(!query_setup()) {
113 name = str;
114 setup = 1;
115}
116
117nomask string query_name() { return name; }
118
119private query_setup() { return setup; }
120
121static void unsetup() { setup = 0; }
122
123string query_cap_name() {
124 return (name ? capitalize(name) : ""); }
125}
126
127void set_short(string str) { short = str; }
128
129string query_short() { return short; }
130
131void set_long(string str) { long = str; }
132
133string query_long() { return str; }
134
135void create() { seteuid(getuid()); }
136
137OBJECT B
138inherit "/std/objectc";
139
140private int wc;
141
142void set_wc(int wc);
143int query_wc();
144int wieldweapon(string str);
145
146void create() { ::create(); }
147
148void init() {
149 if(environment(this_object()) == this_player())
150 add_action("wieldweapon", "wield");
151}
152
153void set_wc(int x) { wc = x; }
154
155int query_wc() { return wc; }
156
157int wieldweapon(string str) {
158 ... code for wielding the weapon ...
159}
160
161OBJECT A
162inherit "/std/objectc";
163
164int ghost;
165
166void create() { ::create(); }
167
168void change_name(string str) {
169 if(!((int)this_object()->is_player())) unsetup();
170 set_name(str);
171}
172
173string query_cap_name() {
174 if(ghost) return "A ghost";
175 else return ::query_cap_name();
176}
177
178As you can see, object C is inherited both by object A and object B.
179Object C is a representation of a much oversimplified base object, with B
180being an equally oversimplified weapon and A being an equally
181simplified living object. Only one copy of each function is retained in
182memory, even though we have here three objects using the functions.
183There are of course, three instances of the variables from Object C in
184memory, with one instance of the variables of Object A and Object B in
185memory. Each object thus gets its own data.
186
1876.4 Function and Variable Labels
188Notice that many of the functions above are proceeded with labels which
189have not yet appeared in either this text or the beginner text, the labels
190static, private, and nomask. These labels define special priveledges
191which an object may have to its data and member functions. Functions
192you have used up to this point have the default label public. This is
193default to such a degree, some drivers do not support the labeling.
194
195A public variable is available to any object down the inheritance tree
196from the object in which the variable is declared. Public variables in
197object C may be accessed by both objects A and B. Similarly, public
198functions may be called by any object down the inheritance tree from the
199object in which they are declared.
200
201The opposite of public is of course private. A private variable or
202function may only be referenced from inside the object which declares it.
203If object A or B tried to make any reference to any of the variables in
204object C, an error would result, since the variables are said to be out of
205scope, or not available to inheriting classes due to their private labels.
206Functions, however, provide a unique challenge which variables do not.
207External objects in LPC have the ability to call functions in other objects
208through call others. The private label does not protect against call
209others.
210
211To protect against call others, functions use the label static. A function
212which is static may only be called from inside the complete object or
213from the game driver. By complete object, I mean object A can call
214static functions in the object C it inherits. The static only protects against
215external call others. In addition, this_object()->foo() is considered an
216internal call as far as the static label goes.
217
218Since variables cannot be referenced externally, there is no need for an
219equivalent label for them. Somewhere along the line, someone decided
220to muddy up the waters and use the static label with variables to have a
221completely separate meaning. What is even more maddening is that this
222label has nothing to do with what it means in the C programming
223language. A static variable is simply a variable that does not get saved to
224file through the efun save_object() and does not get restored through
225restore_object(). Go figure.
226
227In general, it is good practice to have private variables with public
228functions, using query_*() functions to access the values of inherited
229variables, and set_*(), add_*(), and other such functions to change
230those values. In realm coding this is not something one really has to
231worry a lot about. As a matter of fact, in realm coding you do not have
232to know much of anything which is in this chapter. To be come a really
233good realm coder, however, you have to be able to read the mudlib
234code. And mudlib code is full of these labels. So you should work
235around with these labels until you can read code and understand why it
236is written that way and what it means to objects which inherit the code.
237
238The final label is nomask, and it deals with a property of inheritance
239which allows you to rewrite functions which have already been defined.
240For example, you can see above that object A rewrote the function
241query_cap_name(). A rewrite of function is called overriding the
242function. The most common override of a function would be in a case
243like this, where a condition peculiar to our object (object A) needs to
244happen on a call ot the function under certain circumstances. Putting test
245code into object C just so object A can be a ghost is plain silly. So
246instead, we override query_cap_name() in object A, testing to see if the
247object is a ghost. If so, we change what happens when another object
248queries for the cap name. If it is not a ghost, then we want the regular
249object behaviour to happen. We therefore use the scope resolution
250operator (::) to call the inherited version of the query_cap_name()
251function and return its value.
252
253A nomask function is one which cannot be overridden either through
254inheritance or through shadowing. Shadowing is a sort of backwards
255inheritance which will be detailed in the advanced LPC textbook. In the
256example above, neither object A nor object B (nor any other object for
257that matter) can override query_name(). Since we want to use
258query_name() as a unique identifier of objects, we don't want people
259faking us through shadowing or inheritance. The function therefore gets
260the nomask label.
261
2626.5 Summary
263Through inheritance, a coder may make user of functions defined in
264other objects in order to reduce the tedium of producing masses of
265similar objects and to increase the consistency of object behaviour across
266mudlib objects. LPC inheritance allows objects maximum priveledges in
267defining how their data can be accessed by external objects as well as
268objects inheriting them. This data security is maintained through the
269keywords, nomask, private, and static.
270
271In addition, a coder is able to change the functionality of non-protected
272functions by overriding them. Even in the process of overriding a
273function, however, an object may access the original function through
274the scope resolution operator.
275
276Copyright (c) George Reese 1993