Help Guide
This is meant to be a supplement to the internal help. There is much that is not explained or is assumed that once learned is best documented to be shared. This document is structured based on how it all fell out of my head. There wasn't any real planning to it. If someone wants to come along and sort it out or expand on any of it, that would be wonderful.
The basics
Hardcode versus Lua
The built-in game commands are frequently referred to as hardcode, whereas any user-create commands or scripts are often referred to as softcode. MAGA uses Lua for softcode. You may find the two terms used interchangeably in this guide, or even referred to as the parser in some instances.
The parser
When running hardcode commands, you can invoke the Lua parser by enclosing code within square brackets:
[]
. For example:
@print [print(me.Name)]
. There are a few things to note in that example. The first is that if you want any output from the parser, you need to invoke the
print()
function, or another function that produces output. Another is the
me
object, this is a reference to the MAGA database object that is running the code, in this case you.
me.Name
refers to the
Name
attribute of
me
, where
Name
is one of the special 'non-attribute attributes'. More on that in a moment.
Pseudo attributes
There are several specific points of data belonging to an object that aren't stored in attributes. When using Lua, these can be retrieved as if they were attributes, even though they are not. For example, you could
print(me.Name)
when an objects name is in fact not stored in any attribute. Here is a list of the pseudo attributes:
Name |
Returns |
Alias |
Object's alias |
Id |
Numeric portion of object's database ID |
Location |
Numeric portion of object's location database ID |
Name |
Object's name |
Owner |
Numeric portion of object's owner's database ID |
Type |
Object type |
You might notice that all of these start with a capital letter. That is to avoid conflict with any user-created attributes, which are suggested to start with lowercase letters. This brings another important point,
all attribute names are case sensitive. You can
@define FooBar /str on me
and
@define Foobar/str on me
and you will end up with
two attributes defined, one named
FooBar
and one named
Foobar
. You can use this to your advantage if you had an object that needed to store a string that represented a location of some sort, you could use an attribute named
location
without it conflicting with
Location
, however it will probably just lead to confusion in the end.
Objects
The parser creates a couple of special Lua objects, such as
me
, which you've already seen mention of.
Object |
Purpose |
me |
A reference to the database object that is currently running the Lua code |
here |
A reference to the database object of the location of the object that is currently running the Lua code |
(Whew, that last one was a mouth-full)
If you want to retrieve a specific object, take a look at the
thing() function.
Sandboxing
It's not the wisest thing to turn a server full of players loose with access to every Lua function and library running under the same permissions as the server user. You'd invariable have someone invoking
os.remove()
and deleting something they shouldn't, or causing some other sort of mayhem you haven't thought of yet. As a result, a couple of security measures are in place. All of the Lua code runs in a per-user sandbox. One user's code cannot affect another's. In addition, only specific Lua functions and libraries have been exposed. At present the list of functions is not available from the built-in help, but I've managed to squeeze them out of
TheFool76, so here they are:
Function |
Function |
Function |
assert() |
os.date() |
string.* |
error() |
os.difftime() |
table.* |
ipairs() |
os.time() |
tonumber() |
math.* |
pairs() |
tostring() |
next() |
select() |
type() |
os.clock() |
setmetatable() |
|
MySQL
MAGA provides a player MySQL database for those with the
SQL power. It can be accessed either via the
@sql
command or the
sql()
function. It is not necessary to use a trailing semicolon (
;
) for single SQL statements.
Here are some (very) random examples pulled from some incomplete projects:
*Examples:*
@sql SELECT * FROM coords_20
Success!
+-------+----+---+
| objid | x | y |
+-------+----+---+
| 5 | 10 | 0 |
+-------+----+---+
@lua sql("UPDATE SolarSystems SET time=" .. os.time() .. " WHERE objid=2")
@lua myship = sql("SELECT * FROM SolarSystems WHERE objid=2") if myship[0] ~= nil then print(myship[0].lon) end
280
The
sql()
function will always at least return an array with element 0. If there are no results to your query than element 0 will be nil. If there are results, there will be one or more numbered elements, starting at 0. Each will have named elements matching the MySQL column names. In the last example, I had a column named
lon
(representing longitude in relation to the system's primary star). Because I had at least one result (only one in this case), element 0 (
myship[0]
) was not nil, and had an element named
lon
accessible as
myship[0].lon
.
Spreading things out
Code gets hard to manage in a hurry when it's all on one line. One way to deal with this is to spread it across multiple lines.
Any time the MAGA sees your input end in a single backslash (
\
), it will assume you intended to enter a newline in the current input
without terminating the input. For example, we could really make a mess of channels:
+public This is my first line\
and this is my second
[public] QBFreak: This is my first line
and this is my second
What you can't tell from this monochrome, frozen-in-time example is that the command didn't send until after I hit enter at the end of
the second line. In addition, both output lines are formatted with my colors for channel output, not just the first. The MAGA interpreted the
\
as a newline in the current command, not a request to execute the command as thus far entered. You can use this to your advantage when entering multiple lines of code:
@lua \
if tonumber(os.date("%H")) < 12 then \
print("Good morning!") \
end
That was much easier to read than
@lua if tonumber(os.date("%H")) < 12 then print("Good morning!") end
, although a lot harder to go back and make a quick edit and run again. I make use of this MAGA feature in combination with my client's ability to send files as input. This way I can contain all of my code for a particular object or project in a single file and send it all at once. I can edit to my hearts content in an editor that's much friendlier than my client's input line. I can save a local copy which is really nice to have when you make a neat object and want it on multiple servers. Best of all, if I want to experiment and need to make sure I can undo it all, I just have to save a copy of the file before I start making my changes. The really neat thing about sending multi-line functions (or any other attribute value), is they are actually stored this way. If you
@ex
the object, you'll see your data, spread across as many lines as you choose to enter it. This is really nice if you need to quick check some code with
@ex
.
@set foobar/str on me to this\
is\
a\
test
Attribute defined and set.
@ex me
Examining QBFreak (#5)
Attributes
foobar/STRING: this
is
a
test
Advanced MAGA magic
Queue_cmd()
The
queue_cmd()
function allows you to add MAGA commands to the command queue from Lua. This means you can run MAGA-specific commands (
@copy
for instance) from inside a Lua function. In addition to this, you can specify which object runs the command, how long to wait until it runs, and even a string to identify the command. This one function encompasses the functionality of several
TinyMARE commands. The help for
queue_cmd()
is a little light on details, but it will give you the syntax:
queue_cmd( <command>, [<#id>], [<wait_time>], [<timer_id>] )
The most basic usage is to immediately run a command:
@lua queue_cmd("@print Hello, World!")
Hello, World!
The next is to make some other object run a command:
@lua queue_cmd("say Hello, World!",thing("w").Id)
West says, "Hello, World!"
This of course assumes that there is an exit aliased "w" in the same room as you, and that you own it (or otherwise have the power to make it run commands). If you don't own anything just replace thing("w").Id
with me.Id
to make yourself do it.
Probably one of the most common uses of this function, is to run a command after a specific amount of time has elapsed:
@lua queue_cmd("say First command", me.Id, 3000)
say Second command
You say, "Second command"
You say, "First command"
It is important to note that
<wait_time>
is in milliseconds. If you want seconds, you'll want to multiply your value by 1000. In my example above, I waited 3 seconds or 3000 milliseconds. This usage of
queue_cmd()
is quite useful for delaying responses in things like NPCs.
The last usage and probably the most powerful allows you to assign a
<timer_id>
string to your command. If you do this, you can call
queue_cmd()
again before the timer has expired and change the timer or the command. Going into details of
how this is useful is beyond the scope of this document (there are in fact just too many ways), but I will give you an example of how it works:
@lua queue_cmd("say Whew, that was a long wait", me.Id, 10 * 60 * 1000, "mycommand")
@lua queue_cmd("say Well that wasn't so bad", me.Id, 10 * 1000, "mycommand")
You say, "Well that wasn't so bad"
The first command we queued would have waited 10 minutes (1000ms = 1 second * 60 = 1 minute * 10 = 10 minutes) before it ran. The second one, replaced the first in the command queue with not only a new command, but also a new wait time.
--
SolarQBFreak - 05 Apr 2017