11 Apr, 2009, JohnnyStarr wrote in the 1st comment:
Votes: 0
Ok, I'm not trying to ask general questions otherwise I would have posted on
the recent thread. As some may know, my current project is indeed entirely in Ruby,
the network is quite loosely based on Tyche's RockettMud so I am very thankful for
his work. but what I am not looking forward to is "reinventing the wheel" in the sense
that I do love ROM. I find myself just recreating many of the routines and commands in
Ruby, and it just seems sort of dumb. :tongue:

So after reading up on some of the posts, I am wondering how I could embed Ruby into
my old ROM project? My main goal would be to add OOP abilities to the current structures,
which would open the door to room scripting, as well as simplification of adding player commands.

where can I find specifics on this? I know theres been chatter of using Lua in C so is it possible to
use that documentation?

thanks, also, if anyone thinks this is a bad idea, please support your claim with constructive facts. :biggrin:
11 Apr, 2009, elanthis wrote in the 2nd comment:
Votes: 0
The Ruby documentation includes a comprehension manual for its C API. The old edition of Programming Ruby covered the C API, so I would assume the newest edition (http://pragprog.com/titles/ruby3/program...) does as well, but you should check before buying. The specific stuff we've been talking about for Lua is not super relevant because the Ruby C API is totally different in design than Lua's, although the overall approach is more or less the same: create a Ruby interpreter instance, expose your C types and methods to the Ruby interpreter, and then load and run scripts. If you're well-versed in C, the Ruby C API documentation may be all you need: http://www.ruby-doc.org/doxygen/current/.... You can also use one of the many wrapper-generators, including the ever-popular SWIG, to do most of the heavy lifting for you.

Beyond that, you're going to have some more specific questions. :)
11 Apr, 2009, David Haley wrote in the 3rd comment:
Votes: 0
A lot of the codebases make pretty strong assumption about what goes where and which pointers are stored where and who owns which pointers, and the result is a somewhat fragile balance that keeps things together without crashing or leaking (too much) memory.

The problem is that when you start wanting to write non-trivial code in a scripting language, you need to break those assumptions and start storing stuff in new places. For example, you might write Lua/Ruby code that will one way or another want to hold on to a reference to a character. What happens now? How exactly do you store that reference? Storing just the pointer is Very Unsafe, because if that pointer is deleted C-side, your embedded language will blow up when it tries to access that pointer.

The short story is that you very quickly run into the problem of having to implement more complex memory management, and get down and dirty to some extent with the guts of the C code to make it play nicely with the new requirements.

The point of these short paragraphs is to question the value in shoehorning Lua/Ruby onto ROM/SMAUG/whatever if your intention is to start writing your own code anyhow. Yes, you will obviously re-write a lot of things. The real question is to what extent you're actually saving time by using ROM routines that might not make sense in the brave new world of embedded languages.

Anyhow, as elanthis said, for more specific answers you'll need more specific questions :smile:
11 Apr, 2009, quixadhal wrote in the 4th comment:
Votes: 0
One of the big issues that may come up when dealing with embedded languages, in fact, when dealing with ANY interpreted languages, is how to handle updating objects.

I only bring this up because I've recently been poking at an LPMUD, which is of course written as a driver (in C) that is seldom modified, and a mudlib (in LPC) which is where most of the work and all of the game happens. One of their issues is what to do when you have a dozen clones of a sword object, and the sword inherits from a generic weapon object, and someone goes and updates the weapon code. Somehow, all the things that inherit weapon and all the things cloned from them, have to have their code updated without losing their data values.

Switch gears to a DikuMUD using Ruby/Lua/Python/etc as an embedded language. What do you do when someone rewrites the conversation trigger code on the City Guards? Can you reload the interpreted code and aim the mob's pointer at the new version? Does that cause the embedded code to lose all state information? Do you have to dest and reload all the guards?

Personally, I love the idea of using any of these languages as a scripting system for a Diku… You could even code a "mobprog" interpreter IN said scripting language if you wanted to provide backwards compatibility. But, there are issues to think about as you get more complex systems built up.
13 Apr, 2009, JohnnyStarr wrote in the 5th comment:
Votes: 0
Thanks for the input guys, all really good points. It honestly makes me question what to do next. I think i might love learning new languages as much as i love mudding / mud programming. I think i'm going to just stick to my Ruby base and work from there. And i think David is right about considering that it might be worth those hours of building all the commands. It reminds me that my whole reason for programming my own codebase is so that i can make something original.
13 Apr, 2009, elanthis wrote in the 6th comment:
Votes: 0
Dealing with the memory management issues is a lot easier than some people think, honestly.

You have two distinct objects: the "native" C object and the script language wrapper object. You do not need to make any changes to your native code in terms of object lifetime management. What you do need to do, however, is create a single script object wrapper instance and then tie its lifetime to the native object. You can then "null" the wrapper object (these are usually implemented as some kind of script object that includes a pointer) when the native object is destroyed. So long as you check for null pointers in the wrapper methods (throw an script exception/error from them if called) you will be quite safe. You can pass the wrapper object around in the script code like any other object reference and things will Just Work™.

The specifics of how you do this vary by language. Most script engines use some kind of automatic garbage collection system, so the gist of what you have to do is to create the wrapper object for the native object if it doesn't exist, ensure you have a GC-acknowledged reference to the wrapper object, and store a reference to that object in the native object. The Lua runtime has no method of doing those things directly, for example, so you instead must store the wrapper object/table into the Lua registry, which you can do using the native object's address as a key via lightuserdata; that is due to Lua only ever storing data in the Lua stack or environment or registry and never as independent variables in the C code. Ruby's C API works totally differently, though, so the specific there are something else entirely.

As a final note, though, keep in mind that if you build off of Diku/ROM, you are forever saddled with their retarded licensing, while if you code from scratch you can use something standardized and community-friendly (if you plan on releasing this code to the community) or something that at least gives you personal freedom to charge players or whatever (if you plan on keeping the code private).
13 Apr, 2009, David Haley wrote in the 7th comment:
Votes: 0
That's assuming that you only have native C objects, and script objects are all wrappers. Things get interesting when you have native script objects, and C is the "client" of those scripting objects. (For instance, an event system that can deal with an event implemented in C, or in Lua. Or an entity system, where an entity can be native to either space.)

True, it's not impossible or even that difficult, it's just tricky.

elanthis said:
The Lua runtime has no method of doing those things directly, for example, so you instead must store the wrapper object/table into the Lua registry, which you can do using the native object's address as a key via lightuserdata

You probably don't want to use your own keying system, ne? Wouldn't you either create your own registry table and key by native object's address (you'll have trouble if addresses repeat across lifetimes of wrapper objects), or use the normal registry and the registry, err, registration methods?
13 Apr, 2009, elanthis wrote in the 8th comment:
Votes: 0
The Lua userdata just includes a pointer to the native C object, with a proper metatable. The wrapper object is created when the native object is created and stored in the registry, so whenever the native object needs to be sent to a script, you just look up the wrapper from the registry. When the native object is deleted, the userdata is looked up and its pointer is set to NULL. Then the wrapper is removed from the registry (otherwise it would never be garbage collected). That completely removes all references to the native object's address from the system. So if another native object is created at the same address there is no danger of it being mistaken for the old object. It gets a new wrapper object and that wrapper object is then stored in the registry with the native object's address as a key. That's pretty much exactly what lightuserdata in Lua are meant to be used for, because it's entirely unsafe to use them as actual objects – they're just there to use pointers as unique keys.

Ruby's C API is a little more straightforward in this regard, although you have to manually tackle the issue of locking the wrapper objects in memory so the garbage collector doesn't destroy them. This is one of the areas where C++ makes script APIs so much easier, because then the API's ScriptValue type could just automatically add and remove references/GC-locks on the value with no extra work. Ah well.

Back to Lua (following is also necessary for Ruby, although again the specifics will be quite different) if your native object system has a more advanced memory management scheme (e.g. reference counting) then things get a little more tricky. You need to extend the memory management system slightly, either by adding a new flag (to denote if scripts are still using the object) or add special behavior for when the reference count is reduced to 1 in addition to when it's reduced to 0 (and make it so that the Lua wrapper objects hold a reference to the native object). Then when all native references to the object are released (reference count hits zero in the first scheme or hits 1 in the second scheme) you remove the wrapper object from the registry and then add it to a second weak-value table. The wrapper's metatable needs a __gc method that unsets the flag or releases the final reference and frees the native object when the wrapper object is collected. if at any time the reference count increases again, the wrapper object must be looked up from the weak-value table and put back into the registry. The weak value table is just there so that the wrapper object can be found again from the host application. Depending on the specifics of how you architect your wrapper code, you may not need the weak-value table – it's really just there so that if the script calls into a C method that calls into another C function that calls into another C function that makes the native object live, the wrapper object can be found. If you do reference increments in the wrapper methods where the code ahs direct access to the wrapper object, the weak-value table is totally unnecessary. That may be the best bet, but it would require more manual code writing for your wrapper functions.

Ruby (iirc) allows you to just store a direct reference to the wrapper object in the native object, so it's a bit easier to deal with, though you still need all the special logic for reference increments/decrements.

If your native code is using an even more advanced memory management scheme (a custom GC), then things get ugly quick. There are several large papers online covering that topic. I don't recommend mixing memory managers like that; if your native code is using an advanced GC, you unfortunately may be best off writing a custom scripting language that just relies on that same GC. That is what Scriptix (now-dead script engine I wrote for AweMUD) was for, since AweMUD was using the Boehm-GC. The rewrite to Source MUD entailed removed the GC, going to an ownership model using delayed deallocation and TR1::shared_ptr<>, and then the above trick for interacting with the Lua runtime.

This is one of the reasons why few applications support multiple script languages, and why shared runtimes like the CLR/DLR or Parrot are so interesting. Although I highly doubt many of the users are Lua are going to switch to Lua-on-Parrot, because the biggest advantage of Lua is its small and fast runtime. I do hope to see Ruby, Python, Perl, PHP, and so on all switch to Parrot once it matures enough, though. Aside from Parrot potentially making all of them faster due to the number of developers working on powerful but fast optimizations and improved GC techniques, and even a full native JIT engine (possibly with a tracing engine in the future), it makes it all but trivial for an application to support a wide variety of script languages with few compatibility issues. I've yet to see how Parrot plans on dealing with different languages using different object models or even different ideas of how "core" types like arrays and maps work (whether these will be mapped to a common and shareable core object, or if each language will just use different and incompatible types), but I figure they'll get the best approach figured out.
13 Apr, 2009, David Haley wrote in the 9th comment:
Votes: 0
Well, I think I'll stand by my previous claim that this is a non-trivial issue, given how much you just wrote about it. :wink: The things you said are exactly why I said it's not simple.

And you also only talked about the case of objects living in just one state; life gets even more interesting when you have objects that are owned by either of your two spaces (C++/Lua).
14 Apr, 2009, elanthis wrote in the 10th comment:
Votes: 0
Dude, I take six paragraphs to describe everything. I'm hopelessly verbose. :p

It's really not that complicated. Your both-spaces case is handled by the second half of my rambling description, and it really is the Lua API that makes that part as complex as it is. Lua's C API's big weakness is that it really does suck when working in an object-oriented manner from the host system. It's fantastic for simple bindings, and otherwise requires a lot of hoop jumping. Unfortunately pretty much all script engine C APIs suck in one major way or another due to the whole GC thing, but most other languages make it relatively easier to write C++ wrappers than Lua does. It's possible in Lua, just requires a bit more knowledge of how Lua works than doing it for Ruby or several other languages. Since the OP is concerned with Ruby and not Lua, I wouldn't worry too much about it.

With most script APIs (including Ruby's, I believe) your object wrapper code will at worst be structured something like:

void create_entity_object (entity *object, script_object_descriptor *type) {
/* create a script wrapper object and attach native object to it */
object->script_wrapped = script_new_value_of_user_type(type);
script_set_user_data(object->script_wrapped, object);

/* add a reference to the entity for the script wrapper */
ref_entity(object);
}

void ref_entity(entity *object) {
/* if we have only 0 or 1 references already, this is either a new object or a
script-owned object, and the script wrapper is unrooted, so we must root
it to prevent collection */
if (object->refs <= 1)
script_set_root(object->wrapped);

/* increment reference count */
++object->refs;
}

void unref_entity(entity *object) {
/* decrement reference count */
–object->refs;

/* if there's no more references, the object is dead, kill it */
if (object->refs == 0)
free_entity(object);

/* if there's just one reference left, only the script wrapper is keeping us alive;
unroot the wrapper so the wrapper can be collected if not in use */
else if (object->refs == 1)
script_remove_root(object->script_wrapped);
}

entity_script_gc_collection(script_value value) {
/* retrieve native object from script value */
entity *object = (entity*)script_get_user_data(value);
entity_unref(object);
}


That's nothing. The Lua code would be similar, just with a few more lines to deal with pushing/popping items for the registry (in place of the set_root/remove_root calls) and metatable assignment and all that jazz.
14 Apr, 2009, David Haley wrote in the 11th comment:
Votes: 0
FWIW I know how to do this (I've done it already). I think the interesting thing about this kind of code is that while the output is quite simple in its appearance, understanding the greater issues around designing the code is what's complicated. It's the kind of thing that's easy….. once you've grokked the bigger picture and a fair number of concepts.
0.0/11