build scripts
[fishladder.git] / steam.h
1 #ifndef STEAM_H
2 #define STEAM_H
3
4 #define VG_GAME
5 #include "vg/vg_steam.h"
6 #include "vg/vg_steam_utils.h"
7 #include "vg/vg_steam_networking.h"
8 #include "vg/vg_steam_auth.h"
9 #include "vg/vg_steam_http.h"
10 #include "vg/vg_steam_friends.h"
11 #include "vg/vg_steam_user_stats.h"
12 #include "submodules/anyascii/impl/c/anyascii.c"
13
14 /*
15 * We only want to use steamworks if building for the networked version,
16 * theres not much point otherwise. We mainly want steamworks for setting
17 * achievements etc.. so that includes our own server too.
18 *
19 * This file also wraps the functions and interfaces that we want to use to
20 * make them a bit easier to read, since they are the flat API they have very
21 * long names. in non-networked builds they will return default errors or do
22 * nothing.
23 */
24
25 static char steam_username_at_startup[128] = "Unassigned";
26
27 static void recv_steam_warning( int severity, const char *msg )
28 {
29 if( severity == 0 )
30 vg_low( "%s\n", msg );
31 else
32 vg_info( "%s\n", msg );
33 }
34
35 static int steam_ready = 0,
36 steam_stats_ready = 0;
37
38 static void *hSteamNetworkingSockets,
39 *hSteamUser;
40
41 static ISteamUserStats *hSteamUserStats;
42 static HSteamPipe hSteamClientPipe;
43
44 enum mc_achievement {
45 k_mc_achievement_master_engineer,
46 k_mc_achievement_can_do_that,
47 k_mc_achievement_bang,
48 k_mc_achievement_good_enough,
49 k_mc_achievement_tutorials,
50 k_mc_achievement_mighty_consumer,
51 k_mc_achievement_graduate,
52 k_mc_achievement_max
53 };
54
55 static const char *steam_achievement_names[] = {
56 [k_mc_achievement_master_engineer] = "MASTER_ENGINEER",
57 [k_mc_achievement_can_do_that] = "CAN_DO_THAT",
58 [k_mc_achievement_bang] = "BANG",
59 [k_mc_achievement_good_enough] = "GOOD_ENOUGH",
60 [k_mc_achievement_tutorials] = "TUTORIALS",
61 [k_mc_achievement_mighty_consumer] = "MIGHTY_CONSUMER",
62 [k_mc_achievement_graduate] = "GRADUATE"
63 };
64
65 static bool steam_achievement_set[k_mc_achievement_max];
66
67 static void steam_store_achievements(void){
68 if( steam_ready && steam_stats_ready ){
69 SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
70 }
71 }
72
73 static void steam_set_achievement( enum mc_achievement id ){
74 if( steam_achievement_set[ id ] ) return;
75 const char *name = steam_achievement_names[ id ];
76
77 if( steam_ready && steam_stats_ready ){
78 if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
79 vg_success( "Achievement set! '%s'\n", name );
80
81 }
82 else{
83 vg_warn( "Failed to set achievement: %s\n", name );
84 }
85 }
86 else{
87 vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
88 }
89
90 steam_achievement_set[ id ] = 1;
91 }
92
93 static void sw_set_achievement( const char *name ){
94 for( u32 i=0; i<k_mc_achievement_max; i ++ ){
95 if( !strcmp( name, steam_achievement_names[i] ) ){
96 steam_set_achievement( i );
97 steam_store_achievements();
98 return;
99 }
100 }
101 }
102
103 static void steam_clear_achievement( const char *name ){
104 if( steam_ready && steam_stats_ready ){
105 if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
106 vg_info( "Achievement cleared: '%s'\n", name );
107 }
108 else{
109 vg_warn( "Failed to clear achievement: %s\n", name );
110 }
111 }
112 else{
113 vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
114 }
115 }
116
117
118 static void steam_print_all_achievements(void){
119 vg_info( "Achievements: \n" );
120
121 if( steam_ready && steam_stats_ready ){
122 for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
123 steamapi_bool set = 0;
124 const char *name = steam_achievement_names[i];
125
126 if( SteamAPI_ISteamUserStats_GetAchievement(
127 hSteamUserStats, name, &set ) )
128 {
129 vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
130 }
131 else{
132 vg_warn( " Error while fetching achievement status '%s'\n", name );
133 }
134 }
135 }
136 else{
137 vg_warn( " Steam is not initialized, no results\n" );
138 }
139 }
140
141 static int steam_achievement_ccmd( int argc, char const *argv[] )
142 {
143 if( !(steam_ready && steam_stats_ready) ){
144 vg_error( "steam_ready: %d, steam_stats_ready: %d\n",
145 steam_ready, steam_stats_ready );
146 return 1;
147 }
148
149 if( argc == 1 ){
150 if( !strcmp( argv[0], "list" ) ){
151 steam_print_all_achievements();
152 return 0;
153 }
154 else if( !strcmp( argv[0], "clearall" )){
155 for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
156 steam_clear_achievement( steam_achievement_names[i] );
157
158 steam_store_achievements();
159 }
160 }
161
162 if( argc == 2 ){
163 if( !strcmp( argv[0], "set" ) ){
164 sw_set_achievement( argv[1] );
165 return 0;
166 }
167 else if( strcmp( argv[0], "clear" ) ){
168 steam_clear_achievement( argv[1] );
169 steam_store_achievements();
170 return 0;
171 }
172 }
173
174 return 1;
175 }
176
177 static void steam_on_recieve_current_stats( CallbackMsg_t *msg )
178 {
179 UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
180
181 if( rec->m_eResult == k_EResultOK ){
182 vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
183 rec->m_steamIDUser );
184 steam_stats_ready = 1;
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 vg_console_reg_cmd( "ach", steam_achievement_ccmd, NULL );
283
284 /* TODO: On username update callback */
285 str_utf8_collapse( username, steam_username_at_startup,
286 vg_list_size(steam_username_at_startup) );
287
288 return 1;
289 }
290
291 static void steam_update(void)
292 {
293 if( steam_ready ){
294 steamworks_event_loop( hSteamClientPipe );
295 }
296 }
297
298 static void steam_end(void)
299 {
300 if( steam_ready ){
301 vg_info( "Shutting down\n..." );
302 SteamAPI_Shutdown();
303 }
304 }
305
306 #endif /* STEAM_H */