-
-
Notifications
You must be signed in to change notification settings - Fork 311
Integrate Steamworks #797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate Steamworks #797
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ export enum commandType { | |
| getUserInput, | ||
| applyStyle, | ||
| wait, | ||
| callSteam, // 调用Steam功能 | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ export enum commandType { | |
| getUserInput, | ||
| applyStyle, | ||
| wait, | ||
| callSteam, // 调用Steam功能 | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { ISentence } from '@/Core/controller/scene/sceneInterface'; | ||
| import { IPerform } from '@/Core/Modules/perform/performInterface'; | ||
| import { WebGAL } from '@/Core/WebGAL'; | ||
| import { logger } from '@/Core/util/logger'; | ||
| import { getStringArgByKey } from '@/Core/util/getSentenceArg'; | ||
| import { WEBGAL_NONE } from '../constants'; | ||
|
|
||
| /** | ||
| * Unlocks a Steam achievement via the renderer → Electron bridge. | ||
| * The script expects the first positional parameter to be the achievement id. | ||
| */ | ||
| export const callSteam = (sentence: ISentence): IPerform => { | ||
| for (const arg of sentence.args) { | ||
| if (arg.key === 'achievementId') { | ||
| const achievementId = getStringArgByKey(sentence, 'achievementId'); | ||
| if (achievementId) { | ||
| WebGAL.steam | ||
| .unlockAchievement(achievementId) | ||
| .then((result) => { | ||
| logger.info(`callSteam: achievement ${achievementId} unlock ${result ? 'succeeded' : 'failed'}`); | ||
| }) | ||
| .catch((error) => { | ||
| logger.error(`callSteam: achievement ${achievementId} unlock threw`, error); | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| const noperform: IPerform = { | ||
| performName: WEBGAL_NONE, | ||
| duration: 0, | ||
| isHoldOn: false, | ||
| stopFunction: () => {}, | ||
| blockingNext: () => false, | ||
| blockingAuto: () => true, | ||
| stopTimeout: undefined, | ||
| }; | ||
|
|
||
| return noperform; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { logger } from '@/Core/util/logger'; | ||
|
|
||
| interface SteamBridge { | ||
| initialize: (appId: string) => boolean | Promise<boolean>; | ||
| unlockAchievement: (achievementId: string) => boolean | Promise<boolean>; | ||
| } | ||
|
|
||
| const isWindowAvailable = (): boolean => typeof window !== 'undefined'; | ||
|
|
||
| /** | ||
| * Provides a thin bridge between the renderer process and the Electron Steam integration. | ||
| */ | ||
| export class SteamIntegration { | ||
| public appId: string | null = null; | ||
| private initialized = false; | ||
|
|
||
| public get isInitialized(): boolean { | ||
| return this.initialized; | ||
| } | ||
|
|
||
| public async initialize(appId: string): Promise<boolean> { | ||
| this.appId = appId; | ||
| const bridge = this.getSteamBridge(); | ||
| if (!bridge?.initialize) { | ||
| logger.warn('Steam integration initialize call skipped: Electron bridge not present'); | ||
| this.initialized = false; | ||
| return false; | ||
| } | ||
|
|
||
| try { | ||
| const result = await Promise.resolve(bridge.initialize(appId)); | ||
| if (result) { | ||
| logger.info(`Steam integration initialized with AppID ${appId}`); | ||
| } | ||
| this.initialized = result; | ||
| return result; | ||
| } catch (error) { | ||
| logger.error('Steam integration failed to initialize', error); | ||
| this.initialized = false; | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public async unlockAchievement(achievementId: string): Promise<boolean> { | ||
| const bridge = this.getSteamBridge(); | ||
| if (!bridge?.unlockAchievement) { | ||
| logger.warn(`Steam integration unlock call skipped for ${achievementId}: Electron bridge not present`); | ||
| return false; | ||
| } | ||
|
|
||
| if (!this.initialized) { | ||
| if (this.appId) { | ||
| await this.initialize(this.appId); | ||
| } else { | ||
| logger.warn('Steam integration unlock call skipped: AppID not set'); | ||
| return false; | ||
| } | ||
| } | ||
|
Comment on lines
+51
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In if (!this.initialized) {
if (this.appId) {
await this.initialize(this.appId);
if (!this.initialized) {
logger.warn(`Steam integration unlock call skipped for ${achievementId}: initialization failed`);
return false;
}
} else {
logger.warn('Steam integration unlock call skipped: AppID not set');
return false;
}
} |
||
|
|
||
| try { | ||
| const result = await Promise.resolve(bridge.unlockAchievement(achievementId)); | ||
| return result; | ||
| } catch (error) { | ||
| logger.error(`Steam integration failed to unlock achievement ${achievementId}`, error); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| private getSteamBridge(): SteamBridge | undefined { | ||
| if (!isWindowAvailable()) { | ||
| logger.debug('Steam integration unavailable: window is undefined'); | ||
| return undefined; | ||
| } | ||
| return window.electronFuncs?.steam; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| export {}; | ||
|
|
||
| declare global { | ||
| interface Window { | ||
| electronFuncs?: { | ||
| steam?: { | ||
| initialize: (appId: string) => boolean | Promise<boolean>; | ||
| unlockAchievement: (achievementId: string) => boolean | Promise<boolean>; | ||
| }; | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
for...ofloop is unnecessary and potentially incorrect. You iterate over all arguments, and for each one with the keyachievementId, you callgetStringArgByKey.getStringArgByKeyitself finds the first argument with the given key. This means if you have multiple arguments, you might be re-evaluatinggetStringArgByKeyunnecessarily. If there are multiple arguments withkey: 'achievementId', you'll be trying to unlock the same (first) achievement multiple times. It's more efficient and correct to callgetStringArgByKeyonce.