Game Related Functions

Summary
Send some text to a node.
Interpolate the value(s) of variables at runtime.
Check whether a compiled message number is valid (exists).
Expand the value(s) in a compiled message.
Returns the uncompiled, unexpanded message with variable names intact.
Send an expanded msg’s text directly to a node.
Send some text to a node followed by the prompt sentinel.
Send an <ATCP> message to a node.
Simulate an internal error being thrown.
Print some text to the console that Rapture was started on.
Check for any waiting player input.
Wait until some input or node activity is detected.
Signal Rapture that the game code should be reloaded when next shut down.
Force a write of all database information to disk.
Force a write of all database information to disk.
Lookup a potential command and return its numeric verb handle.
This will ‘call’ a verb definition given its ‘handle’.
Turn on internal profiling of execution.
Turn off the internal profiling.
Clear out any existing profiling information and begin fresh.
Generate a human-readable output of current profiling info.
Shell out to the system and execute a command.
Convert a ‘timestamp’ string into a UTC (universal coordinated time) seconds from Epoch value.
Convert a UTC seconds value into a timestamp representation.
Adds IP address to max node IP whitelist.
Remove an IP address from the max node IP whitelist.
Returns the list of whitelisted IP addresses.
Sets the reported IP address for a node.

msgstr

Send some text to a node.  This is the primary output interface for any game core.  It simply writes data to a given node.  The node must be active for anything to be seen.  Output is buffered internally until Rapture detects it’s okay to send.  This can potentially lead to a buffer filling up if you send very large quantities of data before the corresponding client can fetch it (sometimes happens with slower modem users).  However, this buffer is quite large (on the order of 10k per node) and is very rarely a problem.  If this does occur, the buffer is culled and the user is sent a message telling it that some output was culled.  Operation then resumes normally.

It’s suggested that you implement a level of indirection for this routine to permit such things as immortal watching or redirecting output to files or buffers (useful for MORE type interfaces).

Params

nodenode to send text to
str$text to send

Returns

nothing

expand

Interpolate the value(s) of variables at runtime.  This function is quite powerful in that it allows delayed expansion of variables by embedding certain sequences and letting them be expanded later.  For example, say you need to pass along gender information, you could embed the sequence $(he$) in a string and have it expand the value of the global “he$” when this function is called.  This also allows slight modification of the value so that proper capitalization can be used.  Let’s see a larger example.

global he$, him$, his$, actor$, target$;

subroutine set_target_gender(gender)
{
if( gender = MALE )then
{
he$ = "he"; him$ = "him"; his$ = "his";
}
else if( gender = FEMALE )then
{
he$ = "she"; him$ = "her"; his$ = "hers";
}
else
{
he$ = "it"; him$ = "it"; his$ = "its";
}
}

subroutine some_attack_handler(attacker, target, witness)
{
local msg$;

msg$ = "$+(he$) kicks $(target) swiftly in $(his$) leg.\n";

set_target_gender(persona[target].IsFemale);
send_text(witness, expand$(msg$));
damage(target, 5);
}

Now, the above example is overly simplistic.  For a real implementation, you would probably have the set_target_gender be one large routine that would set both the target and the attacker gender info (as well as their names) at once (so it could also expand to “You” if the attacker is the same as the witness, etc).  However, this example is enough to illustrate the advantages of this delayed and embedded syntax.  Ideally the routine to send out messages to attackers and witnesses would be a single central routine and could send the “scoped” messages to everyone after setting the appropriate values to the globals.  It doesn’t require many slightly different versions of the string for each definition.

The format for expansions is the same as compiled messages (see <RMSG>) for info on the capitalization options for expansions.  Database references cannot be expanded however, so put them into variables first.

Params

str$a string with embedded expansions

Returns

A string with the values of each variable expanded in place of the expansion markers.

valid_message

Check whether a compiled message number is valid (exists).  If the message exists in the database then this will check and return true.  Often messages are compiled in a nonlinear manner and it’s hard to determine if one exists.

Params

msgidnumeric id of the compiled message

Returns

True if the message exists.

msg$

Expand the value(s) in a compiled message.  This is much like the <expand$>() function except it fetches the message’s content from the compiled message store before expanding any values in it.  It is slightly faster than expand$() due to the message being already compiled (and the syntax verified to be true).

Params

msgidnumeric id of the compiled message

Returns

A string with the values expanded appropriately.

rawmsg$

Returns the uncompiled, unexpanded message with variable names intact.

Params

msgidnumeric id of the message to be decompiled

Returns

A string with none of the messages variables expanded.

message

Send an expanded msg’s text directly to a node.  This will fetch a message from the compiled message store, expand it, and write it to the node in one single call.  It is equivalent to msgstr(node, msg$(msgid)).  Use of this method is frowned upon due to its lack of flexibility.  It is primarily provided for compatibility with older code.

Params

nodenode to send expanded text to
msgidnumeric id of the compiled message to expand

Returns

nothing

send_prompt

Send some text to a node followed by the prompt sentinel.  This routine differs from msgstr because it sends the text immediately followed by a telnet sequence indicating the text should be treated as a prompt.  (This is a different sequence depending on the telnet options negotiated by each node.)  An example of sending a prompt with stats info.

str$ = "100hp, 100mana, healthy --";
send_prompt(node, str$);

Params

nodenode to send the prompt text to
prompt$the text to be sent as the prompt

Returns

nothing

atcp_msg

Send an <ATCP> message to a node.  ATCP is an ‘invisible’ protocol that piggy-backs on top of telnet.  It allows the client and server to communicate without the user seeing the underlying text.  See the section ATCP for more information.  If ATCP has not been negotiated on the specified node, then this subroutine does nothing.

Params

nodenode to send the message to
msg$the body of the message to send

Returns

nothing

raise_error

Simulate an internal error being thrown.  This will log an error to the error log and immediately transfer control to the error handler <error>.

Params

errmsg$A description of the error (logged to the error log).

Returns

nothing

debugout

Print some text to the console that Rapture was started on.  This is useful mainly for debugging.

Params

str$text to print

Returns

nothing

input

Check for any waiting player input.  This will see if any input has come in and return the first node/player in the queue who has had any activity.  Note that this also refers to node[].status changes as well as the first time a node connects.  <Node[].status> should be used to determine what type of activity this is reporting.  If no input is waiting, it returns 0.  Typically this is used in a tight polling loop with <check_tasks>().

DEPRECATED: This routine is obsolete and has been replaced with <wait_for_input>() which can block and wait for input.

Params

none

Returns

An integer indicating which node has activity.  game.input$ holds what input (if any) the node has sent.

wait_for_input

Wait until some input or node activity is detected.  This is the preferred way to check for player input.  It will turn control over to Rapture and it will check for tasks and input in a highly efficient manner.  This returns the node for which activity was detected.

Params

none

Returns

An integer between 1 and node.maxrecord indicating the node of interest.

reload

Signal Rapture that the game code should be reloaded when next shut down.  To stop executing a running game, simply return control out of the <main>() subroutine.  If this has been called prior to doing that, the executable code is reloaded, various systems are reset/freed, and main is called again in the new version.  Use this to enable hot-loading of new game logic.

Params

none

Returns

nothing

backup

Force a write of all database information to disk.  Databases are stored in memory during execution of a running game, so it is prudent to do a periodic ‘checkpoint’ by writing the data to disk.

This routine returns immediately and performs the backup in the background (in a forked process).  To perform a blocking backup, call backup_nofork.

Params

none

Returns

nothing

backup_nofork

Force a write of all database information to disk.  Databases are stored in memory during execution of a running game, so it is prudent to do a periodic ‘checkpoint’ by writing the data to disk.

This routine blocks execution until the backup concludes.

Params

none

Returns

nothing

search_verb

Lookup a potential command and return its numeric verb handle.  This handle uniquely identifies a verb ‘function’ and can be executed by using <doverb>().  This will work on partial strings if the verb was set to match them.  So, given a verb definition of “l(ook)”, the strings “l”, “lo”, “loo”, and “look” will return the look verb’s handle from this routine.

Params

cmdthe text (possibly partial) describing a verb

Returns

A ‘verb handle’ that can be used to call the verb body using doverb() or 0 if no suitable verb is found.

doverb

This will ‘call’ a verb definition given its ‘handle’.  Basically, to execute a verb, you need to match some text using <search_verb>() to get its handle and then pass that to this routine to actually pass control to the verb.

Params

handlethe numeric handle of a verb (as obtained from search_verb)

Returns

Nothing.

profiling_on

Turn on internal profiling of execution.  This incurs a small overhead for calculating call information, but it can be invaluable when trying to identify code bottlenecks or performance problems.  The routine profile_result$ returns a tablular output that’s human readable.

Params

none

Returns

nothing

profiling_off

Turn off the internal profiling.  This does not reset the profiling information, so you can turn it on and off at will, like a stopwatch.  Successive enablings will simply add to the results.

Params

none

Returns

nothing

reset_profiles

Clear out any existing profiling information and begin fresh.  If profiling is currently off, this will not turn it on (nor will it turn it off if it’s already on).

Params

none

Returns

Nothing.

profile_result$

Generate a human-readable output of current profiling info.  This can be a large result (one for every routine called) and is sorted by percentage of time spent in the routine.  A small sample of the first few lines of this output:

Profile Results [2003/04/01 21:55:17] (Duration: 14.55s, CPU: 1528.2 MHz)
% time t(s) calls ms/call cum. ms name
 60.29    8.77          0       n/a       n/a  sub kernel()
31.83 4.63 20 231.4681 194.1133 func shell(ss)
1.39 0.20 5 40.3722 41.8942 task "nutter_mobiles"
0.68 0.10 8 12.2823 52.4666 task "mobiletask"
0.49 0.07 13 5.4671 13.3637 task "replicacounter"
...

The fields, in order, describe the following information.

% timePercentage of total time spent in this routine
t(s)Time (in seconds) spent in this routine.
callsNumber of times this routine was called.
ms/callAverage milliseconds spent (per call) ONLY in this routine.
cum. msCumulative milliseconds spend in this call and all routines it calls.

Sometimes values in the “ms/call” and “cum.ms” fields do not make logical sense (how can cumulative be less than the single routine?).  This is due to both roundoff and estimations used in the profiling algorithms, when in doubt, trust the ms/call (cumulative is an estimated value).

You probably want to dump these results to a file and save them for posterity.

Params

none

Returns

A string holding a human-readable table of profiling results.

system

Shell out to the system and execute a command.  THIS USES THE DEFAULT SHELL.  For many reasons, this is unsafe and extreme care should be taken when using this function.  Because the text is passed directly to the system shell, all shell interpretations apply.  This includes escape characters and piping and other special characters.  Make sure what you’re executing is what you mean to be executing.  Check any variables you used for special characters or special file names.

Frequently used pitfalls

system(“rm -f “ + file$); -- No!  Unchecked file deletion! system(“cat “ + file$); -- No!  Can embed escape characters such as “`rm -rf *`”.  Etc etc...

Params

cmd$string to be passed to the system shell for execution

Returns

nothing.

stamp2time

Convert a ‘timestamp’ string into a UTC (universal coordinated time) seconds from Epoch value.  (Type of value returned by game.time.)  The format of this string is relatively flexible, but if you stray far from the “YYYY/MM/DD HH:MM:SS” format results are undefined.

Params

stamp$a timestamp (such as that returned from game.timestamp$)

Returns

A UTC “seconds from Epoch” time value (integer).

time2stamp$

Convert a UTC seconds value into a timestamp representation.  This will take an integer value such as 1067581055 and return “2003/10/31 06:17:35”.  The format is obviously “YYYY/MM/DD HH:MM:SS”.

Params

timean integer representing seconds from Epoch in UTC

Returns

A string representation of the date and time.

ipwhitelist_add

Adds IP address to max node IP whitelist.  Rapture has a default limit of 5 nodes connected per IP.  This can be overridden using the MaxPerIP configuration setting, but the IP whitelist provides a safer alternative if you know the IP addresses that many connections will be coming from (in the case of HTTP tunnelling, for example), and also allows you to dynamically allow access to specific IPs without requiring a full reboot of Rapture.  This whitelist will not be preserved if Rapture crashes or shuts down, so it is suggested that you save the list in a user-defined database or as a game.string$, and restore it when the game code is first loaded.

Params

ip$A 4 byte IP

Returns

nothing.

ipwhitelist_remove

Remove an IP address from the max node IP whitelist.  This function will silently fail if the IP is not present in the IP whitelist.

Params

ip$A 4 byte IP

Returns

nothing.

ipwhitelist_list@

Returns the list of whitelisted IP addresses.

Params

none

Returns

A vector of strings.  Each string contains one IP address.

node_set_ip

Sets the reported IP address for a node.  This is useful for setting the external IP address of a connected node that is coming from a known proxy (as in the case of HTTP tunnelling, for example).  Note: node[].host$ updating will be slightly delayed, as hostname resolution is handled by an external process.

Params

nodeA connected node
ip$A 4 byte IP address

Returns

nothing

Send some text to a node.
This is the same as rawinput$, but preceding whitespace has been stripped.