Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD General Discussion Goto page 1, 2, 3  Next
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Jul 10, 2007 10:54 pm   

Lua in CMUD v2.0
 
I've started implementing Lua within CMUD now. I had to convert the Lua header files so that they dynamically linked the LUA.DLL file instead of the normal static linking files that were already available for Delphi. This allows you to remove the Lua support in CMUD by just removing the LUA.DLL file. CMUD first looks in the CMUD.EXE directory for the LUA.DLL, and if not found, then it loads the DLL via Windows.

So, I've got the basics going. Lua appears in the list of script languages. There seem to be a couple of issues with the default Scintilla syntax highlighting for Lua, but it might just be a color style that I haven't defined yet (the ; at the end of the line is black-on-black). I've got the "print" function overridden to display the result to the MUD window.

My question is, should the Lua State be "global"? In other words, should all windows and modules share the same Lua scripting environment, to allow sharing of variables, functions, etc, or should each window/module have it's own Lua environment?

My best guess is that the state should be global to allow windows/modules to communicate with each other. But this is different than how the Microsoft Scripting is currently implemented in zMUD/CMUD. The MSS has a separate script environment for each MUD window.

Anyway, let me know how you feel about this before I get too deep into the implementation. My main job now is to add the various functions to Lua that allow it to access all of the zScript stuff, like variables, commands, functions, etc.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Tue Jul 10, 2007 11:15 pm   Re: Lua in CMUD v2.0
 
Firstly, let me just say: ^________________^

Now.

Zugg wrote:
it loads the DLL via Windows

That's interesting. By default, it's called lua5.1.dll - will CMUD be able to find it?

Zugg wrote:
the ; at the end of the line

You mean the one you don't need? ;P

Zugg wrote:
should the Lua State be "global"?

I'm really not sure about this one. While it'd be nice to aid package communication like this, it's likely to cause incompatibilities with different scripts. Also, many of the cross-package things I need to, I already do via events and don't need Lua to be global as well.

But this would cause problems given that triggers use the window that caused them to fire as the current environment - that could lead to Lua variables being "trapped" in the wrong window and never seen by the module that contains the trigger.

Perhaps a solution where you can choose the scope of your Lua environment would be best - Local modules and windows have their own environments, External and Global modules use a global one?
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Jul 10, 2007 11:47 pm   
 
Quote:
By default, it's called lua5.1.dll - will CMUD be able to find it?

No, CMUD loads the file LUA.DLL. This file will be distributed with CMUD and put into the installation directory. I'm not using the version-specific filename, because when dynamically loading it doesn't really matter that much. As long as new versions maintain the same procedure/function names, you can still load it. You can even load an older version and any new functions just get NIL assigned to them, and as long as CMUD doesn't call them, then there isn't any problem.

That's the whole idea of dynamic loading vs static loading. With static loading, you get error messages from Windows if there is a problem with the DLL (routine missing, routine name change, etc). With dynamic loading I can deal with these errors internally.

Quote:
Perhaps a solution where you can choose the scope of your Lua environment would be best

That's difficult. What I'm really talking about here is something rather low level in the Lua API. All of the routines take an argument called the Lua_State. This contains the environment with all of your variables, functions, tables, etc. Right now CMUD just has one of these structures...it's global. I can create a new structure for every module/window, but I have to pick one way or the other...I can't mix them. If External and Global CMUD modules used a global one, then there would be no way to access any data in this from within your MUD window (since it would be using the local structure).

If people use Lua properly within their CMUD packages, then they *should* be creating their own Lua Libraries within the package. This avoids any name problems or conflicts. To allow this, I'd want to use a single, global Lua environment structure.

Remember, the idea with the Lua support is to try and make it work as much "Lua-Like" as possible. This means CMUD packages should implement Lua "C Modules" (see chapter 26.2 in the "Programming in Lua" book)
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jul 11, 2007 6:15 am   Re: Lua in CMUD v2.0
 
Zugg wrote:

My question is, should the Lua State be "global"? In other words, should all windows and modules share the same Lua scripting environment, to allow sharing of variables, functions, etc, or should each window/module have it's own Lua environment?

My best guess is that the state should be global to allow windows/modules to communicate with each other. But this is different than how the Microsoft Scripting is currently implemented in zMUD/CMUD. The MSS has a separate script environment for each MUD window.


I can't speak for your users, but my inclination would be to do whatever you do for the other script engines. Don't you already have the issue (with VBscript, say) of how the different modules communicate with each other?

Personally I used a global state for each open world, plus an additional state for each plugin file. That way plugin authors don't have to worry about whether someone else used the same global name for some other purpose.

You could always have (if you don't already) some inbuilt command (CMUD function) that lets one state send a message to another (ie, a string).

I found that with the separate states there were some mutterings from users who *wanted* to influence other states, but there were ways of getting around that.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jul 11, 2007 6:20 am   
 
The other thing I did was a mechanism for getting a userdata which was a reference to a different world (ie. a different game session). Then you could supply that userdata as the first argument for any function, which redirected that function to affect the other world. That way you could write something like "send to all worlds" or "bring world x to the front".

It was mildly fiddly to get that to work, but I think the result was worth it. You must already have the problem that, within the Lua state, if the user wants to display something on the screen, *which* screen it appears on (assuming multiple ones are open at once). If you have one state per connection, at least, then you can store in the Lua registry some kind of pointer or whatever Delphi uses, so that you can get back to the connection it is associated with.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Jul 11, 2007 3:47 pm   
 
Hmm, the more I thought about it, the more I think Nick is right about this. While the idea of each package being a module was interesting, it doesn't get around the fact that simple global variables would end up being shared, so doing something as simple as :

i=0

could effect some other script in some other windows. When you add threads to this, it gets even more complicated since you'd want each thread to treat "i" as a local variable, and not a global variable.

And yes, I was just starting to run into the problem where the Lua state needed to know what window it was within. For example, the "print" function needs to know what window to send it's text to. Storing the connection window as userdata within the Lua state sounds like a good idea.

This leads me to another complexity...how do people want to access CMUD variables from within Lua. Remember that I created the "zsvar" object for other scripting objects? Well, the problem with "zsvar" (or with making this into a table within Lua) is that it doesn't allow you to access variables by module/class name. In CMUD you can do this: @//modulename/class/subclass/varname . In other words, variables are stored in a tree hierarchy within CMUD, much like files within a directory structure.

So how do you access a variable like this in Lua? If "zs" refers to the current module/window associated with the Lua environment, then I can have "zs.varname" or "zs[varname]" using a simple table, but that doesn't give access to a variable in a specific class.

Seems like I might need to implement something like a "class" userdata that returns a table of settings within a given class. Something like:

MyClass = zs.class[classname]
Var = MyClass[varname]

Nick, does MUSHClient use a tree hierarchy for variables, or do you have a flat table structure? Any ideas how people would like this to be handled. There's a lot of power in Lua if we can set this up correctly.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Jul 11, 2007 4:42 pm   
 
OK, I think I need to take a day and think more about this design before I just jump into the coding. I need to come up with a clean, easy-to-understand design for doing Lua programming within CMUD. I'm going to read through the "Programming in Lua" book again and see if I can come up with something that fits well into the Lua-way of doing things, that would still make sense to CMUD users.

This design needs to deal with modules/windows, class folders, and the various settings, such as variables, aliases, triggers, etc. But I want it in a way that isn't syntactically cumbersome.

I want something simple like: zs.modulename.classname.subclass.varname to mimic our @//modulename/classname/subclass/varname syntax. But I'd want zs.varname to work like @varname (using the current class, etc). Feel free to discuss any ideas that might help with this design.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Jul 11, 2007 6:24 pm   
 
Hmm, here's another idea...since tables in Lua can have any string value as a key, maybe a syntax like this would be better:

zs.var[name]
zs.var["//module/class/subclass/varname"]

That would be a bit easier to implement I think. What do people think?
Reply with quote
Arminas
Wizard


Joined: 11 Jul 2002
Posts: 1265
Location: USA

PostPosted: Wed Jul 11, 2007 6:42 pm   
 
Sounds like a good Idea to me!
_________________
Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jul 11, 2007 9:46 pm   
 
Zugg wrote:

This leads me to another complexity...how do people want to access CMUD variables from within Lua. Remember that I created the "zsvar" object for other scripting objects? Well, the problem with "zsvar" (or with making this into a table within Lua) is that it doesn't allow you to access variables by module/class name. In CMUD you can do this: @//modulename/class/subclass/varname . In other words, variables are stored in a tree hierarchy within CMUD, much like files within a directory structure.


The way I have done it is to simply give users a function that gets/sets the value of a variable. You simply call this function from Lua (as you do from VBscript). For example:

Code:

x = GetVariable ("some_name")
SetVariable ("some_name", tonumber (x) + 5)


In your case I see no reason why the tree hierarchy could not be used:

Code:

x = GetVariable ("//modulename/classname/subclass/varname")
SetVariable ("//modulename/classname/subclass/varname ", tonumber (x) + 5)


Now if the user wants to make it more "Lua-like" they can make a proxy table and use metatables with __index and __newindex to access the underlying functions. My forum post here describes this idea:

http://www.gammon.com.au/forum/?id=4904

I wouldn't try to overthink the solution. Having a specific function to access CMUD variables seems to me to make a nice deliniation, so you don't accidentally think you can store (say) functions into them.


Zugg wrote:

Nick, does MUSHClient use a tree hierarchy for variables, or do you have a flat table structure? Any ideas how people would like this to be handled. There's a lot of power in Lua if we can set this up correctly.


I have a flat structure, excepting that there is a different way of accessing variables from other plugins (where you specify the plugin name + the variable name).

The trouble with the flat structure is, of course, the tedium of sorting things into nice trees - the very thing Lua is good at. However what I do these days is simply use the MUSHclient variables as a means of storing the Lua data to disk (as MUSHclient variables are saved as part of the world file).

Fortunately it is quite easy to move between a Lua table and a flat string, using table serialization. I developed a module that does that, based somewhat on algorithms in the PiL book. It is described here:

http://www.gammon.com.au/forum/?id=4960

You could use the same thing - you are welcome to make a copy. With some restrictions on the contents of tables (eg, no functions) it will serialize a nested table into a straight string. The inverse operation (turning the string back into tables) is simply done by loadstring in Lua.

I have been adding Lua support to SMAUG FUSS codebase, and used exactly the same techniques to load and save Lua nested tables into a disk file to save player state between sessions.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Thu Jul 12, 2007 5:34 pm   
 
Quote:
x = GetVariable ("some_name")
SetVariable ("some_name", tonumber (x) + 5)

Actually, that's exactly the kind of thing that I *don't* want to do. It's certainly the easiest way to implement it, but it isn't very "Lua-like" in my opinion.
Quote:
Now if the user wants to make it more "Lua-like" they can make a proxy table and use metatables with __index and __newindex to access the underlying functions.

That's what I'm planning to do. I think CMUD should do this for the user, rather than forcing the user to do it themselves. I want to make Lua scripting as accessible as possible, especially for new users.
Quote:
However what I do these days is simply use the MUSHclient variables as a means of storing the Lua data to disk

That's an interesting idea. Since CMUD variables also store just string values, I'll need to use something like this too. I *do* want CMUD users to be able to store Lua tables into a CMUD variable (so that it gets saved with the session). I'll take a look at your forum post on that.
Quote:
Having a specific function to access CMUD variables seems to me to make a nice deliniation

I personally think that just the "zs." in front of the variable name is enough of a delineation. Makes accessing CMUD variables just as easy as using zs.commandname or zs.functionname. Yes, people will need to know that you can't assign functions to these variables. But I'm not really worried about that.

I'll post more today when I get my design a bit more finalized. But I think I've got something pretty good to start working on now.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Thu Jul 12, 2007 9:01 pm   
 
Zugg wrote:
Quote:
x = GetVariable ("some_name")
SetVariable ("some_name", tonumber (x) + 5)

Actually, that's exactly the kind of thing that I *don't* want to do. It's certainly the easiest way to implement it, but it isn't very "Lua-like" in my opinion.

(and earlier)

I want something simple like: zs.modulename.classname.subclass.varname to mimic our @//modulename/classname/subclass/varname syntax.


The problem is I don't see how you are going to do this in Lua without in fact making it not Lua-like.

If you are going to use the syntax zs.modulename.classname.subclass.varname, then you are pretending that these are all Lua tables, when they are not, I gather, they are internal things in CMUD.

If I saw a dataname like that in Lua, I would expect to be able to iterate the table, like this:

Code:

for key, value in pairs (zs.modulename.classname.subclass) do
  print ("key is ", key, "value is", value)
end -- for loop


I would expect to be able to store functions in the variables:

Code:

zs.modulename.classname.subclass.varname = function (x) print (x * 2) end


I would expect to be able to assign nil to them to delete them:

Code:

zs.modulename = nil


I would expect to be able to assign a metatable to it:

Code:

mt = {  } -- some metatable
setmetatable (zs.modulename.classname.subclass, mt)



I would expect to add things to it:

Code:

zs.some_other_variabe = 42


I don't see how you are going to do these things, because the underlying variable is a CMUD variable (a string variable, perhaps?) which doesn't support all this Lua stuff.

If you try to hide the fact that they are transitioning out of the Lua environment, into the CMUD variable space, I think you are going to make things a lot more confusing.

The technique which I showed, which uses a metatable to map Lua variables onto a Lua table works with a single level of hierarchy (and even then, not perfectly, because you cannot iterate the table). However I think you will find that the method of using __index in a metatable to catch a reference to a non-existent entry will tend to fail if you have nested entries.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Thu Jul 12, 2007 9:37 pm   
 
I definitely agree with Nick on this one. It's pointless trying to make CMUD variables into a "Lua-like" syntax, because they're not "Lua-like" variables. There needs to be a clear distinction.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 1:06 am   
 
No, I still disagree. These are "userdata" values in Lua. Look at the example in chapter 28 of the "Programming in Lua" book where they show creating a bit array as a userdata type. They define very specific methods for the userdata type: set, get, size. In their example on page 267, they show this:
Code:
a = array.new(1000)
a[10] = true
print(a[10])
print(#a)

does this mean that you can just automatically assume that you can assign a function to a[10]? No. The userdata type defines what operations are allowed, what methods the object has, etc.

Sorry if I wasn't clear or if you didn't keep up with the discussion, but I'm not interested in the zs.modulename.classname.subclass.varname syntax. The syntax I want to use is this:
Code:

zs.var.varname
zs.var[varname]
zs.var["//modulename/classname/subclass/varname"]

In this case, "zs.var" *is* a table. You can create a new CMUD variable by setting "zs.var["newvar"]=123", and you *can* iterate through the table to display all existing variables if you want. You can assign anything to it that can be converted to a string value (so you can assign a table to a CMUD variable if you want). That's pretty damn close to a normal Lua variable.

But I personally much prefer the scripting syntax:
Code:
zs.var[myvarname] = "hello world"

to your syntax:
Code:
zs.SetVariable("myvarname", "hello world")
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Fri Jul 13, 2007 1:45 am   
 
Zugg wrote:
No, I still disagree. These are "userdata" values in Lua. Look at the example in chapter 28 of the "Programming in Lua" book where they show creating a bit array as a userdata type. They define very specific methods for the userdata type: set, get, size. In their example on page 267, they show this:
Code:
a = array.new(1000)
a[10] = true
print(a[10])
print(#a)

does this mean that you can just automatically assume that you can assign a function to a[10]? No. The userdata type defines what operations are allowed, what methods the object has, etc.


Yes, actually, I would assume that you're allowed to assign a function to a[10]. Using a metatable to create a pseudo-class or other data type doesn't change the fact that the data structure is still just a Lua table. Sure, your metatable functions can restrict the operations to disallow assigning functions and such, but you'd have to write the metatable code to do that then.

I would like to see a more "natural" syntax for accessing CMUD variables through Lua, but I feel that Nick has a valid point about conversion problems. Given his experiences with Lua and coding in general, I'm inclined to listen to what he says. :)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 3:15 am   
 
Quote:
Yes, actually, I would assume that you're allowed to assign a function to a[10]

I think you still missed the point (maybe you don't have the book in front of you and I didn't explain it well enough). The "array" userdata class that is being created in this example from the book is for BIT ARRAYS. How in the world does it make any sense to assign a function to an element of a BIT array? It doesn't.

Just because Lua allows you to assign functions anywhere you want, doesn't make it the right thing to do in all cases.

Sorry, but I really don't get it guys. First you jump up and down about how cool and great tables are in Lua, and then you don't want to use them?? Why don't you want to use a table for CMUD variables? I just don't get this. Using a method call like "SetVariable" is the "COM-way" of doing things. Seems like it totally gets away from the "table-way" of doing things in Lua. With the proper meta-table functions, I probably even *can* allow functions to be assigned (using stuff like lua_dump to convert a function to storeable data).
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Fri Jul 13, 2007 5:30 am   
 
Zugg wrote:


Sorry if I wasn't clear or if you didn't keep up with the discussion, but I'm not interested in the zs.modulename.classname.subclass.varname syntax. The syntax I want to use is this:
Code:

zs.var.varname
zs.var[varname]
zs.var["//modulename/classname/subclass/varname"]

In this case, "zs.var" *is* a table.


OK, so you only have one level of depth, so this looks similar to my "var" idea using a metatable. However rather than getting the users to "require" the function that implements it, you are going to build that in.

Zugg wrote:

Why don't you want to use a table for CMUD variables?


Your earlier example implied there would be multiple levels of tables, however what you have explained here seems simple enough.

Zugg wrote:

Using a method call like "SetVariable" is the "COM-way" of doing things.


In my case I did that because the SetVariable function already existed (eg. for VBscript, Jscript etc.), so there wasn't a crying need to make access to client variables a different way. The "access via a table" idea was added later with a small module you 'require' if you want to access variables that way.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 5:33 am   
 
OK, I'll eventually edit this and add it to the Knowledge Base, but here is the design that I'm using for Lua in CMUD:

Overridden Lua commands
The "print" command in Lua calls the CMUD #ECHO command. The text is sent to the window that currently has keyboard focus

New global Lua commands
These are for convenience only. Normally you should probably use the "zs.command" form shown below, which allows you to specify the exact window or connection that is being used.
echo
Same as the Lua "print" command. Calls CMUD #ECHO in the current MUD window
send
Sends the arguments to the MUD as if they were entered into the command line (which means the command is echoed to the MUD window)
sendraw
Send the arguments to the MUD as raw data. No other echoing or processing is done. It simply writes to the network socket.
windows
A Lua table containing references to all defined CMUD windows. Each value in this table is a zsMudWindow userdata object (see below). The table can be indexed by window name (case sensitive) or by window ID value.

New "zs" global object
A new datatype is introduced to Lua called a "zsMudWindow". This represents a CMUD window (or module) object. The global "zs" object references the MUD window that currently has keyboard focus, or, when used within a trigger, it is the window that received the text which caused the trigger. Here are the properties and methods of a zsMudWindow object:
zs.var
The table of variables stored within the window. You can access variables using the syntax zs.var[name] or zs.var.name (In Lua, both of these syntaxes are equivalent). To specify a variable in an arbitrary module and class, use zs.var["//module/class/subclass/varname"]
zs.name
calls the CMUD command or function of the given name. For example: zs.echo("hello world") is the same as calling the normal CMUD command: #ECHO "hello world". In the case where a CMUD command and function have the same name, this calls the function, and returns the value of the function. To force it to use the command, see the zs.cmd syntax below.
zs.cmd.name
calls the CMUD command of the given name. This is used in cases where CMUD has a command and function of the same name, and you want to call the command, not the function.
zs.func.name
calls the CMUD function of the given name and returns the result. This is used in cases where CMUD has a command and function of the same name, and you want to call the function, not the command.
zs.name
returns the name of the window (same as the CMUD #NAME command, but returns a value as a function)
zs.windowid
returns the unique ID number for the window (to deal with multiple windows that have the same name)
zs.host
the current host name for the window
zs.port
the current port number for the window
zs.char
returns the current username for the window (same as the CMUD %char predefined variable). In fact, many of the Predefined CMUD variables are available via this kind of syntax. A full list will be documented when implemented (and after I deal with name collisions between predefined variables and functions with the same name). In most cases, it's easy to handle since predefined variables do not have any arguments, and functions do. So "zs.char" can return the character name for the window, but "zs.char(65)" can still return the character with the ascii value of 65.

Using multiple windows
You can retrieve the window object for another CMUD window using the global "windows" table. This will return the zsMudWindow object for the specified CMUD window name or windowid. The object result can then be used the same as "zs" in these examples. For example:
Code:
tells = windows["tells"]
tells.echo("this is the tells window")

Note that CMUD handles the references internally as expected. If the "tells" CMUD window is closed, then the "tells" Lua object is marked as invalid and attempts to use the object will result in no action. NOTE: Lua will not crash when using an invalid window object...it simply won't do anything.

You can determine if a window object is valid using the "zs.isvalid" function. This function will return true if the window object is still valid. Returns false if the window has been closed.

Aliases, Triggers, Buttons, and other complex objects
Several methods are available to retrieve and manipulate complex CMUD objects, such as aliases, triggers, buttons, etc. In these examples we will consider Aliases, but similar methods exist for triggers and buttons.

The easier way to retrieve an Alias is using the normal CMUD command via Lua: a = zs.alias("aliasname"). The CMUD #ALIAS command has been extended to return the alias object, rather than displaying the alias in the output window. Since this is calling the CMUD #ALIAS command, it will create a new alias with the given name if it doesn't exist yet. If you want to retrieve an alias without creating a new one, use a = zs.getalias("aliasname") instead.

The Alias object has several properties that can be manipulated:
name
The name of the alias (or the ID name of a trigger or button, for example)
script
The script that is executed by the alias (or trigger, etc)
enabled
true if the alias is enabled, false if it is disabled

Other objects may have other properties. For example, a Trigger object has a "pattern" properties, and various option properties, such as "casesensitive". Buttons have a "caption" property, a "color" property, etc. The full list of properties for all objects will be documented once they are implemented. For example, to disable a trigger, you can use the code:
Code:
t = zs.trigger("idname")
t.enabled = false

or
Code:
zs.trigger("idname").enabled = false


Lua provides a nice syntax for dealing with objects with a complex series of properties. This can be used by the zs.makealias{args} (which is the same as the Lua syntax zs.makealias({args})). In this case, args is a Lua table that specifies the various properties, either by name, or by index. For example, the "name" property always has an index of 1, and the "script" property always has an index of 2. So, you can create a new alias like this:
Code:
a = zs.makealias{ "aliasname", "script text"}

But, since you can also refer to the properties by name, you can also do this:
Code:
a = zs.makealias{ name="aliasname", script="script text"}

For complex objects like Buttons, this is a very powerful way to create new objects without the problems of the CMUD #BUTTON command. For example:
Code:
b = zs.makebutton{ caption="Click me", script="Button was pushed", oncaption="Click me again", offscript="Button was released", type="toggle"}

Objects vs variables
Lua makes a distinction between accessing an object "method" vs accessing a key within a table variable. The Lua syntax for object "methods" is normally: a = zs:alias("aliasname"). Because of the way in which the Lua interface is built, you can use either the object method syntax, or the normal variable access syntax. For example, both of these lines will do the same thing:
Code:
a = zs:alias("aliasname")
a = zs.alias("aliasname")

From the Lua perspective, "alias" is a C Function stored in the table "zs". But the "alias" function checks it's first argument to see if "self" is being passed as an object reference and handles that case too. This allows hard-core Lua coders to continue using the more correct zs:alias syntax, while retaining the more intuitive and obvious zs.alias syntax for new users. It also makes the Lua interface to CMUD more similar to the COM-based interface that is planned for the Microsoft Scripting languages, such as VBScript (which use the zs.alias syntax and do not have an equivalent object method syntax).

Accessing CMUD parameters
When writing a script for a trigger in CMUD, you use %1..%99 to reference subpatterns captured by the trigger pattern. You can also use named-arguments in CMUD, or use the %param(1) or %param(name) functions. Obviously you can use the CMUD functions directly from Lua with the syntax: zs.param(1) or zs.param("name"). For example, here is a trigger script written in Lua:
Code:
Pattern: You get (%d) coins
Script:
  coins = zs.param(1)
  zs.var[totalgold] = zs.var[totalgold] + coins
  if (zs.var[dosplit] ~= 0) then
    zs.send("split "..coins)
  end


CMUD Command line scripts
A new menu option has been added to toggle the "Lua command line" mode. When the Lua command line is enabled, the text in the command line is interpreted as a Lua script, rather than being sent to the MUD. The Ctrl-` key toggles this mode. It is displayed on the command line where the Parse option is normally displayed. The Ctrl-R key continues to toggle Parsing off and on. When Parsing is On, the Ctrl-` key toggles between "zScript" and "Lua" script languages.

Default Scripting Language
A new value has been added to the Scripting Preferences page called the "Default Scripting Language". This has the same drop-down selection of scripting languages that is normally found in the settings script editor. The value that is selected is used as the default scripting language for new scripts. This allows people who prefer to use Lua over zScript an easy way to set the default so that they do not always have to change the Language box in the script editor.

Lua Script Scope
As we discussed earlier, each *Package* will have it's own Lua State. Thus, windows and modules within the package all share the same global Lua namespace. Of course, modules are welcome to use define their own Lua Modules with their own local variables and functions. I haven't yet decided how you will access the Lua State of another package, or whether I will allow this at all.

Sequential scripting in Lua
The Lua scripting interface is threadsafe, and you can even still use the new CMUD sequential scripting within Lua if you'd like. For example, this works in a Lua script as expected:
Code:
zs.send(username)
zs.waitfor("^Password:$")
zs.send(password)

In addition, you can still use all of the normal coroutine and thread functions in Lua (at your own risk of course). But I haven't done anything that should interfere with any normal Lua operations.

Conclusion
That gives the basic design that I am implementing within CMUD. I've done most of the low level work to set up the zsMudWindow userdata type, and to create and handle the "zs" global object. I've got the needed metatables set up, and am using some tricks with upvalues so that the object method zs:method syntax is not required and zs.method works just as well.

I think this provides the full integration between CMUD and Lua that I've been talking about. I'm sure "purists" will still continue to complain about the syntax. But I'll need to hear some really good arguments to change aspects of this design. I have tried to make this interface as "Lua-like" as possible, and yet retain a lot of the existing CMUD commands and functions. Also, a large fraction of this interface should also be possible to implement for other COM-based scripting languages.


Last edited by Zugg on Fri Jul 13, 2007 7:04 pm; edited 1 time in total
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 5:59 am   
 
About the zs.var vs the CMUD #VAR command
As described above, most cases of "zs.name" calls the CMUD command or function with the given name. So, why doesn't "zs.var" called the CMUD #VAR command, just like zs.alias calls the #ALIAS command? Well, the most common use of variables is to set and retrieve their *values*. It is rare that you need to define the properties of the CMUD Variable Object itself. Whereas accessing an Alias (or Trigger, or Button) usually means that you need access to the full object so that you can set it's properties. Once an Alias is created, for example, the *value* of the Alias (the script) rarely changes.

You can still use the zs.cmd.var("name") syntax to access the CMUD #VAR command if absolutely needed. Also, the "zs.getvar" and "zs.makevar" routines still exist to manipulate the Variable objects, just like for Aliases and Triggers.

Hopefully this helps understand why you use zs.var[varname] or zs.var.varname for variables, but use zs.alias("aliasname") for aliases.
Reply with quote
Thinjon100
Apprentice


Joined: 12 Jul 2004
Posts: 190
Location: Canada

PostPosted: Fri Jul 13, 2007 8:03 am   
 
Wow, Zugg... I'd never really looked into Lua before, but reading through your last couple entries I can see where the interface and alternate language support could really open up the scripting possibilities for those enlightened enough to learn the language.

I'm willing to bet a lot of people are getting excited for 2.0 :)
_________________
If you're ever around Aardwolf, I'm that invisible guy you can never see. Wizi ftw! :)
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Fri Jul 13, 2007 10:23 am   
 
Zugg wrote:
You can still use the zs.cmd.var("name") syntax to access the CMUD #VAR command if absolutely needed. Also, the "zs.getvar" and "zs.makevar" routines still exist to manipulate the Variable objects, just like for Aliases and Triggers.

I'm really glad you added that. Being unable to set the "use default" option or to return the default value of a variable really bugged me - hopefully you'll be able to do that in Lua.

EDIT: And, in addendum: Awesome! Cool
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Fri Jul 13, 2007 11:39 am   
 
Okay, I wasn't saying that you *should* set a function as the value of a[10], just that it's possible in Lua and the behavior should be defined when something like that happens in CMUD. Also, I wasn't really against the zs.var.varname syntax for getting/setting CMUD variables. I think it'll be great. I'm just wondering what will happen when we assign things to them that you wouldn't normally put into a CMUD variable, whether it's a function, another table, or what have you. So, to ally our fears and doubts, could you please test this and/or let us know what to expect? :)

The documentation you posted looks great! I really can't wait to dive into 2.0 and start re-writing portions of my scripts to take full advantage of all the new features.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 4:47 pm   
 
If you try to assign a function to a CMUD variable, you will get an error message (using the normal Lua assert system, which will generate proper error messages in CMUD, just like having a zScript syntax error). If it happens in a background script, then the error message is displayed in the MUD output screen, just like if you have an error in an alias that is called by a background trigger in zScript. In other words, the Lua and zScript error mechanisms are the same, and you will get error messages if you try to do something wrong in Lua (whether it's a syntax error, or a runtime error).

Assigning Lua tables to a CMUD variable should work fine. I'm using a serializer similar to what Nick did in MUSHclient. This is built into CMUD and handles full conversion of nested Lua tables to/from CMUD variables. And since CMUD variables are always stored in the database in the background, you don't need to worry about specifically telling CMUD to save the variables, like you do in MUSHclient.

CMUD *should* be able to handle the difference between the Lua "nil" value, and other values, so that shouldn't be any problem. But I'll know more when I get further into the implementation.

So, the data types that you *won't* be able to store in a CMUD variable are: function, lightuserdata, userdata, thread. These will give error messages.
Reply with quote
Larkin
Wizard


Joined: 25 Mar 2003
Posts: 1113
Location: USA

PostPosted: Fri Jul 13, 2007 5:13 pm   
 
Sounds good to me! Thank you for answering that so clearly. Very Happy
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jul 13, 2007 7:15 pm   
 
Updated the documentation above to reflect the new way of handling multiple windows. Instead of using a "getwindow" function, CMUD now creates a true Lua table called "windows". Each window is stored in this table (as userdata) twice: once for the window name, and once for the window id.

If you have multiple windows with the same name, the last defined window will occupy the spot in the table with the name as the index. So if you want a true list of all tables, iterate through the "windows" table by numeric index. For example:
Code:
for i,v in pairs(windows) do
  if (type(i) == "number") then
    v.echo("in window ",v)
  end
end

This loops through all windows and displays "in window name" where name is the name of each window. We test the index of the table to see if it is a number to ensure that we ignore the string keys that are the window names. This ensures that we loop through all windows, even windows with the same name.

Notice that the zsMudWindow object v can be printed as a string (in the v.echo line). The string value of a zsMudWindow object is of the form "Window(name)"


Last edited by Zugg on Fri Jul 13, 2007 8:10 pm; edited 1 time in total
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD General Discussion All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net