Main forum > GLBasic - en

Steam API for GLBasic -> achievements, leaderboards for Your game ;-)

<< < (2/4) > >>

spacefractal:

--- Quote from: dreamerman on 2019-Apr-03 ---Yeah, I wasn't sure but I though that Genius Greedy Mouse had some kind of 'achievements'.. I know that game should defend itself by quality, but now even small games have achievements, and some users just skip games without them with filters or just thinking that game isn't finished or due other reasons.
That's really nice to hear that such integration (into GLB source code) should be now easier to implement. As ability to call Steam API in such simple way (at least those basic things like stats, achievements, maybe leaderboards) would be awesome and that could be another great feature to list on Steam product page.

--- End quote ---

When im did that game for some years ago, the glbasic compilers used was too old, so not include 'achievements' into source code. So im skipped it, but the game was also releaseed when its was easier to use trading cards (which im today kindy as, due there is too much abuse). So no im diddent include the sdk at all, but its should been possible today since compilers for the steam version is updated. Im did do support cloud support as its did not require to use SDK at all.

dreamerman:
Hello, so much time passed, and I'm curious if anyone played with Steam SDK during that time. Because of that I'm bumping this topic, but that's not only reason.
Lately I was checking whats changed with this, one thing updating GCC used by GLBasic helped with some stuff, another thing that I noticed is very important update in Steam SDK - one of previous problems was Callback system, now Valve allows to use 'manual dispatch API' that lets to omit callback's completely. Still there are other issues, so shorting a long story: GLB uses GCC compiler, Steam_api.dll (and internal steam_client.dll) is compiled with MSVC this may cause some problems. I really don't like such messing with C++, all those standards, conventions, pointers, eh..
So really narrowing it down, thanks to all above we can include Steam API headers in GLB projects like this:


--- Code: (glbasic) ---// some Steam SDK playing around

// put 'Steam_SDK\redistributable_bin\steam_api.lib' to '\public' and 'steam_api.dll' to project .app folder where exe is
// add those commands to project options
// cmp -I"your_Steam_SDK_path\public\steam"
// lnk -L"your_Steam_SDK_path\public" -lsteam_api

// put below stuff in separate file
// in main source file just call Steam_Test()

INLINE
// why not to add this
//#include "iostream" // this is causing some errors now, in previous Steam SDK it was needed?
#include "stdio.h"
#include "stdint.h"
#include "assert.h"

// trick to add VS style nullptr to older GCC versions
// Scott Meyers. More Effective C++ 1996
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf
const                        // this is a const object...
class {
public:
  template<class T>          // convertible to any type
    operator T*() const      // of null non-member
    { return 0; }            // pointer...
  template<class C, class T> // or any type of null
    operator T C::*() const  // member pointer...
    { return 0; }
private:
  void operator&() const;    // whose address can't be taken
} nullptr = {};

// current SteamSDK uses 'static_assert' macros in some functions
// trick to add static_assert support on older C/C++ standards (as GLB core wont compile properly on C++11)
// Joseph Quinsey : https://stackoverflow.com/questions/54357949/static-assert-for-c90-on-gcc
#define TOKENPASTE(a, b) a ## b // "##" is the "Token Pasting Operator"
#define TOKENPASTE2(a,b) TOKENPASTE(a, b) // expand then paste
#define static_assert(x, msg) enum { TOKENPASTE2(ASSERT_line_,__LINE__) = 1 / (msg && (x)) }


// and here goes Steam stuff
#include "steam_api.h"
// yeap that's all, now you can use it ;-)


//CSteamID your_uid; // some dummy test
ENDINLINE



FUNCTION Steam_Test%:
LOCAL init_ok%, user_lvl%, app_id%, owner_uid%, your_uid%, friends_count%
LOCAL your_name$, some_name$, test_str$
INLINE
init_ok = SteamAPI_Init();
user_lvl = SteamUser()->GetPlayerSteamLevel();
your_name_Str = SteamFriends()->GetPersonaName();
app_id = SteamUtils()->GetAppID(); // this will give GLBasic appid, as app runs from it, using it from standalone exe will give proper ID
CSteamID steamID;
AccountID_t taccountid;
steamID = SteamApps()->GetAppOwner(); // get CSteamID struct
owner_uid = steamID.ConvertToUint64(); // this should be Your SteamID3 ?? but for some reason it isn't or maybe I'm missing something
//taccountid = steamID.GetAccountID(); // unsigned int
//owner_uid = int(taccountid); // this also doesn't give proper Steam ID

// problems starts here
steamID = SteamUser()->GetSteamID(); // sometime this causes Access Violation Exception :-((
//taccountid = steamID.GetAccountID();
//your_uid = taccountid;
your_uid = steamID.ConvertToUint64(); // this should give your proper SteamID3
// both owner_uid and your_uid should be same, yet they differs, maybe some binary Flags are added that's not mentioned in docs

// even more mess, this is copied from official doc
int nFriends = SteamFriends()->GetFriendCount( k_EFriendFlagImmediate );
if ( nFriends == -1) nFriends = 0;
friends_count = nFriends; // this works
for ( int i = 0; i < nFriends; ++i )
{
CSteamID friendSteamID;
//friendSteamID = SteamFriends()->GetFriendByIndex( i, k_EFriendFlagImmediate ); // this will cause Access Violation Exception
//some_name_Str = SteamFriends()->GetFriendPersonaName( friendSteamID );
}


// end using Steam API
SteamAPI_Shutdown();
ENDINLINE
DEBUG "proper init: " + init_ok% + ", your lvl: " + (user_lvl%) + ", you name: '" + (your_name$) + "'"
DEBUG ", You have: " + (friends_count%) + " friends on Steam, some name: '" + some_name$ + "', your_id: " + your_uid%
DEBUG ", app_id: " + app_id% + ", owner_id: " + owner_uid% + "\n"
// if that debug was in one line then some variables get mesed up - like user_lvl%

ENDFUNCTION

--- End code ---

Remember to put steam_api.lib & .dll to proper directories, above code is another concept how it could look when current issues would be resolved.
Using some stuff causes 'Access Violation Exceptions', some other things doesn't work properly from what I see and I'm not sure why, I'm just C++ peasant ;-)
With this method one important thing matters, You need to use Steam GLB version (>16) as it has newer GCC compiler. Probably some tricks could help with above mentioned problems, but it's just nah.. for me :/

Method showed in my first post - using Steam API functions through 'DECLARE' is still a solution, it's working with older GLB versions, it has some other issues but if achievements stuff would work it would be fine for me..


EDIT: my initial post - just to keep it in history:

I'm curious if anyone tried to make use of Steam API, for achievements, leaderboards or whatever.
As during fast looking at GLBasic made games on Steam I didn't see any project that support those features. Most notable would be support for achievements.

Some basic stuff can be done with INLINE, using DECLARE to call steam_api.dll functions.
main.gbas

--- Code: (glbasic) ---DEBUG "False: " + FALSE + ", True: " + TRUE + "\n"
DEBUG "Steam init: " + SAPI_SteamAPI_init() + "\n"
SAPI_SteamAPI_Shutdown()
--- End code ---
steam_min.gbas

--- Code: (glbasic) ---INLINE
        }
                extern "C" {
                        #include <cstdio>
                       
                       
                }
        namespace __GLBASIC__ {
       
typedef bool _Bool;
typedef unsigned char uint8;
typedef signed char int8;
typedef short int16;
typedef unsigned short uint16;
typedef int int32;

const char* STEAMUSER_INTERFACE_VERSION = "SteamUser019";
       
typedef int32 HSteamPipe;
typedef int32 HSteamUser;
typedef int32 ISteamUser;


        DECLARE(SteamAPI_Init, "steam_api.dll", (), bool);
DECLARE(SteamAPI_Shutdown, "steam_api.dll", (), void);
DECLARE(SteamClient, "steam_api.dll", (), intptr_t);

        DECLARE(SteamAPI_ISteamClient_CreateSteamPipe, "steam_api.dll", (intptr_t), HSteamPipe);
        DECLARE(SteamAPI_ISteamClient_BReleaseSteamPipe, "steam_api.dll", (intptr_t, HSteamPipe), bool);
        DECLARE(SteamAPI_ISteamClient_ConnectToGlobalUser, "steam_api.dll", (intptr_t, HSteamPipe), HSteamUser);
DECLARE(SteamAPI_ISteamClient_GetISteamUser, "steam_api.dll", (intptr_t, HSteamUser, HSteamPipe, const char *), intptr_t);

DECLARE(SteamAPI_ISteamUser_GetPlayerSteamLevel, "steam_api.dll", (intptr_t), int);

ENDINLINE


FUNCTION SAPI_SteamAPI_init%:
LOCAL ret%, user_lvl%
INLINE
ret = SteamAPI_Init();
intptr_t sclient;
        sclient = SteamClient();
        HSteamPipe hsteampipe;
HSteamUser hsteamuser;
hsteampipe = SteamAPI_ISteamClient_CreateSteamPipe(sclient);
hsteamuser = SteamAPI_ISteamClient_ConnectToGlobalUser(sclient, hsteampipe);

intptr_t isteamuser;
isteamuser = SteamAPI_ISteamClient_GetISteamUser(sclient, hsteamuser, hsteampipe, STEAMUSER_INTERFACE_VERSION);;

user_lvl = SteamAPI_ISteamUser_GetPlayerSteamLevel(isteamuser);
SteamAPI_ISteamClient_BReleaseSteamPipe(sclient, hsteampipe);

ENDINLINE
DEBUG "your lvl: " + user_lvl% + "\n"
RETURN ret%
ENDFUNCTION

FUNCTION SAPI_SteamAPI_Shutdown%:
INLINE
SteamAPI_Shutdown();
ENDINLINE
ENDFUNCTION
--- End code ---
Put 'steam_api.dll' to project exe folder, and create 'steam_appid.txt' containing your app_id to make this work. And result is that app will print in Debug Your (currently logged in) Steam Level (that from crafting game badges)..

Only issue with this, I have no idea how to make callback's from those functions (yet from what I saw, some other language ports just ignore this problem), many of them works asynchronously, and after completed they invoke/call some callback function, like this, you request for user stats, when Steam internal function get current stats, it callback to some function that normally is in project source. Can this be done with EXPORT, or are some other tricks to do this.
Including whole Steam API with 'steam_api_flat.h' header also can be some solution but proper including/linking is currently out of my scope :D Help of some C++ expert is needed here :P

dreamerman:
In order to keep all things in one thread and keep it clean I will edit my first post with latest info, for archive reasons I've copied my initial post to my previous response.

And here is todays update.
As I'm getting closer with larger update to my Steam game, I was looking on few things that were hard to implement earlier, one of them were Steam Achievements.
Making it short, there are two ways to use Steam API: a) including it as C++ headers and using directly in inline or b) calling functions that are exported from dll.
a) may be more clean looking, yet it needs latest GLB version, and I still have some issues with either wrong calling convention, pointers or whatever.
b) could be more limited as probably not all functions can be used this way, but still it's a good solution.
After trying to get a) work I turned into option b) and.. Ehm it was supposed to be short ;-) Soo.. I made some help functions and here it is, an 'easy' way to add Steam Achievements to Your game.
Yeah, it isn't clean code, not bullet-proof, and have some issues and it's under constant development so it will fit my needs. Most important thing is that, user stats and achievements are working with it, tested on live app.

Take note, that if You will run project from Steam version GLBasic editor it will inherit its app_id, so You want be able to test it properly, not sure if putting 'steam_appid.txt' into project exe path should fix that, but You may need to either grab STDOUT to file or use alternative editor/older version.
Also read this: https://partner.steamgames.com/doc/features/achievements
Some example template to give Your an idea how to use it:


--- Code: (glbasic) ---GLOBAL mySteam AS stats_manager_object, i1%
mySteam.Init(1)      // after Init this will also request FOR current stats from Steam
i1% = mySteam.addAchievement("achiev_10_games_played")
mySteam.addUserStat("games_played", 10, 0, 0, i1%)
i1% = mySteam.addAchievement("achiev_10000_enemies_destroyed")
mySteam.addUserStat("enemies_destroyed", 10000, 0, 1, i1%)   // frequently changed value
// requestCurrentStats() was called internally in Init()
SETLOOPSUB "GLB_ON_LOOP" // set main loop FOR app
END   // exit app

// GLB_ON LOOPS
SUB GLB_ON_LOOP:
  mySteam.RunFrame()
   // after game played
   IF (match_finished) THEN mySteam.updateUserStat(-1, "games_played", 1, 1)   // increment by 1
   // OR you can do it manually
   IF (enemy_destroyed)
     INC mySteam.userstat_list[1].value_curr_int%
   ENDIF
   // IF You changed value manually THEN update it once per second - when calculation FPS
   IF last_fps_time - GETTIMERALL() >= 1000
     mySteam.updateUserStat(1, "", 0, 0) // update destroyed enemy counter
     // call only IF an achievement was unlocked - TO show it
     IF mySteam.status.achievement_was_unlocked% = 1 THEN mySteam.storeUserStats(0)
   ENDIF 
  IF KEY(01) THEN END // IF escape KEY THEN END app
ENDSUB
called on pausing AND before app exit
SUB GLB_ON_PAUSE:
// you need TO have them both AS RunFrame will call them when user open overlay
ENDSUB
// called on restoring app
SUB GLB_ON_RESUME:
ENDSUB
// called before app axit
SUB GLB_ON_QUIT:
  mySteam.Shoutdown()
ENDSUB
--- End code ---


ps. download link in first post.
ps2. I rushed this a little just to post it today.

spacefractal:
I'd want me to clean up posts let me know.

MrPlow:
Wow! Sounds really promising...steam api functionality!!

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version