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.