lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Here's the guideline I use for developing a scripting interface for
anything: just write some example scripts.  Don't worry about how you're
going to do it just yet; just write some "play" scripts that will define
what your scripting API will feel like.  After writing a few examples
and working
out things you thought were a good idea but now you don't like, you can
start actually making them work.  For example, you have this script for
a room object:

    self:AddExit('north', 32)

That's completely doable.  Since you're working on a script that is
unique to a particular object, though, some people might prefer this:

    AddExit('north', 32)

It's completely up to you!  Think about what you want your scripting
layer to look like, then come back and we can help make that a reality. =)

-Rob

On 10/24/12 5:47 PM, Littlefield, Tyler wrote:
> Hello:
> The loading and execution of the objects is done for all objects.
> Basically here's how this works, or at least how I see it working.
> When objects are currently loaded, their script is executed. The
> script is just stored on a property on the object so I can edit it
> with an in-game editor.
> So, if the script is on a room for example or an NPC, I'd like to do
> something like: self:AddExit("north", 32) or something. Events are the
> same way:
> self:AddEvent("OnEnter", onEnter)
> I have a rather outdated but functional copy of my work at:
> http://code.google.com/p/aspenmud.
> I'd just like to clean up scripting and make it work a lot better, but
> I don't have a very great knowledge of Lua to do so, so I'm looking
> for any advice on how to do so. Mainly I'm trying to make the
> scripting side more OO-like, and I'd like to know if the execution
> method that I had in a couple emails back is what is recommended for
> sandboxing each object. I think here sandboxing is the wrong word, but
> it sort of gives it it's own environment to work in, so that if I have
> say, two onEnter functions on two different rooms, there won't be name
> collisions.
>
>
> On 10/24/2012 8:07 AM, Rob Hoelz wrote:
>> On 10/24/12 3:59 PM, Littlefield, Tyler wrote:
>>> On 10/24/2012 1:38 AM, Rob Hoelz wrote:
>>>> Responses inline.
>>>> Hello:
>>> Thanks. To your first question, I was a bit unclear there. When I load
>>> an object, that's how I create a sandbox of sorts (so there aren't
>>> name collisions). I just put it through that execute method.
>> So each object has its own sandbox? Or just event objects?  I'm still
>> not clear.
>>>> As for the making things OO, I'd like to bind the c++ methods so that
>>>> they can be called with OO. So rather than what I have now, where I
>>>> have to call player.send(ch, "hello world!") I"d like to be able to
>>>> do player:send("Hello world.")
>>> I am curious what you'd do differently. As I said, I'm new to this
>>> whole thing, and revamping the scripting engine is kind of important
>>> before I get to far. Any advice is appreciated.
>> Well, let's take a step back.  What I would do depends on the project
>> itself, and I know next to nothing about yours.  I know so far that
>> you're adding scripting to a MUD, and you want to be able to fire and
>> handle
>> events.  Would you mind going into more detail about how you see this
>> system working?
>>>> On 10/23/12 10:32 PM, Littlefield, Tyler wrote:
>>>>> Hello all:
>>>>> I've been working on a mud for a while, and my scripting engine is
>>>>> rather primative. basically a mud is just a multi-user text-based
>>>>> game, so it can be considered along the same lines as a game engine.
>>>>>
>>>>> I'd really like to revamp my scripting system before it gets more
>>>>> complicated than it is already. I'm not really sure how to do a
>>>>> couple
>>>>> of things though.
>>>>>
>>>>> First, triggers are obviously important. Currently I have events in
>>>>> game that just fire when an action is performed, so I can extend
>>>>> these
>>>>> to hit Lua.
>>>>> I'm thinking each object will just do something like:
>>>>> AddTrigger("give", onGive)
>>>>> This will just bind the onGive function/callback into an event that
>>>>> will be triggered when this happens. is that a decent idea? Every
>>>>> object that is loaded gets it's code executed in a virtual table of
>>>>> sorts. It looks something like this:
>>>>> void Script::Execute(Entity* obj, const std::string &code)
>>>>> {
>>>>>     lua_State* state = Script::GetState();
>>>>>     int ret = 0;
>>>>>     World* world = World::GetPtr();
>>>>>
>>>>>     if (!luaL_loadbuffer(state, code.c_str(), code.length(),
>>>>> "execution"))   // chunk is at -1
>>>>>       {
>>>>> //we need to create the metatable and store it in the registry:
>>>>>         lua_pushinteger(state, obj->GetOnum());
>>>>>         lua_newtable(state); // create shadow environment table at -1
>>>>>         lua_settable(state, LUA_REGISTRYINDEX);
>>>>> //now we get the table back again:
>>>>>         lua_pushinteger(state, obj->GetOnum());
>>>>>         lua_gettable(state, LUA_REGISTRYINDEX);
>>>>>         ObjectToStack(state, obj);
>>>>>         lua_setfield(state, -2, "this");
>>>>>         lua_getfield(state, LUA_REGISTRYINDEX, "meta"); //our
>>>>> metatable
>>>>> is at -1
>>>>>         lua_setmetatable(state, -2); //set table and pop
>>>>>
>>>>>         lua_setfenv(state, -2); //sets the environment
>>>>>         ret = lua_pcall(state, 0, 0, 0);
>>>>>         if (ret)
>>>>>           {
>>>>>             world->WriteLog(lua_tostring(state, -1), SCRIPT,
>>>>> "script");
>>>>>             lua_pop(state, 1);
>>>>>           }
>>>>>       }
>>>>> }
>>>>> Is this recommended? The idea was to keep objects from having naming
>>>>> collisions. For example if I had a function named foo on a sword
>>>>> and a
>>>>> player. Would this also be something to use when loading global
>>>>> scripts?
>>>> I'm not 100% on what this code is supposed to do; is it registering
>>>> new
>>>> event callbacks, or is it invoking the callbacks for an event?
>>>>> Second, I'd really like to objify everything. For example in order to
>>>>> send a message to a player currently, I have to do:
>>>>> player.send(mob, "hello world!")
>>>>> How hard would it be to attach ehese in classes somehow, so I just
>>>>> call self:send("hello world!" or whatever?
>>>>> Right now my registration is done in a table:
>>>>> BOOL InitPlayerScript(Script* s)
>>>>> {
>>>>>     lua_State* lstate = s->GetState();
>>>>>     luaL_newmetatable(lstate, "player");
>>>>>     lua_pushstring(lstate, "__index");
>>>>>     lua_pushvalue(lstate, -2);
>>>>>     lua_settable(lstate, -3);
>>>>>     luaL_openlib(lstate, "player", player_table, 0);
>>>>> ...
>>>>> }
>>>>> static const struct luaL_reg player_table [] =
>>>>>     {"GetTitle", SCR_GetTitle},
>>>>>     {"SetTitle", SCR_SetTitle},
>>>>> ...
>>>>>     {NULL, NULL}
>>>>> };
>>>>> int SCR_GetTitle(lua_State* l)
>>>>> {
>>>>>     UserData* udata = NULL;
>>>>>
>>>>>     if (lua_gettop(l) != 1)
>>>>>       {
>>>>>         SCR_Error(l, "Invalid number of arguments to \'GetTitle\'.");
>>>>>         return 0;
>>>>>       }
>>>>>
>>>>>     udata = (UserData*)lua_touserdata(l, -1);
>>>>>     if (!IsPlayer(l, udata))
>>>>>       {
>>>>>         return 0;
>>>>>       }
>>>>>
>>>>>     lua_pushlstring(l, ((Player*)udata->ptr)->GetTitle().c_str(),
>>>>> ((Player*)udata->ptr)->GetTitle().length());
>>>>>     return 1;
>>>>> }
>>>>> int SCR_SetTitle(lua_State* l)
>>>>> {
>>>>>     const char* title = NULL;
>>>>>     UserData* udata = NULL;
>>>>>
>>>>>     if (lua_gettop(l) != 2)
>>>>>       {
>>>>>         SCR_Error(l, "Invalid number of arguments to \'SetTitle\'.");
>>>>>         return 0;
>>>>>       }
>>>>>
>>>>>     title = lua_tostring(l, -1);
>>>>>     if (!title)
>>>>>       {
>>>>>         SCR_Error(l, "Argument 2 to \'SetTitle\' must be a string.");
>>>>>         return 0;
>>>>>       }
>>>>>
>>>>>     udata = (UserData*)lua_touserdata(l, -2);
>>>>>     if (!IsPlayer(l, udata))
>>>>>       {
>>>>>         return 0;
>>>>>       }
>>>>>
>>>>>     ((Player*)udata->ptr)->SetTitle(title);
>>>>>     return 0;
>>>>> }
>>>>> Can something like this be more OO like?
>>>> Which part do you want to be more OO? The Lua part? The C++ part? 
>>>> Your
>>>> C++ code looks like it would work with OO-style Lua, although I
>>>> would do
>>>> a few things differently (namely, if !IsPlayer, I would throw
>>>> an error).
>>>>> Finally, I'm curious how to go about setting up object properties. a
>>>>> lot of muds that have used lua (I was just looking at the Aard page)
>>>>> sets stuff up
>>>>> like ch for the calling player, self for the current object, then
>>>>> they
>>>>> allow for like ch.gold, ch.int etc. I'm curious how this works. I
>>>>> know
>>>>> about setting values on a table, but it seems that the table would
>>>>> have to call it's underlying c++ object's GetGold method to retrieve
>>>>> it.
>>>> What you could do is have an __index metamethod that returned the
>>>> value
>>>> of the property as needed.  Here's an example in Lua; converting it to
>>>> C++ is left as an exercise for the reader. =)
>>>>
>>>>      local mt      = {}
>>>>      local object = setmetatable({}, mt)
>>>>      function mt:__index(key)
>>>>        if key == 'time' then
>>>>          return os.time()
>>>>        end
>>>>      end
>>>>      print(object.time)
>>>>> Thanks in advance, and sorry for all the questions.
>>>>>
>>>
>>
>
>