THE REST
[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 /*
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.
23 *
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
27 * nothing.
28 */
29
30 static char steam_username_at_startup[128] = "Unassigned";
31
32 static void recv_steam_warning( int severity, const char *msg )
33 {
34 if( severity == 0 )
35 vg_low( "%s\n", msg );
36 else
37 vg_info( "%s\n", msg );
38 }
39
40 static int steam_ready = 0,
41 steam_stats_ready = 0;
42
43 static void *hSteamNetworkingSockets,
44 *hSteamUser;
45
46 static ISteamUserStats *hSteamUserStats;
47 static HSteamPipe hSteamClientPipe;
48
49 static const char *steam_achievement_names[] =
50 {
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",
54 "80FT"
55 };
56
57 static void steam_store_achievements(void)
58 {
59 if( steam_ready && steam_stats_ready ){
60 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
61 }
62 }
63
64 static void update_ach_models(void);
65 static void steam_set_achievement( const char *name ){
66 /* hack lol */
67 if( !strcmp(name,"MARC") ) skaterift.achievements |= 0x1;
68 if( !strcmp(name,"ALBERT") ) skaterift.achievements |= 0x2;
69 if( !strcmp(name,"JANET") ) skaterift.achievements |= 0x4;
70 if( !strcmp(name,"BERNADETTA") ) skaterift.achievements |= 0x8;
71 update_ach_models();
72
73 if( steam_ready && steam_stats_ready ){
74 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
75 vg_success( "Achievement set! '%s'\n", name );
76
77 }
78 else{
79 vg_warn( "Failed to set achievement: %s\n", name );
80 }
81 }
82 else{
83 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
84 }
85 }
86
87 static void steam_clear_achievement( const char *name )
88 {
89 if( steam_ready && steam_stats_ready ){
90 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
91 vg_info( "Achievement cleared: '%s'\n", name );
92 }
93 else{
94 vg_warn( "Failed to clear achievement: %s\n", name );
95 }
96 }
97 else{
98 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
99 }
100 }
101
102
103 static void steam_print_all_achievements(void){
104 vg_info( "Achievements: \n" );
105
106 if( steam_ready && steam_stats_ready ){
107 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
108 steamapi_bool set = 0;
109 const char *name = steam_achievement_names[i];
110
111 if( SteamAPI_ISteamUserStats_GetAchievement(
112 hSteamUserStats, name, &set ) )
113 {
114 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
115 }
116 else{
117 vg_warn( " Error while fetching achievement status '%s'\n", name );
118 }
119 }
120 }
121 else{
122 vg_warn( " Steam is not initialized, no results\n" );
123 }
124 }
125
126 static int steam_achievement_ccmd( int argc, char const *argv[] )
127 {
128 if( !(steam_ready && steam_stats_ready) ) return 1;
129
130 if( argc == 1 ){
131 if( !strcmp( argv[0], "list" ) ){
132 steam_print_all_achievements();
133 return 0;
134 }
135 else if( !strcmp( argv[0], "clearall" )){
136 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
137 steam_clear_achievement( steam_achievement_names[i] );
138
139 steam_store_achievements();
140 }
141 }
142
143 if( argc == 2 ){
144 if( !strcmp( argv[0], "set" ) ){
145 steam_set_achievement( argv[1] );
146 steam_store_achievements();
147 return 0;
148 }
149 else if( strcmp( argv[0], "clear" ) ){
150 steam_clear_achievement( argv[1] );
151 steam_store_achievements();
152 return 0;
153 }
154 }
155
156 return 1;
157 }
158
159 static void steam_on_recieve_current_stats( CallbackMsg_t *msg )
160 {
161 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
162
163 if( rec->m_eResult == k_EResultOK ){
164 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
165 rec->m_steamIDUser );
166 steam_stats_ready = 1;
167
168 steamapi_bool set = 0;
169 if( SteamAPI_ISteamUserStats_GetAchievement(
170 hSteamUserStats, "MARC", &set ) ){
171 if( set ) skaterift.achievements |= 0x1;
172 }
173 if( SteamAPI_ISteamUserStats_GetAchievement(
174 hSteamUserStats, "ALBERT", &set ) ){
175 if( set ) skaterift.achievements |= 0x2;
176 }
177 if( SteamAPI_ISteamUserStats_GetAchievement(
178 hSteamUserStats, "JANET", &set ) ){
179 if( set ) skaterift.achievements |= 0x4;
180 }
181 if( SteamAPI_ISteamUserStats_GetAchievement(
182 hSteamUserStats, "BERNADETTA", &set ) ){
183 if( set ) skaterift.achievements |= 0x8;
184 }
185 update_ach_models();
186 }
187 else{
188 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
189 }
190 }
191
192 static u32 utf8_byte0_byte_count( u8 char0 )
193 {
194 for( u32 k=2; k<4; k++ ){
195 if( !(char0 & (0x80 >> k)) )
196 return k;
197 }
198
199 return 0;
200 }
201
202 static u32 str_utf8_collapse( const char *str, char *buf, u32 length ){
203 u8 *ustr = (u8 *)str;
204 u32 utf32_code = 0x00000000;
205 u32 i=0, j=0, utf32_byte_ct=0;
206
207 for(;j < length-1;){
208 if( ustr[i] == 0x00 )
209 break;
210
211 if( ustr[i] & 0x80 ){
212 if( utf32_byte_ct ){
213 utf32_byte_ct --;
214 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
215
216 if( !utf32_byte_ct ){
217 const char *match;
218 size_t chars = anyascii( utf32_code, &match );
219
220 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
221 buf[ j++ ] = (u8)match[k];
222 }
223 }
224 }
225 else{
226 utf32_byte_ct = utf8_byte0_byte_count( ustr[i] )-1;
227 utf32_code = ustr[i] & (0x3F >> utf32_byte_ct);
228 utf32_code <<= utf32_byte_ct*6;
229 }
230 }
231 else{
232 utf32_byte_ct = 0x00;
233 buf[j ++] = str[i];
234 }
235
236 i++;
237 }
238
239 buf[j] = 0x00;
240 return j;
241 }
242
243 static int steam_init(void){
244 const char *username = "offline player";
245
246 #ifdef SR_NETWORKED
247 vg_info( "Initializing steamworks\n" );
248
249 if( !SteamAPI_Init() ){
250 printf("\n");
251 vg_error( "Steamworks failed to initialize\n" );
252 return 1;
253 }
254
255 steam_ready = 1;
256
257 SteamAPI_ManualDispatch_Init();
258
259 /* Connect interfaces */
260 hSteamClientPipe = SteamAPI_GetHSteamPipe();
261 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
262 hSteamUser = SteamAPI_SteamUser();
263
264 ISteamUtils *utils = SteamAPI_SteamUtils();
265 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
266
267 printf("\n");
268 vg_success( "\nSteamworks API running\n" );
269
270 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
271 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
272
273 /*
274 * Request stats
275 * --------------------------------------------------------
276 */
277 hSteamUserStats = SteamAPI_SteamUserStats();
278 steam_register_callback( k_iUserStatsReceived,
279 steam_on_recieve_current_stats );
280
281 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
282 vg_warn( "No Steam Logon: Cannot request stats\n" );
283
284
285 vg_console_reg_cmd( "ach", steam_achievement_ccmd, NULL );
286
287 #endif
288
289 /* TODO: On username update callback */
290 str_utf8_collapse( username, steam_username_at_startup,
291 vg_list_size(steam_username_at_startup) );
292
293 return 1;
294 }
295
296 static void steam_update(void)
297 {
298 if( steam_ready ){
299 steamworks_event_loop( hSteamClientPipe );
300 }
301 }
302
303 static void steam_end(void)
304 {
305 if( steam_ready ){
306 vg_info( "Shutting down\n..." );
307 SteamAPI_Shutdown();
308 }
309 }
310
311 #endif /* STEAM_H */