Steam wrapper?

BlitzMax Forums/BlitzMax Programming/Steam wrapper?

sswift(Posted 2009) [#1]
I don't know how good my chances of getting my game on Steam are after hearing about some people with better games being turned away, but I heard someone here made some kinf of wrapper which allow Blitzmax to work with the Steam platform. Does anyone have any information about that?


Tachyon(Posted 2009) [#2]
Eschalon is on Steam. I hired Brucey to write the wrapper for me.

However, if you don't use any of the "extras" such as ranking or stats, then you actually don't need any part of the Steamworks SDK. You just use their content tool to package your game into Steam's DRM and upload it, so I never actually needed the BlitzMax Steamworks wrapper.


plash(Posted 2009) [#3]
@Tachyon: That was my assumption of the SDK.. You only need them for the extras.


skidracer(Posted 2009) [#4]
My dll/wrapper is free but I prefer you already have game id established on the steam servers before I distribute to you.

It has two users so far, one Blitz3D and one BlitzMax, achievements only on both games. From my understanding both games weren't allowed on steam without having at minimum a rewards system.


jkrankie(Posted 2009) [#5]
@Skid, actually, I added achievements to Bullet Candy via you wrapper around 2 and a half years after it first appeared on the service (Steamworks didn't exist as it does now when they contacted me with the deal). However, it seems to be the case now that you'd need to add achievements as part of the 'getting on Steam' process.

Cheers
Charlie


sswift(Posted 2009) [#6]
How do achievements work? I mean, with Steam.

Do you call a function to report that the player achieved a particular objective? And do you have to read a player's achievements from the Steam servers so they keep them no matter where they log in? Wouldn't you want to do this with high scores as well? I suppose depending on how you can define the achievements you could store high scores in this way.

It seems like you'd need to have two versions of the game, one for Steam and one for everyone else, if you port stuff like scores and achievments over the accounts.


siread(Posted 2009) [#7]
Valve are interested in my game Super Laser Racer but have requested that I implement a leaderboard and achievements with Steam. I have access to the sdk but no nothing about writing a wrapper. Any chance I could use yours Skid?


skidracer(Posted 2009) [#8]
I have uploaded latest version of stub for bb and bmx here:

http://code.google.com/p/max-edit/downloads/list

Siread and others, please don't publish / advertise this link. I am not currently an official steam developer. You will need dev status with steam then I think it should be ok to post any questions here. sswift you should get basic idea of capabilities of sdk from the decls file.


siread(Posted 2009) [#9]
Thanks Skid, that works beautifully. I punched the air when I saw the Steam notification come up with an achievement. :D

I would like to add the leaderboard commands to the wrapper but don't know how to create the .a and stub.dll files. Can you point me at a thread with info regarding this?


skidracer(Posted 2009) [#10]
if you have a steamid (or not), please email me mods and i'll build an update (visual studio required) simonarmstrong+chr(64)+blitzbasic.com


Sonic(Posted 2012) [#11]
Sorry to revive an old thread, but I was wondering what the possibility of getting a Steam network wrapper for BMax written.

I've just released my previous game on Steam, Lone Survivor [link here] (written in Flash + Air, but the tools were all made in BlitzMax), and I'm thinking about doing something with multiplayer aspects planned from the start. I'm currently thinking about using Unity for it, but I'm still considering a lower-fi version using BlitzMax + MiniB3D as it's what I feel most comfortable with, and I already have a prototype I kind of like in it.

I just wondered in theory if anyone still posting here would have this kind of expertise and might be interested in working on this, paid of course?

To give an idea of what I'm going for, it's kind of an invisible pvp matchmaking similar to From Software's Dark Souls. I want up to four players, 2v2, and I'd be proposing to Steam that I use their networking from the ground up. It's all very nebulous at the moment, and it would depend on Steam's interest in the idea, but I just wanted to see if there was anyone around who might be able to do this in theory?

If you'd prefer to contact me privately, my email is jasper at superflatgames dot com. Thanks!


JoshK(Posted 2013) [#12]
I am looking for something like this as well. I am an approved Steam developer:
http://steamcommunity.com/sharedfiles/filedetails/?id=145716110


Godra25(Posted 2015) [#13]
i'm sorry if i bump this tread..

i've try the "mod" from skidracer. and it worked perfectly :)

but, i need just one more command from steamworks:
SteamUserStats()->ResetAllStats();


how to use that command with skidracer's mod ?

thanks


Grey Alien(Posted 2015) [#14]
Hi Skidracer,

Just checking with you if I can use your Steam wrapper for my BlitzMax game Regency Solitaire: http://steamcommunity.com/sharedfiles/filedetails/?id=383336796

I found a link to steamstub4a.zip (Mar 2010) via the link above.

Do I just include the dlls in the same folder as my exe? I'm guessing I should probably get a newer version of steam_api.dll from valve as the steamstub.dll is yours right? Anything else I should know? I'm a noob.

I'm planning on using it for achievements only at the moment. I'm also doing trading cards but they don't need the API do they? I think I just set them up in the Steam backend, but could be wrong.

Many thanks for your help! :-)


skidracer(Posted 2015) [#15]
Yes and yes.

The Mac setup is a bit more tricky so I recommend starting with windows.


Grey Alien(Posted 2015) [#16]
Great thanks skid!

Anyone got any tips about the Mac integration? I don't know where to start. Thx.


Grey Alien(Posted 2015) [#17]
OK some incoming questions!

1) Does OpenSteam() call SteamUserStats()->RequestCurrentStats()? The docs say the to set any stats or achievements you must have called that first. Also the docs say it's an async callback so I don't know how soon after calling that it's safe to actually start setting stats, or does the lib handle that?

I notice that ReadSteam() returns "stats received!" so I'm guessing it's working as expected (and I do appear to be able to get/set stats.) Note that I was getting a stats error before until I actually set up a test stat on Steamworks and published it.

2) If I peak in the lib I see ClearSteamAchievement but it's not in the steamtest.bmx Is it safe to use it like this?
Function GetSteamAchievement%(id$z)
It would be useful during test to clear them after setting them so I can retest.

3) Also in the lib is CloseSteam. Do I ever need to call that? It's not called in steamtest.bmx

4) When is the best time to call OpenSteam()? Right at the top of my code or after the game has created a graphics window?

5) When should I see that little Steam popup in the corner? I made a build of my game (with no API integrated) and uploaded it via Steam Pipe then installed it. When I click the icon, it calls steam://rungameid/*myappid* and I see the popup in the corner and shift+tab works, yay! However, if I run the game direct in the steamapps\common\Regency Solitaire folder there's no overlay (which is to be expected I guess as there's no API integrated).

BUT when I run the game via my Blitmax compiler (not in the steam install folder) and I'm properly loading the dlls and calling OpenSteam() (which is returning 1) and I'm able to get/set stats OK, I don't see any kind of pop up (and shift+tab doesn't do anything). Should I? I've yet to try this with setting achievements to see if I see an achievements popup.

6) If I just call OpenSteam() and it returns 1, then later I exit my game I see this message in the console window:
"Setting breakpad minidump AppID = *myappid*
Steam_SetMinidumpSteamID: Caching Steam ID: 76561197994138291 [API loaded no]"

What does that mean? Does it mean I need CloseSteam() first? or something failed somewhere?

Also weirdly that only appears when I run in release mode. If I run in debug mode I don't see it. Is that because the exe has a different name and Steam knows?



Phew a lot of questions, sorry, but I want to make sure I've done things correctly.

Also btw, calling FindNumberOfCurrentPlayers() with the latest steam_api.dll results in an Access Violation. So I'm guessing the lib is no longer compatible with that. I don't actually need it, just a heads up for Skid.


Grey Alien(Posted 2015) [#18]
Related to no.5 a friend of mine said I need to call SteamAPI_RunCallbacks() every frame to see the popups and so shift+tab works and achievement popups show etc. That isn't in the lib but I thought that maybe ReadSteam() has the same effect? So I call ReadSteam() every frame (which returns null except for when I get/set stats) but I don't see the Steam overlay. Any ideas?

[EDIT]I checked out steamstub.cpp and I see that ReadSteam() does call SteamAPI_RunCallbacks(), which is good. Also in the SDK docs they say to call that at 10Hz or higher, but say most people call it every render (so maybe 60Hz). So that answers that.


skidracer(Posted 2015) [#19]
Grey, good to see you are up and running. I'll review your questions tonight when I'm home from work.


skidracer(Posted 2015) [#20]
Mac integration thread is here. You don't need the steamstub dll as you can include steamstub.cpp directly into blitzmax but you do need a post compile step where the steam api library needs to be copied into the .app folder.

1 yes, see steamstub.cpp source in other thread if you don't have a copy

2.i will add ClearSteamAchievement to next release, I like the idea of a game that wipes out your achievements if you do something really bad

3. I doubt it, guys in other thread would know more

6. that crash is I think steamstub trying to call printf which it shouldn't be,


I would prefer to build a MSVC based bmk for windows blitzmax steam apps so we could drop use of the extra .dll but until then it will need to be compiled. Are you in a position to build the .dll Grey? I would prefer to focus on Mac support and if you could build the dll from source in visual studio that would make things easier for me.


Grey Alien(Posted 2015) [#21]
Thanks for the answers Skidracer, I'll check out that other thread now. I'm keen to know how to get the Steam overlay (and pop up) showing (as per question 5 above) and to know if I should be calling SteamAPI_RunCallbacks() every frame (or maybe less frequently). Any ideas?

[EDIT: The other thread suggests that the overlay will only appear when running the game via Steam, so I'll test that. However, do I still need to call ReadSteam() every so often for anything? Is it likely to impact performance at all and so I should call it sparingly? Like it won't hitch up gameplay I hope.]

I do have an evaluation copy of Visual Studio Pro 2010 installed (that I can get a trial key for), but I'm not experienced in using it at all, so it depends how easy it is to build the dll - but I'm willing to try.


Grey Alien(Posted 2015) [#22]
Another question:

I can obviously check the result of OpenSteam() and then only call things like GetSteamStat() if Steam is running. If Steam isn't running then calling GetSteamStat() results in an access violation. So what can I do in situation whereSteam is running when the game boots up, but the player closes Steam and keeps playing the game and then I call a function like GetSteamStat()?

At the moment I'll get an access violation unless I ALWAYS call OpenSteam() before any other functions. Is it safe to call it multiple times in a row, is it slow, is there are better way to do this? Wouldn't it be better if GetSteamStat() failed more gracefully than an access violation?

[EDIT] OK a friend tells me that if you try to close Steam whilst a game is running, it'll force quit the game. Tested and confirmed that. So I guess the above is a non issue as long as I cache the result of OpenSteam() on game boot.


Grey Alien(Posted 2015) [#23]
On a related note to the above post, how are people handling stuff like awarding an achievement when Steam isn't running? Store it locally and just send the current state of achievements to Steam every time the game runs? Or just assuming Steam is always running (bit dangerous as the achievements could get out of sync)


Grey Alien(Posted 2015) [#24]
How are people handling non-STEAM builds once they have integrated the lib and dlls?

I don't want to ship the dlls with the game to portals like Big Fish Games, but if I leave the Import "steamstub.lib" in my code and the various Externs like OpenSteam() etc. then when I run the game without the steamstub.dll in the exe folder, it obviously bombs because it can't find the .dll.

So I can comment out all the imports/externs AND every single call to any Steam functions but it's a massive hassle and not a sensible way to do things.

I can't use a Const to filter out those imports/externs and Steam function calls because I need to use a conditional compiler variable instead (a #define basically). However you can't make custom compiler directives in BlitzMax :-(

So maybe I need to make a file called dummySteam.bmx and includ that for non-Steam builds. It could have no externs and I can have empty steam function calls in it so that the rest of my code compiles. Workable?


Pingus(Posted 2015) [#25]
It could have no externs and I can have empty steam function calls in it so that the rest of my code compiles. Workable?

That's what I do for stuff that I do not want to compile in the release build. Not very elegant but of course it works. For things that do not have to change it is tedious but no big deal.


Grey Alien(Posted 2015) [#26]
OK thanks. Guess I'll try that then.


JoshK(Posted 2015) [#27]
If you buy Leadwerks the editor DLL has Steam C functions you can hook into. All you have to do is call SteamworksInitialize() and SteamworksShutdown() at a minimum. It works with BMX.


Pingus(Posted 2015) [#28]
Josh, could you post some bmax code samples ? Sounds interresting...


Grey Alien(Posted 2015) [#29]
Yeah sounds interesting. Btw, why does Shutdown have to be called?


JoshK(Posted 2015) [#30]
You would need the lib file to do it this way, or you can just call LoadLibraryA() and get the function pointers:
?debug
Import "../Library/DLL/Debug/Leadwerks.lib"
?
?Not debug
Import "../Library/DLL/Release/Leadwerks.lib"
?

Extern "C"
	'Steamworks
	Function SteamworksCountSubscribedWorkshopItems:Int()
	Function SteamworksGetSubscribedWorkshopItem(fileid:Byte Ptr,index:Int)
	Function SteamworksGetWorkshopItemTimeUpdated_(fileid:Long,time:Byte Ptr)="SteamworksGetWorkshopItemTimeUpdated"
	Function SteamworksGetWorkshopItemTitle_(fileid:Long,buf:Byte Ptr,sz:Int)="SteamworksGetWorkshopItemTitle"
	Function SteamworksInstallPackage:Int(fileid:Long, installhook(filepath:Byte Ptr))	
	Function SteamworksUninstallPackage:Int(path$z)
	Function SteamworksExtractWorkshopFiles:Int(force:Int)
	Function SteamworksGetWorkshopFileDescription:Int(fileid:Long,buf:Byte Ptr,sz:Int)
	Function SteamworksIncrementAchievementProgress(name$z)
	Function SteamworksSetAchievement(name$z)
	Function SteamworksGetAchievement:Int(name$z)
	Function SteamGetAppID:Int()
	Function SteamGetAppAge:Int(fileid:Long)
	Function SteamworksSubscribeAndDownloadWorkshopFile:Int(fileid:Long,callback:Byte Ptr)
	Function SteamworksUnsubscribeWorkshopFile:Int(fileid:Long,callback:Byte Ptr)
	Function GetUserPublishedFileTags(index:Int,buf:Byte Ptr,sz:Int)
	Function SteamworksDLCSubscribed:Int(appid:Long)
	Function SteamworksDLCInstalled:Int(appid:Long)
	Function SteamworksGetUserID(buf:Byte Ptr)
	Function CountUserPublishedWorkshopFiles:Int()
	Function GetUserPublishedWorkshopFileAppID(index:Int,buf:Byte Ptr)
	Function GetUserPublishedWorkshopFileID(index:Int,buf:Byte Ptr)
	Function GetUserPublishedWorkshopFileTitle(index:Int,buf:Byte Ptr,sz:Int)
	Function SteamworksUpdateWorkshopItemFile:Int(fileid:Long,filename$z,previewfile$z,comment$z,callback:Byte Ptr)
	Function SteamworksUpdateWorkshopItemPreviewFile:Int(fileid:Long,filename$z)
	Function SteamworksSyncSubscribedFiles(callback:Byte Ptr,downloadgames:Int)
	Function GetWorkshopCachePath_(buf:Byte Ptr,sz:Int)="GetWorkshopCachePath"
	Function SteamworksLoadBank:LEBank(path$z)
	Function SteamworksSaveFile_:Int(path$z, data:Byte Ptr, size:Int)="SteamworksSaveFile"
	Function SteamworksPublishScreenshot:Int(path$z,title$z,desc$z,tags$z,callback:Byte Ptr,result:Byte Ptr)
	Function SteamworksAddScreenshot:Int(path$z,width:Int,height:Int)
	Function SteamworksShutdown()
	Function SteamworksInitialize:Int()
	Function SteamworksPublishVideo:Int(vid$z,title$z,desc$z,buf:Byte Ptr)
	Function SteamworksPublishFile:Int(path$z, title$z, desc$z, previewfile$z, price:Int, callback:Byte Ptr, fileid:Byte Ptr, tags$z, privacy:Int, appid:Long)
EndExtern



Grey Alien(Posted 2015) [#31]
Thanks for posting that. Is that an example list of externs or everything you support? It seems like a pretty comprehensive list but there are a few things that seem missing that could be useful unless I'm misinterpreting things:

For testing:

SteamAPI_ISteamUserStats_ClearAchievement(intptr_t instancePtr, const char * pchName);

OR

SteamAPI_ISteamUserStats_ResetAllStats(intptr_t instancePtr, bool bAchievementsToo);

Also GetStat() and SetStat(). Although admittedly I don't plan to use those at the moment, just thought they might be useful.

Also I don't see a RequestStats() call or a StoreStats() call. Apparently we have to call those before and after setting an achievement or do your SteamworksSetAchievement() and SteamworksSetAchievement() function do that internally?

I read that I'm supposed to call SteamAPI_RunCallbacks() every render frame but I don't see a function in your Externs, so how should I handle that?

Thanks.


Grey Alien(Posted 2015) [#32]
@Skidracer: OK I checked out steamstub.cpp and see it has a couple more useful functions such as ClearSteamAchievement() and also CloseSteam() (which the SDK docs say I should call on game shutdown). Previously I found them by looking at the raw .lib file but I didn't see them in your example code. However, I'm guessing they are easy to add as Externs like this:

Function ClearSteamAchievement(id$z)
Function CloseSteam()


That's probably all I need to be honest. Will test out and report back.

Also is it possible to just import the .cpp file into my BlitzMax project instead of using the .lib? That way I can tweak the .cpp as I need to. Sounds like I have to do that on Mac anyway so I'd prefer it if both were the same, but maybe it's not that simple...


skidracer(Posted 2015) [#33]
Unfortunately no, steamstub needs to be built with visual c stool chain.


Grey Alien(Posted 2015) [#34]
I can confirm that adding those two Externs above worked fine. Also I'm using wrapper functions for all the lib functions I use (that test if Steam is active before calling the lib functions). This also means that I can have a file called SteamDummy.bmx with *empty* wrapper functions that I can include in non-Steam builds (so I only need to change one line of code (the include filename) instead of in many places).

Anyway it's all working now, yay! Just writing some code to save my achievements locally so that I can update their status on Steam if they're out of sync when the game boots.

[EDIT]Done that too and have seen the achievement popup in-game when I run a proper Steam build that I've uploaded via Steampipe.

Everything is working as expected although I'm still seeing this in the Console window when I exit (and I am calling CloseSteam() before exiting) Don't know if it's something I should be worried about:

Setting breakpad minidump AppID = 351090
Steam_SetMinidumpSteamID:  Caching Steam ID:  76561197994138291 [API loaded no]



Pingus(Posted 2015) [#35]
Uff, having a game on Steam seems quite a fight...


Grey Alien(Posted 2015) [#36]
OK so the game launched and in theory it seems to work OK. However, I did get a problem when I was testing my achievements where suddenly sending stuff to Steam was failing (more details in a moment.)

Also one user reported some achievements not unlocking and the Steam overlay disappearing (I guess meaning Steam had crashed). I got them to reboot Steam and the game and then their achievements unlocked but that's because I have special code where my game ALWAYS logs the achievements and stores them in a local file and then when the game boots, it compares them to the Steam achievements and updates any that are out of sync. So I'm glad I coded that safeguard.

So what happened when I got the issue in testing was:

I unlocked two achievements in my game when I beat a level and my debug output looked like this:

SET Steam Achievement:Stars20
SET Steam Achievement:Gold5000
<ReadSteam>:stats stored!
<ReadSteam>:Achievement 'Stars20' unlocked!
<ReadSteam>:stats stored failed 16!

You can see that the second achievement never unlocked and that stats stored failed.

Then as I continued to play this happened when I unlocked a single achievement when I beat a level:

SET Steam Achievement:Gold6000
<ReadSteam>:Achievement 'Gold5000' unlocked!
<ReadSteam>:Achievement 'Gold6000' unlocked!
<ReadSteam>:stats stored failed 16!

So the Gold 5000 unlocked then (and I saw it pop up) but I was still getting the stored stats failed message.

I also got another achievement after this and it unlocked and popped up but I was still getting the stats failed message.

Then I exited and reloaded the game but was getting an error on game boot when retrieving the stats from Steam. I shut down steam and logged on again and then it worked. So definitely something went FUBAR.

Here's my code for setting achievements:
Function SetSteamAchievementWrapper(name:String)
	'Do not call this until ReadSteam has returned "stats received"
	If Not SteamIsActive Then Return
	
	SetSteamAchievement(name)
	StoreSteamStats() 'It seems to work without this but the SDK docs say I need this.
	If STEAM_DEBUG Then		
		Print "SET Steam Achievement:" + name
	EndIf		
End Function


Note how it it calls StoreSteamStats() after setting an achievement. This is because that's what it does in the Steam SDK docs. I thought that was kinda weird, but I'm doing it anyway. Maybe it's wrong? Any ideas?

Also, I call ReadSteam() every frame (after each screen draw). This means that I might set multiple achievements using my code above BEFORE calling ReadSteam. In theory that should be find as ReadSteam just processes the Steam callbacks and reports the status, but maybe there's a problem there?

I actually tested this again and successfully unlocked 2 achievements at once. So it's more like an intermittent problem and I don't know the inner workings of things.

Anyway, just reporting it in case anyone else discovers something similar or has any ideas.


skidracer(Posted 2015) [#37]
Why are you calling StoreSteamStats???

If you had looked at the c++ source in the mac thread you can see that steamstub is already calling this function.

I think what you are doing is having another conversation with the steam network that will generate confirmations that will make no sense whatsoever.

Why are you calling StoreSteamStats? It's not even exposed...