From 21fcc9e19c86cb107c02293bdbd480056169e219 Mon Sep 17 00:00:00 2001 From: Mathew Velasquez Date: Wed, 17 Feb 2016 17:12:21 -0500 Subject: [PATCH] Basic Steamworks API support --- binding-mri/steam-binding.cpp | 109 ++++++++++++++++++++++++++++++++++ mkxp.pro | 34 ++++++++++- src/main.cpp | 19 ++++++ src/sharedstate.cpp | 9 +++ src/sharedstate.h | 6 ++ src/steam.cpp | 77 ++++++++++++++++++++++++ src/steam.h | 65 ++++++++++++++++++++ steam_appid.txt | 1 + 8 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 binding-mri/steam-binding.cpp create mode 100644 src/steam.cpp create mode 100644 src/steam.h create mode 100644 steam_appid.txt diff --git a/binding-mri/steam-binding.cpp b/binding-mri/steam-binding.cpp new file mode 100644 index 0000000..367ee52 --- /dev/null +++ b/binding-mri/steam-binding.cpp @@ -0,0 +1,109 @@ +#ifdef STEAM +#include "steam.h" +#endif +#include "etc.h" +#include "sharedstate.h" +#include "binding-util.h" +#include "binding-types.h" +#include "debugwriter.h" + +#ifdef STEAM +static ID achievementIds[Achievement::MAX]; + +static Achievement::ID toAchievementId(ID id) +{ + /* Crude but effective */ + for (int i = 0; i < Achievement::MAX; ++i) + { + if (achievementIds[i] == id) + return static_cast(i); + } + return Achievement::Invalid; +} +#endif + +RB_METHOD(steamEnabled) +{ + RB_UNUSED_PARAM; + +#ifdef STEAM + return Qtrue; +#else + return Qfalse; +#endif +} + +RB_METHOD(steamUnlock) +{ + RB_UNUSED_PARAM; + + ID rbid; + rb_get_args(argc, argv, "n", &rbid RB_ARG_END); + +#ifdef STEAM + Achievement::ID id = toAchievementId(rbid); + if (id == Achievement::Invalid) + rb_raise(rb_eNameError, "unlocked nonexistent achievement"); + else + shState->steam().unlock(id); +#endif + return Qnil; +} + +RB_METHOD(steamLock) +{ + RB_UNUSED_PARAM; + + ID rbid; + rb_get_args(argc, argv, "n", &rbid RB_ARG_END); + +#ifdef STEAM + Achievement::ID id = toAchievementId(rbid); + if (id == Achievement::Invalid) + rb_raise(rb_eNameError, "locked nonexistent achievement"); + else + shState->steam().lock(id); +#endif + return Qnil; +} + +RB_METHOD(steamUnlocked) +{ + RB_UNUSED_PARAM; + + ID rbid; + rb_get_args(argc, argv, "n", &rbid RB_ARG_END); + +#ifdef STEAM + Achievement::ID id = toAchievementId(rbid); + if (id == Achievement::Invalid) + return Qfalse; + return shState->steam().isUnlocked(id) ? Qtrue : Qfalse; +#else + return Qfalse; +#endif +} + +void steamBindingInit() +{ + VALUE module = rb_define_module("Steam"); + + /* Constants */ +#ifdef STEAM + rb_const_set(module, rb_intern("USER_NAME"), rb_str_new2(shState->steam().userName().c_str())); +#else + rb_const_set(module, rb_intern("USER_NAME"), Qnil); +#endif + + /* Functions */ + _rb_define_module_function(module, "enabled?", steamEnabled); + _rb_define_module_function(module, "unlock", steamUnlock); + _rb_define_module_function(module, "lock", steamLock); + _rb_define_module_function(module, "unlocked?", steamUnlocked); + +#ifdef STEAM + /* Cache achievement ids */ + for (int i = 0; i < Achievement::MAX; ++i) + achievementIds[i] = rb_intern(Achievement::names[i]); +#endif +} diff --git a/mkxp.pro b/mkxp.pro index ccab387..c1f9103 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -14,6 +14,13 @@ isEmpty(BINDING) { BINDING = MRI } +isEmpty(STEAMWORKS) { + STEAMWORKS = /home/mathew/Software/Steamworks +} +isEmpty(STEAMARCH) { + STEAMARCH = 64 +} + contains(BINDING, MRI) { contains(_HAVE_BINDING, YES) { error("Only one binding may be selected") @@ -51,10 +58,16 @@ unix { CONFIG -= app_bundle INCLUDEPATH += /System/Library/Frameworks/OpenAL.framework/Headers LIBS += -framework OpenAL + STEAM { + LIBS += -L$$STEAMWORKS/redistributable_bin/osx32 -lsteam_api + } } !macx: { PKGCONFIG += openal INCLUDEPATH += /usr/include/AL /usr/local/include/AL + STEAM { + LIBS += -L$$STEAMWORKS/redistributable_bin/linux$$STEAMARCH -lsteam_api + } } SHARED_FLUID { @@ -92,6 +105,14 @@ win32 { sdl2 SDL2_image SDL2_ttf openal SDL_sound vorbisfile freetype2 LIBS += -lphysfs -lboost_program_options-mt -lsecur32 + STEAM { + equals(STEAMARCH, 64) { + LIBS += -l$$STEAMWORKS/redistributable_bin/win64/steam_api64.lib + } else { + LIBS += -l$$STEAMWORKS/redistributable_bin/steam_api.lib + } + } + # Deal with boost paths... isEmpty(BOOST_I) { BOOST_I = $$(BOOST_I) @@ -119,6 +140,11 @@ win32 { } } +STEAM { + INCLUDEPATH += $$STEAMWORKS/public + DEFINES += STEAM +} + # Input HEADERS += \ src/quadarray.h \ @@ -225,7 +251,13 @@ SOURCES += \ src/autotilesvx.cpp \ src/midisource.cpp \ src/fluid-fun.cpp \ - src/oneshot.cpp + src/oneshot.cpp \ + binding-mri/steam-binding.cpp + +STEAM { + HEADERS += src/steam.h + SOURCES += src/steam.cpp +} EMBED = \ shader/common.h \ diff --git a/src/main.cpp b/src/main.cpp index ceeeb65..23f2b8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,12 @@ #include "binding.h" #include "icon.png.xxd" + +#ifdef STEAM +#include +#else #include "gamecontrollerdb.txt.xxd" +#endif static void rgssThreadError(RGSSThreadData *rtData, const std::string &msg) @@ -179,6 +184,14 @@ int main(int argc, char *argv[]) return 0; } +#ifdef STEAM + if (!SteamAPI_Init()) + { + showInitError("Could not initialize Steamworks API"); + return 0; + } +#endif + if (!EventThread::allocUserEvents()) { showInitError("Error allocating SDL user events"); @@ -294,10 +307,12 @@ int main(int argc, char *argv[]) RGSSThreadData rtData(&eventThread, argv[0], win, alcDev, mode.refresh_rate, conf); +#ifndef STEAM /* Add controller bindings from embedded controller DB */ SDL_RWops *controllerDB = SDL_RWFromConstMem(assets_gamecontrollerdb_txt, assets_gamecontrollerdb_txt_len); SDL_GameControllerAddMappingsFromRW(controllerDB, 1); +#endif int winW, winH; SDL_GetWindowSize(win, &winW, &winH); @@ -358,5 +373,9 @@ int main(int argc, char *argv[]) IMG_Quit(); SDL_Quit(); +#ifdef STEAM + SteamAPI_Shutdown(); +#endif + return 0; } diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index 8ba1999..c0193dc 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -27,6 +27,9 @@ #include "input.h" #include "audio.h" #include "oneshot.h" +#ifdef STEAM +#include "steam.h" +#endif #include "glstate.h" #include "shader.h" #include "texpool.h" @@ -79,6 +82,9 @@ struct SharedStatePrivate Audio audio; Oneshot oneshot; +#ifdef STEAM + Steam steam; +#endif GLState _glState; @@ -229,6 +235,9 @@ GSATT(Graphics&, graphics) GSATT(Input&, input) GSATT(Audio&, audio) GSATT(Oneshot&, oneshot) +#ifdef STEAM +GSATT(Steam&, steam) +#endif GSATT(GLState&, _glState) GSATT(ShaderSet&, shaders) GSATT(TexPool&, texPool) diff --git a/src/sharedstate.h b/src/sharedstate.h index 3aca9cc..fb128a3 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -43,6 +43,9 @@ class Graphics; class Input; class Audio; class Oneshot; +#ifdef STEAM +class Steam; +#endif class GLState; class TexPool; class Font; @@ -73,6 +76,9 @@ struct SharedState Audio &audio() const; Oneshot &oneshot() const; +#ifdef STEAM + Steam &steam() const; +#endif GLState &_glState() const; diff --git a/src/steam.cpp b/src/steam.cpp new file mode 100644 index 0000000..f69bafb --- /dev/null +++ b/src/steam.cpp @@ -0,0 +1,77 @@ +#include "steam.h" +#include "debugwriter.h" + +#include + +/* Achievements */ +const char *const Achievement::names[Achievement::MAX] = { + "SaveWorld", + "SaveNiko", +}; + +void Achievement::update() +{ + SteamUserStats()->GetAchievement(name, &unlocked); +} + +void Achievement::unlock() +{ + unlocked = true; + SteamUserStats()->SetAchievement(name); + SteamUserStats()->StoreStats(); +} + +void Achievement::lock() +{ + unlocked = false; + SteamUserStats()->ClearAchievement(name); + SteamUserStats()->StoreStats(); +} + +/* SteamPrivate */ +struct SteamPrivate +{ + Achievement achievements[Achievement::MAX]; + + int appid; + std::string userName; + + SteamPrivate() + : appid(SteamUtils()->GetAppID()), + userName(SteamFriends()->GetPersonaName()) + { + for (int i = 0; i < Achievement::MAX; ++i) + achievements[i] = Achievement(Achievement::names[i]); + } +}; + +/* Steam */ +Steam::Steam() + : p(new SteamPrivate()) +{ +} + +Steam::~Steam() +{ + delete p; +} + +const std::string &Steam::userName() const +{ + return p->userName; +} + +void Steam::unlock(Achievement::ID id) +{ + p->achievements[id].unlock(); +} + +void Steam::lock(Achievement::ID id) +{ + p->achievements[id].lock(); +} + +bool Steam::isUnlocked(Achievement::ID id) +{ + return p->achievements[id].isUnlocked(); +} diff --git a/src/steam.h b/src/steam.h new file mode 100644 index 0000000..940df0f --- /dev/null +++ b/src/steam.h @@ -0,0 +1,65 @@ +#ifndef STEAM_H +#define STEAM_H + +#include + +struct SteamPrivate; + +class Achievement +{ +public: + enum ID + { + Invalid = -1, + + SaveWorld = 0, + SaveNiko, + + MAX + }; + + static const char *const names[MAX]; + + void unlock(); + void lock(); + bool isUnlocked() { return unlocked; } + +private: + const char *name; + bool unlocked; + + friend struct SteamPrivate; + + Achievement() + : name(0), + unlocked(false) + {} + Achievement(const char *name) + : name(name), + unlocked(false) + { + update(); + } + + void update(); +}; + +class Steam +{ +public: + void unlock(Achievement::ID id); + void lock(Achievement::ID id); + bool isUnlocked(Achievement::ID id); + + const std::string &userName() const; + +private: + Steam(); + ~Steam(); + + friend struct SharedStatePrivate; + + SteamPrivate *p; +}; + +#endif // STEAM_H diff --git a/steam_appid.txt b/steam_appid.txt new file mode 100644 index 0000000..6e91fb8 --- /dev/null +++ b/steam_appid.txt @@ -0,0 +1 @@ +420530