2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 * All trademarks are property of their respective owners
10 #include "vg/vg_steam.h"
11 #include "vg/vg_steam_utils.h"
12 #include "vg/vg_steam_networking.h"
13 #include "vg/vg_steam_auth.h"
14 #include "vg/vg_steam_http.h"
15 #include "vg/vg_steam_friends.h"
16 #include "vg/vg_steam_user_stats.h"
17 #include "submodules/anyascii/impl/c/anyascii.c"
20 * We only want to use steamworks if building for the networked version,
21 * theres not much point otherwise. We mainly want steamworks for setting
22 * achievements etc.. so that includes our own server too.
24 * This file also wraps the functions and interfaces that we want to use to
25 * make them a bit easier to read, since they are the flat API they have very
26 * long names. in non-networked builds they will return default errors or do
30 static char steam_username_at_startup
[128] = "Unassigned";
32 static void recv_steam_warning( int severity
, const char *msg
)
35 vg_low( "%s\n", msg
);
37 vg_info( "%s\n", msg
);
40 static int steam_ready
= 0,
41 steam_stats_ready
= 0;
43 static void *hSteamNetworkingSockets
,
46 static ISteamUserStats
*hSteamUserStats
;
47 static HSteamPipe hSteamClientPipe
;
49 static const char *steam_achievement_names
[] =
51 "ALBERT", "MARC", "JANET", "BERNADETTA",
52 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
53 "ROUTE_TO", "ROUTE_TC", "CITY_COMPLETE", "MTZERO_SILVER", "MTZERO_GOLD",
57 static void steam_store_achievements(void)
59 if( steam_ready
&& steam_stats_ready
){
60 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats
);
64 static void update_ach_models(void);
65 static void steam_set_achievement( const char *name
){
66 if( skaterift
.demo_mode
)
70 if( !strcmp(name
,"MARC") ) skaterift
.achievements
|= 0x1;
71 if( !strcmp(name
,"ALBERT") ) skaterift
.achievements
|= 0x2;
72 if( !strcmp(name
,"JANET") ) skaterift
.achievements
|= 0x4;
73 if( !strcmp(name
,"BERNADETTA") ) skaterift
.achievements
|= 0x8;
76 if( steam_ready
&& steam_stats_ready
){
77 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats
, name
) ){
78 vg_success( "Achievement set! '%s'\n", name
);
82 vg_warn( "Failed to set achievement: %s\n", name
);
86 vg_warn( "Failed to set achievement (steam not ready): %s\n", name
);
90 static void steam_clear_achievement( const char *name
)
92 if( steam_ready
&& steam_stats_ready
){
93 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats
, name
) ){
94 vg_info( "Achievement cleared: '%s'\n", name
);
97 vg_warn( "Failed to clear achievement: %s\n", name
);
101 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name
);
106 static void steam_print_all_achievements(void){
107 vg_info( "Achievements: \n" );
109 if( steam_ready
&& steam_stats_ready
){
110 for( int i
=0; i
<vg_list_size(steam_achievement_names
); i
++ ){
111 steamapi_bool set
= 0;
112 const char *name
= steam_achievement_names
[i
];
114 if( SteamAPI_ISteamUserStats_GetAchievement(
115 hSteamUserStats
, name
, &set
) )
117 vg_info( " %s %s\n", (set
? "[YES]": "[ ]"), name
);
120 vg_warn( " Error while fetching achievement status '%s'\n", name
);
125 vg_warn( " Steam is not initialized, no results\n" );
129 static int steam_achievement_ccmd( int argc
, char const *argv
[] )
131 if( !(steam_ready
&& steam_stats_ready
) ) return 1;
134 if( !strcmp( argv
[0], "list" ) ){
135 steam_print_all_achievements();
138 else if( !strcmp( argv
[0], "clearall" )){
139 for( int i
=0; i
<vg_list_size(steam_achievement_names
); i
++ )
140 steam_clear_achievement( steam_achievement_names
[i
] );
142 steam_store_achievements();
147 if( !strcmp( argv
[0], "set" ) ){
148 steam_set_achievement( argv
[1] );
149 steam_store_achievements();
152 else if( strcmp( argv
[0], "clear" ) ){
153 steam_clear_achievement( argv
[1] );
154 steam_store_achievements();
162 static void steam_on_recieve_current_stats( CallbackMsg_t
*msg
)
164 UserStatsReceived_t
*rec
= (UserStatsReceived_t
*)msg
->m_pubParam
;
166 if( rec
->m_eResult
== k_EResultOK
){
167 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec
->m_nGameID
,
168 rec
->m_steamIDUser
);
169 steam_stats_ready
= 1;
171 steamapi_bool set
= 0;
172 if( SteamAPI_ISteamUserStats_GetAchievement(
173 hSteamUserStats
, "MARC", &set
) ){
174 if( set
) skaterift
.achievements
|= 0x1;
176 if( SteamAPI_ISteamUserStats_GetAchievement(
177 hSteamUserStats
, "ALBERT", &set
) ){
178 if( set
) skaterift
.achievements
|= 0x2;
180 if( SteamAPI_ISteamUserStats_GetAchievement(
181 hSteamUserStats
, "JANET", &set
) ){
182 if( set
) skaterift
.achievements
|= 0x4;
184 if( SteamAPI_ISteamUserStats_GetAchievement(
185 hSteamUserStats
, "BERNADETTA", &set
) ){
186 if( set
) skaterift
.achievements
|= 0x8;
191 vg_error( "Error recieveing stats for user (%u)\n", rec
->m_eResult
);
195 static u32
utf8_byte0_byte_count( u8 char0
)
197 for( u32 k
=2; k
<4; k
++ ){
198 if( !(char0
& (0x80 >> k
)) )
205 static u32
str_utf8_collapse( const char *str
, char *buf
, u32 length
){
206 u8
*ustr
= (u8
*)str
;
207 u32 utf32_code
= 0x00000000;
208 u32 i
=0, j
=0, utf32_byte_ct
=0;
211 if( ustr
[i
] == 0x00 )
214 if( ustr
[i
] & 0x80 ){
217 utf32_code
|= (ustr
[i
] & 0x3F) << (utf32_byte_ct
*6);
219 if( !utf32_byte_ct
){
221 size_t chars
= anyascii( utf32_code
, &match
);
223 for( u32 k
=0; k
<VG_MIN(chars
, length
-1-j
); k
++ ){
224 buf
[ j
++ ] = (u8
)match
[k
];
229 utf32_byte_ct
= utf8_byte0_byte_count( ustr
[i
] )-1;
230 utf32_code
= ustr
[i
] & (0x3F >> utf32_byte_ct
);
231 utf32_code
<<= utf32_byte_ct
*6;
235 utf32_byte_ct
= 0x00;
246 static int steam_init(void){
247 const char *username
= "offline player";
250 vg_info( "Initializing steamworks\n" );
252 if( !SteamAPI_Init() ){
254 vg_error( "Steamworks failed to initialize\n" );
260 SteamAPI_ManualDispatch_Init();
262 /* Connect interfaces */
263 hSteamClientPipe
= SteamAPI_GetHSteamPipe();
264 hSteamNetworkingSockets
= SteamAPI_SteamNetworkingSockets_SteamAPI();
265 hSteamUser
= SteamAPI_SteamUser();
267 ISteamUtils
*utils
= SteamAPI_SteamUtils();
268 SteamAPI_ISteamUtils_SetWarningMessageHook( utils
, recv_steam_warning
);
271 vg_success( "\nSteamworks API running\n" );
273 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
274 username
= SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends
);
278 * --------------------------------------------------------
280 hSteamUserStats
= SteamAPI_SteamUserStats();
281 steam_register_callback( k_iUserStatsReceived
,
282 steam_on_recieve_current_stats
);
284 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats
) )
285 vg_warn( "No Steam Logon: Cannot request stats\n" );
288 vg_console_reg_cmd( "ach", steam_achievement_ccmd
, NULL
);
292 /* TODO: On username update callback */
293 str_utf8_collapse( username
, steam_username_at_startup
,
294 vg_list_size(steam_username_at_startup
) );
299 static void steam_update(void)
302 steamworks_event_loop( hSteamClientPipe
);
306 static void steam_end(void)
309 vg_info( "Shutting down\n..." );