Adding Steam achievements to an Electron game.


Hive P v. S

When Hive P v. S was released on Steam, I used Greenworks to add support for the Steamworks SDK. I had not intended to make any use of it, except to know when the Steam overlay was opened so that I could pause the game.

While fixing some issues that have shown up as the game has been played on Steam, I decided to implement Steam Achievements. A look a the documentation showed that it was a simple process with few steps.

  1. Create an achievement in Steamworks by giving it an ID, name, description and one graphic for when you have achieved it and one for when you have not.
  2. Using Steamworks SDK (or Greenworks in my case), store the achievement completed when the player does so and when suitable send it to Steam servers for permanent storage.
  3. Update game code with the places you want to trigger completed achievements.

KILLEDBH_done.jpg
Achievement for main objective of the game. Image Jens Nilsson


As I use Electron to package my game as a native application, I had some additional issues to solve. The process that renders the game is separate from the process that runs the application, this to make the application safer, as in essence it is a browser with unlimited access to the computer. While player progress happens in the render, Greenworks runs in the application process, therefor I had to add means for the two of them to communicate. For a little while I thought I had to add electron as a dependency to the render code, which would have complicated the otherwise clean code that makes it possible to have same code for web, mobile devices and computers. Thankfully, after some searching I found an example of how to implement it in a way that added no new dependencies to the render code.

The original solution: https://stackoverflow.com/a/68581487

How I used it to send Steam achievement notices from Electron render to Electron main.

//preloads.ts file
import { ipcRenderer, contextBridge } from "electron";
// Setup sending notices about achievements.
contextBridge.exposeInMainWorld("electron", {
    notificationApi: {
        sendNotification(achievement: string) {
            ipcRenderer.send('SteamAchievement', achievement);
        }
    }
});
//index.ts file (main electron)
ipcMain.on('SteamAchievement', (event, achievement) => {
    // Greenworks code to check if achievement should be acheived or already is.
});
//app.ts file (render process)
globalThis.electron.notificationApi.sendNotification(achievement); 


For sending the notices I created a simple helper method, that I could call in the appropriate places to trigger an achievement. To keep it nice and tidy I made an enum with the 40 achievements and used it with the helper method.

// Enum 
export enum Achievement {
    HighScore = "HIGHSCORE",
    Tutorial = "TUTORIAL"
    // ... etc 
}
// Helper method
steamAchievementTrigger = (achievement: Achievement) => {
    // ... Some additional code to only do when in electron, and only once.
    // Send notification to electron.
    globalThis.electron.notificationApi.sendNotification(achievement);
}
// ...In the high score check, trigger achievement for not good enough for high score table.
if (noHighScore) { 
    // Achievement
    this.props.app.steamAchievementTrigger(Achievement.NoHighScore);
}  
// Greenworks in the main process 
// ... Some additional greenworks code to check if achievement not already achieved.
// Local set it achieved. 
greenworks.activateAchievement(achievement,
     () => console.log(`Achieved: ${achievement} ok!`),
     (error: any) => console.log(`Failed: ${achievement}, error: ${error}`)
);
// Send it to Steam for permanent storage. 
greenworks.storeStats(
     () => console.log(`Stored: ${achievement} ok!`),
     (error: any) => console.log(`Failed storing: ${achievement}, error: ${error}`)
);  


That sums it all up for adding the achievements, only 39 more this.props.app.steamAchievementTrigger(Achievement.?); was added to the code... Or rather 9 more, as 20 are for levels and 10 are for power ups, which could be triggered in two places.

PUFREEZE_waiting.jpg
Power up achievement not yet achieved. Image Jens Nilsson

The achievements are not yet live on Steam, currently testing them in the beta before pushing them live.

Thanks for reading, next week it is back to TMoS!

Get Hive P v. S

Buy Now$2.99 USD or more

Leave a comment

Log in with itch.io to leave a comment.