7bd0ab84a42bed5ae86d7695066cd306d2ef12ce
[carveJwlIkooP6JGAAIwe30JlM.git] / steam.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 * All trademarks are property of their respective owners
4 */
5
6 #ifndef STEAM_H
7 #define STEAM_H
8
9 #define VG_GAME
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
18 /*
19 * We only want to use steamworks if building for the networked version,
20 * theres not much point otherwise. We mainly want steamworks for setting
21 * achievements etc.. so that includes our own server too.
22 *
23 * This file also wraps the functions and interfaces that we want to use to
24 * make them a bit easier to read, since they are the flat API they have very
25 * long names. in non-networked builds they will return default errors or do
26 * nothing.
27 */
28
29 VG_STATIC char steam_username_at_startup[128];
30
31 VG_STATIC void recv_steam_warning( int severity, const char *msg )
32 {
33 if( severity == 0 )
34 vg_low( "%s\n", msg );
35 else
36 vg_info( "%s\n", msg );
37 }
38
39 VG_STATIC int steam_ready = 0,
40 steam_stats_ready = 0;
41
42 VG_STATIC void *hSteamNetworkingSockets,
43 *hSteamUser;
44
45 VG_STATIC ISteamUserStats *hSteamUserStats;
46 VG_STATIC HSteamPipe hSteamClientPipe;
47
48 VG_STATIC const char *steam_achievement_names[] =
49 {
50 "ALBERT", "MARC",
51 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
52 "ROUTE_TO", "ROUTE_TC"
53 };
54
55 VG_STATIC void steam_store_achievements(void)
56 {
57 if( steam_ready && steam_stats_ready ){
58 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
59 }
60 }
61
62 VG_STATIC void steam_set_achievement( const char *name )
63 {
64 if( steam_ready && steam_stats_ready ){
65 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
66 vg_success( "Achievement set! '%s'\n", name );
67 }
68 else{
69 vg_warn( "Failed to set achievement: %s\n", name );
70 }
71 }
72 else{
73 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
74 }
75 }
76
77 VG_STATIC void steam_clear_achievement( const char *name )
78 {
79 if( steam_ready && steam_stats_ready ){
80 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
81 vg_info( "Achievement cleared: '%s'\n", name );
82 }
83 else{
84 vg_warn( "Failed to clear achievement: %s\n", name );
85 }
86 }
87 else{
88 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
89 }
90 }
91
92
93 VG_STATIC int steam_list_achievements( int argc, char const *argv[] )
94 {
95 vg_info( "Achievements: \n" );
96
97 if( steam_ready && steam_stats_ready ){
98 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
99 int set = 0;
100 const char *name = steam_achievement_names[i];
101
102 if( SteamAPI_ISteamUserStats_GetAchievement(
103 hSteamUserStats, name, &set ) )
104 {
105 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
106 }
107 else{
108 vg_warn( " Error while fetching achievement status '%s'\n", name );
109 }
110 }
111 }
112 else{
113 vg_warn( " Steam is not initialized, no results\n" );
114 }
115
116 return 0;
117 }
118
119 VG_STATIC int steam_clear_all_achievements( int argc, char const *argv[] )
120 {
121 if( steam_ready && steam_stats_ready ){
122 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
123 steam_clear_achievement( steam_achievement_names[i] );
124 }
125
126 steam_store_achievements();
127 }
128 else{
129 vg_warn( "steam is not initialized, cannot clear\n" );
130 }
131
132 return 0;
133 }
134
135 VG_STATIC int steam_set_achievemnt_test( int argc, char const *argv[] )
136 {
137 if( argc < 2 )
138 return 0;
139
140 if( strcmp( argv[0], "monkey_island" ) )
141 return 0;
142
143 steam_set_achievement( argv[1] );
144 steam_store_achievements();
145
146 return 0;
147 }
148
149 VG_STATIC void steam_on_recieve_current_stats( CallbackMsg_t *msg )
150 {
151 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
152
153 if( rec->m_eResult == k_EResultOK ){
154 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
155 rec->m_steamIDUser );
156 steam_stats_ready = 1;
157 }
158 else{
159 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
160 }
161 }
162
163 VG_STATIC ISteamInput *steam_hInput;
164
165 VG_STATIC u32 utf8_byte0_byte_count( u8 char0 )
166 {
167 for( u32 k=2; k<4; k++ ){
168 if( !(char0 & (0x80 >> k)) )
169 return k;
170 }
171
172 return 0;
173 }
174
175 VG_STATIC void str_utf8_collapse( const char *str, char *buf, u32 length )
176 {
177 u8 *ustr = (u8 *)str;
178 u32 utf32_code = 0x00000000;
179 u32 i=0, j=0, utf32_byte_ct=0;
180
181 for(;i < length-1;){
182 if( ustr[i] == 0x00 )
183 break;
184
185 if( ustr[i] & 0x80 ){
186 if( utf32_byte_ct ){
187 utf32_byte_ct --;
188 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
189
190 if( !utf32_byte_ct ){
191 const char *match;
192 size_t chars = anyascii( utf32_code, &match );
193
194 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
195 buf[ j++ ] = (u8)match[k];
196 }
197 }
198 }
199 else{
200 utf32_byte_ct = utf8_byte0_byte_count( ustr[i] )-1;
201 utf32_code = ustr[i] & (0x3F >> utf32_byte_ct);
202 utf32_code <<= utf32_byte_ct*6;
203 }
204 }
205 else{
206 utf32_byte_ct = 0x00;
207 buf[j ++] = str[i];
208 }
209
210 i++;
211 }
212
213 buf[j] = 0x00;
214 }
215
216 VG_STATIC int steam_init(void)
217 {
218 const char *username = NULL;
219
220 #ifdef SR_NETWORKED
221 vg_info( "Initializing steamworks\n" );
222
223 if( !SteamAPI_Init() ){
224 printf("\n");
225 vg_error( "Steamworks failed to initialize\n" );
226 return 1;
227 }
228
229 steam_ready = 1;
230
231 SteamAPI_ManualDispatch_Init();
232
233 /* Connect interfaces */
234 hSteamClientPipe = SteamAPI_GetHSteamPipe();
235 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
236 hSteamUser = SteamAPI_SteamUser();
237
238 ISteamUtils *utils = SteamAPI_SteamUtils();
239 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
240
241 printf("\n");
242 vg_success( "\nSteamworks API running\n" );
243
244 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
245 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
246
247 /*
248 * Request stats
249 * --------------------------------------------------------
250 */
251 hSteamUserStats = SteamAPI_SteamUserStats();
252
253 steam_register_callback( k_iUserStatsReceived,
254 steam_on_recieve_current_stats );
255
256 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
257 vg_warn( "No Steam Logon: Cannot request stats\n" );
258
259
260 vg_console_reg_cmd( "ach_list", steam_list_achievements, NULL );
261 vg_console_reg_cmd( "ach_clear_all", steam_clear_all_achievements, NULL );
262 vg_console_reg_cmd( "ach_set", steam_set_achievemnt_test, NULL );
263
264 steam_hInput = SteamAPI_SteamInput();
265 SteamAPI_ISteamInput_Init( steam_hInput, 0 );
266 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
267
268 #endif
269
270 /* TODO: On username update callback */
271 str_utf8_collapse( username, steam_username_at_startup,
272 vg_list_size(steam_username_at_startup) );
273
274 return 1;
275 }
276
277 VG_STATIC void steam_update(void)
278 {
279 if( steam_ready ){
280 steamworks_event_loop( hSteamClientPipe );
281
282 /* TODO
283 * We can probably request this from SDL too
284 */
285 if( steam_hInput ){
286 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
287
288 InputHandle_t joy0 = SteamAPI_ISteamInput_GetControllerForGamepadIndex(
289 steam_hInput, 0 );
290
291 vg_input.controller_should_use_trackpad_look = 0;
292 if( joy0 != 0 ){
293 ESteamInputType type = SteamAPI_ISteamInput_GetInputTypeForHandle(
294 steam_hInput, joy0 );
295
296 if( type == k_ESteamInputType_SteamController ){
297 vg_input.controller_should_use_trackpad_look = 1;
298 menu_display_controller = k_menu_controller_type_steam;
299 }
300 else if( type == k_ESteamInputType_SteamDeckController ){
301 menu_display_controller = k_menu_controller_type_steam_deck;
302 }
303 else if( type == k_ESteamInputType_PS3Controller ||
304 type == k_ESteamInputType_PS4Controller ||
305 type == k_ESteamInputType_PS5Controller )
306 {
307 menu_display_controller = k_menu_controller_type_playstation;
308 }
309 else if( type == k_ESteamInputType_XBox360Controller ||
310 type == k_ESteamInputType_XBoxOneController )
311 {
312 menu_display_controller = k_menu_controller_type_xbox;
313 }
314 else{
315 /* currently unsupported controller */
316 menu_display_controller = k_menu_controller_type_xbox;
317 }
318 }
319 else
320 menu_display_controller = k_menu_controller_type_keyboard;
321 }
322 }
323 }
324
325 VG_STATIC void steam_end(void)
326 {
327 if( steam_ready ){
328 vg_info( "Shutting down\n..." );
329 SteamAPI_Shutdown();
330 }
331 }
332
333 #endif /* STEAM_H */