Daimonin MMORPG Logo
Daimonin voting system

Recent Posts

[Scripts] Attack Register by smacky
April 15, 2014, 07:41:09 pm
[Dev Server] MOVED: Rewritten pick up/drop code by smacky
April 15, 2014, 03:14:20 pm
[Community chat] Clobber's Garden by Shroud
April 14, 2014, 07:32:21 pm
[Community chat] Last one to post wins! by Shroud
April 13, 2014, 08:13:28 pm
[Bug discussion] v0.10.6 Bugs by smacky
April 13, 2014, 08:05:27 pm
[Daimonin project] v0.10.7 development by smacky
April 12, 2014, 09:49:49 pm
[Arches] Joes archs by Shroud
April 07, 2014, 05:19:31 pm
[Bug discussion] Lost Skills/Levels by smacky
April 04, 2014, 04:04:54 pm

Daimonin Forums

 
Pages: [1] 2 3   Go Down
  Print  
Author Topic: Lua obj:AddBuff() and obj:CheckBuff()  (Read 29915 times)
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« on: January 12, 2012, 12:54:34 am »

The two functions object:AddBuff() and object:CheckBuff() work together to make a system which limits the enhancements items can receive.

object:AddBuff() accepts a string argument which is a unique string describing the buff such as "tower of bebeniss weapon blessing". It will always return nil.

object:CheckBuff() accepts a string argument, which is also a unique id. It will return a boolean, whether or not that buff is in the item already (atm the documentation says it returns a number, but that was a mistake).

Scripters should responsibly use these two when creating item buffs. It is possible to buff an item more than once with the same buff by suffixing the buff string with "_2" or something. Only do this if it is appropriate for the buff.

An example of a buff which is only allowed on an item once:

Code: [Select]
local item = something -- Pretend that something is an object here.
local buffstring = 'guildhall_smith_rebalance'

if not item:CheckBuff(buffstring) then
    item:AddBuff(buffstring)
else
    -- This buff is already on the item.
end

An example of an item with two of the same buff:

Code: [Select]
local item = something -- Pretend something is an object.
local buffstring = 'guildhall_smith_rebalance'

if not item:CheckBuff(buffstring)
    item:AddBuff(buffstring)
else
    if not item:CheckBuff(buffstring .. '_2') then
        item:AddBuff(buffstring .. '_2')
    else
        -- Disallow the buff.
    end
end

Known issues:

The function the comparison uses is fairly limited in the way it checks for a matching string. If an item has, for example, a buff 'guildhall_foo_something', and CheckBuff checks for 'foo', it will return true. I just thought up a way to fix that, but it has yet to be implemented.
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #1 on: January 12, 2012, 05:45:39 am »


Great - thanks!  One question; are there server side changes here (I guess there are), or is it lua script implementation only?  I only ask because I can't recompile the server atm; let me know if I need to (although I guess I'm going to have to cross that bridge sooner or later).
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« Reply #2 on: January 12, 2012, 06:07:22 am »

These changes are only found in the trunk server. So yes, you'll have to compile it from the latest source from SF.
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #3 on: January 12, 2012, 11:17:37 am »

OK - no problem - thanks!
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #4 on: January 17, 2012, 11:05:10 pm »

Hmmm ... stuck again.  Now that I've recompiled \trunk, I thought I'd test this.  I've got a simple script that just does a CheckBuff(), but when the lua script runs, the server crashes (with no messages in the log file).

The script is very small (only for testing), and when I comment out the if not object:CheckBuff(buffstring) line I get no crashes.

I tried object:XXXCheckBuff(buffstring), and as expected got a lua error (no such member) with no server crash, so I'm sure my \trunk server does have CheckBuff implemented ...

Help!
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« Reply #5 on: January 18, 2012, 12:20:47 am »

Hm. Maybe I messed something up after I tested it. I'll check it out.
smacky
The Singular Soul of Lom Lobon Tribe
*
*
*
*
*
*
*



Posts: 8522
Karma: +272/-134


View Profile
« Reply #6 on: January 20, 2012, 01:05:13 pm »

(Using r6679 which puts it on main as reference) There are a few problems. First the AddBuff and CheckBuff functions themselves:

Code: [Select]
+/*****************************************************************************/
+/* Name   : GameObject_AddBuff                                               */
+/* Lua    : object:AddBuff(buffname)                                         */
+/* Info   : Add a string to the item's buff list. obj:CheckBuff() can then   */
+/*          check to see if that buff is already on the item.                */
+/* Status : Tested/Stable                                                    */
+/*****************************************************************************/
+static int GameObject_AddBuff(lua_State *L)
+{
+    lua_object     *self = NULL;
+    char           *buff = NULL;
+    object         *obj;
+
+    get_lua_args(L, "Os", &self, &buff);
+
+    obj = self->data.object;
+
+    char *result = malloc(strlen(obj->buffs) + strlen(buff) + 1);
+    memcpy(result, obj->buffs, strlen(obj->buffs));
+    strcat(result, ";");
+    strcat(result, buff);
+    obj->buffs = result;
+
+    return 1;
+}
+
+/*****************************************************************************/
+/* Name   : GameObject_CheckBuff                                             */
+/* Lua    : object:CheckBuff(buffname)                                       */
+/* Info   : Check to see if the specified buff is already on the item. This  */
+/*          will ensure that the item cannot get more buffs than what is     */
+/*          allowed.                                                         */
+/* Returns: The number of times a buff has been placed on an item. Some buffs*/
+/*          may be placed more than once.                                    */
+/* TODO   : Atm this function only uses a basic check for the buff. If the   */
+/*          buff is anywhere in the string, it will return true. For example,*/
+/*          if the object's buff string is "111example222;" and the function */
+/*          checks for "example", it will return true.                       */
+/* Status : Tested/Stable                                                    */
+/*****************************************************************************/
+static int GameObject_CheckBuff(lua_State *L)
+{
+    lua_object     *self = NULL;
+    char           *buff = NULL;
+    object         *obj;
+
+    get_lua_args(L, "Os", &self, &buff);
+
+    obj = self->data.object;
+
+    char *result = strstr(obj->buffs, buff);
+
+    if (result)
+    {
+        lua_pushboolean(L, TRUE);
+    } else
+    {
+        lua_pushboolean(L, FALSE);
+    }
+
+    return 1;
+}

Declaring a local variable in the middle of a {...} block (cf, char *result) apparently make some compilers cry. Do all the declarations at the start then (re)assign later on.

What's going on in AddBuff?

Code: [Select]
+    char *result = malloc(strlen(obj->buffs) + strlen(buff) + 1);
+    memcpy(result, obj->buffs, strlen(obj->buffs));
+    strcat(result, ";");
+    strcat(result, buff);
+    obj->buffs = result;

First you never free result so memory leak. Also please use the MALLOC() etc macros (but don't do this here, see below).

Second you don't give result enough space. it needs another byte for the ';'.

Third keep seeing below. ;)

Did I not remove TRUE and FALSE from the source? Maybe that was just the client. Anyway please use 1 and 0 instead.

OK it's below.

Our basic problem is that the server doesn't really handle lists in attributes, especially strings. Handling with the str* functions is pretty slow, hence the shstr stuff. But this also has drawbacks and is not helpful for lists anyway.

But in object.c you set and compare obj->buff as a shared string but in daimonin_object.c it is done as a normal string. These are incompatible. For instance obj->buff initiialises as NULL always, hence Torch's CheckBuff crash when it tries to strlen() and memcpy() this.

Needs a rethink and removal from main (Tested/Stable? Bit optimistic ;) Test properly on dev first).

EDIT: Reverted from msin.
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #7 on: January 22, 2012, 08:44:04 pm »


Well doctor - what's the prognosis?  Do we think AddBuff will live anytime soon (I don't know if the above is a difficult fix or an easy one ...)?  Should I still wait for this to be done or go ahead and commit my temporary fix to the ToB quests?
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« Reply #8 on: January 22, 2012, 09:03:34 pm »

Well I had it working at one point on local or I wouldn't have committed it to Dev. So I assume I did something else to it afterwards and didn't test thoroughly enough.

At the time I hadn't used malloc/realloc/calloc/free at all, so that could explain the issues there. But why do we need macros in the first place?
smacky
The Singular Soul of Lom Lobon Tribe
*
*
*
*
*
*
*



Posts: 8522
Karma: +272/-134


View Profile
« Reply #9 on: January 22, 2012, 09:57:20 pm »

MALLOC() initialises the memory (to 0) while malloc() doesn't. It also reports OOM on failure.

MALLOC_STRING() does these things too. It automatically allocates the right space for string + \0 and copies the string to it (so it is essentially strdup() -- we used to have a complicated number of versions of strdup() because it seems Windows has its own version as apparently do most devs and often don't seem to look at the code that is already there -- so we had at least strdup(), str_dup(), and strdup_local() which we either synonyms or duplicates).

FREE() both frees the pointer and sets it to NULL, which is very important as that's how you query is something is actually freed.

That said we should avoid MALLOC*() where possible in the server (except for little things maybe) -- use mempool and shstr in preference.


Here's a thought to avoid lists in attributes and MALLOC() and use shstrs.

Decide on a (small) limit on the number of buffs any one object can take; lets say 4. We can justify this even and say it's dependent on quacon. So qua must be >= 80 to take one buff, >= 85 for two, >= 90 for three, and >= 95 for four.

So we have attributes buff1 -> buff4. Each takes a shstr  (so NULL meaning no buff or a string). The plugin funcs can then look at these four attributes in turn, knowing that each has only one value.

Shstrs are case-sensitive though so xxxxxx and Xxxxxx and xXxxxx etc are different buffs.
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #10 on: January 22, 2012, 10:31:25 pm »


That sounds reasonable.  No item can take the blessing of a god (or honing by a smith) more than 4 times ...

I suggest a wrapper script that hides the 4 separate calls, but other than that sounds a good idea IMO.
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« Reply #11 on: January 22, 2012, 10:37:22 pm »

Thanks for the description, Smacky.

I suggest a wrapper script that hides the 4 separate calls, but other than that sounds a good idea IMO.

Well I was thinking to have the check of all four calls inside the obj:CheckBuff() code, so no wrappers needed. I.e.

Code: [Select]
if (obj->buff1 == arg)
{
    num_buffs++;
}
if (obj->buff2 == arg)
{
    num_buffs++;
}
if (obj->buff3 == arg)
{
    num_buffs++;
}
if (obj->buff4 == arg)
{
    num_buffs++;
}

lua_pushnumber(L, num_buffs); // Not syntactically correct.

That way it's possible for an item to be buffed with the same buff twice, although hopefully mappers remain sensible about this.

Another perk of a fixed-number system is that powerful buffs can hog more than one slot.
smacky
The Singular Soul of Lom Lobon Tribe
*
*
*
*
*
*
*



Posts: 8522
Karma: +272/-134


View Profile
« Reply #12 on: January 23, 2012, 01:19:19 pm »

I hadn't thought of powerful buffs taking multiple slots.

I'm not sure (why) you'd want to allow the same buff twice.

I think you would need 4 Lua methods like this (note that this implementation does not bother with qua, should be simple to add such a restriction though):

Code: [Select]
/* Return: positive if op has buff (this is the slot it occupies); negative if
 * op does not have buff but has free slots (this is the first free slot); or
 * zero if op does not have buff and no free slots. */
static sint8 CheckBuff(object *op, shstr *buff)
{
    sint8 freeslot = 0;

    /* There must be a less pedestrian way to do this (ie, a for loop) but I
     * can't think how ATM. */
    if (op->buff4)
    {
        if (op->buff4 == buff)
        {
            return 4;
        }
    }
    else
    {
        freeslot = -4;
    }

    if (op->buff3)
    {
        if (op->buff3 == buff)
        {
            return 3;
        }
    }
    else
    {
        freeslot = -3;
    }

    if (op->buff2)
    {
        if (op->buff2 == buff)
        {
            return 2;
        }
    }
    else
    {
        freeslot = -2;
    }

    if (op->buff1)
    {
        if (op->buff1 == buff)
        {
            return 1;
        }
    }
    else
    {
        freeslot = -1;
    }

    return freeslot;
}

/* Lua returns: false if obj does not have buff; true if it does. */
static int GameObject_CheckBuff(lua_State *L)
{
    lua_object *self;
    char       *buff_in;
    shstr      *buff_out = NULL;

    get_lua_args(L, "Os", &self, &buff_in);
    FREE_AND_COPY_HASH(buff_out, buff_in);

    if (CheckBuff(WHO, buff_out) <= 0)
    {
        lua_pushboolean(L, 0);
    }
    else
    {
        lua_pushboolean(L, 1);
    }

    FREE_AND_CLEAR_HASH(buff_out);

    return 1;
}

/* Lua returns two values. The first is boolean and indicates if buff was
 * added. The second is a number and is the slot in question. So: false and 0
 * if obj does not have buff and has no free slots; true and n if obj does not
 * have buff and does have a free slot (so buff has now been added to this
 * slot); or false and n if obj already has buff andd the slot it occupies. */
static int GameObject_AddBuff(lua_State *L)
{
    lua_object *self;
    char       *buff_in;
    shstr      *buff_out = NULL;
    sint8       freeslot;

    get_lua_args(L, "Os", &self, &buff_in);
    FREE_AND_COPY_HASH(buff_out, buff_in);
    freeslot = CheckBuff(WHO, buff_out);

    if (!freeslot)
    {
        lua_pushboolean(L, 0);
        lua_pushnumber(L, 0);
    }
    else if (freeslot < 0)
    {
        /* Again, less pedstrian way is needed. */
        if (freeslot == -1)
        {
            FREE_AND_COPY_HASH(WHO->buff1, buff_out);
        }
        else if (freeslot == -2)
        {
            FREE_AND_COPY_HASH(WHO->buff2, buff_out);
        }
        else if (freeslot == -3)
        {
            FREE_AND_COPY_HASH(WHO->buff3, buff_out);
        }
        else if (freeslot == -4)
        {
            FREE_AND_COPY_HASH(WHO->buff4, buff_out);
        }

        lua_pushboolean(L, 1);
        lua_pushnumber(L, 0 - freeslot);
    }
    else
    {
        lua_pushboolean(L, 0);
        lua_pushnumber(L, freeslot);
    }

    FREE_AND_CLEAR_HASH(buff_out);

    return 2;
}

/* Lua returns: false if obj does not have buff; true if obj does have
 * buff (so buff has now been removed). */
static int GameObject_RemoveBuff(lua_State *L)
{
    lua_object *self;
    char       *buff_in;
    shstr      *buff_out = NULL;
    sint8       freeslot;

    get_lua_args(L, "Os", &self, &buff_in);
    FREE_AND_COPY_HASH(buff_out, buff_in);

    if ((freeslot = CheckBuff(WHO, buff_out)) <= 0)
    {
        lua_pushboolean(L, 0);
    }
    else
    {
        /* Again, less pedstrian way is needed. */
        if (freeslot == 1)
        {
            FREE_AND_CLEAR_HASH(WHO->buff1);
        }
        else if (freeslot == 2)
        {
            FREE_AND_CLEAR_HASH(WHO->buff2);
        }
        else if (freeslot == 3)
        {
            FREE_AND_CLEAR_HASH(WHO->buff3);
        }
        else if (freeslot == 4)
        {
            FREE_AND_CLEAR_HASH(WHO->buff4);
        }

        lua_pushboolean(L, 1);
    }

    FREE_AND_CLEAR_HASH(buff_out);

    return 1;
}

/* Lua returns four values, each of which is eiither a string (the buff for
 * that slot) or nil. */
static int GameObject_GetBuff(lua_State *L)
{
    lua_object *self;

    get_lua_args(L, "O", &self);

    /* Again, less pedstrian way is needed. */
    if (WHO->buff1)
    {
        lua_pushstring(L, WHO->buff1);
    }
    else
    {
        lua_pushnil(L);
    }

    if (WHO->buff2)
    {
        lua_pushstring(L, WHO->buff2);
    }
    else
    {
        lua_pushnil(L);
    }

    if (WHO->buff3)
    {
        lua_pushstring(L, WHO->buff3);
    }
    else
    {
        lua_pushnil(L);
    }

    if (WHO->buff4)
    {
        lua_pushstring(L, WHO->buff4);
    }
    else
    {
        lua_pushnil(L);
    }

    return 4;
}

EDIT: It has just occurred to me that this whole idea is probably covering ground that the artifact system already covers but not as well (because with this we can add/remove/check a string indicating iif an object has a buff but there is no way to find out what the effects of the buff actually are). So if Torch adds a buff in his script then I write a general debuffer script, how do I know what to do to the object to actually debuff it and return it to it's original state?
Torchwood
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1494
Karma: +44/-7


View Profile
« Reply #13 on: January 24, 2012, 04:35:07 pm »

I don't think there should be separate LUA methods for each buff slot - I think the implementation of the buff system (whether by individual attributes for each buff slot, or by lists, or whatever) should be hidden from the user.  Then, we can change the implementation as required, and any LUA scripts will not required re-write.

I would suggest we need more than just add and check functions.  How about this (the below assumes the concept of a limited number of separate 'slots' as above):

ClearAllBuffs()
- clears all buff slots
- true on success, false on failure
- mostly used in debugging npc scripts, unlikely to be used in a real quest, but I think still important to have
 
ClearBuff(buff-string)
- removes buff-string from any slots that contain it
- true on success, false on failure
- might be used within a quest where the npc can improve the item in small increments several times?  So, at stage one of the quest, your item gets a minor update, and at stage 2 you get a second update.  We don't want to take up 2 buff slots, so we delete the first and add the second buff-string.

AddBuff(buff-string, optional number-of-slots default = 1)
- adds buff-string to number-of-slots
- true on success, false on failure
- e.g. it might be decided that a big item improvement should prevent any other improvements (to prevent super-items), so here we can fill all remaining buff-slots up
 
CheckBuff(buff-string)
- returns number of buff slots that hold buff-string, 0, 1, 2, …

CheckSpaceBuff()
- returns number of empty / available buff slots, 0, 1, 2, …
- The LUA script writer can then check the status of an item to see if it can be blessed or whether it is already full (and give appropriate feedback to the player)
 
SizeOfBuff()
- returns total number of buff slots (full and empty) on this item, 0, 1, 2, …
- Umm ... don't know why we would want this, but it completes the 'set' of Buff functions I think

======

Also, could Buffs be implemented in the same way as an item story?  I don't really know, but doesn't this allow line breaks and use something like "endmsg"?  Would this help us to use a single attribute (or am I barking up the wrong tree)?  Maybe there's no benefit in doing so ...
_people_
Warlord of Moroch Army
*
*
*
*
*
*
*



Posts: 1511
Karma: +53/-4


View Profile
« Reply #14 on: January 25, 2012, 05:48:59 am »

That's what we were saying. The Lua user doesn't have to put up with buff1, 2, etc. That's the server coder's job (and it's unavoidable for server coders).

The current buff system uses an approach similar to your last suggestion. It uses ; as a delimiter instead of \n. And there are obviously issues with that implementation.
Pages: [1] 2 3   Go Up
 
 

Powered by SMF 2.0 RC1-1 | SMF © 2006–2008, Simple Machines LLC
Page created in 0.158 seconds with 23 queries.
Checkout