small clean
[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 {
59 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
60 }
61 }
62
63 VG_STATIC void steam_set_achievement( const char *name )
64 {
65 if( steam_ready && steam_stats_ready )
66 {
67 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) )
68 {
69 vg_success( "Achievement set! '%s'\n", name );
70 }
71 else
72 {
73 vg_warn( "Failed to set achievement: %s\n", name );
74 }
75 }
76 else
77 {
78 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
79 }
80 }
81
82 VG_STATIC void steam_clear_achievement( const char *name )
83 {
84 if( steam_ready && steam_stats_ready )
85 {
86 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) )
87 {
88 vg_info( "Achievement cleared: '%s'\n", name );
89 }
90 else
91 {
92 vg_warn( "Failed to clear achievement: %s\n", name );
93 }
94 }
95 else
96 {
97 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
98 }
99 }
100
101
102 VG_STATIC int steam_list_achievements( int argc, char const *argv[] )
103 {
104 vg_info( "Achievements: \n" );
105
106 if( steam_ready && steam_stats_ready )
107 {
108 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
109 {
110 int set = 0;
111 const char *name = steam_achievement_names[i];
112
113 if( SteamAPI_ISteamUserStats_GetAchievement(
114 hSteamUserStats, name, &set ) )
115 {
116 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
117 }
118 else
119 {
120 vg_warn( " Error while fetching achievement status '%s'\n", name );
121 }
122 }
123 }
124 else
125 {
126 vg_warn( " Steam is not initialized, no results\n" );
127 }
128
129 return 0;
130 }
131
132 VG_STATIC int steam_clear_all_achievements( int argc, char const *argv[] )
133 {
134 if( steam_ready && steam_stats_ready )
135 {
136 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
137 {
138 steam_clear_achievement( steam_achievement_names[i] );
139 }
140
141 steam_store_achievements();
142 }
143 else
144 {
145 vg_warn( "steam is not initialized, cannot clear\n" );
146 }
147
148 return 0;
149 }
150
151 VG_STATIC int steam_set_achievemnt_test( int argc, char const *argv[] )
152 {
153 if( argc < 2 )
154 return 0;
155
156 if( strcmp( argv[0], "monkey_island" ) )
157 return 0;
158
159 steam_set_achievement( argv[1] );
160 steam_store_achievements();
161
162 return 0;
163 }
164
165 VG_STATIC void steam_on_recieve_current_stats( CallbackMsg_t *msg )
166 {
167 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
168
169 if( rec->m_eResult == k_EResultOK )
170 {
171 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
172 rec->m_steamIDUser );
173 steam_stats_ready = 1;
174 }
175 else
176 {
177 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
178 }
179 }
180
181 VG_STATIC ISteamInput *steam_hInput;
182
183 VG_STATIC u32 utf8_byte0_byte_count( u8 char0 )
184 {
185 for( u32 k=2; k<4; k++ )
186 {
187 if( !(char0 & (0x80 >> k)) )
188 return k;
189 }
190
191 return 0;
192 }
193
194 VG_STATIC void str_utf8_collapse( const char *str, char *buf, u32 length )
195 {
196 u8 *ustr = (u8 *)str;
197 u32 utf32_code = 0x00000000;
198 u32 i=0, j=0, utf32_byte_ct=0;
199
200 for(;i < length-1;)
201 {
202 if( ustr[i] == 0x00 )
203 break;
204
205 if( ustr[i] & 0x80 )
206 {
207 if( utf32_byte_ct )
208 {
209 utf32_byte_ct --;
210 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
211
212 if( !utf32_byte_ct )
213 {
214 const char *match;
215 size_t chars = anyascii( utf32_code, &match );
216
217 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ )
218 {
219 buf[ j++ ] = (u8)match[k];
220 }
221 }
222 }
223 else
224 {
225 utf32_byte_ct = utf8_byte0_byte_count( ustr[i] )-1;
226 utf32_code = ustr[i] & (0x3F >> utf32_byte_ct);
227 utf32_code <<= utf32_byte_ct*6;
228 }
229 }
230 else
231 {
232 utf32_byte_ct = 0x00;
233 buf[j ++] = str[i];
234 }
235
236 i++;
237 }
238
239 buf[j] = 0x00;
240 }
241
242 VG_STATIC int steam_init(void)
243 {
244 const char *username = NULL;
245
246 #ifdef SR_NETWORKED
247 vg_info( "Initializing steamworks\n" );
248
249 if( !SteamAPI_Init() )
250 {
251 printf("\n");
252 vg_error( "Steamworks failed to initialize\n" );
253 return 1;
254 }
255
256 steam_ready = 1;
257
258 SteamAPI_ManualDispatch_Init();
259
260 /* Connect interfaces */
261 hSteamClientPipe = SteamAPI_GetHSteamPipe();
262 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
263 hSteamUser = SteamAPI_SteamUser();
264
265 ISteamUtils *utils = SteamAPI_SteamUtils();
266 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
267
268 printf("\n");
269 vg_success( "\nSteamworks API running\n" );
270
271 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
272 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
273
274 /*
275 * Request stats
276 * --------------------------------------------------------
277 */
278 hSteamUserStats = SteamAPI_SteamUserStats();
279
280 steam_register_callback( k_iUserStatsReceived,
281 steam_on_recieve_current_stats );
282
283 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
284 vg_warn( "No Steam Logon: Cannot request stats\n" );
285
286
287 vg_function_push( (struct vg_cmd)
288 {
289 .name = "ach_list",
290 .function = steam_list_achievements
291 });
292
293 vg_function_push( (struct vg_cmd)
294 {
295 .name = "ach_clear_all",
296 .function = steam_clear_all_achievements
297 });
298
299 vg_function_push( (struct vg_cmd)
300 {
301 .name = "ach_set",
302 .function = steam_set_achievemnt_test
303 });
304
305 steam_hInput = SteamAPI_SteamInput();
306 SteamAPI_ISteamInput_Init( steam_hInput, 0 );
307 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
308
309 #endif
310
311 /* TODO: On username update callback */
312 str_utf8_collapse( username, steam_username_at_startup,
313 vg_list_size(steam_username_at_startup) );
314
315 return 1;
316 }
317
318 VG_STATIC void steam_update(void)
319 {
320 if( steam_ready )
321 {
322 steamworks_event_loop( hSteamClientPipe );
323
324 /* TODO
325 * We can probably request this from SDL too
326 */
327 if( steam_hInput )
328 {
329 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
330
331 InputHandle_t joy0 = SteamAPI_ISteamInput_GetControllerForGamepadIndex(
332 steam_hInput, 0 );
333
334 vg_input.controller_should_use_trackpad_look = 0;
335 if( joy0 != 0 )
336 {
337 ESteamInputType type = SteamAPI_ISteamInput_GetInputTypeForHandle(
338 steam_hInput, joy0 );
339
340 if( type == k_ESteamInputType_SteamController )
341 {
342 vg_input.controller_should_use_trackpad_look = 1;
343 menu_display_controller = k_menu_controller_type_steam;
344 }
345 else if( type == k_ESteamInputType_SteamDeckController )
346 {
347 menu_display_controller = k_menu_controller_type_steam_deck;
348 }
349 else if( type == k_ESteamInputType_PS3Controller ||
350 type == k_ESteamInputType_PS4Controller ||
351 type == k_ESteamInputType_PS5Controller )
352 {
353 menu_display_controller = k_menu_controller_type_playstation;
354 }
355 else if( type == k_ESteamInputType_XBox360Controller ||
356 type == k_ESteamInputType_XBoxOneController )
357 {
358 menu_display_controller = k_menu_controller_type_xbox;
359 }
360 else
361 {
362 /* currently unsupported controller */
363 menu_display_controller = k_menu_controller_type_xbox;
364 }
365 }
366 else
367 menu_display_controller = k_menu_controller_type_keyboard;
368 }
369 }
370 }
371
372 VG_STATIC void steam_end(void)
373 {
374 if( steam_ready )
375 {
376 vg_info( "Shutting down\n..." );
377 SteamAPI_Shutdown();
378 }
379 }
380
381 #endif /* STEAM_H */