-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Ordered xml tags #9497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ordered xml tags #9497
Changes from all commits
6042544
c16b03d
3cc331e
fa5a095
fc763d4
375fcf4
b09e7d0
a4c764e
9d093b5
e5ccfbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,7 +117,13 @@ local function composeNode(frag, node, lvl) | |
| t_insert(frag, '<') | ||
| t_insert(frag, node.elem) | ||
| if node.attrib then | ||
| for key, val in pairs(node.attrib) do | ||
| local sortedKeys = {} | ||
| for key in pairs(node.attrib) do | ||
| t_insert(sortedKeys, key) | ||
| end | ||
| table.sort(sortedKeys) | ||
| for _, key in ipairs(sortedKeys) do | ||
| local val = node.attrib[key] | ||
| if val then | ||
| if type(key) ~= "string" then | ||
| return "invalid xml tree (attribute name in <"..node.elem.."> is not a string)" | ||
|
Comment on lines
+120
to
129
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,13 @@ | |
| -- This wrapper allows the program to run headless on any OS (in theory) | ||
| -- It can be run using a standard lua interpreter, although LuaJIT is preferable | ||
|
|
||
| -- Store command line arguments before they get modified by the loading process | ||
| local originalArgs = {} | ||
| if arg then | ||
| for i = -5, 10 do | ||
| originalArgs[i] = arg[i] | ||
| end | ||
| end | ||
|
|
||
| -- Callbacks | ||
| local callbackTable = { } | ||
|
|
@@ -89,12 +96,12 @@ function IsKeyDown(keyName) end | |
| function Copy(text) end | ||
| function Paste() end | ||
| function Deflate(data) | ||
| -- TODO: Might need this | ||
| return "" | ||
| local zlib = require("zlib") | ||
| return zlib.deflate()(data, "finish") | ||
| end | ||
| function Inflate(data) | ||
| -- TODO: And this | ||
| return "" | ||
| local zlib = require("zlib") | ||
| return zlib.inflate()(data, "finish") | ||
| end | ||
|
Comment on lines
98
to
105
|
||
| function GetTime() | ||
| return 0 | ||
|
|
@@ -176,6 +183,22 @@ function require(name) | |
| if name == "lcurl.safe" then | ||
| return | ||
| end | ||
| -- Provide a basic utf8 stub for headless mode | ||
| if name == "lua-utf8" then | ||
| return { | ||
| len = function(s) return #s end, | ||
| sub = function(s, i, j) return string.sub(s, i, j) end, | ||
| char = function(...) return string.char(...) end, | ||
| byte = function(s, i) return string.byte(s, i) end, | ||
| find = function(s, pattern, init, plain) return string.find(s, pattern, init, plain) end, | ||
| gmatch = function(s, pattern) return string.gmatch(s, pattern) end, | ||
| gsub = function(s, pattern, repl, n) return string.gsub(s, pattern, repl, n) end, | ||
| match = function(s, pattern, init) return string.match(s, pattern, init) end, | ||
| reverse = function(s) return string.reverse(s) end, | ||
| upper = function(s) return string.upper(s) end, | ||
| lower = function(s) return string.lower(s) end, | ||
| } | ||
|
Comment on lines
+186
to
+200
|
||
| end | ||
| return l_require(name) | ||
| end | ||
|
|
||
|
|
@@ -214,6 +237,148 @@ function loadBuildFromJSON(getItemsJSON, getPassiveSkillsJSON) | |
| runCallback("OnFrame") | ||
| local charData = build.importTab:ImportItemsAndSkills(getItemsJSON) | ||
| build.importTab:ImportPassiveTreeAndJewels(getPassiveSkillsJSON, charData) | ||
| -- You now have a build without a correct main skill selected, or any configuration options set | ||
| -- Good luck! | ||
| -- Try to select the first main skill group if available | ||
| if build and build.skillsTab and build.skillsTab.socketGroupList and #build.skillsTab.socketGroupList > 0 then | ||
| build.mainSocketGroup = 1 | ||
| build.skillsTab:SetActiveSkill(1, 1) | ||
| runCallback("OnFrame") -- Recalculate stats | ||
| end | ||
| -- You now have a build with a main skill selected (if available) | ||
| end | ||
|
|
||
| -- Check if JSON files were provided as command line arguments using original args | ||
| local itemsJSONPath, passivesJSONPath | ||
|
|
||
| -- Check original arguments for JSON files | ||
| if originalArgs[1] and originalArgs[2] then | ||
| itemsJSONPath = originalArgs[1] | ||
| passivesJSONPath = originalArgs[2] | ||
| -- print("Found JSON files in arguments - loading items and passives data...") | ||
|
|
||
| -- Read the JSON files | ||
| local itemsFile = io.open(itemsJSONPath, "r") | ||
| local passivesFile = io.open(passivesJSONPath, "r") | ||
|
|
||
| if itemsFile and passivesFile then | ||
| local itemsJSON = itemsFile:read("*all") | ||
|
Comment on lines
+258
to
+263
|
||
| local passivesJSON = passivesFile:read("*all") | ||
| itemsFile:close() | ||
| passivesFile:close() | ||
|
|
||
| -- print("Calling loadBuildFromJSON...") | ||
| local success, error_msg = pcall(function() | ||
| loadBuildFromJSON(itemsJSON, passivesJSON) | ||
| end) | ||
|
Comment on lines
+249
to
+271
|
||
|
|
||
| if not success then | ||
| -- print("Warning: loadBuildFromJSON encountered an error:", error_msg) | ||
| -- print("Continuing to check what data was loaded...") | ||
| end | ||
|
|
||
| -- print() | ||
| -- print("Build loading completed (with or without errors). Checking build data...") | ||
|
|
||
| -- Print some information from the build object to verify it's working | ||
| -- print("\n=== BUILD OBJECT VERIFICATION ===") | ||
| if build then | ||
| -- print("✓ build global object exists") | ||
| -- print("build type:", type(build)) | ||
|
|
||
| -- Check if build.spec exists (passive tree) | ||
| if build.spec then | ||
| -- print("✓ build.spec exists (passive tree)") | ||
| if build.spec.nodes then | ||
| local nodeCount = 0 | ||
| for _ in pairs(build.spec.nodes) do | ||
| nodeCount = nodeCount + 1 | ||
| end | ||
| -- print(" - Number of passive nodes:", nodeCount) | ||
| end | ||
| if build.spec.allocNodes then | ||
| -- print(" - Number of allocated nodes:", #build.spec.allocNodes) | ||
| end | ||
| else | ||
| -- print("✗ build.spec not found") | ||
| end | ||
|
|
||
| -- Check if character data exists | ||
| if build.characterLevel then | ||
| -- print("✓ Character level:", build.characterLevel) | ||
| end | ||
| if build.characterName then | ||
| -- print("✓ Character Name: ", build.characterName) | ||
| end | ||
| if build.characterClass then | ||
| -- print("✓ Character class:", build.characterClass) | ||
| end | ||
|
|
||
| -- Check if items exist | ||
| if build.itemsTab and build.itemsTab.items then | ||
| local itemCount = 0 | ||
| for _ in pairs(build.itemsTab.items) do | ||
| itemCount = itemCount + 1 | ||
| end | ||
| -- print("✓ Items loaded in build.itemsTab.items, count:", itemCount) | ||
|
|
||
| -- Show a few example items | ||
| local count = 0 | ||
| for k, item in pairs(build.itemsTab.items) do | ||
| if count < 3 and item.name then | ||
| -- print(" - Item " .. (count + 1) .. ":", item.name, "(" .. (item.baseName or "unknown base") .. ")") | ||
| end | ||
| count = count + 1 | ||
| if count >= 3 then break end | ||
| end | ||
| elseif build.itemsTab and build.itemsTab.list then | ||
| local itemCount = 0 | ||
| for _ in pairs(build.itemsTab.list) do | ||
| itemCount = itemCount + 1 | ||
| end | ||
| -- print("✓ Items loaded in build.itemsTab.list, count:", itemCount) | ||
| else | ||
| -- print("✗ Items not found or not loaded") | ||
| end | ||
|
|
||
| -- Print some build calculation results if available | ||
| if build.calcsTab and build.calcsTab.buildOutput then | ||
| -- print("✓ Build calculations available") | ||
| local output = build.calcsTab.buildOutput | ||
| if output.Life then | ||
| -- print(" - Total Life:", output.Life) | ||
| end | ||
| if output.EnergyShield then | ||
| -- print(" - Total Energy Shield:", output.EnergyShield) | ||
| end | ||
| if output.TotalDPS then | ||
| -- print(" - Total DPS:", output.TotalDPS) | ||
| end | ||
| end | ||
|
|
||
| -- Try to export build code (requires working Deflate function) | ||
| local buildCode = common.base64.encode(Deflate(build:SaveDB("code"))):gsub("+","-"):gsub("/","_") | ||
| print(buildCode) | ||
| -- local f = io.open("/home/alexander/dev/investigations/PathOfBuilding/buildcode.txt", "w") | ||
| -- if f then | ||
| -- f:write(buildCode) | ||
| -- f:close() | ||
| -- -- print("Build code written to buildcode.txt") | ||
| -- else | ||
| -- -- print("Failed to open buildcode.txt for writing!") | ||
| -- end | ||
| else | ||
| -- print("✗ build global object does not exist!") | ||
| end | ||
| -- print("=== END BUILD VERIFICATION ===\n") | ||
| else | ||
| -- print("Error: Could not open JSON files") | ||
| if not itemsFile then | ||
| -- print(" - Could not open items file: " .. itemsJSONPath) | ||
| end | ||
| if not passivesFile then | ||
| -- print(" - Could not open passives file: " .. passivesJSONPath) | ||
| end | ||
| end | ||
| else | ||
| -- print("No JSON files provided as command line arguments") | ||
| -- print("Usage: luajit HeadlessWrapper.lua <items.json> <passives.json>") | ||
| end | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1896,11 +1896,15 @@ function buildMode:SaveDB(fileName) | |||||||||||||
| t_insert(dbXML, node) | ||||||||||||||
| end | ||||||||||||||
|
|
||||||||||||||
| -- Call on all savers to save their data in their respective sections | ||||||||||||||
| for elem, saver in pairs(self.savers) do | ||||||||||||||
| local node = { elem = elem } | ||||||||||||||
| saver:Save(node) | ||||||||||||||
| t_insert(dbXML, node) | ||||||||||||||
| -- Call on all savers to save their data in their respective sections (fixed order for deterministic output) | ||||||||||||||
| local saverOrder = {"Config", "Notes", "Party", "Tree", "TreeView", "Items", "Skills", "Calcs", "Import"} | ||||||||||||||
|
||||||||||||||
| local saverOrder = {"Config", "Notes", "Party", "Tree", "TreeView", "Items", "Skills", "Calcs", "Import"} | |
| local saverOrder = {} | |
| for key in pairs(self.savers) do | |
| t_insert(saverOrder, key) | |
| end | |
| t_sort(saverOrder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change alters XML serialization ordering (attributes now sorted). Given
xml.ComposeXMLis a core utility, it would be good to add a small busted test covering deterministic attribute ordering (and that invalid attrib keys still return an error message rather than throwing), to prevent regressions.