1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #include <ctime>
14 #include <cctype>
15 
16 #include "network/multi.h"
17 #include "network/multi_data.h"
18 #include "network/multi_xfer.h"
19 #include "network/multiutil.h"
20 #include "playerman/player.h"
21 #include "cfile/cfile.h"
22 #include "globalincs/systemvars.h"
23 
24 
25 
26 // -------------------------------------------------------------------------
27 // MULTI DATA DEFINES/VARS
28 //
29 
30 #define MAX_MULTI_DATA					40
31 
32 // player data struct
33 typedef struct np_data {
34 	ushort player_id;									// id of the player who sent the file
35 	char filename[MAX_FILENAME_LEN+1];			// filename
36 	ubyte status[MAX_PLAYERS];						// status for all players (0 == don't have it, 1 == have or sending, 2 == i sent it originally)
37 	ubyte used;											// in use or not
38 } np_data;
39 
40 np_data Multi_data[MAX_MULTI_DATA];
41 
42 // this doesn't travel off platform to don't _fs_time_t it
43 time_t Multi_data_time = 0;
44 
45 
46 // -------------------------------------------------------------------------
47 // MULTI DATA FORWARD DECLARATIONS
48 //
49 
50 // is the given filename for a "multi data" file (pcx, wav, etc)
51 int multi_data_is_data(char *filename);
52 
53 // get a free np_data slot
54 int multi_data_get_free();
55 
56 // server side - add a new file
57 int multi_data_add_new(char *filename, int player_index);
58 
59 // maybe try and reload player squad logo bitmaps
60 void multi_data_maybe_reload();
61 
62 
63 // -------------------------------------------------------------------------
64 // MULTI DATA FUNCTIONS
65 //
66 
67 // reset the data xfer system
multi_data_reset()68 void multi_data_reset()
69 {
70 	int idx;
71 
72 	// clear out all used bits
73 	for(idx=0; idx<MAX_MULTI_DATA; idx++){
74 		Multi_data[idx].used = 0;
75 	}
76 }
77 
78 // handle a player join (clear out lists of files, etc)
multi_data_handle_join(int player_index)79 void multi_data_handle_join(int player_index)
80 {
81 	int idx;
82 
83 	// clear out his status for all files
84 	for(idx=0;idx<MAX_MULTI_DATA;idx++){
85 		Multi_data[idx].status[player_index] = 0;
86 	}
87 }
88 
89 // handle a player drop (essentially the same as multi_data_handle_join)
multi_data_handle_drop(int player_index)90 void multi_data_handle_drop(int player_index)
91 {
92 	int idx;
93 
94 	// mark all files he sent as being unused
95 	for(idx=0;idx<MAX_MULTI_DATA;idx++){
96 		if(Multi_data[idx].player_id == Net_players[player_index].player_id){
97 			Multi_data[idx].used = 0;
98 		}
99 	}
100 }
101 
102 // do all sync related data stuff (server-side only)
multi_data_do()103 void multi_data_do()
104 {
105 	int idx, p_idx;
106 
107 	// only do this once a second
108 	if((time(NULL) - Multi_data_time) < 1){
109 		return;
110 	}
111 
112 	// maybe try and reload player squad logo bitmaps
113 	multi_data_maybe_reload();
114 
115 	// reset the time
116 	Multi_data_time = time(NULL);
117 
118 	// if I'm the server
119 	if(Net_player && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
120 		// for all valid files
121 		for(idx=0; idx<MAX_MULTI_DATA; idx++){
122 			// a valid file that isn't currently xferring (ie, anything we've got completely on our hard drive)
123 			if(Multi_data[idx].used && (multi_xfer_lookup(Multi_data[idx].filename) < 0)){
124 				// send it to all players who need it
125 				for(p_idx=0; p_idx<MAX_PLAYERS; p_idx++){
126 					// if he doesn't have it
127 					if ( (Net_player != &Net_players[p_idx]) && MULTI_CONNECTED(Net_players[p_idx]) && (Net_players[p_idx].reliable_socket != PSNET_INVALID_SOCKET) && (Multi_data[idx].status[p_idx] == 0) ) {
128 						// queue up the file to send to him, or at least try to
129 						if(multi_xfer_send_file(Net_players[p_idx].reliable_socket, Multi_data[idx].filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE) < 0){
130 							nprintf(("Network", "Failed to send data file! Trying again later...\n"));
131 						} else {
132 							// mark his status
133 							Multi_data[idx].status[p_idx] = 1;
134 						}
135 					}
136 				}
137 			}
138 		}
139 	}
140 }
141 
142 // handle an incoming xfer request from the xfer system
multi_data_handle_incoming(int handle)143 void multi_data_handle_incoming(int handle)
144 {
145 	int player_index = -1;
146 	PSNET_SOCKET_RELIABLE sock;
147 	char *fname;
148 
149 	// get the player who is sending us this file
150 	sock = multi_xfer_get_sock(handle);
151 	player_index = find_player_socket(sock);
152 
153 	// get the filename of the file
154 	fname = multi_xfer_get_filename(handle);
155 	if(fname == NULL){
156 		nprintf(("Network", "Could not get file xfer filename! wacky...!\n"));
157 
158 		// kill the stream
159 		multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
160 		return;
161 	}
162 
163 	// if this is not a valid data file
164 	if(!multi_data_is_data(fname)){
165 		nprintf(("Network", "Not accepting xfer request because its not a valid data file!\n"));
166 
167 		// kill the stream
168 		multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
169 		return;
170 	}
171 
172 	// if we already have a copy of this file, abort the xfer
173 	// Does file exist in \multidata?
174 	if (cf_exists(fname, CF_TYPE_MULTI_CACHE)) {
175 		nprintf(("Network", "Not accepting file xfer because a duplicate exists!\n"));
176 
177 		// kill the stream
178 		multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
179 
180 		// if we're the server, we still need to add it to the list though
181 		if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_index >= 0)){
182 			multi_data_add_new(fname, player_index);
183 		}
184 		return;
185 	}
186 
187 	// if I'm the server of the game, do stuff a little differently
188 	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
189 		if(player_index == -1){
190 			nprintf(("Network", "Could not find player for incoming player data stream!\n"));
191 
192 			// kill the stream
193 			multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
194 			return;
195 		}
196 
197 		// attempt to add the file
198 		if(!multi_data_add_new(fname, player_index)){
199 			// kill the stream
200 			multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
201 			return;
202 		} else {
203 			// force the file to go into the multi cache directory
204 			multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE);
205 
206 			// mark it as autodestroy
207 			multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY);
208 		}
209 	}
210 	// if i'm a client, this is an incoming file from the server
211 	else {
212 		// if i'm not accepting pilot pics, abort
213 		if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX)){
214 			nprintf(("Network", "Client not accepting files because we don't want 'em!\n"));
215 
216 			// kill the stream
217 			multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
218 			return;
219 		}
220 
221 		// set the xfer handle to autodestroy itself since we don't really want to have to manage all incoming pics
222 		multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY);
223 
224 		// force the file to go into the multi cache directory
225 		multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE);
226 
227 		// begin receiving the file
228 		nprintf(("Network", "Client receiving xfer handle %d\n",handle));
229 	}
230 }
231 
232 // send all my files as necessary
multi_data_send_my_junk()233 void multi_data_send_my_junk()
234 {
235 	char *with_ext;
236 	int bmap, w, h;
237 	int ok_to_send = 1;
238 
239 	// pilot pic --------------------------------------------------------------
240 
241 	// verify that my pilot pic is valid
242 	if(strlen(Net_player->m_player->image_filename)){
243 		bmap = bm_load(Net_player->m_player->image_filename);
244 		if(bmap == -1){
245 			ok_to_send = 0;
246 		}
247 
248 		// verify image dimensions
249 		if(ok_to_send){
250 			w = -1;
251 			h = -1;
252 			bm_get_info(bmap,&w,&h);
253 
254 			// release the bitmap
255 			bm_release(bmap);
256 			bmap = -1;
257 
258 			// if the dimensions are invalid, kill the filename
259 			if((w != PLAYER_PILOT_PIC_W) || (h != PLAYER_PILOT_PIC_H)){
260 				ok_to_send = 0;
261 			}
262 		}
263 	} else {
264 		ok_to_send = 0;
265 	}
266 
267 	if(ok_to_send){
268 		with_ext = cf_add_ext(Net_player->m_player->image_filename, NOX(".pcx"));
269 		if(with_ext != NULL){
270 			strcpy_s(Net_player->m_player->image_filename, with_ext);
271 		}
272 
273 		// host should put his own pic file in the list now
274 		if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (Net_player->m_player->image_filename[0] != '\0')){
275 			multi_data_add_new(Net_player->m_player->image_filename, MY_NET_PLAYER_NUM);
276 		}
277 		// otherwise clients should just queue up a send
278 		else {
279 			// add a file extension if necessary
280 			multi_xfer_send_file(Net_player->reliable_socket, Net_player->m_player->image_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE);
281 		}
282 	}
283 
284 
285 	// squad logo --------------------------------------------------------------
286 
287 	// verify that my pilot pic is valid
288 	ok_to_send = 1;
289 	if(strlen(Net_player->m_player->m_squad_filename)){
290 		bmap = bm_load(Net_player->m_player->m_squad_filename);
291 		if(bmap == -1){
292 			ok_to_send = 0;
293 		}
294 
295 		if(ok_to_send){
296 			// verify image dimensions
297 			w = -1;
298 			h = -1;
299 			bm_get_info(bmap,&w,&h);
300 
301 			// release the bitmap
302 			bm_release(bmap);
303 			bmap = -1;
304 
305 			// if the dimensions are invalid, kill the filename
306 			if((w != PLAYER_SQUAD_PIC_W) || (h != PLAYER_SQUAD_PIC_H)){
307 				ok_to_send = 0;
308 			}
309 		}
310 	} else {
311 		ok_to_send = 0;
312 	}
313 
314 	if(ok_to_send){
315 		with_ext = cf_add_ext(Net_player->m_player->m_squad_filename, NOX(".pcx"));
316 		if(with_ext != NULL){
317 			strcpy_s(Net_player->m_player->m_squad_filename,with_ext);
318 		}
319 
320 		// host should put his own pic file in the list now
321 		if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (Net_player->m_player->m_squad_filename[0] != '\0')){
322 			multi_data_add_new(Net_player->m_player->m_squad_filename, MY_NET_PLAYER_NUM);
323 		}
324 		// otherwise clients should just queue up a send
325 		else {
326 			// add a file extension if necessary
327 			multi_xfer_send_file(Net_player->reliable_socket, Net_player->m_player->m_squad_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE);
328 		}
329 	}
330 }
331 
332 
333 // -------------------------------------------------------------------------
334 // MULTI DATA FORWARD DECLARATIONS
335 //
336 
337 // is the give file xfer handle for a "multi data" file (pcx, wav, etc)
multi_data_is_data(char * filename)338 int multi_data_is_data(char *filename)
339 {
340 	Assert(filename != NULL);
341 
342 	// some kind of error
343 	if(filename == NULL){
344 		return 0;
345 	}
346 
347 	// convert to lowercase
348 	SCP_tolower(filename);
349 
350 	// check to see if the extension is .pcx
351 	if(strstr(filename, NOX(".pcx"))){
352 		return 1;
353 	}
354 
355 	// not a data file
356 	return 0;
357 }
358 
359 // get a free np_data slot
multi_data_get_free()360 int multi_data_get_free()
361 {
362 	int idx;
363 
364 	// find a free slot
365 	for(idx=0; idx<MAX_MULTI_DATA; idx++){
366 		if(!Multi_data[idx].used){
367 			return idx;
368 		}
369 	}
370 
371 	// couldn't find one
372 	return -1;
373 }
374 
375 // server side - add a new file. return 1 on success
multi_data_add_new(char * filename,int player_index)376 int multi_data_add_new(char *filename, int player_index)
377 {
378 	int slot;
379 
380 	// try and get a free slot
381 	slot = multi_data_get_free();
382 	if(slot < 0){
383 		nprintf(("Network", "Could not get free np_data slot! yikes!\n"));
384 		return 0;
385 	}
386 
387 	// if the netgame isn't flagged as accepting data files
388 	if(!(Netgame.options.flags & MSO_FLAG_ACCEPT_PIX)){
389 		nprintf(("Network", "Server not accepting pilot pic because we don't want 'em!\n"));
390 		return 0;
391 	}
392 
393 	// assign the data
394 	memset(&Multi_data[slot], 0, sizeof(np_data));								// clear the slot out
395 	strcpy_s(Multi_data[slot].filename, filename);									// copy the filename
396 	Multi_data[slot].used = 1;															// set it as being in use
397 	Multi_data[slot].player_id = Net_players[player_index].player_id;		// player id of who's sending the file
398 	Multi_data[slot].status[player_index] = 2;									// mark his status appropriately
399 
400 	// success
401 	return 1;
402 }
403 
404 // maybe try and reload player squad logo bitmaps
multi_data_maybe_reload()405 void multi_data_maybe_reload()
406 {
407 	int idx;
408 
409 	// go through all connected and try to reload their images if necessary
410 	for(idx=0; idx<MAX_PLAYERS; idx++){
411 		if(MULTI_CONNECTED(Net_players[idx]) && strlen(Net_players[idx].m_player->m_squad_filename) && (Net_players[idx].m_player->insignia_texture == -1)){
412 			Net_players[idx].m_player->insignia_texture = bm_load_duplicate(Net_players[idx].m_player->m_squad_filename);
413 
414 			// if the bitmap loaded properly, lock it as a transparent texture
415 			if(Net_players[idx].m_player->insignia_texture != -1){
416 				bm_lock(Net_players[idx].m_player->insignia_texture, 16, BMP_TEX_XPARENT);
417 				bm_unlock(Net_players[idx].m_player->insignia_texture);
418 			}
419 		}
420 	}
421 }
422