Many bugs found in tmwAthena server code

Ask for help regarding any technical issue or report any bug or OS independent issues.


Post Reply
quietlyquietly
Peon
Peon
Posts: 35
Joined: 07 Sep 2023, 09:40

Many bugs found in tmwAthena server code

Post by quietlyquietly »

I was writing scripts and had so much trouble that I read the source code and made a script command guide.
During this process I found a number of BUGS in the tmwAthena server code.

// BUGS
BUG: builtin_getarraysize
Error message identifies it as function "builtin_copyarray".
Message should have "builtin_getarraysize".

BUG: builtin_call
Error message identifies it as function "callfunc".
Message should have "builtin_call".

BUG:
builtin_goto
builtin_callfunc
builtin_call
builtin_callsub
menu
builtin_else
builtin_elif
This executes abort when not a label. This is too severe as would
kill entire script engine.
Should execute something with more
graceful recovery, like "builtin_end".

BUG:
Game code should not have have any "assert" that are not conditional
on DEBUG.
These can halt entire server.
A more graceful recovery is always more practical.
Kill the script, do not kill the server.
Perhaps you have a way to disable all assert, but it makes me nervous to see them, one mistake.

BUG: builtin_countitem
PRINTF message has different format than any other error message.
Should be "builtin_countitem: wrong item ID (%i)".

BUG: builtin_npcareawarp
The vars lx,ly are initialized to -1, but are never set to any other
values. This defeats the intended usage as the last valid position found.


Replace this code:

Code: Select all

    max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3;
    if (max > 1000)
        max = 1000;

P<map_local> m = nd->bl_m;
if (cb) {
    do
    {
        x = random_::in(x0, x1);
        y = random_::in(y0, y1);
    }
    while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)
         && (++j) < max);
    if (j >= max)
    {
        if (lx >= 0)
        {                   // Since reference went wrong, the place which boiled before is used.
            x = lx;
            y = ly;
        }
        else
            return;       // Since reference of the place which boils first went wrong, it stops.
    }
}
else
{
    x = random_::in(x0, x1);
    y = random_::in(y0, y1);
}

With this code:

Code: Select all

    P<map_local> m = nd->bl_m;
    if (cb)
    {
        int j = 0;
        int max = (y1 - y0 + 1) * (x1 - x0 + 1);
        if (max > 750)
            max = 750;

    do
    {
        x = random_::in(x0, x1);
        y = random_::in(y0, y1);
        if( ! bool(map_getcell(m, x, y) & MapCell::UNWALKABLE )  goto warp_the_npc;
    } while ((++j) < max);

    // Search went wrong, find any WALKABLE spot.
    do
    {
        // Increment from last random position, searching for a WALKABLE.	
    x++;
        if( x > x1 )
        {
            x = x0;
            y++;
            if( y > y1 )  y = y0;
        }
        if( ! bool(map_getcell(m, x, y) & MapCell::UNWALKABLE )  goto warp_the_npc;
    } while( --j > 0 )
    return;  // WALKABLE place was not found, do not warp.
}
else
{
    x = random_::in(x0, x1);
    y = random_::in(y0, y1);
}
  warp_the_npc:

BUG: builtin_getusers
FIXME


BUG: builtin_getusers
// Has disabled code due to SIGSEG.
Replace this code:

Code: Select all

    val = 0;
    switch (flag & 0x07)
    {
        case 0:
            // FIXME: SIGSEGV error
            //val = bl->bl_m->users;
            PRINTF("Sorry, but getusers(0) is disabled. Please use getmapusers() instead.\n"_fmt);
            break;

With this code:

Code: Select all

    val = 0;  // by default 0 users
    switch (flag & 0x07)
    {
        case 0:
            // Given protection against segfault
	    if( bl && bl->bl_m )
	    {
                val = bl->bl_m->users;
	    }
            break;

Existing scripts only have getusers(1),
so it could be improved if that was preserved.

So could use this improved code:

Code: Select all

    dumb_ptr<block_list> bl = map_id2bl((flag == 0) ? st->oid : st->rid);
    val = 0;  // by default 0 users
    switch (flag & 0x07)
    {
        case 0:  // NPC map
	case 2:  // Player map
            // Given protection against segfault
	    if( bl && bl->bl_m )
	    {
                val = bl->bl_m->users;
	    }
            break;

BUG: builtin_camera
The "rid" and "player" options are broken due to
an IF-stmt not blocking the final ELSE clause.

Existing code:

Code: Select all

            dumb_ptr<block_list> bl;
            short x = 0, y = 0;
            bool rel = false;
            if (auto *u = AARG(0).get_if<ScriptDataInt>())
                bl = map_id2bl(wrap<BlockId>(u->numi));
            if (auto *g = AARG(0).get_if<ScriptDataStr>())
            {
                if (g->str == "rid"_s || g->str == "player"_s)
                    bl = sd;
                if (g->str == "relative"_s)
                    rel = true;
                else if (g->str == "oid"_s || g->str == "npc"_s)
                    bl = map_id2bl(st->oid);
                else
                    bl = npc_name2id(stringish<NpcName>(g->str));
            }
            if (HARG(1) && HARG(2))
            {
                x = conv_num(st, &AARG(1));
                y = conv_num(st, &AARG(2));
            }
            if (rel)
                clif_npc_action(sd, st->oid, 4, 0, x, y); // camera relative from current camera
            else
                clif_npc_action(sd, st->oid, 2, unwrap<BlockId>(bl->bl_id), x, y); // camera to actor

Replace with the following code:

Code: Select all

            dumb_ptr<block_list> bl;
            short x = 0, y = 0;
            if (HARG(1) && HARG(2))
            {
	        // default is 0,0
                x = conv_num(st, &AARG(1));
                y = conv_num(st, &AARG(2));
            }
            if (auto *u = AARG(0).get_if<ScriptDataInt>())
	    {
	        // interpret as map_id
                bl = map_id2bl(wrap<BlockId>(u->numi));
	    }
            else if (auto *g = AARG(0).get_if<ScriptDataStr>())
            {
                if (g->str == "relative"_s)
		{
                    clif_npc_action(sd, st->oid, 4, 0, x, y); // camera relative from current camera
		    return;
		}
                else if (g->str == "rid"_s || g->str == "player"_s)
                    bl = sd;  // player map
                else if (g->str == "oid"_s || g->str == "npc"_s)
                    bl = map_id2bl(st->oid);  // script NPC map
                else
                    bl = npc_name2id(stringish<NpcName>(g->str));  // NPC name
            }
	    clif_npc_action(sd, st->oid, 2, unwrap<BlockId>(bl->bl_id), x, y); // camera to actor
	    return;

I am sure you will want to look over these code suggestions.
However they are bugs and something will need to be done about them by somebody.

User avatar
WildX
Source of Mana
Source of Mana
Posts: 2141
Joined: 07 Aug 2010, 14:13
Location: United Kingdom
Contact:

Re: Many bugs found in tmwAthena server code

Post by WildX »

Thank you for pointing these out. tmwAthena is open to contributions and depends on them for updates. You can open a PR at https://git.themanaworld.org/legacy/tmwa - I'm sure someone would be willing to apply changes for you, but I prefer it this way so we can more accurately credit our contrubutors. :)

Mana Team member

Post Reply