fnugz's idea
[carveJwlIkooP6JGAAIwe30JlM.git] / steam.h
1 /*
2 * Copyright (C) 2021-2023 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 #include "submodules/anyascii/impl/c/anyascii.c"
18
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
25 };
26
27 VG_STATIC enum steam_controller_type steam_display_controller;
28
29 /*
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.
33 *
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
37 * nothing.
38 */
39
40 VG_STATIC char steam_username_at_startup[128];
41
42 VG_STATIC void recv_steam_warning( int severity, const char *msg )
43 {
44 if( severity == 0 )
45 vg_low( "%s\n", msg );
46 else
47 vg_info( "%s\n", msg );
48 }
49
50 VG_STATIC int steam_ready = 0,
51 steam_stats_ready = 0;
52
53 VG_STATIC void *hSteamNetworkingSockets,
54 *hSteamUser;
55
56 VG_STATIC ISteamUserStats *hSteamUserStats;
57 VG_STATIC HSteamPipe hSteamClientPipe;
58
59 VG_STATIC const char *steam_achievement_names[] =
60 {
61 "ALBERT", "MARC",
62 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
63 "ROUTE_TO", "ROUTE_TC"
64 };
65
66 VG_STATIC void steam_store_achievements(void)
67 {
68 if( steam_ready && steam_stats_ready ){
69 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
70 }
71 }
72
73 VG_STATIC void steam_set_achievement( const char *name )
74 {
75 if( steam_ready && steam_stats_ready ){
76 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
77 vg_success( "Achievement set! '%s'\n", name );
78 }
79 else{
80 vg_warn( "Failed to set achievement: %s\n", name );
81 }
82 }
83 else{
84 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
85 }
86 }
87
88 VG_STATIC void steam_clear_achievement( const char *name )
89 {
90 if( steam_ready && steam_stats_ready ){
91 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
92 vg_info( "Achievement cleared: '%s'\n", name );
93 }
94 else{
95 vg_warn( "Failed to clear achievement: %s\n", name );
96 }
97 }
98 else{
99 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
100 }
101 }
102
103
104 VG_STATIC int steam_list_achievements( int argc, char const *argv[] )
105 {
106 vg_info( "Achievements: \n" );
107
108 if( steam_ready && steam_stats_ready ){
109 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
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 vg_warn( " Error while fetching achievement status '%s'\n", name );
120 }
121 }
122 }
123 else{
124 vg_warn( " Steam is not initialized, no results\n" );
125 }
126
127 return 0;
128 }
129
130 VG_STATIC int steam_clear_all_achievements( int argc, char const *argv[] )
131 {
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] );
135 }
136
137 steam_store_achievements();
138 }
139 else{
140 vg_warn( "steam is not initialized, cannot clear\n" );
141 }
142
143 return 0;
144 }
145
146 VG_STATIC int steam_set_achievemnt_test( int argc, char const *argv[] )
147 {
148 if( argc < 2 )
149 return 0;
150
151 if( strcmp( argv[0], "monkey_island" ) )
152 return 0;
153
154 steam_set_achievement( argv[1] );
155 steam_store_achievements();
156
157 return 0;
158 }
159
160 VG_STATIC void steam_on_recieve_current_stats( CallbackMsg_t *msg )
161 {
162 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
163
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;
168 }
169 else{
170 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
171 }
172 }
173
174 VG_STATIC ISteamInput *steam_hInput;
175
176 VG_STATIC u32 utf8_byte0_byte_count( u8 char0 )
177 {
178 for( u32 k=2; k<4; k++ ){
179 if( !(char0 & (0x80 >> k)) )
180 return k;
181 }
182
183 return 0;
184 }
185
186 VG_STATIC void str_utf8_collapse( const char *str, char *buf, u32 length )
187 {
188 u8 *ustr = (u8 *)str;
189 u32 utf32_code = 0x00000000;
190 u32 i=0, j=0, utf32_byte_ct=0;
191
192 for(;i < length-1;){
193 if( ustr[i] == 0x00 )
194 break;
195
196 if( ustr[i] & 0x80 ){
197 if( utf32_byte_ct ){
198 utf32_byte_ct --;
199 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
200
201 if( !utf32_byte_ct ){
202 const char *match;
203 size_t chars = anyascii( utf32_code, &match );
204
205 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
206 buf[ j++ ] = (u8)match[k];
207 }
208 }
209 }
210 else{
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;
214 }
215 }
216 else{
217 utf32_byte_ct = 0x00;
218 buf[j ++] = str[i];
219 }
220
221 i++;
222 }
223
224 buf[j] = 0x00;
225 }
226
227 VG_STATIC int steam_init(void)
228 {
229 const char *username = NULL;
230
231 #ifdef SR_NETWORKED
232 vg_info( "Initializing steamworks\n" );
233
234 if( !SteamAPI_Init() ){
235 printf("\n");
236 vg_error( "Steamworks failed to initialize\n" );
237 return 1;
238 }
239
240 steam_ready = 1;
241
242 SteamAPI_ManualDispatch_Init();
243
244 /* Connect interfaces */
245 hSteamClientPipe = SteamAPI_GetHSteamPipe();
246 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
247 hSteamUser = SteamAPI_SteamUser();
248
249 ISteamUtils *utils = SteamAPI_SteamUtils();
250 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
251
252 printf("\n");
253 vg_success( "\nSteamworks API running\n" );
254
255 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
256 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
257
258 /*
259 * Request stats
260 * --------------------------------------------------------
261 */
262 hSteamUserStats = SteamAPI_SteamUserStats();
263
264 steam_register_callback( k_iUserStatsReceived,
265 steam_on_recieve_current_stats );
266
267 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
268 vg_warn( "No Steam Logon: Cannot request stats\n" );
269
270
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 );
274
275 steam_hInput = SteamAPI_SteamInput();
276 SteamAPI_ISteamInput_Init( steam_hInput, 0 );
277 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
278
279 #endif
280
281 /* TODO: On username update callback */
282 str_utf8_collapse( username, steam_username_at_startup,
283 vg_list_size(steam_username_at_startup) );
284
285 return 1;
286 }
287
288 VG_STATIC void steam_update(void)
289 {
290 if( steam_ready ){
291 steamworks_event_loop( hSteamClientPipe );
292
293 /* TODO
294 * We can probably request this from SDL too
295 */
296 if( steam_hInput ){
297 SteamAPI_ISteamInput_RunFrame( steam_hInput, 0 );
298
299 InputHandle_t joy0 = SteamAPI_ISteamInput_GetControllerForGamepadIndex(
300 steam_hInput, 0 );
301
302 vg_input.controller_should_use_trackpad_look = 0;
303 if( joy0 != 0 ){
304 ESteamInputType type = SteamAPI_ISteamInput_GetInputTypeForHandle(
305 steam_hInput, joy0 );
306
307 if( type == k_ESteamInputType_SteamController ){
308 vg_input.controller_should_use_trackpad_look = 1;
309 steam_display_controller = k_steam_controller_type_steam;
310 }
311 else if( type == k_ESteamInputType_SteamDeckController ){
312 steam_display_controller = k_steam_controller_type_steam_deck;
313 }
314 else if( type == k_ESteamInputType_PS3Controller ||
315 type == k_ESteamInputType_PS4Controller ||
316 type == k_ESteamInputType_PS5Controller )
317 {
318 steam_display_controller = k_steam_controller_type_playstation;
319 }
320 else if( type == k_ESteamInputType_XBox360Controller ||
321 type == k_ESteamInputType_XBoxOneController )
322 {
323 steam_display_controller = k_steam_controller_type_xbox;
324 }
325 else{
326 /* currently unsupported controller */
327 steam_display_controller = k_steam_controller_type_xbox;
328 }
329 }
330 else
331 steam_display_controller = k_steam_controller_type_keyboard;
332 }
333 }
334 }
335
336 VG_STATIC void steam_end(void)
337 {
338 if( steam_ready ){
339 vg_info( "Shutting down\n..." );
340 SteamAPI_Shutdown();
341 }
342 }
343
344 #endif /* STEAM_H */