SteamWorks Plugin README
This is a plugin to integrate the Steamworks SDK into the Shiva 3D game engine. Right now this is built with:
- Steamworks 1.44
- Shiva 2.0 DR FEB-2019
This plugin was started as a joint effort between Shaderman and error454. The last error454 version was 1.29. Current store packages are compiled and maintained by broozar. DLC addition by Tuomas Karmakallio. Steam Controller addition by Felix Caffier.
- Add the file steam_appid.txt to the folder where your shiva editor executable is. In the file, put your steam app ID, or 480 if you want to run in test mode. 480 is the appID of "Spacewar", the Steam SDK test project.
- Add the Steam AI as a user AI.
- Add the Steam plugin to your project.
- Edit the
statsInitfunction and insert all of the stat names/types for your game.
- Edit the
achievementsInitfunction and add all your achievement identifiers.
If you want to run in debug mode, set the
bDebug variable to true.
When the Steam SDK initializes, it automatically grabs the stats and achievements for the current user. When those are available,
onUserStatsReceived will be called. This callback will only be called once, after which you can interact with Stats and Achievements.
Steamworks.GetStat to get stat details. See
debugPrintStats for an example of how to use these.
Use the handlers:
onStatIncrement ( sStatID, nDelta, nTime ) onStatSetMax ( sStatID, nValue )
Steamworks.GetAchievement* to get achievement information. See
debugPrintStats for an example of how to use these.
Use the handler:
onAchievementUnlock ( sAchievementID )
When you increment stats and unlock achievements, the changes aren't committed to the Steam servers until you call the handler:
onStatStore ( )
It's recommended to call this handler periodically. For instance, if you want users to immediately see when an achievement has been unlocked you should call
onStatStore after unlocking the achievement.
To get leaderboard entries, call:
onDownloadScores ( sAI, sHandler, sLeaderboardName, kLeaderboardRequest, start, nEnd )
You'll receive a callback to
sHandler when the scores are available. The parameters passed to the callback are:
nResult, nTotalResults, nGlobalRank, nScore, sUser
To submit a highscore to a leaderboard, call:
onSubmitHighscore ( sLeaderboardName, nScore )
If you are implementing Steam Workshop, you'll need to do some additional configuration:
Create any required folders in
Steamworks.MakeDir ( system.getDocumentsDirectory ( ) .. "/RageRunner" ) Steamworks.MakeDir ( system.getDocumentsDirectory ( ) .. "/RageRunner/Mine" ) Steamworks.MakeDir ( system.getDocumentsDirectory ( ) .. "/RageRunner/UGC" )
Running->onEnterset your UGC download location and initiate the first workshop query.
-- Where the UGC downloads too, note that this was created in onInit Steamworks.SetUGCDownloadLocation ( system.getDocumentsDirectory ( ) .. "/RageRunner/UGC/" )
-- Init the Workshop Query variables this.nLastWorkshopQuery ( Steamworks.kWorkshopQuerySubscribed ) this.nLastWorkshopSort ( Steamworks.kWorkshopListSortSubscriptionDateDesc ) this.nLastQueryPage ( 1 ) this.bForceDownloadAfterQuery ( true )
-- Query the workshop content, this will download any subscribed workshop items Steamworks.QueryWorkshopUserContent ( this.nLastWorkshopQuery ( ), this.nLastWorkshopSort ( ), this.sAppID ( ), this.sAppID ( ), this.nLastQueryPage ( ) )
The remaining workshop stuff, well, I'm too lazy to document it right now, it works similarly to the leaderboards and is fully implemented.
- in the Steam.dlcInit ( ) Lua function, declare your DLC:
htAdd ( ht, "MyDlcName", 123456 ) -- where 123456 is the Dlc Id
- check for Dlc by calling :
user.sendEventImmediate ( hUser, "Steam", "onDlcItemConfirmation", "123456" )
- optionally, you can pass an environment variable, and the function will save the boolean value to it
user.sendEventImmediate ( hUser, "Steam", "onDlcItemConfirmation", "123456", "my_boolean_env_variable" )
Note that a user may switch Dlc on and off in their game control panel in Steam, so the status should always be checked on launch.
Steam Controller Setup
- Read https://partner.steamgames.com/documentation/steamcontroller
- Create an In-Game Actions file according to above in-game specifications, or use the game_actions_example.vdf Portal 2 example file. -- Important: If you want to test it first with the default demo game "SpaceWar" (ID 480), do NOT make a game_actions_480.vdf.
- Steam tells you to make a Steam shortcut with "-forcecontrollerappid <your game's AppID>" (example: "-forcecontrollerappid 480" for the "Spacewar" demo game), but I found that quite pointless, so don't do it.
- If you want to test with the SpaceWar game, here are the strings you need to know: ActionSets "menu_controls,ship_controls", Analog "analog_controls", Digital "turn_left,turn_right,forward_thrust,backward_thrust,fire_lasers,pause_menu,menu_up,menu_down,menu_left,menu_right,menu_select,menu_cancel"
- Assigning buttons to your game actions has to be done from Steam Big Picture Mode. There is no other way. Valve says: "Run Steam in Big Picture mode, and navigate to your game's Game Details page. Select Manage Game, and then Configure Controller. If you receive any errors at this point, they'll be identifying issues in your IGA file and you'll need to go fix them."
- "If you don't receive any errors, you're now looking at an empty controller configuration for your game, and it should be fully aware of your in-game actions. Use the UI to create a default configuration. Make sure you set defaults for all your In-Game Action Sets, not just the first one.""
- "Once you've got a configuration, save it privately. Don't publish it, because your game is not yet ready to receive IGAs."
- If you just want to run the SpaceWar test game, run Big Picture Mode, go to the Game Details page, configure your controls there, then close Big Picture Mode again, go to ShiVa and you should be able to use the setup you just configured.
Feel free to use the steamcon_ icons in the project, they are just renamed versions of the "Official Controller Glyphs" from steam/partner/controller/SteamControllerGlyphs_v1.zip
- Everything Steam Controller related has the prefix "Scon", short for "Steam Controller", somewhere in the name.
- Add your digital, analog and action set strings to the Steam.scon_* AIModel member variables, or open the function Steam.initSCon() and add them there if you like text editing better.
Using the Steam Controller in ShiVa
- All connected controllers will be written automatically into the table Steam.tSconIDs. It will be updated every frame and deletes/adds (dis)connected controllers for you.
- Before you can receive any input, you must tell a controller which actionSet you are using, for instance:
Steamworks.sconSetActionSet( table.getFirst (this.tSconIDs ( )), "ship_controls" )
Only call this one after you can be sure that the controller has been initialized, e.g. at the beginning of your main menu level, NOT in the init() function as the controller needs a couple of frames to register.
- You can now receive Digital and Analog actions from the controller. There are 3 new handlers for that:
Steam.onSconDigitalActionOn ( sControllerID, sAction, sActionSet, kSource ) Steam.onSconDigitalActionOff ( sControllerID, sAction, sActionSet, kSource ) Steam.onSconAnalogActionContinuous ( sControllerID, sAction, sActionSet, nX, nY, kSource, kSourceMode )
- DigitalActionOn and DigitalActionOff behave very much like onJoytickButtonUp/Down - thesy are events that only fire when something happens. sControllerID is the string representation of the UINT64 controller ID that is stored in tSconIDs which can be used to identify controllers. sAction and sActionSet are the action name/actionset name that belong to the event tat fired.
- kSource is a number that can be interpreted by the table tSconActionOrigins, for instance:
table.getAt ( this.tSconActionOrigins ( ), kSource )
- AnalogActionContinuous fires every frame no matter what, and thus behaves more like a regular function. nX and nY are the numbers that come back from e.g. the right touchpad. However, those numbers are also highly dependent on kSourceMode:
- kSourceMode is a number that tells you where the analog action originated from and how nX and nY have to be handled. kSourceMode can be interpreted by the table this.tSconSourceModes, for instance:
table.getAt ( this.tSconSourceModes ( ), kSourceMode )
- All buttons and analog inputs furthermore have a constant equivalent, for instance:
Steamworks.kSconButtonA Steamworks.kSconButtonB Steamworks.kSconButtonX Steamworks.kSconButtonY
- All source modes furthermore have a constant equivalent, for instance:
###Other useful functions:###
- sconGetConnectedControllers -- returns the number of currently connected controllers
- sconGetAnalogActionOrigin -- returns the kOrigin constant of a given analog action
- sconGetDigitalActionOrigin -- returns the kOrigin constant of a given digital action
- sconGetActionSet -- returns the current action set name for a given controller
- sconShowBindingPanel -- accesses the binding config panel for a given controller, only works in Big Picture Mode
- sconPulse -- sends a haptic feedback pulse to a given controller pad. Durations under 200 are barely noticable, durations over 800 do not seem to make the effect any more pronounced. Try 500, like so:
Steamworks.sconPulse( table.getFirst ( this.tSconIDs ( ) ), Steamworks.kSconRightPad, 500 )
Misc Functions and Callbacks
onGameOverlayActivated ( bActivated )
Called when the steam overlay is activated/deactivated. It's a good idea to pause/unpause your game here.
onOpenItemInBrowser ( sItem )
Will open a steam item ID in the built-in steam browser.
If you need to know whether the Steam overlay is active you can call:
Steam.overlayActive ( )
Building and Distribution
If you plan to re-build or fork the plugin, please consult Docs\BUILD.TXT
Linux Distribution: After game is exported, open the "data" folder of the exported game and copy Plugins\com.shaderman.steamworks\Frameworks\Linux\x86_64\lib\libsteam_api.so into the folder.
Steam AppID: You need to create a file named "steam_appid.txt", containing only the number of your game's steam app ID (example game: 480), in the data folder.