PDA

View Full Version : Not sure why not finding "children" for a node I see has "children"



Varsuuk
March 28th, 2019, 13:48
[SOLVED - rather I was misusing "#" there was never anything wrong - # isn't used on non integer tables]

This is the output from console, as you see it counts there to be no nods - under what I THINK should be a node (classes) having 1 child (class)
I am confused, the original code obviously works, it is from 5E and in (modified) AD&D Core as well. I made very small changes but nothing substantial - I don't see what I did wrong. But then I am really stumped at times by the xml interactions ;)

The most likely culprit is the XML does not say what I think it does or the definition of "children" has a requirement nuance?




...
Runtime Notice: s'n=id-00003, class=reference_class, rec=reference.classes.fighter@Swords & Wizardry Complete - PHB'
Runtime Notice: s'sRecord: reference.classes.fighter@Swords & Wizardry Complete - PHB'
Runtime Notice: s'create or get from: id-00003'
Runtime Notice: s'nodeList: classes'
Runtime Notice: s'resource = ' | s'char_abilities_message_classadd'
Runtime Notice: s'#nodes=0'
Script Error: [string "campaign/scripts/manager_char.lua"]:187: attempt to call field 'findNode' (a nil value)


The snippet from "addClassRef" method, novelist for the char sheet in question who not have 0 children (if I understand what qualifies as children and likely THAT is my mistake.)


-- Get the list we are going to add to. If "classes" did not exist, it would be created.
Debug.console("create or get from: " .. nodeChar.getName());
local nodeList = nodeChar.createChild("classes");
Debug.console("nodeList: " .. nodeList.getName());

if not nodeList then
-- I cannot imagine what error could occur here, but as the API mentions nil
-- as a possibility in case of error, I'll leave the check.
Output.chatMessage("** ALERT DEV: nodeChar.createChild(\"classes\" returned nil");
return;
end

-- New class name.
local sClassName = DB.getValue(nodeSource, "name", "");

-- Notify
Output.chatMessageFormatted("char_abilities_message_classadd", sClassName, DB.getValue(nodeChar, "name", ""))


-- Check to see if the character already has this class
local nodeClass = nil;
local sRecordSansModule = StringManager.split(sRecord, "@")[1];
local sRecordSourceModule = StringManager.split(sRecord, "@")[2];

local aCharClassNodes = nodeList.getChildren();
Debug.console("#nodes=" .. #aCharClassNodes);
local xxx = nodeList.findNode(".class"); <<<------- LINE 187 which above indicates null, was trying different ways to "grab" class
Debug.console("num xxx: ", #xxx);


XML being looked at in code above:


</wisdom>
</abilities>
<classes>
<class>
<active type="number">1</active>
<level type="number">2</level>
<name type="string">fighter</name>
<xp type="number">2100</xp>
<xpactive type="number">1</xpactive>
</class>
</classes>
<defenses>
<ac>

Varsuuk
March 28th, 2019, 14:30
To test my creation (because I had already hand-edited to the XML prior to coding the add class ref method to be able to list classes in the "Class/Level" control) after the nodeList=createChild("classes"), I added nodelist2=nodeList.createChild("foo") and then looked in XML and sure enough foo was a sibling. But getChildren on nodeList STILL shows 0?

ddavison
March 28th, 2019, 14:48
What is the nodeList=createChild("classes")?

Don't you want nodeList = parent.createChild("classes"), where parent is the node that you want to hold the "classes" element?

ddavison
March 28th, 2019, 14:58
class is a child of classes. There are not special or unique XML nuances there. It's a standard parent/child relationship.

Here is code from manager_char.lua in 5E that iterates through classes. In this case, nodeList is already pointed to the classes element.


-- Get the list we are going to add to
local nodeList = nodeChar.createChild("classes");
if not nodeList then
return;
end




for _,v in pairs(nodeList.getChildren()) do
local _,sExistingClassPath = DB.getValue(v, "shortcut", "", "");
if sExistingClassPath == "" then
local sExistingClassName = StringManager.trim(DB.getValue(v, "name", "")):lower();
if sExistingClassName ~= "" and (sExistingClassName == sClassNameLower) then
nodeClass = v;
break;
end
else
local sExistingClassPathSansModule = StringManager.split(sExistingClassPath, "@")[1];
if sExistingClassPathSansModule == sRecordSansModule then
nodeClass = v;
break;
end
end
end




-- If class already exists, then add a level; otherwise, create a new class entry
local nLevel = 1;
local bExistingClass = false;
if nodeClass then
bExistingClass = true;
nLevel = DB.getValue(nodeClass, "level", 1) + 1;
else
nodeClass = nodeList.createChild();
end

-- Any way you get here, overwrite or set the class reference link with the most current
DB.setValue(nodeClass, "shortcut", "windowreference", sClass, sRecord);

Varsuuk
March 28th, 2019, 15:37
What is the nodeList=createChild("classes")?

Don't you want nodeList = parent.createChild("classes"), where parent is the node that you want to hold the "classes" element?

Sorry Doug, that was me quickly typing in an afterthought of something I tested real quick.
The actual code was in first message and it did have the "parent." portion: local nodeList = nodeChar.createChild("classes");

I just figured, I had MANUALLY created the <class> ... </class> entry for that character (chicken and egg thing) so I thought I typed or something. Then I added the: nodelist2=nodeList.createChild("foo") to ensure that the lua code ITSELF added a node so of course it should find it with "getChildren()"

Like I said, I know it works in 5E so I know I am making a mistake somewhere ;)

Varsuuk
March 28th, 2019, 15:42
See inline <<VARSUUK>>> comments where I try to line up my code vs what you gave me from 5E:


class is a child of classes. There are not special or unique XML nuances there. It's a standard parent/child relationship.

Here is code from manager_char.lua in 5E that iterates through classes. In this case, nodeList is already pointed to the classes element.


-- Get the list we are going to add to
local nodeList = nodeChar.createChild("classes");
if not nodeList then
return;
end




<<VARSUUK>>
-- Get the list we are going to add to. If "classes" did not exist, it would be created.
Debug.console("create or get from: " .. nodeChar.getName());
local nodeList = nodeChar.createChild("classes");
Debug.console("nodeList: " .. nodeList.getName());




for _,v in pairs(nodeList.getChildren()) do
local _,sExistingClassPath = DB.getValue(v, "shortcut", "", "");
if sExistingClassPath == "" then
local sExistingClassName = StringManager.trim(DB.getValue(v, "name", "")):lower();
if sExistingClassName ~= "" and (sExistingClassName == sClassNameLower) then
nodeClass = v;
break;
end
else
local sExistingClassPathSansModule = StringManager.split(sExistingClassPath, "@")[1];
if sExistingClassPathSansModule == sRecordSansModule then
nodeClass = v;
break;
end
end
end




<<<VARSUUK>>>
local aCharClassNodes = nodeList.getChildren();
Debug.console("#nodes=" .. #aCharClassNodes); ---> This says "# notes=0" in console.


<<< Varsuuk I didn't continue with your third code snip since I already failed in one just before this... >>>



-- If class already exists, then add a level; otherwise, create a new class entry
local nLevel = 1;
local bExistingClass = false;
if nodeClass then
bExistingClass = true;
nLevel = DB.getValue(nodeClass, "level", 1) + 1;
else
nodeClass = nodeList.createChild();
end

-- Any way you get here, overwrite or set the class reference link with the most current
DB.setValue(nodeClass, "shortcut", "windowreference", sClass, sRecord);

Varsuuk
March 28th, 2019, 17:11
AHA!

I AM misunderstanding something...

I went into 5E code to add debug statements to trace how it differs and perhaps jar my mind into looking at the problem correctly.
I added the Debug.log() below (only change were the console log lines:)


-- Check to see if the character already has this class; or create a new class entry
local nodeClass = nil;
local sRecordSansModule = StringManager.split(sRecord, "@")[1];
local aCharClassNodes = nodeList.getChildren();
Debug.console("# of aCharClassNodes = " .. #aCharClassNodes);
for _,v in pairs(aCharClassNodes) do
Debug.console("HERE1");
local _,sExistingClassPath = DB.getValue(v, "shortcut", "", "");
local sExistingClassPathSansModule = StringManager.split(sExistingClassPath, "@")[1];
if sExistingClassPathSansModule == sRecordSansModule then
nodeClass = v;
break;
end
end
if not nodeClass then
for _,v in pairs(aCharClassNodes) do
Debug.console("HERE2");
local sExistingClassName = StringManager.trim(DB.getValue(v, "name", "")):lower();
if (sExistingClassName == sClassNameLower) and (sExistingClassName ~= "") then
nodeClass = v;
break;
end
end
end
local bExistingClass = false;
if nodeClass then
bExistingClass = true;
else
nodeClass = nodeList.createChild();
end


This is what I see in the log (note no HERE):


Runtime Notice: Reloading ruleset
Ruleset Warning: Font (windowtitle): Windows replaced specified font face (Alegreya Sans SC Bold) with (Alegreya Sans SC)
>> Here I drag Fighter to new char:
Runtime Notice: s'# of aCharClassNodes = 0'
>> Here I drag Fighter to Lvl 1 Fighter Char above:
Runtime Notice: s'# of aCharClassNodes = 0'
Runtime Notice: s'HERE1'
>> Here I drag Cleric to Lvl 2 Fighter Char above:
Runtime Notice: s'# of aCharClassNodes = 0'
Runtime Notice: s'HERE1'
Runtime Notice: s'HERE2'


And this is the XML post my 2nd attempt at copying Fighter and 1 of Cleric to the character's Class in 5E Ruleset:



</backgroundlink>
<classes>
<id-00001>
<casterlevelinvmult type="number">0</casterlevelinvmult>
<casterpactmagic type="number">0</casterpactmagic>
<hddie type="dice">d10</hddie>
<hdused type="number">0</hdused>
<level type="number">3</level>
<name type="string">Fighter</name>
<shortcut type="windowreference">
<class>reference_class</class>
<recordname>reference.classdata.fighter@DD PHB Deluxe</recordname>
</shortcut>
</id-00001>
<id-00002>
<casterlevelinvmult type="number">1</casterlevelinvmult>
<hddie type="dice">d8</hddie>
<hdused type="number">0</hdused>
<level type="number">1</level>
<name type="string">Cleric</name>
<shortcut type="windowreference">
<class>reference_class</class>
<recordname>reference.classdata.cleric@DD PHB Deluxe</recordname>
</shortcut>
</id-00002>
</classes>
<coins>


So yes, I guess I am not understanding what the # code should be expecting to do with the result of getChildren
Probably the length thing doesn't work because it is a dictionary table not a normal integer indexed table?

Going to do a similar set of prints in mine next - but guessing that "#" wasn't applicable then.

Varsuuk
March 28th, 2019, 17:19
So - adding the same "HERE" prints shows me that both 5E and my S&W hit same code.
So, now my next step is the continuation of the "class module" work (vs my hardcoded before) - I fell for a trap of my own making because I usually test iteratively as I develop. I checked to make sure I got the expected number of entries with what I thought was the LENGTH operator on the table, "#" instead of printing.

So, next when not working from home I will do some reading to see in Lua docs why doesn't work for this sort of textkey-value table returned.

Sorry for the confusion!

Varsuuk
March 28th, 2019, 19:28
BTW - if anyone can clue me into why the first log line prints "0" and yet "HERE" is printed because the loop finds entries to loop over:

...
local aCharClassNodes = nodeList.getChildren();
Debug.console("# of aCharClassNodes = " .. #aCharClassNodes);

for _,v in pairs(aCharClassNodes) do
Debug.console("HERE1");
...

Moon Wizard
March 28th, 2019, 21:07
That's because # operator only works with tables that have integer keys starting at zero, similar to the way that ipairs() only works with tables that have integer keys starting at zero.

Since getChildren() returns a table of entries with the database child name as the key and the database child node object as the value, it will always return "0" in the first debug statement (i.e. doesn't use integer keys). In the second debug statement, the pairs() will iterate over any table (as opposed to ipairs()), not just integer-based tables.

If you need to count the number of nodes in a table that doesn't use integer-based keys, you have to use a for ... in pairs(...) do ... end to count manually.

Regards,
JPG

Varsuuk
March 28th, 2019, 21:26
OK - that makes sense.
No, I didn't actually need the number of entries. I did that because I was using it to ensure I found the entries before I coded the changed loop (it is different than the 5E one) - and the silly test caused me to go down this rabbit hole - I'm sorry. An example of writing a poor test is sometimes worse than not writing a test and assuming you had it right lol... sigh :)