Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
033bcf9
Apply changes from https://github.com/PathOfBuildingCommunity/PathOfB…
oboking Apr 17, 2026
7a26224
Add missing variables required for compare
vaisest May 10, 2026
0342beb
Update compare calcs tab for poe2
vaisest May 11, 2026
a2ffc56
Fix incorrect stat set control handlers
vaisest May 11, 2026
1194111
Use correct realm for comparison trade
vaisest May 11, 2026
3df428e
Remove crucible and scourge mods from compare trader
vaisest May 11, 2026
3283e06
Fix comparison tool issues with local vs global mods, inverted values
vaisest May 12, 2026
0afacf1
Change tradehash exports to export a map of hash to stat description …
vaisest May 12, 2026
360cfb8
Fix radius jewels, charms, and desecrate-only mods in comparison trader
vaisest May 12, 2026
80fcaa1
Add comparison stat set selector
vaisest May 12, 2026
2a4dc7f
Fix comparison minion control anchor
vaisest May 12, 2026
75a96c2
Fix stages in comparison tab
vaisest May 12, 2026
787b518
remove .rej file
vaisest May 13, 2026
14266cc
Deduplicate trade category code. Fixes poe2 category differences for …
vaisest May 13, 2026
77ef3dc
port compatible trader tool changes from pob1
vaisest Apr 18, 2026
d12a45c
Fix corrupted mods being fractured mods in trade mod generation
vaisest Apr 19, 2026
3131840
Fix radius jewel weight generation
vaisest Apr 19, 2026
1ac917f
Regenerate QueryMods.lua
vaisest Apr 19, 2026
b4315ef
convert trade tool mod weight generation to use tradeHash
vaisest Apr 19, 2026
91d975e
wip: change poesessid to bearer token
vaisest Apr 19, 2026
288dceb
Cleanup: remove extra logout button and fix poeapi comments
vaisest Apr 20, 2026
f28fa02
Fix trader crash when rate limited on startup
vaisest Apr 20, 2026
d6d78af
Use https://poe.ninja/poe2/api/economy/exchange/current/overview for …
vaisest Apr 20, 2026
c6c8d06
Fix poe.ninja tests
vaisest Apr 20, 2026
ebcb817
Fix trader section anchor
vaisest Apr 20, 2026
1553d5f
Adjust price scaling factor due to things being in divs (still an abi…
vaisest Apr 20, 2026
9c09f8c
Clarify price options and rate limit waits, and use Retry-After for r…
vaisest Apr 20, 2026
cba3435
rate limiting pls work
vaisest Apr 20, 2026
ac1658f
Fix perfect essences not appearing in generated weights, and regenera…
vaisest Apr 20, 2026
0e738c4
Fix tradehashes for radius jewels
vaisest Apr 20, 2026
f259ad1
Improve rate limit countdown to prevent simplegraphic suspension prob…
vaisest Apr 21, 2026
d26ffef
Fix debug print causing crash, and remove extra debug print
vaisest Apr 21, 2026
dd080e9
disable wiping trader controls to fix crash when it is closed and a s…
vaisest Apr 21, 2026
f9247f2
Add note about doing weird filter requirements (e.g. adorned)
vaisest Apr 21, 2026
187f537
remove whisper for instant buyout items
vaisest Apr 21, 2026
261745a
make cspell happy
vaisest Apr 21, 2026
f255e5f
Fix database radius jewels being nonfunctional
vaisest Apr 22, 2026
27f7843
Fix currency conversion button not being updated after reopening trad…
vaisest Apr 23, 2026
5b6896c
Avoid useless search in "search for" button
vaisest Apr 23, 2026
a2d69e0
fix api error on invalid token
vaisest Apr 24, 2026
7f0b4b0
disable reuseaddr
vaisest Apr 25, 2026
1cbce0b
Rework OAuth server launch code to avoid shared port usage
Wires77 May 3, 2026
edd0dc4
Remove error code on login and fix hanging item slot controls in trad…
vaisest May 4, 2026
1bddcdc
Fix QueryMods.lua generation. Change soulcores.lua to export trade ha…
vaisest May 14, 2026
533641d
Add note about trade hashes
vaisest May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runtime/lua/socket.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function _M.bind(host, port, backlog)
sock, err = socket.tcp6()
end
if not sock then return nil, err end
sock:setoption("reuseaddr", true)
-- sock:setoption("reuseaddr", true)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seemed to make PoB think it's listening on the first port when another program was already listening, which meant the key goes to the wrong program

Copy link
Copy Markdown
Member

@Wires77 Wires77 Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I thought I tested this specifically since it's a security issue, but I can check again. Regardless, the right fix for this would likely be to use the option in LaunchServer.lua instead: server:setoption(option [, value]) from https://lunarmodules.github.io/luasocket/tcp.html

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested again, you can see PoB change port to an unused one if a server is running elsewhere:
image

Copy link
Copy Markdown
Member

@Wires77 Wires77 Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've figured out the root issues here:

  • We're only allowed to redirect users to http://localhost from GGG's server due to their implementation restrictions
  • Binding to port 0.0.0.0 (or *) with LuaSocket will still connect because that address is not conflicting with 127.0.0.1 (apparently)
  • Binding with the default method will choose an IPv6 address if the IPv4 one is busy, but with the same port

So in this case:

  • Start a server with python3 -m http.server -b 127.0.0.1 49082
  • LuaSocket connects to :: as IPv6
  • Redirect to http://localhost reaches the python server instead because localhost resolves to the IPv4 address and server

Forcing IPv4 revealed the other issue, where 0.0.0.0 doesn't conflict with 127.0.0.1 when binding to the port (at least on Windows). This picture reveals some of the results. Port 49083 was the only one that works properly.
image

Here's the fix to go into LaunchServer.lua:

local luaSocket = require("socket")
local server = luaSocket.tcp4()
local function bindSocket()
	local res, err
	server:setoption("reuseaddr", true)
	res, err = server:bind("localhost", 49082) or server:bind("localhost", 49083) or server:bind("localhost", 49084)
	if not res then
		server:close()
	else
		res, err = server:listen(1)
		if not res then
			server:close()
		else
			return server
		end
	end
	return nil, err
end
assert(bindSocket())

res, err = sock:bind(alt.addr, port)
if not res then
sock:close()
Expand Down
64 changes: 34 additions & 30 deletions spec/System/TestTradeQueryCurrency_spec.lua
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
describe("TradeQuery Currency Conversion", function()
local mock_tradeQuery = new("TradeQuery", { itemsTab = {} })
local mock_tradeQuery

-- test case for commit: "Skip callback on errors to prevent incomplete conversions"
describe("FetchCurrencyConversionTable", function()
-- Pass: Callback not called on error
-- Fail: Callback called, indicating partial data risk
it("skips callback on error", function()
local orig_launch = launch
local spy = { called = false }
launch = {
DownloadPage = function(url, callback, opts)
callback(nil, "test error")
end
}
mock_tradeQuery:FetchCurrencyConversionTable(function()
spy.called = true
end)
launch = orig_launch
assert.is_false(spy.called)
end)
before_each(function()
mock_tradeQuery = new("TradeQuery", { itemsTab = {} })
end)

describe("ConvertCurrencyToChaos", function()
-- Pass: Ceils amount to integer (e.g., 4.9 -> 5)
-- Fail: Wrong value or nil, indicating broken rounding/baseline logic, causing inaccurate chaos totals
describe("ConvertCurrencyToDivs", function()
-- Pass: Calculates price in divs
-- Fail: Wrong value or nil, indicating broken rounding/baseline logic
it("handles chaos currency", function()
mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 1 } }
mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 0.1 } }
mock_tradeQuery.pbLeague = "league"
local result = mock_tradeQuery:ConvertCurrencyToChaos("chaos", 4.9)
assert.are.equal(result, 5)
local result = mock_tradeQuery:ConvertCurrencyToDivs("chaos", 5)
assert.are.equal(result, 0.5)
end)

-- Pass: Returns nil without crash
-- Fail: Crashes or wrong value, indicating unhandled currencies, corrupting price conversions
it("returns nil for unmapped", function()
local result = mock_tradeQuery:ConvertCurrencyToChaos("exotic", 10)
local result = mock_tradeQuery:ConvertCurrencyToDivs("exotic", 10)
assert.is_nil(result)
end)
end)

describe("PriceBuilderProcessPoENinjaResponse", function()
-- Pass: Processes without error, restoring map
-- Pass: Processes without error, restoring map while adding a notice
-- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions
it("handles unmapped currency", function()
it("handles empty response", function()
local orig_conv = mock_tradeQuery.currencyConversionTradeMap
mock_tradeQuery.currencyConversionTradeMap = { div = "id" }
local resp = { exotic = 10 }
mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp)
mock_tradeQuery.pbLeague = "league"
mock_tradeQuery.pbCurrencyConversion = { league = {} }
mock_tradeQuery.controls.pbNotice = { label = "" }
local resp = { lines = { }}
mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp.lines)
-- No crash expected
assert.is_true(true)
assert.is_true(mock_tradeQuery.controls.pbNotice.label == "No currencies received from PoE Ninja")
mock_tradeQuery.currencyConversionTradeMap = orig_conv
end)

-- Pass: Processes without error, restoring map while adding a notice
-- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions
it("handles empty response", function()
local orig_conv = mock_tradeQuery.currencyConversionTradeMap
mock_tradeQuery.currencyConversionTradeMap = { div = "id" }
mock_tradeQuery.pbLeague = "league"
mock_tradeQuery.pbCurrencyConversion = { league = {} }
mock_tradeQuery.controls.pbNotice = { label = "" }
local resp = { lines = { { malformedLine = "lol"} }}
mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp.lines)
-- No crash expected
assert.is_true(true)
assert.is_true(mock_tradeQuery.controls.pbNotice.label == "Currencies not updated: malformed PoE Ninja response")
mock_tradeQuery.currencyConversionTradeMap = orig_conv
end)
end)
Expand Down
36 changes: 36 additions & 0 deletions spec/System/TestTradeQueryRequests_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,42 @@ describe("TradeQueryRequests", function()
launch = orig_launch
end)

-- Pass: Does not crash on 401, and passes error message
-- Fail: Crash, or returned error is wrong
it("does not crash on 401", function()
local json = '"{"error":"invalid_token","error_description":"The access token provided is invalid or has expired"}"'
local header = [[HTTP/1.1 401 Unauthorized
Date: Fri, 24 Apr 2026 07:30:38 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Server: cloudflare
WWW-Authenticate: Bearer realm="pathofexile:production", error="invalid_token", error_description="The access token provided is invalid or has expired"
Cache-Control: no-store
Strict-Transport-Security: max-age=63115200; includeSubDomains; preload]]
local orig_launch = launch
launch = {
DownloadPage = function(url, onComplete, opts)
onComplete({ body = json, header = header }, nil)
end
}
table.insert(requests.requestQueue.search, {
url = "test",
callback = function(body, msg)
assert.are.equal(body, json)
assert.truthy(msg:find("Response code: 401"))
end,
retryTime = nil
})
local function mock_next_time(self, policy, time)
return time - 1
end
mock_limiter.NextRequestTime = mock_next_time
requests:ProcessQueue()
assert.are.equal(#requests.requestQueue.search, 0)
launch = orig_launch
end)

-- Pass: Retries with increasing backoff up to cap, preventing infinite loops
-- Fail: No backoff or uncapped, indicating retry bug, risking API bans
it("retries on 429 with exponential backoff", function()
Expand Down
4 changes: 2 additions & 2 deletions src/Classes/CalcSectionControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function CalcSectionClass:Draw(viewPort, noTooltip)
DrawString(x + 3, lineY + 3, "LEFT", 16, "VAR BOLD", textColor..subSec.label..":")
if subSec.data.extra then
local x = x + 3 + DrawStringWidth(16, "VAR BOLD", subSec.label) + 10
DrawString(x, lineY + 3, "LEFT", 16, "VAR", "^7"..self:FormatStr(subSec.data.extra, actor))
DrawString(x, lineY + 3, "LEFT", 16, "VAR", "^7"..formatCalcStr(subSec.data.extra, actor))
end
end
-- Draw line below label
Expand Down Expand Up @@ -301,7 +301,7 @@ function CalcSectionClass:Draw(viewPort, noTooltip)
end
local textSize = rowData.textSize or 14
SetViewport(colData.x + 3, colData.y, colData.width - 4, colData.height)
DrawString(1, 9 - textSize/2, "LEFT", textSize, "VAR", "^7"..self:FormatStr(colData.format, actor, colData))
DrawString(1, 9 - textSize/2, "LEFT", textSize, "VAR", "^7"..formatCalcStr(colData.format, actor, colData))
SetViewport()
end
end
Expand Down
Loading
Loading