build system revision
[carveJwlIkooP6JGAAIwe30JlM.git] / steam.h
1 /*
2 * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 * All trademarks are property of their respective owners
4 */
5 #pragma once
6 #include "vg/vg_steam.h"
7 #include "vg/vg_steam_utils.h"
8 #include "vg/vg_steam_networking.h"
9 #include "vg/vg_steam_auth.h"
10 #include "vg/vg_steam_http.h"
11 #include "vg/vg_steam_friends.h"
12 #include "vg/vg_steam_user_stats.h"
13 #include "submodules/anyascii/impl/c/anyascii.c"
14
15 /*
16 * We only want to use steamworks if building for the networked version,
17 * theres not much point otherwise. We mainly want steamworks for setting
18 * achievements etc.. so that includes our own server too.
19 *
20 * This file also wraps the functions and interfaces that we want to use to
21 * make them a bit easier to read, since they are the flat API they have very
22 * long names. in non-networked builds they will return default errors or do
23 * nothing.
24 */
25
26 static char steam_username_at_startup[128] = "Unassigned";
27
28 static void recv_steam_warning( int severity, const char *msg )
29 {
30 if( severity == 0 )
31 vg_low( "%s\n", msg );
32 else
33 vg_info( "%s\n", msg );
34 }
35
36 static int steam_ready = 0,
37 steam_stats_ready = 0;
38
39 static void *hSteamNetworkingSockets,
40 *hSteamUser;
41
42 static ISteamUserStats *hSteamUserStats;
43 static HSteamPipe hSteamClientPipe;
44
45 static const char *steam_achievement_names[] =
46 {
47 "ALBERT", "MARC", "JANET", "BERNADETTA",
48 "ROUTE_MPY", "ROUTE_MPG", "ROUTE_MPB", "ROUTE_MPR",
49 "ROUTE_TO", "ROUTE_TC", "CITY_COMPLETE", "MTZERO_SILVER", "MTZERO_GOLD",
50 "80FT"
51 };
52
53 static void steam_store_achievements(void)
54 {
55 if( steam_ready && steam_stats_ready ){
56 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
57 }
58 }
59
60 static void update_ach_models(void);
61 static void steam_set_achievement( const char *name ){
62 if( skaterift.demo_mode )
63 return;
64
65 /* hack lol */
66 if( !strcmp(name,"MARC") ) skaterift.achievements |= 0x1;
67 if( !strcmp(name,"ALBERT") ) skaterift.achievements |= 0x2;
68 if( !strcmp(name,"JANET") ) skaterift.achievements |= 0x4;
69 if( !strcmp(name,"BERNADETTA") ) skaterift.achievements |= 0x8;
70 update_ach_models();
71
72 if( steam_ready && steam_stats_ready ){
73 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
74 vg_success( "Achievement set! '%s'\n", name );
75
76 }
77 else{
78 vg_warn( "Failed to set achievement: %s\n", name );
79 }
80 }
81 else{
82 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
83 }
84 }
85
86 static void steam_clear_achievement( const char *name )
87 {
88 if( steam_ready && steam_stats_ready ){
89 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
90 vg_info( "Achievement cleared: '%s'\n", name );
91 }
92 else{
93 vg_warn( "Failed to clear achievement: %s\n", name );
94 }
95 }
96 else{
97 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
98 }
99 }
100
101
102 static void steam_print_all_achievements(void){
103 vg_info( "Achievements: \n" );
104
105 if( steam_ready && steam_stats_ready ){
106 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
107 steamapi_bool set = 0;
108 const char *name = steam_achievement_names[i];
109
110 if( SteamAPI_ISteamUserStats_GetAchievement(
111 hSteamUserStats, name, &set ) )
112 {
113 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
114 }
115 else{
116 vg_warn( " Error while fetching achievement status '%s'\n", name );
117 }
118 }
119 }
120 else{
121 vg_warn( " Steam is not initialized, no results\n" );
122 }
123 }
124
125 static int steam_achievement_ccmd( int argc, char const *argv[] )
126 {
127 if( !(steam_ready && steam_stats_ready) ) return 1;
128
129 if( argc == 1 ){
130 if( !strcmp( argv[0], "list" ) ){
131 steam_print_all_achievements();
132 return 0;
133 }
134 else if( !strcmp( argv[0], "clearall" )){
135 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
136 steam_clear_achievement( steam_achievement_names[i] );
137
138 steam_store_achievements();
139 }
140 }
141
142 if( argc == 2 ){
143 if( !strcmp( argv[0], "set" ) ){
144 steam_set_achievement( argv[1] );
145 steam_store_achievements();
146 return 0;
147 }
148 else if( strcmp( argv[0], "clear" ) ){
149 steam_clear_achievement( argv[1] );
150 steam_store_achievements();
151 return 0;
152 }
153 }
154
155 return 1;
156 }
157
158 static void steam_on_recieve_current_stats( CallbackMsg_t *msg )
159 {
160 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
161
162 if( rec->m_eResult == k_EResultOK ){
163 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
164 rec->m_steamIDUser );
165 steam_stats_ready = 1;
166
167 steamapi_bool set = 0;
168 if( SteamAPI_ISteamUserStats_GetAchievement(
169 hSteamUserStats, "MARC", &set ) ){
170 if( set ) skaterift.achievements |= 0x1;
171 }
172 if( SteamAPI_ISteamUserStats_GetAchievement(
173 hSteamUserStats, "ALBERT", &set ) ){
174 if( set ) skaterift.achievements |= 0x2;
175 }
176 if( SteamAPI_ISteamUserStats_GetAchievement(
177 hSteamUserStats, "JANET", &set ) ){
178 if( set ) skaterift.achievements |= 0x4;
179 }
180 if( SteamAPI_ISteamUserStats_GetAchievement(
181 hSteamUserStats, "BERNADETTA", &set ) ){
182 if( set ) skaterift.achievements |= 0x8;
183 }
184 update_ach_models();
185 }
186 else{
187 vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
188 }
189 }
190
191 static u32 utf8_byte0_byte_count( u8 char0 )
192 {
193 for( u32 k=2; k<4; k++ ){
194 if( !(char0 & (0x80 >> k)) )
195 return k;
196 }
197
198 return 0;
199 }
200
201 static u32 str_utf8_collapse( const char *str, char *buf, u32 length ){
202 u8 *ustr = (u8 *)str;
203 u32 utf32_code = 0x00000000;
204 u32 i=0, j=0, utf32_byte_ct=0;
205
206 for(;j < length-1;){
207 if( ustr[i] == 0x00 )
208 break;
209
210 if( ustr[i] & 0x80 ){
211 if( utf32_byte_ct ){
212 utf32_byte_ct --;
213 utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
214
215 if( !utf32_byte_ct ){
216 const char *match;
217 size_t chars = anyascii( utf32_code, &match );
218
219 for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
220 buf[ j++ ] = (u8)match[k];
221 }
222 }
223 }
224 else{
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 utf32_byte_ct = 0x00;
232 buf[j ++] = str[i];
233 }
234
235 i++;
236 }
237
238 buf[j] = 0x00;
239 return j;
240 }
241
242 static int steam_init(void){
243 const char *username = "offline player";
244
245 vg_info( "Initializing steamworks\n" );
246
247 if( !SteamAPI_Init() ){
248 printf("\n");
249 vg_error( "Steamworks failed to initialize\n" );
250 return 1;
251 }
252
253 steam_ready = 1;
254
255 SteamAPI_ManualDispatch_Init();
256
257 /* Connect interfaces */
258 hSteamClientPipe = SteamAPI_GetHSteamPipe();
259 hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
260 hSteamUser = SteamAPI_SteamUser();
261
262 ISteamUtils *utils = SteamAPI_SteamUtils();
263 SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
264
265 printf("\n");
266 vg_success( "\nSteamworks API running\n" );
267
268 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
269 username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
270
271 /*
272 * Request stats
273 * --------------------------------------------------------
274 */
275 hSteamUserStats = SteamAPI_SteamUserStats();
276 steam_register_callback( k_iUserStatsReceived,
277 steam_on_recieve_current_stats );
278
279 if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
280 vg_warn( "No Steam Logon: Cannot request stats\n" );
281
282
283 vg_console_reg_cmd( "ach", steam_achievement_ccmd, NULL );
284
285 /* TODO: On username update callback */
286 str_utf8_collapse( username, steam_username_at_startup,
287 vg_list_size(steam_username_at_startup) );
288
289 return 1;
290 }
291
292 static void steam_update(void)
293 {
294 if( steam_ready ){
295 steamworks_event_loop( hSteamClientPipe );
296 }
297 }
298
299 static void steam_end(void)
300 {
301 if( steam_ready ){
302 vg_info( "Shutting down\n..." );
303 SteamAPI_Shutdown();
304 }
305 }