Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ luajit/
spec/test_results.log
spec/test_generation.log
src/luacov.stats.out
runtime/lua/debugger.lua

# Release
manifest-updated.xml
Expand All @@ -38,4 +39,8 @@ src/Data/TimelessJewelData/*.bin
runtime/imgui.ini
runtime/SimpleGraphic/SimpleGraphic.log

src/poe_api_response.json
src/poe_api_response.json
runtime/SimpleGraphic/Screenshots

.emmyrc.json
.luarc.json
93 changes: 91 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ It is recommended to use it over the built-in Lua plugins.
Please note that EmmyLua is not available for other editors based on Visual Studio Code,
such as [VSCodium](https://vscodium.com) or [Eclipse Theia](https://theia-ide.org) but can be built from source if needed.

Another alternative on VSCode is to use [sumneko's Lua language server](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) along with [actboy168's debugger](https://marketplace.visualstudio.com/items?itemName=actboy168.lua-debug). These can potentially offer more features than EmmyLua, such as conditional breakpoints.

## Runtime environment

It is recommended that you configure your IDE to include `src/_SimpleGraphic.def.lua` somehow. Path of Building runs inside a special Lua environment via SimpleGraphic which implements a small API. These are not defined inside the project, which means that the aforementioned meta/hint file is required for the IDE to know which functions exist. As the file is not included in code, it must be explicitly mentioned as a library in whichever language server you are using, or otherwise it will not be read.

This file is normally not executed, but does contain basic implementations for parts of the API, which allows many parts of PoB to work without running inside SimpleGraphic. If you wish to test individual changes, it might be possible to do so through a script using Luajit directly. To do so, see HeadlessWrapper.lua for an example. It should be noted that some parts of the API (such as subscripts) are not implemented, which means some parts of PoB are unusable.

### Visual Studio Code

1. Create a new <kbd>Debug Configuration</kbd> of type <kbd>EmmyLua New Debug</kbd>
Expand Down Expand Up @@ -168,20 +176,54 @@ such as [VSCodium](https://vscodium.com) or [Eclipse Theia](https://theia-ide.or
1. In VSCode click <kbd>Start Debugging</kbd> (the green icon) or press <kbd>F5</kbd>
1. The debugger should connect

You might also want to use actboy168 debugger. This is possible by using for example the following launch.json configuration:

```json
{
"version": "0.2.0",
"configurations": [
{
"name": "🍄attach",
"type": "lua",
"request": "attach",
"stopOnEntry": false,
"address": "127.0.0.1:12306",
"luaVersion": "luajit",
},

]
}
```

Then, similarly to the EmmyLua example:

1. Find the sub-folder that looks like `actboy168.lua-debug-x.y.z-win32-x64` in `%USERPROFILE%/.vscode/extensions`. Navigate to it and find the `debugger.lua` script under the script folder. Copy this to `runtime/lua`.
2. Copy-paste the following code snippet into `launch:OnInit()`:
```lua
local debugger = require("debugger"):start("127.0.0.1:12306")
-- debugger:event("wait") -- Uncomment this line if you want PoB to wait until the debugger is attached.
```


#### Excluding directories from EmmyLua

Depending on the amount of system ram you have available and the amount that gets assigned to the jvm running the emmylua language server you might run into issues when trying to debug Path of building.
Files in `/Data` `/Export` and `/TreeData` can be massive and cause the EmmyLua language server to use a significant amount of memory. Sometimes causing the language server to crash. To avoid this and speed up initialization consider adding an `.emmyrc.json` file to the `.vscode` folder in the root of the Path of building folder with the following content:
Files in `/Data` `/Export` and `/TreeData` can be massive and cause the EmmyLua language server to use a significant amount of memory. Sometimes causing the language server to crash. To avoid this and speed up initialization consider adding an `.emmyrc.json` to the root of the Path of building folder with the following content:

```json
{
"$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json",
"runtime": {
"version": "LuaJIT"
"version": "LuaJIT",
// this is not technically correct as LoadModule behaviour can
// differ from require, but it is useful for now
"requireLikeFunction": ["LoadModule"],
},
"workspace": {
"ignoreGlobs": [
"**/*_spec.lua",
"spec/**/*.lua",
"runtime/lua/sha1/lua53_ops.lua",
"**/src/Data/**/*.lua",
"**/src/TreeData/**/*.lua",
"**/src/Modules/ModParser.lua"
Expand All @@ -190,6 +232,46 @@ Files in `/Data` `/Export` and `/TreeData` can be massive and cause the EmmyLua
}
```

This file can be customised according to what you want. It is a good idea to ignore test files as these tend to add things to the global namespace, which will look confusing, and they are designed to be run by Busted. `lua53_ops.lua` produces errors and doesn't actually get imported when using LuaJIT. It can be useful to keep the data and mod parser files, but generally this will increase the time the LSP takes to index the project on startup.

### Excluding directories from Sumneko's language server

If you prefer to not use EmmyLua, the following configuration works well for Sumneko's VS Code extension:

```json
{
"Lua.workspace.ignoreDir": [
".vscode",
// these files add things to global that aren't there in normal
// operation
"spec/*",
"src/Export/*",
"src/HeadlessWrapper.lua",

// this has lua 5.3 code which produces errors, but doesn't actually run
"src/runtime/lua/sha1/*",

// avoid overriding the below library setting
"src/_SimpleGraphic.def.lua",
],
"Lua.diagnostics.disable": ["inject-field"],
// disables diagnostics even when you open one of the above
"Lua.diagnostics.ignoredFiles": "Disable",
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.preloadFileSize": 1000,
// this is not technically correct as LoadModule behaviour can
// differ from require, but it is useful for now
"Lua.runtime.special": {
"LoadModule": "require"
},
"Lua.workspace.library": [
"src/_SimpleGraphic.def.lua"
],
}
```

The extension will automatically skip large files from being preloaded (controlled by `Lua.workspace.preloadFileSize`), so they don't have to be excluded. The configuration file can be found by pressing Ctrl-Shift-P and selecting `Preferences: Open Workspace Settings (JSON)`. If you wish to check test files, you can remove the "ignoredFiles" option and install the busted, LuaFileSystem, and luassert LuaLS addons through `Lua: Open Addon Manager`.

### PyCharm Community / IntelliJ Idea Community

1. Create a new "Debug Configuration" of type "Emmy Debugger(NEW)".
Expand Down Expand Up @@ -223,6 +305,13 @@ More tests can be added to this folder to test specific functionality, or new te

Please try to include tests for your new features in your pull request. Additionally, if your pr breaks a test that should be passing please update it accordingly.

It is a good idea to prefer Docker due to it having a very reliable Lua setup. But if you have performance problems with it, installing `busted` locally might help as it tends to be faster to execute:

1. Install Luajit (due to PoB accessing the jit library, it non-Luajit versions might not work)
2. Install [Luarocks](https://luarocks.org/) (for example, through Scoop or your Linux package manager)
Comment thread
vaisest marked this conversation as resolved.
3. Run `luarocks install busted`
4. Run `busted --lua=luajit` to run the tests. You can also use e.g. `-p TestUtils_spec.lua` to run a specific file.

### Debugging tests
When running tests with a docker container it is possible to use EmmyLua for debugging. Paste in the following right under `function launch:OnInit()` in `./src/Launch.lua`:
```lua
Expand Down
70 changes: 70 additions & 0 deletions spec/System/TestCommon_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
describe("Common", function()
describe("Class creation and use", function()
it("produces error when parent constructors are not called", function()
local ParentClass = newClass("ConstructorTestParentClass")
function ParentClass:init()
return self
end
local ChildClass = newClass("ConstructorTestProblemChildClass", "ConstructorTestParentClass")
function ChildClass:init()
-- Intentionally does not call self:ConstructorTestParentClass()
return self
end
common.classes.ConstructorTestParent = ParentClass
common.classes.ConstructorTestProblemChild = ChildClass

assert.has_error(function()
new("ConstructorTestProblemChild"):init()
end, "Parent class 'ConstructorTestParentClass' of class 'ConstructorTestProblemChild' must be initialised")
common.classes.ConstructorTestParent = nil
common.classes.ConstructorTestProblemChild = nil
end)
it("produces an error if additional arguments are passed", function()
local StupidClass = newClass("NewAbuse")
function StupidClass:init(someParam)
return self
end

common.classes.NewAbuse = StupidClass

assert.has_no.errors(function()
local newObj = new("NewAbuse"):init("fish")
end)
assert.has_error(function()
local newObj = new("NewAbuse", "look I'm using the old syntax")
end)
end)
it("produces an error if it calls a parent class without giving it self", function()
local ParentClass = newClass("ConstructorTestParentClass")
function ParentClass:init()
return self
end

local ChildClass = newClass("ConstructorTestProblemChildClass", "ConstructorTestParentClass")
function ChildClass:init()
self.ConstructorTestParentClass()
return self
end

common.classes.ConstructorTestParent = ParentClass
common.classes.ConstructorTestProblemChild = ChildClass

assert.has_error(function()
new("ConstructorTestProblemChild"):init()
end)
common.classes.ConstructorTestParent = nil
common.classes.ConstructorTestProblemChild = nil
end)
it("produces an error if its constructor doesn't return the object", function()
local StupidClass = newClass("StupidClass")
function StupidClass:init()
end

common.classes.StupidClass = StupidClass

assert.has_error(function()
new("StupidClass"):init()
end, "Class StupidClass constructor did not return a value")
end)
end)
end)
20 changes: 10 additions & 10 deletions spec/System/TestCompareBuySimilar_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ describe("Buy similar mod stat matching", function()

describe("addModEntries mod matching", function()
it("matches impossible escape mods as options", function()
local fromNothing = new("Item", [[
local fromNothing = new("Item"):init([[
Impossible Escape
Viridian Jewel
LevelReq: 0
Expand Down Expand Up @@ -32,7 +32,7 @@ Corrupted]])
end)

it("matches thread of hope radius as an option", function()
local thread = new("Item", [[
local thread = new("Item"):init([[
Rarity: UNIQUE
Thread of Hope
Crimson Jewel
Expand All @@ -52,7 +52,7 @@ Passage]])
end)

it("combines mods that are the same stat", function()
local lifeDiamond = new("Item", [[
local lifeDiamond = new("Item"):init([[
Test Subject
Diamond
Implicits: 0
Expand All @@ -67,7 +67,7 @@ Implicits: 0
assert.equal("+50 to Maximum Life", StripEscapes(entries[1].formattedLines[2]))
assert.equal(150, entries[1].value)

local lifelessDiamond = new("Item", [[
local lifelessDiamond = new("Item"):init([[
Test Subject
Diamond
Implicits: 0
Expand All @@ -82,7 +82,7 @@ Implicits: 0
end)

it("is not case-sensitive", function ()
local funnyItem = new("Item", [[
local funnyItem = new("Item"):init([[
Test Subject
Diamond
Implicits: 1
Expand All @@ -93,7 +93,7 @@ Implicits: 1
end)

it("does not combine implicit and explicit mods", function()
local lifelessDiamond = new("Item", [[
local lifelessDiamond = new("Item"):init([[
Test Subject
Diamond
Implicits: 1
Expand Down Expand Up @@ -140,7 +140,7 @@ Implicits: 1
end)

local function openPopup(item, slotName)
item = item or new("Item", "Rarity: Rare\nTest Ring\nRuby Ring\nImplicits: 0\n+50 to maximum Life")
item = item or new("Item"):init("Rarity: Rare\nTest Ring\nRuby Ring\nImplicits: 0\n+50 to maximum Life")
bs.openPopup(item, slotName or "Ring", build)
local controls = main.popups[1].controls
_G.Copy = function(url) copiedUrl = url end
Expand Down Expand Up @@ -194,7 +194,7 @@ Implicits: 1
end)

it("encodes option values in the generated query", function()
local item = new("Item", [[
local item = new("Item"):init([[
Rarity: UNIQUE
Impossible Escape
Viridian Jewel
Expand All @@ -214,7 +214,7 @@ Corrupted]])
end)

it("inverts reduced stat bounds in the generated query", function()
local item = new("Item", [[
local item = new("Item"):init([[
Rarity: Rare
Test Ring
Ruby Ring
Expand All @@ -230,7 +230,7 @@ Implicits: 0
end)

it("uses a count group for ambiguous trade stats", function()
local item = new("Item", [[
local item = new("Item"):init([[
Rarity: Rare
Test Ring
Ruby Ring
Expand Down
2 changes: 1 addition & 1 deletion spec/System/TestImport_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe("TestImport", function()
end)

function importAndReimportWithOldJewel(shouldDelete)
local oldJewel = new("Item", [[Rarity: RARE
local oldJewel = new("Item"):init([[Rarity: RARE
TEST JEWEL
Crimson Jewel
Crafted: true
Expand Down
2 changes: 1 addition & 1 deletion spec/System/TestItemDBControl_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe("ItemDBControl", function()
return item ~= invalidItem
end,
}
local control = new("ItemDBControl", nil, { 0, 0, 100, 100 }, itemsTab, {
local control = new("ItemDBControl"):init(nil, { 0, 0, 100, 100 }, itemsTab, {
list = { invalidItem, betterItem, worseItem },
}, "RARE")
control.sortDetail = {
Expand Down
4 changes: 2 additions & 2 deletions spec/System/TestItemMods_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -614,12 +614,12 @@ describe("TetsItemMods", function()
end)

it("shows a fallback tooltip when an item's base is no longer supported", function()
local item = new("Item", [[
local item = new("Item"):init([[
Rarity: Unique
Legacy Item
Removed Base
]])
local tooltip = new("Tooltip")
local tooltip = new("Tooltip"):init()

assert.has_no.errors(function()
build.itemsTab:AddItemTooltip(tooltip, item)
Expand Down
Loading
Loading