.nut script does not pass data to SQL database when params[3] in the script is set to 1
22:36 26 Dec 2025

I have a problem with a .nut file that's part of a game server that's written in c++.
I did not write these server files, they're part of an open source project that is no longer available to download, otherwise I would link to the github page.

The .nut script is supposed to create/modify an entry in an SQL database, with the following information:

UID: ID of the entry
Character: the ID of the player attached to this entry, if any.
RelatedTo: The ID of the player's clan, if any.
Type: The ID of the counter.
Counter: The value of the counter.
PreExpireType: I'm not 100% sure what this value does.
GroupCounter: I'm not 100% sure what this value does.
TimeStamp: Timestamp of when counter was changed.

The .nut script that's called by the server's event system looks like this:

function define(script)
{
    script.Name = "action_eventCounter";
    script.Type = "ActionCustom";
    return 0;
}

// Create/Modify an event counter
// - params[0]: event counter ID
// - params[1]: value to change/set counter by
// - params[2]: counter should be decreased if 1, set to params[1] if 2, flag
//   mask (add) if 3, floored at value if 4
// - params[3]: Option flags, if 1 set the world counter instead, if 2 set
//   current clan on RelatedTo field, if 4 retrieve value from zone character
//   flag matching param 1 instead
function run(source, cState, dState, zone, server, params)
{
    local syncManager = server.GetChannelSyncManager();
    local worldDB = server.GetWorldDatabase();

    local eCounter = null;
    local characterUID = null;
    local counterValue = params.len() >= 2 ? params[1].tointeger() : 1;

    if(params.len() >= 4 && (params[3].tointeger() & 4) != 0)
    {
        if(cState == null || zone == null)
        {
            return Result_t.FAIL;
        }

        counterValue = zone.GetFlagState(counterValue, 0, cState.GetWorldCID());
    }

    if(params.len() >= 4 && (params[3].tointeger() & 1) != 0)
    {
        // Use world counter
        eCounter = syncManager.GetWorldEventCounter(params[0].tointeger());
        characterUID = UUID();
    }
    else
    {
        // Use character counter
        local character = cState != null ? cState.GetEntity() : null;
        if(character == null)
        {
            return Result_t.FAIL;
        }
    
        eCounter = cState.GetEventCounter(params[0].tointeger(), true);

        characterUID = character.GetUUID();
    }

    if(params.len() == 3)
    {
        if(params[2].tointeger() == 1)
        {
            // Decrease
            counterValue = counterValue * -1;
        }
        else if(params[2].tointeger() == 3)
        {
            // Flag mask (add)
            counterValue = 1 << counterValue;
        }
    }

    local relatedTo = eCounter != null ? eCounter.GetRelatedTo() : UUID();
    if(params.len() >= 4 && (params[3].tointeger() & 2) != 0)
    {
        local character = cState != null ? cState.GetEntity() : null;
        local clan = character != null ? character.GetClan().Get() : null;
        if(clan != null)
        {
            relatedTo = clan.GetUUID();
        }
    }

    if(!eCounter || eCounter.GetUUID().IsNull())
    {
        if(!eCounter)
        {
            eCounter = EventCounter();
            eCounter.SetCharacter(characterUID);
            eCounter.SetType(params[0].tointeger());
        }

        eCounter.SetCounter(counterValue);
        eCounter.SetRelatedTo(relatedTo);

        if(!PersistentObject.Register(eCounter, UUID()) || !eCounter.Insert(worldDB))
        {
            return Result_t.FAIL;
        }
    }
    else
    {
        if(params.len() == 3 && params[2].tointeger() == 2)
        {
            // Setting
        }
        else if(params.len() == 3 && params[2].tointeger() == 3)
        {
            // Flag mask (add)
            counterValue = eCounter.GetCounter() | counterValue;
        }
        else if(params.len() == 3 && params[2].tointeger() == 4)
        {
            // Flooring
            counterValue = eCounter.GetCounter() > counterValue
                ? eCounter.GetCounter() : counterValue;
        }
        else
        {
            // Adding
            counterValue = eCounter.GetCounter() + counterValue;
        }

        if(eCounter.GetCounter() == counterValue &&
            eCounter.GetRelatedTo() == relatedTo)
        {
            // Nothing to do
            return Result_t.SUCCESS;
        }

        eCounter.SetCounter(counterValue);
        eCounter.SetRelatedTo(relatedTo);

        if(!eCounter.Update(worldDB))
        {
            return Result_t.FAIL;
        }
    }

    syncManager.UpdateRecord(eCounter, "EventCounter");
    syncManager.SyncOutgoing();

    return Result_t.SUCCESS;
}

So we can call the script with up to 4 parameters:
params[0]: counter ID
params[1]: counter value
params[2]: enum, whether we add, set or subtract the value of params[1] from the counter.
params[3]: enum, some options - mainly whether this counter is part of the world, or attached to a player and/or their clan.

The problem:

When I try to call this script with params[3] set to 1, something fails in the script and the parameters are not passed correctly into the SQL database:

action_eventCounter(-101, 1, 2, 1) results in the database entry looking like this:

UID Character RelatedTo Type Counter PreExpireType GroupCounter TimeStamp
b6e0c504-6705-4414-8ca1-6c8768bac167 00000000-0000-0000-0000-000000000000 00000000-0000-0000-0000-000000000000 -101 0 0 0 0

Character and RelatedTo are expected as strings of 0 - However, all values after -101 end up as 0, which makes me think that the script terminates early when params[3] is set to 1. Expected behavior in this case is for Counter to have a value of 1, and for TimeStamp to have an integer representing server time.

This behavior works correctly when params[3] is set to 0 - which makes me think that the script somehow fails when params[3] is equals to 1.

What I have tried:

I have tried using AgentRansack to search through the source code for related code. However, I cannot narrow down the problem and I admit that I am not good enough as a programmer to identify the cause of the problem.
At the start of the .nut script, it's referencing a syncManager and worldDB. It's possible that something is going wrong in functions called in other scripts, but I am first looking to see if there's something wrong with the .nut script that would cause the values to not pass through properly.

c++ squirrel