Skip to content

Commit

Permalink
steamworks: packager part (#880)
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin authored Jul 23, 2024
1 parent 5880e9a commit f60fe5d
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 2 deletions.
21 changes: 21 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,27 @@
"maxTextureDimension": {
"string": "Increase max vector costume resolution to make large costumes look better. May increase memory use and cause crashes."
},
"steamworksUnavailable": {
"string": "To enable the Steamworks extension, you must use one of the Electron environments."
},
"steamworksAvailable": {
"string": "This project is using the Steamworks extension. You can find your game's app ID in Steamworks. 480 is the ID of the Steamworks demo game (Spacewar)."
},
"steamworksAppId": {
"string": "App ID"
},
"steamworksOnError": {
"string": "If there is an error initializing Steamworks"
},
"steamworksIgnore": {
"string": "Do nothing"
},
"steamworksWarning": {
"string": "Show a warning but continue"
},
"steamworksError": {
"string": "Show an error and exit"
},
"package": {
"string": "Package",
"context": "Button to package the project"
Expand Down
32 changes: 32 additions & 0 deletions src/p4/PackagerOptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@
thing = 'Electron';
} else if (detail.asset === 'webview-mac') {
thing = 'WKWebView';
} else if (detail.asset === 'steamworks.js') {
thing = 'Steamworks.js';
}
if (thing) {
task.setProgressText($_('progress.loadingLargeAsset').replace('{thing}', thing));
Expand Down Expand Up @@ -1009,6 +1011,36 @@
</div>
{/if}

{#if projectData.project.analysis.usesSteamworks}
<Section
accent="#136C9F"
reset={() => {
resetOptions([
'steamworks'
]);
}}
>
<h2>Steamworks</h2>
{#if $options.target.startsWith('electron-')}
<p>{$_('options.steamworksAvailable')}</p>
<label class="option">
{$_('options.steamworksAppId')}
<input pattern="\d+" minlength="1" bind:value={$options.steamworks.appId}>
</label>
<label class="option">
{$_('options.steamworksOnError')}
<select bind:value={$options.steamworks.onError}>
<option value="ignore">{$_('options.steamworksIgnore')}</option>
<option value="warning">{$_('options.steamworksWarning')}</option>
<option value="error">{$_('options.steamworksError')}</option>
</select>
</label>
{:else}
<p>{$_('options.steamworksUnavailable')}</p>
{/if}
</Section>
{/if}

<Section>
<DropArea on:drop={(e) => importOptionsFromDataTransfer(e.detail)}>
<div class="buttons">
Expand Down
3 changes: 3 additions & 0 deletions src/packager/download-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const unknownAnalysis = () => ({
stageVariables: [],
stageComments: [],
usesMusic: true,
usesSteamworks: false,
extensions: []
});

Expand Down Expand Up @@ -40,12 +41,14 @@ const analyzeScratch3 = (projectData) => {
.map((i) => i.text);
// TODO: usesMusic has possible false negatives
const usesMusic = projectData.extensions.includes('music');
const usesSteamworks = projectData.extensions.includes('steamworks');
const extensions = projectData.extensionURLs ? Object.values(projectData.extensionURLs) : [];
return {
...unknownAnalysis(),
stageVariables,
stageComments,
usesMusic,
usesSteamworks,
extensions
};
};
Expand Down
5 changes: 5 additions & 0 deletions src/packager/large-assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export default {
sha256: 'b5636571cd9be2aae2f6dac1ab090fdf829c8fdfe91f462cc2feb2d324705f9f',
estimatedSize: 3425601
},
'steamworks.js': {
src: externalFile('steamworks.js-0.3.2.zip'),
sha256: 'fd8bc80a97cd880d71113dfc5f81b124b6e212335393db73e3df168c5c546fbc',
estimatedSize: 3279554,
},
scaffolding: {
src: relativeScaffolding('scaffolding-full.js'),
estimatedSize: 4564032,
Expand Down
111 changes: 109 additions & 2 deletions src/packager/packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ cd "$(dirname "$0")"
const contentsPrefix = isMac ? `${rootPrefix}${packageName}.app/Contents/` : rootPrefix;
const resourcesPrefix = isMac ? `${contentsPrefix}Resources/app/` : `${contentsPrefix}resources/app/`;
const electronMainName = 'electron-main.js';
const electronPreloadName = 'electron-preload.js';
const iconName = 'icon.png';

const icon = await Adapter.getAppIcon(this.options.app.icon);
Expand All @@ -537,8 +538,8 @@ cd "$(dirname "$0")"
};
zip.file(`${resourcesPrefix}package.json`, JSON.stringify(manifest, null, 4));

const mainJS = `'use strict';
const {app, BrowserWindow, Menu, shell, screen, dialog} = require('electron');
let mainJS = `'use strict';
const {app, BrowserWindow, Menu, shell, screen, dialog, ipcMain} = require('electron');
const path = require('path');
const isWindows = process.platform === 'win32';
Expand Down Expand Up @@ -571,6 +572,7 @@ const createWindow = (windowOptions) => {
sandbox: true,
contextIsolation: true,
nodeIntegration: false,
preload: path.resolve(__dirname, ${JSON.stringify(electronPreloadName)}),
},
frame: ${this.options.app.windowControls !== 'frameless'},
show: true,
Expand Down Expand Up @@ -725,7 +727,106 @@ app.whenReady().then(() => {
createProjectWindow(defaultProjectURL);
});
`;

let preloadJS = `'use strict';
const {contextBridge, ipcRenderer} = require('electron');
`;

if (this.project.analysis.usesSteamworks) {
mainJS += `
const enableSteamworks = () => {
const APP_ID = +${JSON.stringify(this.options.steamworks.appId)};
const steamworks = require('./steamworks.js/');
const client = steamworks.init(APP_ID);
const async = (event, callback) => ipcMain.handle(event, (e, ...args) => {
return callback(...args);
});
const sync = (event, callback) => ipcMain.on(event, (e, ...args) => {
e.returnValue = callback(...args);
});
async('Steamworks.achievement.activate', (achievement) => client.achievement.activate(achievement));
async('Steamworks.achievement.clear', (achievement) => client.achievement.clear(achievement));
sync('Steamworks.achievement.isActivated', (achievement) => client.achievement.isActivated(achievement));
sync('Steamworks.apps.isDlcInstalled', (dlc) => client.apps.isDlcInstalled(dlc));
sync('Steamworks.localplayer.getName', () => client.localplayer.getName());
sync('Steamworks.localplayer.getLevel', () => client.localplayer.getLevel());
sync('Steamworks.localplayer.getIpCountry', () => client.localplayer.getIpCountry());
sync('Steamworks.localplayer.getSteamId', () => client.localplayer.getSteamId());
async('Steamworks.overlay.activateToWebPage', (url) => client.overlay.activateToWebPage(url));
steamworks.electronEnableSteamOverlay();
sync('Steamworks.ok', () => true);
};
try {
enableSteamworks();
} catch (e) {
console.error(e);
ipcMain.on('Steamworks.ok', (e) => {
e.returnValue = false;
});
app.whenReady().then(() => {
const ON_ERROR = ${JSON.stringify(this.options.steamworks.onError)};
const window = BrowserWindow.getAllWindows()[0];
if (ON_ERROR === 'warning') {
dialog.showMessageBox(window, {
type: 'error',
message: 'Error initializing Steamworks: ' + e,
});
} else if (ON_ERROR === 'error') {
dialog.showMessageBoxSync(window, {
type: 'error',
message: 'Error initializing Steamworks: ' + e,
});
app.quit();
}
});
}`;

preloadJS += `
const enableSteamworks = () => {
const sync = (event) => (...args) => ipcRenderer.sendSync(event, ...args);
const async = (event) => (...args) => ipcRenderer.invoke(event, ...args);
contextBridge.exposeInMainWorld('Steamworks', {
ok: sync('Steamworks.ok'),
achievement: {
activate: async('Steamworks.achievement.activate'),
clear: async('Steamworks.achievement.clear'),
isActivated: sync('Steamworks.achievement.isActivated'),
},
apps: {
isDlcInstalled: async('Steamworks.apps.isDlcInstalled'),
},
leaderboard: {
uploadScore: async('Steamworks.leaderboard.uploadScore'),
},
localplayer: {
getName: sync('Steamworks.localplayer.getName'),
getLevel: sync('Steamworks.localplayer.getLevel'),
getIpCountry: sync('Steamworks.localplayer.getIpCountry'),
getSteamId: sync('Steamworks.localplayer.getSteamId'),
},
overlay: {
activateToWebPage: async('Steamworks.overlay.activateToWebPage'),
},
});
};
enableSteamworks();`;

const steamworksBuffer = await this.fetchLargeAsset('steamworks.js', 'arraybuffer');
const steamworksZip = await (await getJSZip()).loadAsync(steamworksBuffer);
for (const [path, file] of Object.entries(steamworksZip.files)) {
const newPath = path.replace(/^package\//, 'steamworks.js/');
setFileFast(zip, `${resourcesPrefix}${newPath}`, file);
}
}

zip.file(`${resourcesPrefix}${electronMainName}`, mainJS);
zip.file(`${resourcesPrefix}${electronPreloadName}`, preloadJS);

for (const [path, data] of Object.entries(projectZip.files)) {
setFileFast(zip, `${resourcesPrefix}${path}`, data);
Expand Down Expand Up @@ -1702,6 +1803,12 @@ Packager.DEFAULT_OPTIONS = () => ({
y: 0
}
},
steamworks: {
// 480 is Spacewar, the Steamworks demo game
appId: '480',
// 'ignore' (no alert), 'warning' (alert and continue), or 'error' (alert and exit)
onError: 'warning'
},
extensions: [],
bakeExtensions: true,
maxTextureDimension: 2048
Expand Down

0 comments on commit f60fe5d

Please sign in to comment.