@next2d/builder 0.4.5 → 0.4.6
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/docs/steam.md +4 -0
- package/package.json +1 -1
- package/templates/electron/index.js +78 -3
package/docs/steam.md
CHANGED
|
@@ -107,6 +107,8 @@ const off = window.next2dWindow.onResume(() => { /* 状態の再送など */ });
|
|
|
107
107
|
|
|
108
108
|
重ね表示が有効なときは、アプリは**1つだけ**起動します(2つ目の起動はすぐに終了します。macOSではDockにアイコンも出しません)。開発中に複数起動したいときは `--next2d-allow-multiple` を付けて起動します。
|
|
109
109
|
|
|
110
|
+
スリープ(ノートPCのふたを閉じる等)から復帰すると、GPUがリセットされて描画が止まることがあるため、画面(renderer)を読み込み直します(ゲームは自動保存を前提にしてください)。電源・ディスプレイ・GPUのイベントは `userData/diagnostics.log`(Windowsは `%APPDATA%\<appId>\diagnostics.log`)に記録されます。
|
|
111
|
+
|
|
110
112
|
Linuxの透明表示はコンポジタが必要です。
|
|
111
113
|
|
|
112
114
|
### STEP4:認証を用意する
|
|
@@ -327,6 +329,8 @@ const off = window.next2dWindow.onResume(() => { /* resend state, etc. */ });
|
|
|
327
329
|
|
|
328
330
|
With the overlay enabled the app runs **once**: a second launch quits at once (and, on macOS, never shows a Dock icon). Start with `--next2d-allow-multiple` to run several copies while developing.
|
|
329
331
|
|
|
332
|
+
After sleep (e.g. closing a laptop lid) the GPU may be reset and drawing stops, so the page (renderer) is loaded again; the game should save automatically. Power, display and GPU events are logged to `userData/diagnostics.log` (Windows: `%APPDATA%\<appId>\diagnostics.log`).
|
|
333
|
+
|
|
330
334
|
Transparency on Linux requires a compositor.
|
|
331
335
|
|
|
332
336
|
### STEP4: Prepare credentials
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next2d/builder",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Multi-platform builder for Next2d Framework, supporting export to various platforms such as macOS, Windows, iOS, Android and Web(HTML)",
|
|
5
5
|
"author": "Toshiyuki Ienaga <ienaga@next2d.app>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,6 +66,50 @@ const refreshForwarding = () => {
|
|
|
66
66
|
mainWindow.setIgnoreMouseEvents(false);
|
|
67
67
|
mainWindow.setIgnoreMouseEvents(true, { forward: true });
|
|
68
68
|
};
|
|
69
|
+
// A small log of power/display/GPU events (userData/diagnostics.log) to find out what happened after sleep.
|
|
70
|
+
const diagnosticsFile = () => path.join(app.getPath("userData"), "diagnostics.log");
|
|
71
|
+
const diag = (message) => {
|
|
72
|
+
if (!overlay) return;
|
|
73
|
+
try {
|
|
74
|
+
const file = diagnosticsFile();
|
|
75
|
+
if (fs.existsSync(file) && fs.statSync(file).size > 200000) fs.writeFileSync(file, "");
|
|
76
|
+
fs.appendFileSync(file, `${new Date().toISOString()} ${message}\n`);
|
|
77
|
+
} catch { /* diagnostics must never break the game */ }
|
|
78
|
+
};
|
|
79
|
+
// After sleep (closing a laptop lid) the GPU is often reset. The renderer then keeps showing its last frame but can
|
|
80
|
+
// no longer draw (the web page is not told), so the pet looks alive and menus never appear. Load the page again to
|
|
81
|
+
// get a fresh GPU device. The game saves automatically, so progress is kept.
|
|
82
|
+
// Counts of mouse events the page really receives (moves are forwarded while click-through; down/up only arrive
|
|
83
|
+
// while the window accepts clicks). Logged when they change, to tell "clicks never reach the page" from other causes.
|
|
84
|
+
const DIAG_SCRIPT = `(() => {
|
|
85
|
+
if (window.__n2dDiag) return;
|
|
86
|
+
const d = window.__n2dDiag = { moves: 0, downs: 0, ups: 0, keys: 0, last: null };
|
|
87
|
+
const mark = (name) => (event) => { d[name]++; d.last = [event.clientX, event.clientY]; };
|
|
88
|
+
addEventListener("mousemove", mark("moves"), true);
|
|
89
|
+
addEventListener("pointerdown", mark("downs"), true);
|
|
90
|
+
addEventListener("pointerup", mark("ups"), true);
|
|
91
|
+
addEventListener("keydown", () => { d.keys++; }, true);
|
|
92
|
+
})()`;
|
|
93
|
+
let lastSnapshot = "";
|
|
94
|
+
const snapshotRenderer = async () => {
|
|
95
|
+
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
96
|
+
try {
|
|
97
|
+
const raw = await mainWindow.webContents.executeJavaScript("JSON.stringify(window.__n2dDiag || null)");
|
|
98
|
+
if (raw !== lastSnapshot) {
|
|
99
|
+
lastSnapshot = raw;
|
|
100
|
+
diag(`renderer ${raw} ignore=${overlayIgnore} onTop=${mainWindow.isAlwaysOnTop()} bounds=${JSON.stringify(mainWindow.getBounds())}`);
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
diag(`renderer snapshot failed: ${error?.message ?? error}`);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const reloadRenderer = () => {
|
|
107
|
+
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
108
|
+
diag("reload renderer (resume)");
|
|
109
|
+
overlayIgnore = Boolean(overlay?.clickThrough);
|
|
110
|
+
mainWindow.setIgnoreMouseEvents(overlayIgnore, overlayIgnore ? { forward: true } : undefined);
|
|
111
|
+
mainWindow.webContents.reloadIgnoringCache();
|
|
112
|
+
};
|
|
69
113
|
const createWindow = async () => {
|
|
70
114
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
|
71
115
|
// The preload reads these flags; each API is exposed only when its feature is configured.
|
|
@@ -103,8 +147,25 @@ const createWindow = async () => {
|
|
|
103
147
|
if (overlay.clickThrough) {
|
|
104
148
|
mainWindow.setIgnoreMouseEvents(true, { forward: true });
|
|
105
149
|
}
|
|
150
|
+
// Errors the game prints (a failed window request, for example) are otherwise invisible in a packaged app.
|
|
151
|
+
let consoleLogged = 0;
|
|
152
|
+
mainWindow.webContents.on("console-message", (...args) => {
|
|
153
|
+
const first = args[0];
|
|
154
|
+
const detail = first && typeof first === "object" && "message" in first ? first : { level: args[1], message: args[2] };
|
|
155
|
+
if (!["error", "warning", 2, 3].includes(detail.level) || ++consoleLogged > 200) return;
|
|
156
|
+
diag(`console ${detail.level}: ${String(detail.message).slice(0, 300)}`);
|
|
157
|
+
});
|
|
158
|
+
mainWindow.webContents.on("did-finish-load", () => {
|
|
159
|
+
diag("renderer loaded");
|
|
160
|
+
mainWindow.webContents.executeJavaScript(DIAG_SCRIPT).catch(() => {});
|
|
161
|
+
});
|
|
162
|
+
const snapshotTimer = setInterval(snapshotRenderer, 5000);
|
|
163
|
+
snapshotTimer.unref?.();
|
|
164
|
+
mainWindow.once("closed", () => clearInterval(snapshotTimer));
|
|
165
|
+
mainWindow.webContents.on("unresponsive", () => diag("renderer unresponsive"));
|
|
166
|
+
mainWindow.webContents.on("responsive", () => diag("renderer responsive"));
|
|
106
167
|
const events = ["display-metrics-changed", "display-added", "display-removed"];
|
|
107
|
-
const onDisplayChange = () => { placeOverlay(); keepOnTop(); };
|
|
168
|
+
const onDisplayChange = () => { diag("display changed"); placeOverlay(); keepOnTop(); };
|
|
108
169
|
for (const name of events) screen.on(name, onDisplayChange);
|
|
109
170
|
mainWindow.once("closed", () => {
|
|
110
171
|
for (const name of events) screen.removeListener(name, onDisplayChange);
|
|
@@ -187,7 +248,10 @@ app.whenReady().then(async () => {
|
|
|
187
248
|
}
|
|
188
249
|
if (overlay) {
|
|
189
250
|
const trusted = (event) => {
|
|
190
|
-
if (!isTrustedSender(event, mainWindow))
|
|
251
|
+
if (!isTrustedSender(event, mainWindow)) {
|
|
252
|
+
diag(`window API access denied (frame url: ${event?.senderFrame?.url})`);
|
|
253
|
+
throw new Error("Window API access denied");
|
|
254
|
+
}
|
|
191
255
|
};
|
|
192
256
|
ipcMain.handle("next2d:window:set-ignore-mouse-events", (event, ignore) => {
|
|
193
257
|
trusted(event);
|
|
@@ -210,7 +274,18 @@ app.whenReady().then(async () => {
|
|
|
210
274
|
mainWindow.setIgnoreMouseEvents(overlayIgnore, overlayIgnore ? { forward: true } : undefined);
|
|
211
275
|
mainWindow.webContents.send("next2d:window:resume");
|
|
212
276
|
};
|
|
213
|
-
for (const name of ["
|
|
277
|
+
for (const name of ["suspend", "lock-screen"]) powerMonitor.on(name, () => diag(name));
|
|
278
|
+
for (const name of ["resume", "unlock-screen"]) {
|
|
279
|
+
powerMonitor.on(name, () => {
|
|
280
|
+
diag(name);
|
|
281
|
+
setTimeout(() => {
|
|
282
|
+
recoverOverlay();
|
|
283
|
+
// Only a resume from sleep can reset the GPU (unlocking the screen cannot).
|
|
284
|
+
if (name === "resume") reloadRenderer();
|
|
285
|
+
}, 1000);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
app.on("child-process-gone", (_event, details) => diag(`child-process-gone ${details.type} ${details.reason}`));
|
|
214
289
|
ipcMain.handle("next2d:window:quit", (event) => {
|
|
215
290
|
trusted(event);
|
|
216
291
|
// Reply before the window is torn down.
|