Skip to content

Instantly share code, notes, and snippets.

@silbinarywolf
Created September 22, 2019 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save silbinarywolf/18bd9bdcb5e9a113d6661eea17143ac4 to your computer and use it in GitHub Desktop.
Save silbinarywolf/18bd9bdcb5e9a113d6661eea17143ac4 to your computer and use it in GitHub Desktop.
// +build windows
package steamworks
import (
"log"
"syscall"
"unsafe"
)
var (
steamClientInterfaceVersion = []byte("SteamClient017\x00")
steamUserStatsInterfaceVersion = []byte("STEAMUSERSTATS_INTERFACE_VERSION011\x00")
)
var (
steamUser int32
steamPipe int32
steamClient uintptr
steamUserStats uintptr
)
var (
// DLL
steamApi syscall.Handle
// GetProcAddress
isSteamRunning uintptr
runCallbacks uintptr
steamInternal_CreateInterface uintptr
iSteamUserStats_SetAchievement uintptr
iSteamUserStats_ClearAchievement uintptr
)
func lazyInit() {
if steamApi == 0 {
steamApi, _ = syscall.LoadLibrary("steam_api64.dll")
isSteamRunning, _ = syscall.GetProcAddress(steamApi, "SteamAPI_IsSteamRunning")
runCallbacks, _ = syscall.GetProcAddress(steamApi, "SteamAPI_RunCallbacks")
steamInternal_CreateInterface, _ = syscall.GetProcAddress(steamApi, "SteamInternal_CreateInterface")
iSteamUserStats_SetAchievement, _ = syscall.GetProcAddress(steamApi, "SteamAPI_ISteamUserStats_SetAchievement")
iSteamUserStats_ClearAchievement, _ = syscall.GetProcAddress(steamApi, "SteamAPI_ISteamUserStats_ClearAchievement")
}
}
func IsSteamRunning() bool {
ret, _, callErr := syscall.Syscall(uintptr(isSteamRunning), 0, 0, 0, 0)
if callErr != 0 {
log.Fatalln(callErr.Error())
}
return int(ret) == 1
}
func RunCallbacks() error {
if _, _, callErr := syscall.Syscall(uintptr(runCallbacks), 0, 0, 0, 0); callErr != 0 {
return callErr
}
return nil
}
func RestartAppIfNecessary(appId uint32) bool {
lazyInit()
restartAppIfNecessary, _ := syscall.GetProcAddress(steamApi, "SteamAPI_RestartAppIfNecessary")
const nargs = 1
ret, _, callErr := syscall.Syscall6(
uintptr(restartAppIfNecessary),
nargs,
uintptr(appId),
0,
0,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln(callErr)
}
return ret == 1
}
func Init() bool {
// TODO(Jake): 2019-09-22
// Add guards to prevent calling more than once
lazyInit()
steamApiInit, _ := syscall.GetProcAddress(steamApi, "SteamAPI_Init")
const nargs = 0
ret, _, callErr := syscall.Syscall(
uintptr(steamApiInit),
nargs,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln(callErr)
}
switch ret {
case 0:
return false
case 1:
// ignore and continue loading
default:
log.Fatalf("Invalid return code: %v\n", ret)
}
// Initialize
steamUser = getHSteamUser()
steamPipe = getHSteamPipe()
steamClient = createInterface(steamClientInterfaceVersion)
steamUserStats = getISteamUserStats(steamClient, steamUser, steamPipe, steamUserStatsInterfaceVersion)
return true
}
func SetAchievement(name string) bool {
nameNullTerminated := []byte(name + "\x00")
const nargs = 2
ret, _, callErr := syscall.Syscall6(
iSteamUserStats_SetAchievement,
nargs,
steamUserStats,
uintptr(unsafe.Pointer(&nameNullTerminated[0])),
0,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln("SetAchievement: " + callErr.Error())
}
switch ret {
case 0:
return false
case 1:
return true
default:
// This can occur if "steamUserStats" is invalid
log.Fatalf("SetAchievement: Bad return value: %v\n", ret)
}
return false
}
func ClearAchievement(name string) bool {
nameNullTerminated := []byte(name + "\x00")
const nargs = 2
ret, _, callErr := syscall.Syscall6(
iSteamUserStats_ClearAchievement,
nargs,
steamUserStats,
uintptr(unsafe.Pointer(&nameNullTerminated[0])),
0,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln("ClearAchievement: " + callErr.Error())
}
switch ret {
case 0:
return false
case 1:
return true
default:
// This can occur if "steamUserStats" is invalid
log.Fatalf("ClearAchievement: Bad return value: %v\n", ret)
}
return false
}
func createInterface(name []byte) uintptr {
const nargs = 1
res, _, callErr := syscall.Syscall(
steamInternal_CreateInterface,
nargs,
uintptr(unsafe.Pointer(&name[0])),
0,
0,
)
if callErr != 0 {
log.Fatalln("createInterface: " + callErr.Error())
}
return res
}
// getHSteamUser number, starting at 1
func getHSteamUser() int32 {
steamAPI_GetHSteamUser, _ := syscall.GetProcAddress(steamApi, "SteamAPI_GetHSteamUser")
const nargs = 0
ret, _, callErr := syscall.Syscall(
steamAPI_GetHSteamUser,
nargs,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln("getHSteamUser: " + callErr.Error())
}
return int32(ret)
}
// getHSteamPipe number, starting at 1
func getHSteamPipe() int32 {
steamAPI_GetHSteamPipe, _ := syscall.GetProcAddress(steamApi, "SteamAPI_GetHSteamPipe")
const nargs = 0
ret, _, callErr := syscall.Syscall6(
steamAPI_GetHSteamPipe,
nargs,
0,
0,
0,
0,
0,
0,
)
if callErr != 0 {
log.Fatalln("getHSteamPipe: " + callErr.Error())
}
return int32(ret)
}
func getISteamUserStats(instancePtr uintptr, steamUser int32, steamPipe int32, version []byte) uintptr {
steamAPI_ISteamClient_GetISteamUserStats, _ := syscall.GetProcAddress(steamApi, "SteamAPI_ISteamClient_GetISteamUserStats")
const nargs = 4
ret, _, callErr := syscall.Syscall6(
steamAPI_ISteamClient_GetISteamUserStats,
nargs,
instancePtr,
uintptr(steamUser),
uintptr(steamPipe),
uintptr(unsafe.Pointer(&version[0])),
0,
0,
)
if callErr != 0 {
log.Fatalln("getHSteamPipe: " + callErr.Error())
}
return ret
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment