/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ /* * $Logfile: /Freespace2/code/Network/multi_pmsg.cpp $ * $Revision: 2.11.2.1 $ * $Date: 2006-10-24 13:18:35 $ * $Author: taylor $ * * $Log: not supported by cvs2svn $ * Revision 2.11 2006/03/30 04:15:20 Goober5000 * hehe, grammar * --Goober5000 * * Revision 2.10 2005/10/10 17:21:07 taylor * remove NO_NETWORK * * Revision 2.9 2005/07/13 03:35:32 Goober5000 * remove PreProcDefine #includes in FS2 * --Goober5000 * * Revision 2.8 2005/03/02 21:18:19 taylor * better support for Inferno builds (in PreProcDefines.h now, no networking support) * make sure NO_NETWORK builds are as friendly on Windows as it is on Linux/OSX * revert a timeout in Client.h back to the original value before Linux merge * * Revision 2.7 2005/02/04 10:12:31 taylor * merge with Linux/OSX tree - p0204 * * Revision 2.6 2004/07/26 20:47:42 Kazan * remove MCD complete * * Revision 2.5 2004/07/12 16:32:57 Kazan * MCD - define _MCD_CHECK to use memory tracking * * Revision 2.4 2004/03/05 09:02:02 Goober5000 * Uber pass at reducing #includes * --Goober5000 * * Revision 2.3 2002/08/01 01:41:08 penguin * The big include file move * * Revision 2.2 2002/07/22 01:22:25 penguin * Linux port -- added NO_STANDALONE ifdefs * * Revision 2.1 2002/07/07 19:55:59 penguin * Back-port to MSVC * * Revision 2.0 2002/06/03 04:02:26 penguin * Warpcore CVS sync * * Revision 1.1 2002/05/02 18:03:11 mharris * Initial checkin - converted filenames and includes to lower case * * * 7 3/10/99 6:50p Dave * Changed the way we buffer packets for all clients. Optimized turret * fired packets. Did some weapon firing optimizations. * * 6 3/09/99 6:24p Dave * More work on object update revamping. Identified several sources of * unnecessary bandwidth. * * 5 2/24/99 2:25p Dave * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn * bug for dogfight more. * * 4 11/19/98 8:03a Dave * Full support for D3-style reliable sockets. Revamped packet lag/loss * system, made it receiver side and at the lowest possible level. * * 3 11/17/98 11:12a Dave * Removed player identification by address. Now assign explicit id #'s. * * 2 10/07/98 10:53a Dave * Initial checkin. * * 1 10/07/98 10:50a Dave * * 18 6/13/98 9:32p Mike * Kill last character in file which caused "Find in Files" to report the * file as "not a text file." * * 17 6/13/98 6:01p Hoffoss * Externalized all new (or forgot to be added) strings to all the code. * * 16 6/13/98 3:19p Hoffoss * NOX()ed out a bunch of strings that shouldn't be translated. * * 15 5/13/98 6:54p Dave * More sophistication to PXO interface. Changed respawn checking so * there's no window for desynchronization between the server and the * clients. * * 14 5/08/98 5:05p Dave * Go to the join game screen when quitting multiplayer. Fixed mission * text chat bugs. Put mission type symbols on the create game list. * Started updating standalone gui controls. * * 13 5/02/98 5:38p Dave * Put in new tracker API code. Put in ship information on mp team select * screen. Make standalone server name permanent. Fixed standalone server * text messages. * * 12 4/23/98 6:18p Dave * Store ETS values between respawns. Put kick feature in the text * messaging system. Fixed text messaging system so that it doesn't * process or trigger ship controls. Other UI fixes. * * 11 4/22/98 4:59p Allender * new multiplayer dead popup. big changes to the comm menu system for * team vs. team. Start of debriefing stuff for team vs. team Make form * on my wing work with individual ships who have high priority orders * * 10 4/16/98 6:35p Dave * Display more informative prompt when typing in a text message. * * 9 4/06/98 10:24p Dave * Fixed up Netgame.respawn for the standalone case. * * 8 4/05/98 3:30p Dave * Print netplayer messages in brighter green on the hud, with * accompanying sound. Echo netplayer messages on sending machine. Fixed * standalone sequencing bug where host never get the "enter mission" * button. * * 7 4/04/98 4:22p Dave * First rev of UDP reliable sockets is done. Seems to work well if not * overly burdened. * * 6 4/03/98 1:03a Dave * First pass at unreliable guaranteed delivery packets. * * 5 4/02/98 5:50p Dave * Put in support for standard comm messages to get sent to netplayers as * well as ai ships. Make critical button presses not get evaluated on the * observer. * * 4 4/01/98 5:56p Dave * Fixed a messaging bug which caused msg_all mode in multiplayer not to * work. Compile out a host of multiplayer options not available in the * demo. * * 3 3/27/98 11:57a Dave * Put in expression checking for text messages. * * 2 3/25/98 2:16p Dave * Select random default image for newly created pilots. Fixed several * multi-pause messaging bugs. Begin work on online help for multiplayer * keys. * * 1 3/19/98 5:04p Dave * * * $NoKeywords: $ */ #include #include "network/multi_pmsg.h" #include "network/multi.h" #include "network/multimsgs.h" #include "network/multiutil.h" #include "network/multi_kick.h" #include "gamesnd/gamesnd.h" #include "hud/hudmessage.h" #include "hud/hudsquadmsg.h" #include "io/key.h" #include "io/timer.h" #include "playerman/player.h" #include "ship/ship.h" #include "object/object.h" #include "parse/parselo.h" #include "sound/fsspeech.h" // ---------------------------------------------------------------------------------- // MULTI MESSAGING DEFINES/VARS // // if a key is down less than this time, fire up the test messaging system, otherwise fire up the voice messaging system #define MULTI_MSG_KEYDOWN_WAIT 325 // in ms // sound to play before displaying incoming text messages in-mission #define MULTI_MSG_TEXT_SOUND SND_CUE_VOICE // max length of a string we'll allow players to send #define MULTI_MSG_MAX_LEN 75 // current message processing mode int Multi_msg_mode = MULTI_MSG_NONE; // timestamp for timing keydown int Multi_msg_stamp = -1; // flag indicating if there is _still_ a key down for the current message mode int Multi_msg_repeat_flag = 0; // timestamp set when we leave messaging mode, use to keep eating keys for a short period of time int Multi_msg_eat_stamp = -1; // text message input vars int Multi_msg_text_enter = 0; char Multi_msg_text[MULTI_MSG_MAX_TEXT_LEN+1]; // command defines - all these commands must be followed by a ":" so that we can easily tokenize and recognize // it as a command instead of a word. they also must be immediately at the beginning of a text string // SO : kick dave would not work // kick: dave would work // Finally, if no command is found but there is a ":", it uses the text before the : as an expression to // lookup players to route the text to #define MULTI_MSG_CMD_COUNT 1 // # of commands #define MULTI_MSG_CMD_KICK 0 // kick command //XSTR:OFF char *Multi_msg_commands[MULTI_MSG_CMD_COUNT] = { // commands themselves "kick" }; //XSTR:ON // process an entered line of text and maybe perform a command on it (return 1 if an action was performed, 0 if not) int multi_msg_check_command(char *str); // perform the passed command (MULTI_MSG_CMD_* define) with the passed string argument void multi_msg_perform_command(int command,char *param); // ---------------------------------------------------------------------------------- // MULTI MESSAGING FUNCTIONS // // called when a messaging key has been detected as being pressed void multi_msg_key_down(int mode) { // keep eating keys for a short period of time if((Multi_msg_eat_stamp != -1) && !timestamp_elapsed(Multi_msg_eat_stamp)){ return; } // if our player flags are marked as being in msg mode, don't do anything if(Player->flags & PLAYER_FLAGS_MSG_MODE){ return; } // if there already is a keydown if(Multi_msg_mode != MULTI_MSG_NONE){ // if it is the same as the current mode, set the "still down" flag if((mode == Multi_msg_mode) && !Multi_msg_text_enter){ Multi_msg_repeat_flag = 1; } // return here return; } // otherwise set the message mode and set the timestamp Multi_msg_mode = mode; Multi_msg_repeat_flag = 1; Multi_msg_stamp = timestamp(MULTI_MSG_KEYDOWN_WAIT); } // returns true when messaging system has determined that we should be messaging with voice int multi_msg_voice_record() { return ((Multi_msg_mode != MULTI_MSG_NONE) && timestamp_elapsed(Multi_msg_stamp) && Multi_msg_repeat_flag && !Multi_msg_text_enter) ? 1 : 0; } // general processing function to do things like timing keydown, etc. call from multi_do_frame() void multi_msg_process() { // keep eating keys for a short period of time if((Multi_msg_eat_stamp != -1) && timestamp_elapsed(Multi_msg_eat_stamp)){ Multi_msg_eat_stamp = -1; return; } // if we don't currently have a valid mode set, don't do anything if(Multi_msg_mode == MULTI_MSG_NONE){ return; } // if the key has been released if(!Multi_msg_repeat_flag && (Multi_msg_stamp != -1) && !Multi_msg_text_enter){ // if the timestamp had not yet elapsed, fire up the text messaging system // this is the equivalent of a (TAP) if(!timestamp_elapsed(Multi_msg_stamp) && !Multi_msg_text_enter){ // fire up text messaging system here Multi_msg_text_enter = 1; memset(Multi_msg_text,0,MULTI_MSG_MAX_TEXT_LEN+1); } else { Multi_msg_mode = MULTI_MSG_NONE; Multi_msg_stamp = -1; } } // unset the repeat flag every frame Multi_msg_repeat_flag = 0; } // get the current messaging mode int multi_msg_mode() { return Multi_msg_mode; } // return 0 or 1 if in text chat mode or not int multi_msg_text_mode() { return Multi_msg_text_enter; } // process a text string entered by the local player void multi_msg_eval_text_msg() { int player_index; // if its a 0 length string, don't do anything if(strlen(Multi_msg_text) <= 0){ return; } // evaluate any special commands here if(multi_msg_check_command(Multi_msg_text)){ return; } // get the player if in MSG_TARGET mode if(Multi_msg_mode == MULTI_MSG_TARGET){ if(Player_ai->target_objnum != -1){ player_index = multi_find_player_by_object(&Objects[Player_ai->target_objnum]); if(player_index != -1){ // send the chat packet send_game_chat_packet(Net_player, Multi_msg_text, Multi_msg_mode, &Net_players[player_index]); // echo the message locally multi_msg_display_mission_text(Multi_msg_text, MY_NET_PLAYER_NUM); } } } // all other modes else { // send the chat packet send_game_chat_packet(Net_player, Multi_msg_text, Multi_msg_mode, NULL); // echo the message locally multi_msg_display_mission_text(Multi_msg_text, MY_NET_PLAYER_NUM); } } // maybe process a keypress in text messaging mode, return true if the key was processed int multi_msg_text_process(int k) { char str[2]; // keep eating keys for a short period of time if((Multi_msg_eat_stamp != -1) && !timestamp_elapsed(Multi_msg_eat_stamp)){ return 1; } // if we're not in text message mode, return 0 if(!Multi_msg_text_enter){ return 0; } switch(k){ // cancel the message case KEY_ESC: multi_msg_text_flush(); break; // send the message case KEY_ENTER: multi_msg_eval_text_msg(); multi_msg_text_flush(); break; // backspace case KEY_BACKSP: if(strlen(Multi_msg_text) > 0){ Multi_msg_text[strlen(Multi_msg_text)-1] = '\0'; } break; // ignore these individual keys case KEY_LSHIFT + KEY_SHIFTED: case KEY_RSHIFT + KEY_SHIFTED: case KEY_LALT + KEY_SHIFTED: case KEY_RALT + KEY_SHIFTED: case KEY_LCTRL + KEY_SHIFTED: case KEY_RCTRL + KEY_SHIFTED: break; // stick other printable characters onto the text default : // if we're not already at the maximum length if(strlen(Multi_msg_text) < MULTI_MSG_MAX_LEN){ str[0] = (char)key_to_ascii(k); str[1] = '\0'; strcat(Multi_msg_text,str); } break; } return 1; } // return 0 or 1 if there is multi text to be rendered (filling in txt if necessary) int multi_msg_message_text(char *txt) { // if we're not in text message mode, return 0 if(!Multi_msg_text_enter){ return 0; } // put the target of the message at the front of the string switch(Multi_msg_mode){ // messaging all players case MULTI_MSG_ALL: strcpy(txt,XSTR("ALL : ",694)); break; // messaging friendly players case MULTI_MSG_FRIENDLY: strcpy(txt,XSTR("FRIENDLY : ",695)); break; // messaging hostile players case MULTI_MSG_HOSTILE: strcpy(txt,XSTR("HOSTILE : ",696)); break; // messaging targeted ship case MULTI_MSG_TARGET: strcpy(txt,XSTR("TARGET : ",697)); break; default : Int3(); } strcat(txt,Multi_msg_text); strcat(txt,"_"); return 1; } // display ingame,inmission message text void multi_msg_display_mission_text(char *msg,int player_index) { // play a cue voice sound and text to speech if not from this player if(Net_players[player_index].player_id != MY_NET_PLAYER_NUM) { snd_play(&Snds[MULTI_MSG_TEXT_SOUND]); fsspeech_play(FSSPEECH_FROM_MULTI, msg); } if(MULTI_STANDALONE(Net_players[player_index])){ HUD_sourced_printf(HUD_SOURCE_NETPLAYER,"%s %s",XSTR("", 698), msg); } else { HUD_sourced_printf(HUD_SOURCE_NETPLAYER,"%s: %s", Net_players[player_index].m_player->callsign, msg); } } // if the passed net_player's callsign matches the reg expression of the passed expr int multi_msg_matches_expr(net_player *np,char *expr) { char callsign[CALLSIGN_LEN+1]; int len,idx; // some error checking if((np == NULL) || (expr == NULL) || (strlen(expr) <= 0)){ return 0; } // get the completely lowercase callsign memset(callsign,0,CALLSIGN_LEN+1); len = strlen(np->m_player->callsign); for(idx=0;idxm_player->callsign[idx]); } // see if this guy's callsign matches the expr len = strlen(expr); for(idx=0;idx : ",699),source->m_player->callsign); // get the target obj if possible target_obj = NULL; target_obj = multi_get_network_object(target_sig); should_display = 1; // add the command specific text switch(command){ // attack my target case ATTACK_TARGET_ITEM : if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){ sprintf(temp_string,XSTR("Attack %s",700),Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // disable my target case DISABLE_TARGET_ITEM: if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){ sprintf(temp_string,XSTR("Disable %s",701),Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // protect my target case PROTECT_TARGET_ITEM: if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){ sprintf(temp_string,XSTR("Protect %s",702),Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // ignore my target case IGNORE_TARGET_ITEM: if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){ sprintf(temp_string,XSTR("Ignore %s",703),Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // disarm my target case DISARM_TARGET_ITEM: if((target_obj != NULL) && (target_obj->type == OBJ_SHIP)){ sprintf(temp_string,XSTR("Disarm %s",704),Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // disable subsystem on my target case DISABLE_SUBSYSTEM_ITEM: if((target_obj != NULL) && (target_obj->type == OBJ_SHIP) && (subsys_type != -1) && (subsys_type != 0)){ sprintf(temp_string,XSTR("Disable subsystem %s on %s",705),Multi_msg_subsys_name[subsys_type],Ships[target_obj->instance].ship_name); strcat(hud_string,temp_string); } else { should_display = 0; } break; // form on my wing case FORMATION_ITEM: strcat(hud_string,XSTR("Form on my wing",706)); break; // cover me case COVER_ME_ITEM: strcat(hud_string,XSTR("Cover me",707)); break; // engage enemy case ENGAGE_ENEMY_ITEM: strcat(hud_string,XSTR("Engage enemy!",708)); break; default : should_display =0; break; } // print it out if(should_display){ HUD_printf(hud_string); } } // evaluate if the given netplayer exists in the passed wingnum int multi_msg_player_in_wing(int wingnum,net_player *pl) { int idx; // if this guy doesn't have a valid ship, bail if((pl->m_player->objnum == -1) || (Objects[pl->m_player->objnum].type != OBJ_SHIP)){ return 0; } // look through all ships in the wing for(idx=0;idxm_player->objnum].instance){ return 1; } } return 0; } // evaluate if the given netplayer is flying the passed shipnum int multi_msg_player_in_ship(int shipnum,net_player *pl) { // if we found a matching ship if((pl->m_player->objnum != -1) && (Objects[pl->m_player->objnum].type == OBJ_SHIP) && (shipnum == Objects[pl->m_player->objnum].instance)){ return 1; } // not a matching ship return 0; } // send a squadmsg packet to a player void multi_msg_send_squadmsg_packet(net_player *target,net_player *source,int command,ushort net_sig,int subsys_type) { ubyte data[100]; char s_val; int packet_size; Assert(source != NULL); Assert(target != NULL); if((source == NULL) || (target == NULL)){ return; } // build the header BUILD_HEADER(SQUADMSG_PLAYER); // add the command and targeting data ADD_INT(command); // add the id of the guy sending the order ADD_SHORT(source->player_id); // net signature ADD_USHORT(net_sig); // targeted subsytem (or -1 if none) s_val = (char)subsys_type; ADD_DATA(s_val); // send to the player multi_io_send_reliable(target, data, packet_size); } // evaluate if a wing SQUADMATE MESSAGE command should be sent to a player // return 0 if at least one ai ship got the order, 1 if only players int multi_msg_eval_wing_squadmsg(int wingnum,int command,ai_info *aif, int player_num) { int idx; ushort net_sig; int subsys_type; int sent_count; // get the index of the sender if(player_num == -1) player_num = MY_NET_PLAYER_NUM; // get the target information if(aif->target_objnum == -1){ net_sig = 0; } else { net_sig = Objects[aif->target_objnum].net_signature; } subsys_type = -1; if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){ subsys_type = -1; } else { subsys_type = aif->targeted_subsys->system_info->type; } // go through all netplayers and find all matched sent_count = Wings[wingnum].current_count; for(idx=0;idxtarget_objnum == -1){ net_sig = 0; } else { net_sig = Objects[aif->target_objnum].net_signature; } subsys_type = -1; if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){ subsys_type = -1; } else { subsys_type = aif->targeted_subsys->system_info->type; } // go through all netplayers and find all matched for(idx=0;idx