Here is the report on the messaging system that I have written
up for Aeternity. I'm posting it here as it might be helpful documenting
for others and to get feedback from you all concerning anything I might
have missed or should correct. Thanks!
Also, thanks to Levi for the help in this.
Lance
The Difference Between Ctext and
CML ------------------------------------------------------- Before I
explain the messaging system, you need to know the difference between CML and
Ctext. CML is the tag imbedded text that staffers will enter messaging
as. It looks like:
You take [this].
Ctext is the compiled
form of CML that cold C can work with. It is a frob datatype using the
$ctext_frob object class. It looks like:
<$ctext_frob, [["You
take ", <$generator, ["this", [], [], 'gen_this]>
In general, a
coder making use of any of these existing systems does not have to work with
Ctext directly. They only need to call methods that generate or
retrieve the Ctext and send it to other methods.
Messaging
system -------------------------- Help definition of a message: The
message system is used to group several different Ctext messages under a
specific message type for single evaluation. A message is composed of a base
and any number of branches. The branches defne each specific variation on the
message, such as with teleport messages--there is the actor's message, the
source message and the destination message. This message's base is "teleport"
with the branches of "actor", "source" and "dest". By default if no branch is
specified, the "general" branch is used instead (so referencing a message
with no branch, and with the "general" branch are equivalent).
What
this means: This is essentially a callback system to objects to
return uncompiled Ctext (CML) messages for use by other systems. A
message base can be considered a single message case. Branches can be
considered facets of a base. For example, here is a look at the
exit-close message base defined on $path (as seen with @msg
$path:)
exit-close = [actor] closes [this].
exit-close.actor = You close [this].
In this case, exit-close is the
message base, and general and actor are the branches. When a person
opens this particular path, the open command method will make a call to the
path to retrieve this messaging. The "You close [this]." actor branch
will be displayed to the person invoking the command, while the "[actor]
closes [this]." general branch will be displayed to the rest of the
room.
Message bases and branches can easily be added or subtracted with
the @msg-define and @msg-undefine commands. Further customization of
messages can be accomplished with the addition of new CML tags.
The
strengths of this system are: · It lets a non-coder override default
messaging for different object's (people, places, things, paths, etc.)
without ever having to code on the object or know anything more than how to
use the @msg command and CML. · It lets a coder derive unique messaging from
an object for use by their systems. · It gives a unique 'look' to
different game systems while preserving how their mechanics work. ·
Message's are inherited, enabling us to set default messaging for object's on
core object classes as opposed to each individual object, saving memory space
and making it unnecessary for GM's to set messaging on every object they
create. For example, all default messaging for paths are defined on the
$path object. When a GM creates a new path, say $path_22, anything
that makes use of messages on it will look first at $path_22 for the
messaging and then at $path for the default if nothing is found. · Message
bases and branches can be added or subtracted from objects as needed with
in-game commands (@def-msg and @undef-msg). No coding
is necessary. · The messaging system is further expandable with the
addition of new CML tags.
The drawbacks of the present system
are: · It is not very "intelligent." You have to write different
messaging for different viewpoints, even if all parts of the message are the
same. It is possible to have a more intelligent messaging system that
is able to generate messaging for different viewpoints from one single
message in these instances. · Though the system is easy on coders, it
could be made even more easy by creating a standard method to call to objects
to return the dictionary of ctext variables for use with the .eval_message()
method (see the Retrieving & Displaying A Message Via Cold C
Section). · The command parser itself could generate the ctext variable
dictionary based on those defined in the command invoking object. It
could then pass this directly to the command method for use in the
.eval_message() method (see the Retrieving & Displaying A Message Via
Cold C Section).
Viewing & Modifying
Messages -------------------------------------------- To view or modify
messages on an object, you must use the @msg
command. Syntax:
@msg|@message [-clear]
[<target>:][<msg>=<value>]
<target> is the target
object <msg> is the message branch and base in the format
base.branch. <value> is the new message. <-clear> will clear
the message.
Example: @msg $place_42: -- Messages on
the place ($place): connect = [actor] wakes up. disconnect =
[actor] falls asleep. housekeeper = The housekeeper arrives and takes
[actor]'s body away. ---
@msg $place_42:connect = [actor] blinks his
eyes confusedly, as if startled out of a trance. -- Message changed
to: connect = [actor] blinks his eyes confusedly, as if startled out
of a trance.
Defining And Undefining
Messages ------------------------------------------------- To define a new
message base on an object, you must use the @def-msg command. Syntax
is:
@def-msg <target>:<msg> [options] <target> is
the object. <msg> is the message base. [options] are the options for
the command, as follows:
+b?ranches Define the branches, in a comma
delimited list (no spaces).
+c?ompiler Define the CML compiler object.
Defaults to $compiler.
+e?valuator Define the CML evaluator object.
Defaults to $bs_eval.
+g?etter
Define the getter. Defaults to 'standard_get_msg
Example: @def-msg
me:test +b=general,actor Message $user_wauric:test defined. Message
branches defined: $user_wauric:test.general
$user_wauric:test.actor
In general, the compiler, evaluator and getter
should remain the same, though this does give us the ability to create new
compilers, evaluaters and getters. do have the ability to create new
compilers, evaluators
To undefine a message base and all branches from an
object, you must use the @udef-msg command. Syntax
is:
@undef-msg <target>:<msg> <target> is the
object. <msg> is the message base.
Example: @undef-msg
me:jump Message $user_lance:jump undefined.
Retrieving &
Displaying A Message From An Object Via Cold
C ---------------------------------------------------------------------------- ------------ The
messaging system is contained in the $foundation object, which makes
it definable on most object's in the core. The message definitions are
stored in the object's defined_msgs variable, while the actual messages are
found in the msgs variable. The method for retrieving these messages
for display is:
$foundation.eval_message(name, definer,
vars)
This method will retrieve the ctext messaging for all branches of a
message base. The name argument is a string of the message base.
The definer argument should be the object name of the object that the message
is defined in. The vars argument should be a dictionary containing the
Ctext variables for the message. The keys to this vars dictionary
should be strings, while their associated values should be objnames for the
$-variables and whatever data types are appropriate for the others. If
the object receiving this call does not have a message-definition or actual
message stored on it for the name specified, then this method will look at
the definer object for defaults. Example call:
cvars =
#[["actor", sender().name()], ["$actor", sender()], ["this", this().name()],
["$this", this()]]; msg = $path.eval_message("exit-close", $path,
vars);
The return value of a call to .eval_message is a message frob
(class $message_frob). Its data part is a dictionary. The
dictionary keys are the objnames of the message branch targets (except for
the general branch, if it exists, which remans string "general"). The
associated values of those keys are Ctext frobs that contain the text to be
displayed to the targets. Example return from the above
call:
<$message_frob, #[["general", <$ctext_frob, [["Lance", "
closes ", "Lance", "."], #[["actor", "Lance"], ["$actor", $user_lance],
["this", "Lance"], ["$this", $user_lance], ['evaluator, $bs_eval], ['varkeys,
0], ['this, $path], ['time, 'pre], ['sender, $message_frob]]]>],
[$user_lance, <$ctext_frob, [["You close ", "Lance", "."], #[["actor",
"Lance"], ["$actor", $user_lance], ["this", "Lance"], ["$this",
$user_lance], ['evaluator, $bs_eval], ['varkeys, 0], ['this, $path], ['time,
pre], ['sender, $message_frob]]]>]]>
The coder does not need to
be concerned with that value, however. I list it here only for
reference. To make it display the branches correctly to the different
parties, all the coder has to do is call .announce() in the appropriate
locations sending this message frob as the argument. Continuing the
above example, this would
be:
sender().location().announce(msg);
In this case, this
will display the actor branch to the actor and the general branch to the rest
of the room.
One of the ways the system could be improved is to create a
standard method for retrieving the ctext variables from an object. On
some objects there is a method that returns this variable dictionary for you,
such as $path.path_msg_vars(), while on others you have to know the
variable dictionary to send it as an argument, such as when calling
.eval_message() to a descendant of $user.
Walkthrough Of Message
System Use ----------------------------------------------------- The
following is a walkthrough for creating the jump verb on $user_wauric that
uses messaging defined on $user_wauric. I also show how the
messaging can be changed easily in game with the @msg command, though the
verb mechanics remain the same.
@def-msg me:jump
+b=general,actor Message $user_wauric:jump defined. Message branches
defined: $user_wauric:jump.general
$user_wauric:jump.actor
@msg me:jump=[actor] jumps up and down! --
Message changed to: jump = [actor] jumps up and down!
@msg
me:jump.actor=You jump up and down! -- Message changed to:
jump.actor = You jump up and down!
@program .jump_cmd -- Reprogramming
public method $user_wauric.jump_cmd() -- arg cmdstr, cmd; var vars,
message;
vars = #[["actor", sender().name()], ["$actor",
sender()]]; message = .eval_message("jump", $user_wauric,
vars); sender().location().announce(message); .
@ac "jump" to
$user_wauric.jump_cmd Command "jump" added to
$user_wauric.jump_cmd()
@rehash Rehashing (raw) commands on Wauric
($user_wauric)... Done.
jump You jump up and down!
Rest of
room sees: Wauric jumps up and down!
@msg me:jump=[actor] hops around
in circles. -- Message changed to: jump = [actor] hops around in
circles.
@msg me:jump.actor=You hop around in a circle. -- Message
changed to: jump.actor = You hop around in a
circle.
jump You hop around in a circle.
Rest of room
sees: Wauric hops around in circles.
Improving The System With Ctext
Variable Retrieval
Methods ---------------------------------------------------------------------------- -------- To
give an example of how the current system can be made easier to use through
ctext variable retrieval methods, I created the following .get_ctext_vars()
method on $user_wauric:
arg @args; var vars;
// Standard
ctext variable initialization method. // // args argument is for a list of
optional objects (if needed). // // args[1] = the target object //
args[2] = the destination place //
vars = #[]; vars =
dict_add(vars, "actor", .name()); vars = dict_add(vars, "$actor",
this()); vars = dict_add(vars, "source", .location()); vars =
dict_add(vars, "$source", .location()); (| vars = dict_add(vars, "target",
args[1].name()) |); (| vars = dict_add(vars, "$target", args[1]) |); (|
vars = dict_add(vars, "dest", args[2]) |); (| vars = dict_add(vars, "$dest",
args[2]) |); return vars;
I then reprogrammed the
$user_wauric.jump_cmd() method as follows:
arg cmdstr,
cmd; var vars, message;
vars =
.get_ctext_vars(); message = .eval_message("jump", $user_wauric,
vars); sender().location().announce(message);
The benefit of
creating these retrieval methods is that coders do not have to remember
what Ctext variables exist on an object or what format to pass them as an
argument to .eval_message(). Needless to say, this would save coder time
down the road.
|