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"
19 enum steam_controller_type
{
20 k_steam_controller_type_keyboard
,
21 k_steam_controller_type_xbox
,
22 k_steam_controller_type_playstation
,
23 k_steam_controller_type_steam
,
24 k_steam_controller_type_steam_deck
27 VG_STATIC
enum steam_controller_type steam_display_controller
;
30 * We only want to use steamworks if building for the networked version,
31 * theres not much point otherwise. We mainly want steamworks for setting
32 * achievements etc.. so that includes our own server too.
34 * This file also wraps the functions and interfaces that we want to use to
35 * make them a bit easier to read, since they are the flat API they have very
36 * long names. in non-networked builds they will return default errors or do
40 VG_STATIC
char steam_username_at_startup
[128];
42 VG_STATIC
void recv_steam_warning( int severity
, const char *msg
)
45 vg_low( "%s\n", msg
);
47 vg_info( "%s\n", msg
);
50 VG_STATIC
int steam_ready
= 0,
51 steam_stats_ready
= 0;
53 VG_STATIC
void *hSteamNetworkingSockets
,
56 VG_STATIC ISteamUserStats
*hSteamUserStats
;
57 VG_STATIC HSteamPipe hSteamClientPipe
;
59 VG_STATIC
const char *steam_achievement_names
[] =
62 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
63 "ROUTE_TO", "ROUTE_TC"
66 VG_STATIC
void steam_store_achievements(void)
68 if( steam_ready
&& steam_stats_ready
){
69 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats
);
73 VG_STATIC
void steam_set_achievement( const char *name
)
75 if( steam_ready
&& steam_stats_ready
){
76 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats
, name
) ){
77 vg_success( "Achievement set! '%s'\n", name
);
80 vg_warn( "Failed to set achievement: %s\n", name
);
84 vg_warn( "Failed to set achievement (steam not ready): %s\n", name
);
88 VG_STATIC
void steam_clear_achievement( const char *name
)
90 if( steam_ready
&& steam_stats_ready
){
91 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats
, name
) ){
92 vg_info( "Achievement cleared: '%s'\n", name
);
95 vg_warn( "Failed to clear achievement: %s\n", name
);
99 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name
);
104 VG_STATIC
int steam_list_achievements( int argc
, char const *argv
[] )
106 vg_info( "Achievements: \n" );
108 if( steam_ready
&& steam_stats_ready
){
109 for( int i
=0; i
<vg_list_size(steam_achievement_names
); i
++ ){
111 const char *name
= steam_achievement_names
[i
];
113 if( SteamAPI_ISteamUserStats_GetAchievement(
114 hSteamUserStats
, name
, &set
) )
116 vg_info( " %s %s\n", (set
? "[YES]": "[ ]"), name
);
119 vg_warn( " Error while fetching achievement status '%s'\n", name
);
124 vg_warn( " Steam is not initialized, no results\n" );
130 VG_STATIC
int steam_clear_all_achievements( int argc
, char const *argv
[] )
132 if( steam_ready
&& steam_stats_ready
){
133 for( int i
=0; i
<vg_list_size(steam_achievement_names
); i
++ ){
134 steam_clear_achievement( steam_achievement_names
[i
] );
137 steam_store_achievements();
140 vg_warn( "steam is not initialized, cannot clear\n" );
146 VG_STATIC
int steam_set_achievemnt_test( int argc
, char const *argv
[] )
151 if( strcmp( argv
[0], "monkey_island" ) )
154 steam_set_achievement( argv
[1] );
155 steam_store_achievements();
160 VG_STATIC
void steam_on_recieve_current_stats( CallbackMsg_t
*msg
)
162 UserStatsReceived_t
*rec
= (UserStatsReceived_t
*)msg
->m_pubParam
;
164 if( rec
->m_eResult
== k_EResultOK
){
165 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec
->m_nGameID
,
166 rec
->m_steamIDUser
);
167 steam_stats_ready
= 1;
170 vg_error( "Error recieveing stats for user (%u)\n", rec
->m_eResult
);
174 VG_STATIC ISteamInput
*steam_hInput
;
176 VG_STATIC u32
utf8_byte0_byte_count( u8 char0
)
178 for( u32 k
=2; k
<4; k
++ ){
179 if( !(char0
& (0x80 >> k
)) )
186 VG_STATIC
void str_utf8_collapse( const char *str
, char *buf
, u32 length
)
188 u8
*ustr
= (u8
*)str
;
189 u32 utf32_code
= 0x00000000;
190 u32 i
=0, j
=0, utf32_byte_ct
=0;
193 if( ustr
[i
] == 0x00 )
196 if( ustr
[i
] & 0x80 ){
199 utf32_code
|= (ustr
[i
] & 0x3F) << (utf32_byte_ct
*6);
201 if( !utf32_byte_ct
){
203 size_t chars
= anyascii( utf32_code
, &match
);
205 for( u32 k
=0; k
<VG_MIN(chars
, length
-1-j
); k
++ ){
206 buf
[ j
++ ] = (u8
)match
[k
];
211 utf32_byte_ct
= utf8_byte0_byte_count( ustr
[i
] )-1;
212 utf32_code
= ustr
[i
] & (0x3F >> utf32_byte_ct
);
213 utf32_code
<<= utf32_byte_ct
*6;
217 utf32_byte_ct
= 0x00;
227 VG_STATIC
int steam_init(void)
229 const char *username
= NULL
;
232 vg_info( "Initializing steamworks\n" );
234 if( !SteamAPI_Init() ){
236 vg_error( "Steamworks failed to initialize\n" );
242 SteamAPI_ManualDispatch_Init();
244 /* Connect interfaces */
245 hSteamClientPipe
= SteamAPI_GetHSteamPipe();
246 hSteamNetworkingSockets
= SteamAPI_SteamNetworkingSockets_SteamAPI();
247 hSteamUser
= SteamAPI_SteamUser();
249 ISteamUtils
*utils
= SteamAPI_SteamUtils();
250 SteamAPI_ISteamUtils_SetWarningMessageHook( utils
, recv_steam_warning
);
253 vg_success( "\nSteamworks API running\n" );
255 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
256 username
= SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends
);
260 * --------------------------------------------------------
262 hSteamUserStats
= SteamAPI_SteamUserStats();
264 steam_register_callback( k_iUserStatsReceived
,
265 steam_on_recieve_current_stats
);
267 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats
) )
268 vg_warn( "No Steam Logon: Cannot request stats\n" );
271 vg_console_reg_cmd( "ach_list", steam_list_achievements
, NULL
);
272 vg_console_reg_cmd( "ach_clear_all", steam_clear_all_achievements
, NULL
);
273 vg_console_reg_cmd( "ach_set", steam_set_achievemnt_test
, NULL
);
275 steam_hInput
= SteamAPI_SteamInput();
276 SteamAPI_ISteamInput_Init( steam_hInput
, 0 );
277 SteamAPI_ISteamInput_RunFrame( steam_hInput
, 0 );
281 /* TODO: On username update callback */
282 str_utf8_collapse( username
, steam_username_at_startup
,
283 vg_list_size(steam_username_at_startup
) );
288 VG_STATIC
void steam_update(void)
291 steamworks_event_loop( hSteamClientPipe
);
294 * We can probably request this from SDL too
297 SteamAPI_ISteamInput_RunFrame( steam_hInput
, 0 );
299 InputHandle_t joy0
= SteamAPI_ISteamInput_GetControllerForGamepadIndex(
302 vg_input
.controller_should_use_trackpad_look
= 0;
304 ESteamInputType type
= SteamAPI_ISteamInput_GetInputTypeForHandle(
305 steam_hInput
, joy0
);
307 if( type
== k_ESteamInputType_SteamController
){
308 vg_input
.controller_should_use_trackpad_look
= 1;
309 steam_display_controller
= k_steam_controller_type_steam
;
311 else if( type
== k_ESteamInputType_SteamDeckController
){
312 steam_display_controller
= k_steam_controller_type_steam_deck
;
314 else if( type
== k_ESteamInputType_PS3Controller
||
315 type
== k_ESteamInputType_PS4Controller
||
316 type
== k_ESteamInputType_PS5Controller
)
318 steam_display_controller
= k_steam_controller_type_playstation
;
320 else if( type
== k_ESteamInputType_XBox360Controller
||
321 type
== k_ESteamInputType_XBoxOneController
)
323 steam_display_controller
= k_steam_controller_type_xbox
;
326 /* currently unsupported controller */
327 steam_display_controller
= k_steam_controller_type_xbox
;
331 steam_display_controller
= k_steam_controller_type_keyboard
;
336 VG_STATIC
void steam_end(void)
339 vg_info( "Shutting down\n..." );