]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_data.cpp
Initial revision
[taylor/freespace2.git] / src / network / multi_data.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/multi_data.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * $Log$
8  * Revision 1.1  2002/05/03 03:28:10  root
9  * Initial revision
10  * 
11  * 
12  * 8     6/16/99 4:06p Dave
13  * New pilot info popup. Added new draw-bitmap-as-poly function.
14  * 
15  * 7     1/21/99 2:06p Dave
16  * Final checkin for multiplayer testing.
17  * 
18  * 6     1/14/99 6:06p Dave
19  * 100% full squad logo support for single player and multiplayer.
20  * 
21  * 5     12/18/98 12:31p Johnson
22  * Fixed a bug where the server would not properly redistribute a data
23  * file that he already had to other players.
24  * 
25  * 4     12/14/98 4:01p Dave
26  * Got multi_data stuff working well with new xfer stuff. 
27  * 
28  * 3     12/14/98 12:13p Dave
29  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
30  * Need to test now.
31  * 
32  * 2     10/07/98 10:53a Dave
33  * Initial checkin.
34  * 
35  * 1     10/07/98 10:50a Dave
36  * 
37  * 19    6/13/98 3:18p Hoffoss
38  * NOX()ed out a bunch of strings that shouldn't be translated.
39  * 
40  * 18    5/21/98 3:45a Sandeep
41  * Make sure file xfer sender side uses correct directory type.
42  * 
43  * 17    5/17/98 6:32p Dave
44  * Make sure clients/servers aren't kicked out of the debriefing when team
45  * captains leave a game. Fixed chatbox off-by-one error. Fixed image
46  * xfer/pilot info popup stuff.
47  * 
48  * 16    5/13/98 6:54p Dave
49  * More sophistication to PXO interface. Changed respawn checking so
50  * there's no window for desynchronization between the server and the
51  * clients.
52  * 
53  * 15    4/30/98 4:53p John
54  * Restructured and cleaned up cfile code.  Added capability to read off
55  * of CD-ROM drive and out of multiple pack files.
56  * 
57  * 14    4/21/98 4:44p Dave
58  * Implement Vasudan ships in multiplayer. Added a debug function to bash
59  * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
60  * problem in options screen. 
61  * 
62  * 13    4/16/98 1:55p Dave
63  * Removed unneeded Assert when processing chat packets. Fixed standalone
64  * sequencing bugs. Laid groundwork for join screen server status
65  * icons/text.
66  * 
67  * 12    4/14/98 12:19p Dave
68  * Revised the pause system yet again. Seperated into its own module.
69  * 
70  * 11    4/08/98 2:51p Dave
71  * Fixed pilot image xfer once again. Solidify team selection process in
72  * pre-briefing multiplayer.
73  * 
74  * 10    4/04/98 4:22p Dave
75  * First rev of UDP reliable sockets is done. Seems to work well if not
76  * overly burdened.
77  * 
78  * 9     3/30/98 6:27p Dave
79  * Put in a more official set of multiplayer options, including a system
80  * for distributing netplayer and netgame settings.
81  * 
82  * 8     3/26/98 6:01p Dave
83  * Put in file checksumming routine in cfile. Made pilot pic xferring more
84  * robust. Cut header size of voice data packets in half. Put in
85  * restricted game host query system.
86  * 
87  * 7     3/23/98 5:42p Dave
88  * Put in automatic xfer of pilot pic files. Changed multi_xfer system so
89  * that it can support multiplayer sends/received between client and
90  * server simultaneously.
91  * 
92  * 6     3/21/98 7:14p Dave
93  * Fixed up standalone player slot switching. Made training missions not
94  * count towards player stats.
95  * 
96  * 5     3/15/98 4:17p Dave
97  * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size
98  * of network orientation matrices.
99  * 
100  * 4     3/11/98 2:04p Dave
101  * Removed optimized build warnings.
102  * 
103  * 3     2/20/98 4:43p Dave
104  * Finished support for multiplayer player data files. Split off
105  * multiplayer campaign functionality.
106  * 
107  * 2     2/19/98 6:44p Dave
108  * Finished server getting data from players. Now need to rebroadcast the
109  * data.
110  * 
111  * 1     2/19/98 6:21p Dave
112  * Player data file xfer module.
113  * 
114  * $NoKeywords: $
115  */
116
117 #include "bmpman.h"
118 #include "psnet.h"
119 #include "multi_data.h"
120 #include "freespace.h"
121 #include "multi_xfer.h"
122 #include "multi.h"
123 #include "multimsgs.h"
124 #include "multiutil.h"
125 #include "player.h"
126
127 // -------------------------------------------------------------------------
128 // MULTI DATA DEFINES/VARS
129 //
130
131 #define MAX_MULTI_DATA                                  40
132
133 // player data struct
134 typedef struct np_data {
135         char filename[MAX_FILENAME_LEN+1];                      // filename
136         ubyte status[MAX_PLAYERS];                                              // status for all players (0 == don't have it, 1 == have or sending, 2 == i sent it originally)
137         ushort player_id;                                                                       // id of the player who sent the file
138         ubyte used;                                                                                     // in use or not
139 } np_data;
140
141 np_data Multi_data[MAX_MULTI_DATA];
142
143 time_t Multi_data_time = 0;
144
145
146 // -------------------------------------------------------------------------
147 // MULTI DATA FORWARD DECLARATIONS
148 //
149
150 // is the given filename for a "multi data" file (pcx, wav, etc)
151 int multi_data_is_data(char *filename);
152
153 // get a free np_data slot
154 int multi_data_get_free();
155
156 // server side - add a new file
157 int multi_data_add_new(char *filename, int player_index);
158
159 // maybe try and reload player squad logo bitmaps
160 void multi_data_maybe_reload();
161
162
163 // -------------------------------------------------------------------------
164 // MULTI DATA FUNCTIONS
165 //
166
167 // reset the data xfer system
168 void multi_data_reset()
169 {               
170         int idx;
171
172         // clear out all used bits
173         for(idx=0; idx<MAX_MULTI_DATA; idx++){
174                 Multi_data[idx].used = 0;
175         }
176 }
177
178 // handle a player join (clear out lists of files, etc)
179 void multi_data_handle_join(int player_index)
180 {
181         int idx;
182         
183         // clear out his status for all files           
184         for(idx=0;idx<MAX_MULTI_DATA;idx++){
185                 Multi_data[idx].status[player_index] = 0;
186         }
187 }
188
189 // handle a player drop (essentially the same as multi_data_handle_join)
190 void multi_data_handle_drop(int player_index)
191 {
192         int idx;
193                 
194         // mark all files he sent as being unused
195         for(idx=0;idx<MAX_MULTI_DATA;idx++){
196                 if(Multi_data[idx].player_id == Net_players[player_index].player_id){
197                         Multi_data[idx].used = 0;
198                 }
199         }
200 }
201
202 // do all sync related data stuff (server-side only)
203 void multi_data_do()
204 {               
205         int idx, p_idx;
206
207         // only do this once a second
208         if((time(NULL) - Multi_data_time) < 1){         
209                 return;
210         }
211
212         // maybe try and reload player squad logo bitmaps
213         multi_data_maybe_reload();
214
215         // reset the time
216         Multi_data_time = time(NULL);
217
218         // if I'm the server
219         if(Net_player && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
220                 // for all valid files
221                 for(idx=0; idx<MAX_MULTI_DATA; idx++){
222                         // a valid file that isn't currently xferring (ie, anything we've got completely on our hard drive)
223                         if(Multi_data[idx].used && (multi_xfer_lookup(Multi_data[idx].filename) < 0)){
224                                 // send it to all players who need it
225                                 for(p_idx=0; p_idx<MAX_PLAYERS; p_idx++){
226                                         // if he doesn't have it
227                                         if((Net_player != &Net_players[p_idx]) && MULTI_CONNECTED(Net_players[p_idx]) && (Net_players[p_idx].reliable_socket != INVALID_SOCKET) && (Multi_data[idx].status[p_idx] == 0)){                                               
228                                                 // queue up the file to send to him, or at least try to
229                                                 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){
230                                                         nprintf(("Network", "Failed to send data file! Trying again later...\n"));
231                                                 } else {
232                                                         // mark his status
233                                                         Multi_data[idx].status[p_idx] = 1;
234                                                 }
235                                         }
236                                 }
237                         }
238                 }
239         }
240 }
241
242 // handle an incoming xfer request from the xfer system
243 void multi_data_handle_incoming(int handle)
244 {       
245         int player_index = -1;
246         PSNET_SOCKET_RELIABLE sock = INVALID_SOCKET;    
247         char *fname;            
248
249         // get the player who is sending us this file   
250         sock = multi_xfer_get_sock(handle);
251         player_index = find_player_socket(sock);
252
253         // get the filename of the file
254         fname = multi_xfer_get_filename(handle);
255         if(fname == NULL){
256                 nprintf(("Network", "Could not get file xfer filename! wacky...!\n"));
257
258                 // kill the stream
259                 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
260                 return;
261         }
262
263         // if this is not a valid data file
264         if(!multi_data_is_data(fname)){
265                 nprintf(("Network", "Not accepting xfer request because its not a valid data file!\n"));
266                 
267                 // kill the stream              
268                 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);   
269                 return;
270         }       
271
272         // if we already have a copy of this file, abort the xfer               
273         // Does file exist in \multidata?
274         if( cf_exist(fname, CF_TYPE_MULTI_CACHE) ){                     
275                 nprintf(("Network", "Not accepting file xfer because a duplicate exists!\n"));                  
276         
277                 // kill the stream              
278                 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);   
279
280                 // if we're the server, we still need to add it to the list though
281                 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_index >= 0)){
282                         multi_data_add_new(fname, player_index);
283                 }
284                 return;
285         }       
286         
287         // if I'm the server of the game, do stuff a little differently
288         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
289                 if(player_index == -1){
290                         nprintf(("Network", "Could not find player for incoming player data stream!\n"));
291
292                         // kill the stream
293                         multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
294                         return;
295                 }       
296
297                 // attempt to add the file
298                 if(!multi_data_add_new(fname, player_index)){
299                         // kill the stream
300                         multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
301                         return;
302                 } else {
303                         // force the file to go into the multi cache directory
304                         multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE);
305                         
306                         // mark it as autodestroy                       
307                         multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY);
308                 }
309         }
310         // if i'm a client, this is an incoming file from the server
311         else {
312                 // if i'm not accepting pilot pics, abort
313                 if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX)){
314                         nprintf(("Network", "Client not accepting files because we don't want 'em!\n"));
315
316                         // kill the stream              
317                         multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);   
318                         return;
319                 }
320
321                 // set the xfer handle to autodestroy itself since we don't really want to have to manage all incoming pics
322                 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY);
323                 
324                 // force the file to go into the multi cache directory
325                 multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE);
326
327                 // begin receiving the file
328                 nprintf(("Network", "Client receiving xfer handle %d\n",handle));
329         }               
330 }
331
332 // send all my files as necessary
333 void multi_data_send_my_junk()
334 {               
335         char *with_ext; 
336         int bmap, w, h;
337         int ok_to_send = 1;
338
339         // pilot pic --------------------------------------------------------------
340
341         // verify that my pilot pic is valid
342         if(strlen(Net_player->player->image_filename)){
343                 bmap = bm_load(Net_player->player->image_filename);
344                 if(bmap == -1){                 
345                         ok_to_send = 0;
346                 }
347
348                 // verify image dimensions
349                 if(ok_to_send){
350                         w = -1;
351                         h = -1;
352                         bm_get_info(bmap,&w,&h);
353
354                         // release the bitmap
355                         bm_release(bmap);
356                         bmap = -1;
357
358                         // if the dimensions are invalid, kill the filename
359                         if((w != PLAYER_PILOT_PIC_W) || (h != PLAYER_PILOT_PIC_H)){
360                                 ok_to_send = 0;
361                         }
362                 }
363         } else {                
364                 ok_to_send = 0;
365         }
366
367         if(ok_to_send){
368                 with_ext = cf_add_ext(Net_player->player->image_filename, NOX(".pcx"));
369                 if(with_ext != NULL){
370                         strcpy(Net_player->player->image_filename, with_ext);
371                 }
372
373                 // host should put his own pic file in the list now
374                 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (strlen(Net_player->player->image_filename) > 0)){
375                         multi_data_add_new(Net_player->player->image_filename, MY_NET_PLAYER_NUM);
376                 }
377                 // otherwise clients should just queue up a send
378                 else {
379                         // add a file extension if necessary                    
380                         multi_xfer_send_file(Net_player->reliable_socket, Net_player->player->image_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE);
381                 }               
382         }
383
384
385         // squad logo --------------------------------------------------------------
386
387         // verify that my pilot pic is valid
388         ok_to_send = 1;
389         if(strlen(Net_player->player->squad_filename)){
390                 bmap = bm_load(Net_player->player->squad_filename);
391                 if(bmap == -1){                 
392                         ok_to_send = 0;
393                 }
394
395                 if(ok_to_send){
396                         // verify image dimensions
397                         w = -1;
398                         h = -1;
399                         bm_get_info(bmap,&w,&h);
400
401                         // release the bitmap
402                         bm_release(bmap);
403                         bmap = -1;
404
405                         // if the dimensions are invalid, kill the filename
406                         if((w != PLAYER_SQUAD_PIC_W) || (h != PLAYER_SQUAD_PIC_H)){
407                                 ok_to_send = 0;
408                         }
409                 }
410         } else {                
411                 ok_to_send = 0;
412         }
413
414         if(ok_to_send){
415                 with_ext = cf_add_ext(Net_player->player->squad_filename, NOX(".pcx"));
416                 if(with_ext != NULL){
417                         strcpy(Net_player->player->squad_filename,with_ext);
418                 }
419
420                 // host should put his own pic file in the list now
421                 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (strlen(Net_player->player->squad_filename) > 0)){
422                         multi_data_add_new(Net_player->player->squad_filename, MY_NET_PLAYER_NUM);
423                 }
424                 // otherwise clients should just queue up a send
425                 else {
426                         // add a file extension if necessary                    
427                         multi_xfer_send_file(Net_player->reliable_socket, Net_player->player->squad_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE);
428                 }               
429         }
430 }
431
432
433 // -------------------------------------------------------------------------
434 // MULTI DATA FORWARD DECLARATIONS
435 //
436
437 // is the give file xfer handle for a "multi data" file (pcx, wav, etc)
438 int multi_data_is_data(char *filename)
439 {               
440         int len,idx;
441
442         Assert(filename != NULL);
443
444         // some kind of error
445         if(filename == NULL){
446                 return 0;
447         }
448
449         // convert to lowercase
450         len = strlen(filename);
451         for(idx=0;idx<len;idx++){
452                 filename[idx] = (char)tolower(filename[idx]);
453         }
454
455         // check to see if the extension is .pcx
456         if(strstr(filename, NOX(".pcx"))){
457                 return 1;
458         }
459
460         // not a data file
461         return 0;
462 }
463
464 // get a free np_data slot
465 int multi_data_get_free()
466 {
467         int idx;
468
469         // find a free slot
470         for(idx=0; idx<MAX_MULTI_DATA; idx++){
471                 if(!Multi_data[idx].used){
472                         return idx;
473                 }
474         }
475
476         // couldn't find one
477         return -1;
478 }
479
480 // server side - add a new file. return 1 on success
481 int multi_data_add_new(char *filename, int player_index)
482 {       
483         int slot;
484                 
485         // try and get a free slot
486         slot = multi_data_get_free();
487         if(slot < 0){
488                 nprintf(("Network", "Could not get free np_data slot! yikes!\n"));              
489                 return 0;
490         }
491
492         // if the netgame isn't flagged as accepting data files
493         if(!(Netgame.options.flags & MSO_FLAG_ACCEPT_PIX)){
494                 nprintf(("Network", "Server not accepting pilot pic because we don't want 'em!\n"));    
495                 return 0;
496         }                       
497
498         // assign the data
499         memset(&Multi_data[slot], 0, sizeof(np_data));                                                          // clear the slot out
500         strcpy(Multi_data[slot].filename, filename);                                                                    // copy the filename
501         Multi_data[slot].used = 1;                                                                                                                      // set it as being in use
502         Multi_data[slot].player_id = Net_players[player_index].player_id;               // player id of who's sending the file
503         Multi_data[slot].status[player_index] = 2;                                                                      // mark his status appropriately
504
505         // success
506         return 1;
507 }
508
509 // maybe try and reload player squad logo bitmaps
510 void multi_data_maybe_reload()
511 {
512         int idx;
513
514         // go through all connected and try to reload their images if necessary
515         for(idx=0; idx<MAX_PLAYERS; idx++){
516                 if(MULTI_CONNECTED(Net_players[idx]) && strlen(Net_players[idx].player->squad_filename) && (Net_players[idx].player->insignia_texture == -1)){
517                         Net_players[idx].player->insignia_texture = bm_load_duplicate(Net_players[idx].player->squad_filename);
518
519                         // if the bitmap loaded properly, lock it as a transparent texture
520                         if(Net_players[idx].player->insignia_texture != -1){
521                                 bm_lock(Net_players[idx].player->insignia_texture, 16, BMP_TEX_XPARENT);
522                                 bm_unlock(Net_players[idx].player->insignia_texture);
523                         }
524                 }
525         }       
526 }