/* * 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_campaign.cpp $ * $Revision: 2.14.2.3 $ * $Date: 2007-10-15 06:43:16 $ * $Author: taylor $ * * $Log: not supported by cvs2svn $ * Revision 2.14.2.2 2006/07/15 18:15:35 taylor * missed the extern apparently * * Revision 2.14.2.1 2006/07/15 15:50:49 taylor * we probably need to be processing the number of weapons here rather than the number of ships ;) (Mantis bug #988) * * Revision 2.14 2005/12/29 08:08:39 wmcoolmon * Codebase commit, most notably including objecttypes.tbl * * Revision 2.13 2005/10/10 17:21:07 taylor * remove NO_NETWORK * * Revision 2.12 2005/07/13 03:25:59 Goober5000 * remove PreProcDefine #includes in FS2 * --Goober5000 * * Revision 2.11 2005/05/12 17:49:15 taylor * use vm_malloc(), vm_free(), vm_realloc(), vm_strdup() rather than system named macros * fixes various problems and is past time to make the switch * * Revision 2.10 2005/04/25 00:28:17 wmcoolmon * MAX_SHIP_CLASSES > Num_ship_classes * * Revision 2.9 2005/04/11 05:50:36 taylor * some limits.h fixes to make GCC happier * revert timer asm change since it doesn't even get used with Linux and couldn't have been the slowdown problem * * 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 2003/11/11 02:15:45 Goober5000 * ubercommit - basically spelling and language fixes with some additional * warnings disabled * --Goober5000 * * Revision 2.2 2002/08/01 01:41:07 penguin * The big include file move * * Revision 2.1 2002/07/22 01:22:25 penguin * Linux port -- added NO_STANDALONE ifdefs * * Revision 2.0 2002/06/03 04:02:26 penguin * Warpcore CVS sync * * Revision 1.1 2002/05/02 18:03:10 mharris * Initial checkin - converted filenames and includes to lower case * * * 6 3/10/99 6:50p Dave * Changed the way we buffer packets for all clients. Optimized turret * fired packets. Did some weapon firing optimizations. * * 5 3/09/99 6:24p Dave * More work on object update revamping. Identified several sources of * unnecessary bandwidth. * * 4 12/12/98 3:17p Andsager * Clean up mission eval, goal, event and mission scoring. * * 3 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. * * 2 10/07/98 10:53a Dave * Initial checkin. * * 1 10/07/98 10:50a Dave * * 31 6/13/98 6:01p Hoffoss * Externalized all new (or forgot to be added) strings to all the code. * * 30 5/23/98 7:09p Dave * Fixed a potentially serious ingame join campaign bug. * * 29 5/23/98 4:28p Jasen * Insure goals pointer is not null. * * 28 5/22/98 11:11a Allender * more ingame join campaign fixes * * 27 5/21/98 11:34p Allender * ingame join fixes * * 26 5/21/98 2:03a Allender * fix for campaign misison names * * 25 5/21/98 12:14a Allender * fix ingame join problems * * 24 5/20/98 3:25p Allender * ingame join changes (which probably won't make the final version). * Added RAS code into psnet * * 23 5/19/98 8:35p Dave * Revamp PXO channel listing system. Send campaign goals/events to * clients for evaluation. Made lock button pressable on all screens. * * 22 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. * * 21 5/06/98 12:36p Dave * Make sure clients can leave the debrief screen easily at all times. Fix * respawn count problem. * * 20 5/05/98 7:25p Adam * Fixed a few potential sequencing problems (packets getting lost or * ignored when doing state transitions) * * 19 5/05/98 5:02p Dave * Fix end-of-campaign sequencing to work right. Make all individual * missions of a campaign replayable. * * 18 5/05/98 2:10p Dave * Verify campaign support for testing. More new tracker code. * * 17 5/04/98 10:39p Dave * Put in endgame sequencing. Need to check campaign situations. * Realigned ship info on team select screen. * * 16 4/22/98 5:52p Dave * Large reworking of endgame sequencing. Updated host options screen for * new artwork. Put in checks to end game if host leaves or if team * captains leave mid-game. * * 15 4/20/98 4:56p Allender * allow AI ships to respawn as many times as there are respawns in the * mission. * * 14 4/06/98 10:24p Dave * Fixed up Netgame.respawn for the standalone case. * * 13 4/06/98 6:37p Dave * Put in max_observers netgame server option. Make sure host is always * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS * can always join but need to be kicked before commit can happen. Put in * support for server ending a game and notifying clients of a special * condition. * * 12 3/24/98 4:59p Dave * Fixed several ui bugs. Put in pre and post voice stream playback sound * fx. Put in error specific popups for clients getting dropped from games * through actions other than their own. * * 11 3/17/98 12:16a Allender * asteroids in multiplayer -- minor problems with position being correct * * 10 3/16/98 2:35p Dave * Numerous bug fixes. Made the "cue sound" sound play before incoming * voice. * * 9 3/15/98 4:17p Dave * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size * of network orientation matrices. * * 8 3/12/98 10:45p Allender * more ingame join stuff. Mission events are not evaluated during * critical ingame portion. Wings/ships appear to work great. Support * ships still has a few problems I think * * 7 3/12/98 5:45p Dave * Put in new observer HUD. Made it possible for observers to join at the * beginning of a game and follow it around as an observer full-time. * * 6 3/05/98 5:03p Dave * More work on team vs. team support for multiplayer. Need to fix bugs in * weapon select. * * 5 3/03/98 5:12p Dave * 50% done with team vs. team interface issues. Added statskeeping to * secondary weapon blasts. Numerous multiplayer ui bug fixes. * * 4 2/23/98 11:09p Dave * Finished up multiplayer campaign support. Seems bug-free. * * 3 2/23/98 5:08p Allender * made net_signature an unsigned short. Now using permanent and * non-permanent object "pools". * * 2 2/22/98 2:53p Dave * Put in groundwork for advanced multiplayer campaign options. * * 1 2/20/98 4:39p Dave * Split up mp campaign functionality into its own module. * * $NoKeywords: $ */ #include // this is need even when not building debug!! #include "gamesequence/gamesequence.h" #include "network/multi.h" #include "network/multiui.h" #include "freespace2/freespace.h" #include "network/multimsgs.h" #include "network/multiutil.h" #include "popup/popup.h" #include "network/multi_campaign.h" #include "network/multi_endgame.h" #include "network/stand_gui.h" #include "mission/missiongoals.h" #include "mission/missioncampaign.h" #include "mission/missionparse.h" // ------------------------------------------------------------------------------------ // MULTIPLAYER CAMPAIGN DEFINES/VARS // // packet codes #define MC_CODE_POOL 0 // pool/weapons update #define MC_CODE_DONE 1 // campaign is "done" #define MC_CODE_DEBRIEF 2 // debrief info #define MC_CODE_START 3 // start campaign information // packet code for ingame joining #define MC_JIP_INITIAL_PACKET 1 // initial data #define MC_JIP_GE_STATUS 2 // goal event status for all missions #define MC_JIP_GOAL_NAMES 3 // goal names #define MC_JIP_EVENT_NAMES 4 // event names #define MC_JIP_END_DATA 5 // this is the end of the data #define MC_INGAME_DATA_SLOP 50 // flags indicating the "accept" status of all players involved in a campaign int Multi_campaign_accept_flags[MAX_PLAYERS]; // ------------------------------------------------------------------------------------ // MULTIPLAYER CAMPAIGN FUNCTIONS // // load a new campaign file or notify the standalone if we're not the server void multi_campaign_start(char *filename) { int max_players; char str[255]; // set the netgame mode Netgame.campaign_mode = MP_CAMPAIGN; // set the campaign filename strcpy(Netgame.campaign_name,filename); // add the campaign mode flag Game_mode |= GM_CAMPAIGN_MODE; // if we're the server of the game we should also be loading the campaign up. otherwise, we let the standalone do it if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // start the campaign, passing 0 so we do _not_ load the savefile. this is only for starting // new campaigns mission_campaign_load(filename); mission_campaign_next_mission(); // setup various filenames and mission names strcpy(Netgame.mission_name,Campaign.missions[Campaign.current_mission].name); strcpy(Netgame.campaign_name,filename); strcpy(Game_current_mission_filename,Netgame.mission_name); // if we're the standalone server, set the mission and campaign names if(Game_mode & GM_STANDALONE_SERVER){ memset(str,0,255); strcpy(str,Netgame.mission_name); strcat(str," ("); strcat(str,Netgame.campaign_name); strcat(str,")"); // set the control on the stand_gui std_multi_set_standalone_mission_name(str); } // maybe override the Netgame.respawn setting max_players = mission_parse_get_multi_mission_info( Netgame.mission_name ); Netgame.respawn = The_mission.num_respawns; nprintf(("Network","MULTI CAMPAIGN : overriding respawn setting with mission max %d\n",The_mission.num_respawns)); // send a "start campaign" packet multi_campaign_send_start(); } } // client-side start of a campaign void multi_campaign_client_start() { memset(&Campaign,0,sizeof(Campaign)); // set campaign mode. why not. Game_mode |= GM_CAMPAIGN_MODE; } // move everything and eveyrone into the next mission state void multi_campaign_next_mission() { char str[255]; // flush the important data multi_campaign_flush_data(); // call the campaign over function mission_campaign_mission_over(); // now we should be sequencing through the next stage (mission load, etc) // this will eventually be replaced with the real filename of the next mission if(Campaign.current_mission != -1){ strncpy(Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN); strcpy(Netgame.mission_name,Game_current_mission_filename); // if we're the standalone server, set the mission and campaign names if(Game_mode & GM_STANDALONE_SERVER){ memset(str,0,255); strcpy(str,Netgame.mission_name); strcat(str," ("); strcat(str,Netgame.campaign_name); strcat(str,")"); // set the control on the stand_gui std_multi_set_standalone_mission_name(str); } } } // flush all important data between missions void multi_campaign_flush_data() { // blast the accept flags memset(Multi_campaign_accept_flags,0,sizeof(int) * MAX_PLAYERS); // flush mission stuff multi_flush_mission_stuff(); } // call in the debriefing stage to evaluate what we should be doing in regards to the campaign // if player_status == 0, nothing should be done // == 1, players want to continue to the next mission // == 2, players want to repeat the previous mission void multi_campaign_do_debrief(int player_status) { // the server (standalone or no) if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Campaign.current_mission != -1) && player_status){ // if players want to go to the next mission if(player_status == 1){ // move the multiplayer campaign along multi_campaign_next_mission(); // if we're at the end of the campaign if(Campaign.current_mission == -1){ // set the netgame state to be forming and continue Netgame.game_state = NETGAME_STATE_FORMING; send_netgame_update_packet(); if(Game_mode & GM_STANDALONE_SERVER){ gameseq_post_event(GS_EVENT_STANDALONE_MAIN); } else { gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP); } multi_reset_timestamps(); } // if we're still in the campaign else { Netgame.game_state = NETGAME_STATE_MISSION_SYNC; send_netgame_update_packet(); Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC); multi_reset_timestamps(); } } // if the players want to replay the current mission else if(player_status == 2){ Netgame.game_state = NETGAME_STATE_MISSION_SYNC; send_netgame_update_packet(); Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC); multi_reset_timestamps(); } else { Int3(); } } } // display the done popup void multi_campaign_done_popup() { popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("The Campaign Is Complete. Thank You For Playing",643)); multi_quit_game(PROMPT_NONE); } // evaluate post mission goal stuff for the campaign and send all relevant stuff to clients void multi_campaign_eval_debrief() { // evaluate mission stuff (fills in structures, etc). // DKA 12/12/98 already done // mission_campaign_eval_next_mission(); // send the campaign debriefing packet multi_campaign_send_debrief_info(); } // clients should store mission goal/event names in the campaign now void multi_campaign_client_store_goals(int mission_num) { int idx; // copy mission goals into the campaign goals for(idx=0;idxstate = NETPLAYER_STATE_CPOOL_ACK; send_netplayer_update_packet(); break; case MC_CODE_DEBRIEF: GET_DATA(cur_mission); GET_DATA(next_mission); // add the filename GET_STRING(fname); Campaign.missions[cur_mission].name = vm_strdup(fname); // add the # of goals and events GET_DATA(val); Campaign.missions[cur_mission].num_goals = val; Campaign.missions[cur_mission].goals = (mgoal*)vm_malloc(sizeof(mgoal) * val); GET_DATA(val); Campaign.missions[cur_mission].num_events = val; Campaign.missions[cur_mission].events = (mevent*)vm_malloc(sizeof(mevent) * val); // add the goals for(idx=0;idxflags & NETINFO_FLAG_AM_MASTER); multi_io_send_to_all_reliable(data, packet_size); // notification message multi_common_add_text(XSTR("Campaign ship/weapon pool\n",644),1); } // send a "start campaign" packet void multi_campaign_send_start(net_player *pl) { ubyte data[MAX_PACKET_SIZE],val; int idx; int packet_size = 0; // build the header BUILD_HEADER(CAMPAIGN_UPDATE); // add the code val = MC_CODE_START; ADD_DATA(val); // add the # of missions, and their filenames ADD_DATA(Campaign.num_missions); for(idx=0;idx MAX_PACKET_SIZE ) { *ptr = goal_count; multi_io_send_reliable(pl, data, packet_size); BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); packet_type = MC_JIP_GOAL_NAMES; ADD_DATA( packet_type ); ADD_INT(i); ptr = &data[packet_size]; goal_count = 0; ADD_DATA( goal_count ); starting_goal_num = (ubyte)j; ADD_DATA( starting_goal_num ); } } *ptr = goal_count; multi_io_send_reliable(pl, data, packet_size); } // send the goal/event names. for ( i = 0; i < Campaign.num_missions; i++ ) { ubyte event_count, starting_event_num; // first the goal names Assert( Campaign.missions[i].num_events < UCHAR_MAX ); num_events = (ubyte)Campaign.missions[i].num_events; // don't do anything if mission hasn't been completed if ( !Campaign.missions[i].completed ) continue; BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); packet_type = MC_JIP_EVENT_NAMES; ADD_DATA(packet_type); ADD_INT(i); // save a pointer so we can put the number of goals written here. ptr = &data[packet_size]; event_count = 0; ADD_DATA( event_count ); starting_event_num = 0; ADD_DATA( starting_event_num ); for ( j = 0; j < num_events; j++ ) { ADD_STRING( Campaign.missions[i].events[j].name ); event_count++; // if packet will get too big, send it off. if ( (packet_size + MC_INGAME_DATA_SLOP) > MAX_PACKET_SIZE ) { *ptr = event_count; multi_io_send_reliable(pl, data, packet_size); BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); packet_type = MC_JIP_EVENT_NAMES; ADD_DATA( packet_type ); ADD_INT(i); ptr = &data[packet_size]; event_count = 0; ADD_DATA( event_count ); starting_event_num = (ubyte)j; ADD_DATA( starting_event_num ); } } *ptr = event_count; multi_io_send_reliable(pl, data, packet_size); } } // add the stop byte BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); packet_type = MC_JIP_END_DATA; ADD_DATA(packet_type); multi_io_send_reliable(pl, data, packet_size); } void multi_campaign_process_ingame_start( ubyte *data, header *hinfo ) { int offset, mission_num, i; ubyte packet_type, num_goals, num_events, status, starting_num; char fname[255]; offset = HEADER_LENGTH; GET_DATA( packet_type ); switch( packet_type ) { case MC_JIP_INITIAL_PACKET: // clear out the names of the missions mission_campaign_close(); // should free all data structures which need to be freed // get the number of campaigns and their names. GET_INT(Campaign.num_missions); for( i = 0; i < Campaign.num_missions ; i++) { GET_STRING(fname); Campaign.missions[i].name = vm_strdup(fname); } break; case MC_JIP_GE_STATUS: GET_INT( mission_num ); GET_DATA( num_goals ); // need to malloc out the data Assert( Campaign.missions[mission_num].num_goals == 0 ); Campaign.missions[mission_num].num_goals = num_goals; if ( num_goals > 0 ){ Campaign.missions[mission_num].goals = (mgoal *)vm_malloc( sizeof(mgoal) * num_goals ); } for ( i = 0; i < num_goals; i++ ) { GET_DATA(status); // AL: .goals was a NULL pointer here! I have no idea why. Putting // in a check to avoid the unhandled exception if ( Campaign.missions[mission_num].goals ) { Campaign.missions[mission_num].goals[i].status = status; } } // now the events GET_DATA( num_events ); // need to malloc out the data Assert( Campaign.missions[mission_num].num_events == 0 ); Campaign.missions[mission_num].num_events = num_events; if ( num_events > 0 ){ Campaign.missions[mission_num].events = (mevent *)vm_malloc( sizeof(mevent) * num_events ); } for ( i = 0; i < num_events; i++ ) { GET_DATA(status); Campaign.missions[mission_num].events[i].status = status; } break; case MC_JIP_GOAL_NAMES: GET_INT( mission_num ); GET_DATA( num_goals ); GET_DATA( starting_num ); for ( i = starting_num; i < (starting_num + num_goals); i++ ) { GET_STRING(Campaign.missions[mission_num].goals[i].name); } break; case MC_JIP_EVENT_NAMES: GET_INT( mission_num ); GET_DATA( num_events ); GET_DATA( starting_num ); for ( i = starting_num; i < (starting_num + num_events); i++ ) { GET_STRING(Campaign.missions[mission_num].events[i].name); } break; case MC_JIP_END_DATA: Net_player->state = NETPLAYER_STATE_INGAME_CINFO; send_netplayer_update_packet(); break; } PACKET_SET_SIZE(); }