Heya QuietBloke. I too am working on a MUD server, and am modeling it loosely off of the MUD illustrated in Mr. Penton's book.
I don't necessarily have a lot of good answers for you as I've decided to switch languages recently, and never fully got up to speed with Lua prior to doing so.
Anyhow, you can "sort of" pass types to Lua. Actually, you don't pass the type into Lua per se, what happens is that space (a block large enough to hold the type) is allocated on the virtual stack. A pointer is returned to your application, and it is through this pointer that you can access that data from in your application. Lua can access the data through a variable - a variable that is assigned to the same block of data on the stack when it calls your "type creation" function. This type of data is called "user data," and Lua manages the memory.
For a much better overview of this, take a look at the following piece from the book "Programming in Lua."
http://www.lua.org/pil/28.1.html
Actually, the whole book is available for free there, (just click the Programming in Lua link at the top of the page). For more clarification you can head back to Chapter 24 as it's the beginning of the section on interfacing C with Lua. Or, as I'd recommend, start from the beginning. You'll understand Lua much better that way.
Anyhow, back to the issue at hand. The problem with this user data is that Bmax doesn't offer pointers to user defined types. So if you have type TPlayer, you can't utilize the pointer returned to use by calling lua_newuserdata(). This means that you'll have to resort to implementing any and all functions that act on a player in Lua itself if you go this route, since you can't access the object in Bmax at all. As far as I can tell there is no way around this with Bmax. Lua creates the data structure on it's virtual stack. You can't do something like passing the address of the TPlayer to Lua and then let it access that memory directly - at least not with the user data option.
You can look into lightuserdata, but from what I understand of it it's either not going to work well with BMax or end up being quite hack'ish (due to BMax's lack of true pointer support)
The approach I was planning to use was to pass the unique EntityID of any and all objects needed by the script to Lua, and then have Lua pass those values back in it's callback functions.
Let me clarify that a bit...basically, all of the core functionality (and action you'd like a Lua script to act on) would have to be defined as a BMax function. So a trivial example that modifies a player stat might look like so
This function would need to get registered with lua (see programming in lua for that process) and then you can call it from within Lua - and lua will pass the arguments passed to the function call in the script to your Bmax function - where the leftmost argument in the lua script will be at stack index 1, the next argument at stack index 2, and so on.
So the call in the lua script, assuming the function was registered as "ModifyPlayerStat" might look like so
ModifyPlayerStat( player.player_id, player_stats["strength"]) <---where player_stats["strength"] returns the index value of that stat stored in the TPlayer type in Bmax
Obviously this is a bit of a hack'ish work around, and doesn't let you easily extend the MUD because, as as yOu were worried about everything requires changes in the Bmax code. It is workable though - you just have to have all of the core physics (mud driver stuff) implemented in BMax and make it callable by Lua.
I wish there was a better solution, but given Max's lack of pointers the only solution I could see is creating yet another layer between Lua and BMax in C, and utilizing that C code to convert the return value of lua_newuserdata to a specific BMax type.
The other solution, and this is what I was planning on implementing, was to utilize an extremely flexible entity type in BMax (structured around a hash table that could hold any type of value for any index.) This would allow the calls from Lua to Bmax to be much more generic, such as
if player.getvalue( player.id, "Encumbrance") > 50 then
player.sendstring( "Sorry, you are too weighted down to perform that action." )
Basically, I was planning on emulating the flexibility of the Lua table type in Bmax itself. This seemed like the most flexible approach given the limitations I mentioned above.
All in all, I didn't find a great way to interface Lua and BMax in such a manner that allows extensibility from the Lua side. Lua works great with Bmax in cases where you know upfront what functions you might want or need Lua to call in Bmax, but an extensible MUD is definitely not one of those cases.
Anyhow, for a variety of reasons I'm switching my MUD over to a different language. First, BMax doesn't have multi-threading, which can be a killer when you are accessing a database with a slow query (I'm using MySQL as the DB for my mud.) Secondly, polling all of your active sockets every frame or tick or whatever is just a waste of CPU power - and I can't afford to waste those cycles because down the road I'd like to have really extensive AI, dynamic weather, and other CPU intensive processes running in the background. Third - interfacing BMax with Lua was proving to be too ugly and slow.
Sorry if this was a long read with few answers, but that's where I got before deciding that I needed to jump ship. I may be missing some elegant solution though, seeing as I'm both a relative newbie to BMax and a definite newb to Lua - so don't take this as a definitive answer.
*all code written on the fly and likely is error prone _ for illustrative purposes only ;)
I don't necessarily have a lot of good answers for you as I've decided to switch languages recently, and never fully got up to speed with Lua prior to doing so.
Anyhow, you can "sort of" pass types to Lua. Actually, you don't pass the type into Lua per se, what happens is that space (a block large enough to hold the type) is allocated on the virtual stack. A pointer is returned to your application, and it is through this pointer that you can access that data from in your application. Lua can access the data through a variable - a variable that is assigned to the same block of data on the stack when it calls your "type creation" function. This type of data is called "user data," and Lua manages the memory.
For a much better overview of this, take a look at the following piece from the book "Programming in Lua."
http://www.lua.org/pil/28.1.html
Actually, the whole book is available for free there, (just click the Programming in Lua link at the top of the page). For more clarification you can head back to Chapter 24 as it's the beginning of the section on interfacing C with Lua. Or, as I'd recommend, start from the beginning. You'll understand Lua much better that way.
Anyhow, back to the issue at hand. The problem with this user data is that Bmax doesn't offer pointers to user defined types. So if you have type TPlayer, you can't utilize the pointer returned to use by calling lua_newuserdata(). This means that you'll have to resort to implementing any and all functions that act on a player in Lua itself if you go this route, since you can't access the object in Bmax at all. As far as I can tell there is no way around this with Bmax. Lua creates the data structure on it's virtual stack. You can't do something like passing the address of the TPlayer to Lua and then let it access that memory directly - at least not with the user data option.
You can look into lightuserdata, but from what I understand of it it's either not going to work well with BMax or end up being quite hack'ish (due to BMax's lack of true pointer support)
The approach I was planning to use was to pass the unique EntityID of any and all objects needed by the script to Lua, and then have Lua pass those values back in it's callback functions.
Let me clarify that a bit...basically, all of the core functionality (and action you'd like a Lua script to act on) would have to be defined as a BMax function. So a trivial example that modifies a player stat might look like so
ModifyStat( lua_state:byte ptr) local player_id:int local Player:TPlayer player_id = int(lua_tonumber( lua_state, 1)) rem Lua is passing the player id of the player this function is acting on as the first argument (the second parameter in lua_tonumber is the index of the stack element we are accessing... the first or bottommost stack element in this case). We cast to the returned value to int because lua returns doubles (it's only number type). endrem rem 'look up the actual player we're acting on from the player database 'assume GPlayerDB is the player DB endrem Player = GPlayerDB.GetPlayerById( player_id ) rem 'now let's get the second item on the stack. In this case assume that the Lua script is passing the array index of the player stat we want to modify in stack index 2, and the new value for that array index in stack index 3 endrem local statIndex:int = int(lua_tonumber( lua_state, 2)) local newStatValue:int = int(lua_tonumber(lua_state,3)) Player.statArray[statIndex] = newStatValue
This function would need to get registered with lua (see programming in lua for that process) and then you can call it from within Lua - and lua will pass the arguments passed to the function call in the script to your Bmax function - where the leftmost argument in the lua script will be at stack index 1, the next argument at stack index 2, and so on.
So the call in the lua script, assuming the function was registered as "ModifyPlayerStat" might look like so
ModifyPlayerStat( player.player_id, player_stats["strength"]) <---where player_stats["strength"] returns the index value of that stat stored in the TPlayer type in Bmax
Obviously this is a bit of a hack'ish work around, and doesn't let you easily extend the MUD because, as as yOu were worried about everything requires changes in the Bmax code. It is workable though - you just have to have all of the core physics (mud driver stuff) implemented in BMax and make it callable by Lua.
I wish there was a better solution, but given Max's lack of pointers the only solution I could see is creating yet another layer between Lua and BMax in C, and utilizing that C code to convert the return value of lua_newuserdata to a specific BMax type.
The other solution, and this is what I was planning on implementing, was to utilize an extremely flexible entity type in BMax (structured around a hash table that could hold any type of value for any index.) This would allow the calls from Lua to Bmax to be much more generic, such as
if player.getvalue( player.id, "Encumbrance") > 50 then
player.sendstring( "Sorry, you are too weighted down to perform that action." )
Basically, I was planning on emulating the flexibility of the Lua table type in Bmax itself. This seemed like the most flexible approach given the limitations I mentioned above.
All in all, I didn't find a great way to interface Lua and BMax in such a manner that allows extensibility from the Lua side. Lua works great with Bmax in cases where you know upfront what functions you might want or need Lua to call in Bmax, but an extensible MUD is definitely not one of those cases.
Anyhow, for a variety of reasons I'm switching my MUD over to a different language. First, BMax doesn't have multi-threading, which can be a killer when you are accessing a database with a slow query (I'm using MySQL as the DB for my mud.) Secondly, polling all of your active sockets every frame or tick or whatever is just a waste of CPU power - and I can't afford to waste those cycles because down the road I'd like to have really extensive AI, dynamic weather, and other CPU intensive processes running in the background. Third - interfacing BMax with Lua was proving to be too ugly and slow.
Sorry if this was a long read with few answers, but that's where I got before deciding that I needed to jump ship. I may be missing some elegant solution though, seeing as I'm both a relative newbie to BMax and a definite newb to Lua - so don't take this as a definitive answer.
*all code written on the fly and likely is error prone _ for illustrative purposes only ;)