stovesdk.js 0.0.0-stage → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +223 -2
- package/binding.gyp +73 -0
- package/index.d.ts +241 -0
- package/index.js +117 -0
- package/lib/sdk-dir.js +58 -0
- package/package.json +52 -4
- package/prebuilds/win32-x64/SHA256SUMS +1 -0
- package/prebuilds/win32-x64/stovesdk.node +0 -0
- package/src/stovesdk.cc +904 -0
- package/tools/check-tarball.js +60 -0
- package/tools/fetch-sdk.js +193 -0
- package/tools/sdk-dir.js +36 -0
- package/tools/sdk-manifest.json +19 -0
- package/tools/smoke.js +102 -0
- package/tools/syntax-check.sh +40 -0
package/index.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Entry point: locates the compiled addon and loads it.
|
|
4
|
+
//
|
|
5
|
+
// Search order (first hit wins):
|
|
6
|
+
// 1. STOVE_PCSDK_ADDON — explicit path to a .node file
|
|
7
|
+
// 2. ./build/Release/stovesdk.node — local `npm run build`
|
|
8
|
+
// 3. ./prebuilds/win32-x64/stovesdk.node — CI prebuild dropped into the package
|
|
9
|
+
//
|
|
10
|
+
// DLL lookup: the .node hard-imports BaseSDK.dll / OwnershipSDK.dll / GameSupportSDK.dll. Node
|
|
11
|
+
// loads addons with LOAD_WITH_ALTERED_SEARCH_PATH, so DLLs placed next to the .node are found
|
|
12
|
+
// first; the directory of the host executable is the other place the Windows loader always looks.
|
|
13
|
+
// When the require() fails with ERR_DLOPEN_FAILED, `load()` checks those two directories for the
|
|
14
|
+
// three DLLs and, if any is absent from both, rethrows with code STOVE_PCSDK_DLL_NOT_FOUND naming
|
|
15
|
+
// the files — Node's own message names neither the DLL nor the directory, and reads as if the
|
|
16
|
+
// .node itself were missing.
|
|
17
|
+
//
|
|
18
|
+
// Nothing here imports Electron, so the same file works from plain Node and the Electron main
|
|
19
|
+
// process. The load is lazy and explicit (`load()`) so merely requiring this module on a
|
|
20
|
+
// non-Windows machine (tests, tooling) does not throw.
|
|
21
|
+
|
|
22
|
+
const fs = require('fs')
|
|
23
|
+
const path = require('path')
|
|
24
|
+
const { SDK_MODULES } = require('./lib/sdk-dir')
|
|
25
|
+
|
|
26
|
+
const ADDON_FILE = 'stovesdk.node'
|
|
27
|
+
const SDK_DLLS = Object.freeze(SDK_MODULES.map(mod => `${mod}.dll`))
|
|
28
|
+
|
|
29
|
+
const ErrorCode = Object.freeze({
|
|
30
|
+
NOT_INITIALIZED: -1,
|
|
31
|
+
ALREADY_INITIALIZED: -2,
|
|
32
|
+
IN_PROGRESS: -3,
|
|
33
|
+
ABORTED: -4,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
function candidateAddonPaths(env = process.env, baseDir = __dirname) {
|
|
37
|
+
const list = []
|
|
38
|
+
if (env.STOVE_PCSDK_ADDON && env.STOVE_PCSDK_ADDON.trim()) list.push(path.resolve(env.STOVE_PCSDK_ADDON.trim()))
|
|
39
|
+
list.push(path.join(baseDir, 'build', 'Release', ADDON_FILE))
|
|
40
|
+
list.push(path.join(baseDir, 'prebuilds', 'win32-x64', ADDON_FILE))
|
|
41
|
+
return list
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveAddonPath(env = process.env, baseDir = __dirname, exists = fs.existsSync) {
|
|
45
|
+
const candidates = candidateAddonPaths(env, baseDir)
|
|
46
|
+
const found = candidates.find(exists)
|
|
47
|
+
if (found) return found
|
|
48
|
+
const err = new Error(
|
|
49
|
+
`[stovesdk] compiled addon not found. Looked in:\n${candidates.map(c => ` - ${c}`).join('\n')}\n` +
|
|
50
|
+
'Build it with `npm run build` on Windows x64 (with the SDK under sdk/ or STOVE_PCSDK_DIR), ' +
|
|
51
|
+
'or set STOVE_PCSDK_ADDON to a prebuilt .node file.'
|
|
52
|
+
)
|
|
53
|
+
err.code = 'STOVE_PCSDK_ADDON_NOT_FOUND'
|
|
54
|
+
throw err
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The directories this module can vouch for: the addon's own and the host executable's. The
|
|
58
|
+
// Windows loader also searches the system directories and PATH, but a consumer does not put
|
|
59
|
+
// the SDK there, so a DLL absent from these two is reported as not found.
|
|
60
|
+
function sdkDllSearchDirs(addonPath, execPath = process.execPath) {
|
|
61
|
+
return [...new Set([path.dirname(path.resolve(addonPath)), path.dirname(path.resolve(execPath))])]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The SDK DLLs found in none of those directories. Empty when all three are present — then a
|
|
65
|
+
// failed load has another cause (a DLL built for another architecture, its own imports such as
|
|
66
|
+
// the VC++ runtime missing), and the original error says more than this module can.
|
|
67
|
+
function missingSdkDlls(addonPath, execPath = process.execPath, exists = fs.existsSync) {
|
|
68
|
+
const dirs = sdkDllSearchDirs(addonPath, execPath)
|
|
69
|
+
return SDK_DLLS.filter(dll => !dirs.some(dir => exists(path.join(dir, dll))))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The error `load()` throws when `require(addonPath)` threw `err`: a coded error naming the
|
|
73
|
+
// missing SDK DLLs when that is what happened, otherwise `err` itself, untouched.
|
|
74
|
+
function explainLoadError(err, addonPath, execPath = process.execPath, exists = fs.existsSync) {
|
|
75
|
+
if (!err || err.code !== 'ERR_DLOPEN_FAILED') return err
|
|
76
|
+
const missing = missingSdkDlls(addonPath, execPath, exists)
|
|
77
|
+
if (!missing.length) return err
|
|
78
|
+
const searched = sdkDllSearchDirs(addonPath, execPath)
|
|
79
|
+
const explained = new Error(
|
|
80
|
+
`[stovesdk] the addon imports ${SDK_DLLS.join(', ')}; ` +
|
|
81
|
+
`${missing.join(', ')} ${missing.length === 1 ? 'was' : 'were'} not found next to the addon or next to the host executable. Looked in:\n` +
|
|
82
|
+
`${searched.map(dir => ` - ${dir}`).join('\n')}\n` +
|
|
83
|
+
`Copy the SDK's <Module>/Deploy/Bin/x64/Release/<Module>.dll files beside ${path.resolve(addonPath)} ` +
|
|
84
|
+
'or beside your executable. They are part of the STOVE PC SDK, not of this package.',
|
|
85
|
+
{ cause: err }
|
|
86
|
+
)
|
|
87
|
+
explained.code = 'STOVE_PCSDK_DLL_NOT_FOUND'
|
|
88
|
+
explained.missing = missing
|
|
89
|
+
explained.searched = searched
|
|
90
|
+
return explained
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let cached = null
|
|
94
|
+
|
|
95
|
+
function load(addonPath) {
|
|
96
|
+
if (cached) return cached
|
|
97
|
+
if (process.platform !== 'win32') {
|
|
98
|
+
const err = new Error(`[stovesdk] the STOVE PC SDK is Windows-only (platform: ${process.platform})`)
|
|
99
|
+
err.code = 'STOVE_PCSDK_UNSUPPORTED_PLATFORM'
|
|
100
|
+
throw err
|
|
101
|
+
}
|
|
102
|
+
const file = addonPath ? path.resolve(addonPath) : resolveAddonPath()
|
|
103
|
+
if (!fs.existsSync(file)) {
|
|
104
|
+
const err = new Error(`[stovesdk] addon not found: ${file}`)
|
|
105
|
+
err.code = 'STOVE_PCSDK_ADDON_NOT_FOUND'
|
|
106
|
+
throw err
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
// eslint-disable-next-line global-require -- native module, resolved at runtime by design
|
|
110
|
+
cached = require(file)
|
|
111
|
+
} catch (err) {
|
|
112
|
+
throw explainLoadError(err, file)
|
|
113
|
+
}
|
|
114
|
+
return cached
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { ADDON_FILE, SDK_DLLS, ErrorCode, candidateAddonPaths, resolveAddonPath, missingSdkDlls, explainLoadError, load }
|
package/lib/sdk-dir.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Resolves where the user-supplied STOVE PC SDK lives and what binding.gyp expects to find there.
|
|
4
|
+
//
|
|
5
|
+
// STOVE_PCSDK_DIR explicit location (highest priority)
|
|
6
|
+
// <repo>/sdk default (gitignored except its README)
|
|
7
|
+
//
|
|
8
|
+
// Expected layout (the shape the SDK zips unpack to — one folder per module):
|
|
9
|
+
// <sdk>/BaseSDK/Deploy/Include/BaseSDK.h
|
|
10
|
+
// <sdk>/BaseSDK/Deploy/Lib/x64/Release/BaseSDK.lib
|
|
11
|
+
// <sdk>/BaseSDK/Deploy/Bin/x64/Release/BaseSDK.dll
|
|
12
|
+
// ... and the same for OwnershipSDK and GameSupportSDK.
|
|
13
|
+
// <sdk>/VERSION optional, one line: the SDK version string baked into getAddonInfo().
|
|
14
|
+
|
|
15
|
+
const fs = require('fs')
|
|
16
|
+
const path = require('path')
|
|
17
|
+
|
|
18
|
+
const SDK_MODULES = ['BaseSDK', 'OwnershipSDK', 'GameSupportSDK']
|
|
19
|
+
|
|
20
|
+
function defaultSdkDir() {
|
|
21
|
+
return path.join(__dirname, '..', 'sdk')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveSdkDir(env = process.env) {
|
|
25
|
+
const fromEnv = env.STOVE_PCSDK_DIR
|
|
26
|
+
return fromEnv && fromEnv.trim() ? path.resolve(fromEnv.trim()) : defaultSdkDir()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function headerPath(sdkDir, mod) {
|
|
30
|
+
return path.join(sdkDir, mod, 'Deploy', 'Include', `${mod}.h`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function libPath(sdkDir, mod) {
|
|
34
|
+
return path.join(sdkDir, mod, 'Deploy', 'Lib', 'x64', 'Release', `${mod}.lib`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function dllPath(sdkDir, mod) {
|
|
38
|
+
return path.join(sdkDir, mod, 'Deploy', 'Bin', 'x64', 'Release', `${mod}.dll`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Returns the list of required files that are missing (empty = layout is complete).
|
|
42
|
+
function missingSdkFiles(sdkDir, exists = fs.existsSync) {
|
|
43
|
+
const required = []
|
|
44
|
+
for (const mod of SDK_MODULES) required.push(headerPath(sdkDir, mod), libPath(sdkDir, mod), dllPath(sdkDir, mod))
|
|
45
|
+
return required.filter(file => !exists(file))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Version string for getAddonInfo().sdkVersion: <sdk>/VERSION if present, else 'unknown'.
|
|
49
|
+
function readSdkVersion(sdkDir, readFile = fs.readFileSync) {
|
|
50
|
+
try {
|
|
51
|
+
const text = String(readFile(path.join(sdkDir, 'VERSION'), 'utf8')).trim()
|
|
52
|
+
return text || 'unknown'
|
|
53
|
+
} catch {
|
|
54
|
+
return 'unknown'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { SDK_MODULES, defaultSdkDir, resolveSdkDir, headerPath, libPath, dllPath, missingSdkFiles, readSdkVersion }
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stovesdk.js",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Unofficial Node.js / Electron bindings for the STOVE PC SDK (N-API)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "firejune",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Blosharper/stovesdk.js.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/Blosharper/stovesdk.js/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/Blosharper/stovesdk.js#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"stove",
|
|
17
|
+
"pcsdk",
|
|
18
|
+
"electron",
|
|
19
|
+
"napi",
|
|
20
|
+
"gamedev",
|
|
21
|
+
"achievements"
|
|
22
|
+
],
|
|
23
|
+
"main": "index.js",
|
|
24
|
+
"types": "index.d.ts",
|
|
25
|
+
"gypfile": false,
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"index.js",
|
|
31
|
+
"index.d.ts",
|
|
32
|
+
"lib/",
|
|
33
|
+
"src/",
|
|
34
|
+
"tools/",
|
|
35
|
+
"binding.gyp",
|
|
36
|
+
"prebuilds/",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"check-sdk": "node tools/sdk-dir.js check",
|
|
42
|
+
"build": "node-gyp rebuild --release --arch=x64",
|
|
43
|
+
"clean": "node-gyp clean",
|
|
44
|
+
"smoke": "node tools/smoke.js",
|
|
45
|
+
"test": "node --test \"test/**/*.test.js\"",
|
|
46
|
+
"typecheck": "tsc --noEmit -p test/tsconfig.json"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.20.5",
|
|
50
|
+
"node-addon-api": "8.9.2",
|
|
51
|
+
"node-gyp": "13.0.2",
|
|
52
|
+
"typescript": "5.9.3"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
799924d339bf570c0fbddc3a00d4a5289216af8f80b85aa91fed0ecbc3ddd77b *./stovesdk.node
|
|
Binary file
|