Tutorial on how I implemented Steam Achievements with luasteam

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
cip
Prole
Posts: 15
Joined: Wed Jun 07, 2023 5:44 pm

Tutorial on how I implemented Steam Achievements with luasteam

Post by cip »

Hi,

Here is a short tutorial on how I implemented Steam Achievements with the help of luasteam. I used the documentation from luasteam, as well as code from a327ex, which has tons of useful code (with an MIT license), as well as some video tutorials from AuroDev and the SteamWorks documentation.

I would like to note in advance that my writing style is a bit too verbose, so I apologize in advance for the huge wall of text. I am not too good at writing tutorials. Also, this tutorial is a bit later than I had planned to release it :(

What I used to add Steam Achievements were four things:

1. A SteamWorks SDK .dll file (from the official SteamWorks SDK)
2. A luasteam .dll file
3. Added the achievements in Steam.
(I followed this tutorial from AuroDev's YouTube video)
4. Functions that start, run and exit the Steam SDK, and functions that unlock the achievements.

I released my first game in 2022, so the SteamWorks SDK version I used is the one from 2022. This is important, because some functions might have changed in the meantime for newer versions of luasteam and SteamWorks SDK, so please be aware of this. For the version of SteamWorks SDK I used, this was the luasteam I downloaded (Version 2.0.0),
https://github.com/uspgamedev/luasteam/ ... .0.0%2Bfix

From the above link I downloaded the win64_luasteam.dll file and renamed it to luasteam.dll (like how it says in the official documentation).

Next you have to download the SteamWorks SDK file for your system.

Now you place these two files you just downloaded in your game's folder, where you have your main.lua file.

Upon starting the game, like all libraries you might be using, you have to require and initialize the luasteam library. I followed the method from here, and added the following code,

Code: Select all

Steam = require 'luasteam'
if type(Steam) == 'boolean' then Steam = nil end
Like in the link above, I also required the following,

Code: Select all

ffi = require('ffi')
because I wanted to create a menu in the game where you could see what achievements you currently have unlocked, and from what I saw requiring ffi will help you retrieve this information.

Next, in the love.load() function I initialized Steam with luasteam like this,

Code: Select all

Steam.init()
For my game I used a custom love.run function (to ensure a fixed timestep), but I wasn't sure if I could add the "Steam.runCallbacks()" function in the love.run function, so I just added the "Steam.runCallbacks()" in my update loops.

Now, for the actual function that will unlock the Steam achievements I used the following function from a327ex as starting point, but I also had to add the Steam.userStats.requestCurrentStats() before actually setting the achievement,

Code: Select all

function unlockAchievement(achievement_name)
   if achievements[achievement_name] then return end
    achievements[achievement_name] = true
   if Steam.init() then
      Steam.userStats.requestCurrentStats()
      Steam.userStats.setAchievement(achievement_name)
      timer:after(0.5, function() Steam.userStats.storeStats() end) -- the timer I used was from vrld's excellent HUMP library
   end
end
We will get back to this function once we actually call the function in the game.

Now we should add the achievements in Steam. I followed this tutorial by AuroDev,
https://www.youtube.com/watch?v=s554p28MTxY

We should add these achievements in our game as well. I did this like so,

Code: Select all

achievements = {"score1k", "score10k", "50skeletons", etc.}
After adding these achievements in Steam and in our game, we have to unlock them when a certain condition in our game is met. For example, let's say we have an achievement that unlocks when we eliminate 50 skeletons, we can have something like this on our game:
in the logic we have for checking if we can remove a skeleton, we can implement something like this,

Code: Select all

if numberSkeletonsEliminated >= 50 then 
   unlockAchievement("50skeletons") 
end
unlockAchievement() is the name of the function we have defined above, and "50skeletons" is the name of the achievement we have defined in Steam. Basically, this is what I did in order to add achievements to my game.

Bonus: for loading what achievements a player has unlocked, you can use the following function (from a327ex):

Code: Select all

function loadAchievementsFromSteam()
    if Steam.init() then 
      Steam.userStats.requestCurrentStats()
        for _, achievement_name in ipairs(achievement_names) do 
            local b = ffi.new('bool[1]')
            Steam.userStats.getAchievement(achievement_name, b)
            achievements[achievement_name] = b[0]            
        end
    end
end
I used this function to create a screen for the player to see what they have unlocked so far.

Hope this helps! Let me know if you have any questions.

EDIT: One thing I forgot to mention in my original post is that I also added a Steam.shutdown() when the player exits the game. So, when you exit the game you can do something like,

Code: Select all

Steam.shutdown()
love.event.quit()
Last edited by cip on Thu Aug 15, 2024 6:01 pm, edited 1 time in total.
User avatar
togFox
Party member
Posts: 828
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Re: Tutorial on how I implemented Steam Achievements with luasteam

Post by togFox »

Amazing stuff. This demystifies Steam integration greatly and the sample code is very straight forward. Thanks for sharing your learnings!
Last project:
https://togfox.itch.io/hwarang
A card game that brings sword fighting to life.
Current project:
Turn-based PBEM horse stable (racing) management sim: https://togfox.itch.io/horse-stable-manager
https://discord.gg/HeHgwE5nsZ
User avatar
dusoft
Party member
Posts: 635
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: Tutorial on how I implemented Steam Achievements with luasteam

Post by dusoft »

That works on Linux, too? With a DLL?
MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: Tutorial on how I implemented Steam Achievements with luasteam

Post by MrFariator »

On Linux you'd grab an .so instead of a .DLL, otherwise the setup is similar.
I did find, however, that when making an AppImage for distribution on Linux (using the steps on the wiki), I had to place the .so under:
squashfs-root/lib/lua/5.1/luasteam.so

Other paths probably work with the correct configurations (by touching package.cpath or so), but that's what I ended up with.
cip
Prole
Posts: 15
Joined: Wed Jun 07, 2023 5:44 pm

Re: Tutorial on how I implemented Steam Achievements with luasteam

Post by cip »

togFox wrote: Tue Aug 06, 2024 2:40 am Amazing stuff. This demystifies Steam integration greatly and the sample code is very straight forward. Thanks for sharing your learnings!
@togFox: thank you for the kinds words! I hope it helps! If you have any questions, let me know.
dusoft wrote: Wed Aug 07, 2024 4:07 pm That works on Linux, too? With a DLL?
@dusoft: sorry, I am not sure how it works on Linux yet, because unfortunately at the moment I don't have experience with this operating system. I plan on learning how to work with Linux, though. Please take a look at what @MrFariator suggested.
MrFariator wrote: Thu Aug 08, 2024 12:02 pm On Linux you'd grab an .so instead of a .DLL, otherwise the setup is similar.
I did find, however, that when making an AppImage for distribution on Linux (using the steps on the wiki), I had to place the .so under:
squashfs-root/lib/lua/5.1/luasteam.so

Other paths probably work with the correct configurations (by touching package.cpath or so), but that's what I ended up with.
@MrFariator: thank you for the help!
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 4 guests