Skip to content

Commit 5fd06b4

Browse files
fix multi crash loading campaigns (#6871)
A minor bug in #6077 resulted in a crash loading campaigns on the multi ui. The bug itself was not fatal, however it triggered a cascade of poor error handling which led to a crash. A lot of these issues date back to retail. This fixes the minor bug, and hopefully adds enough error handling to preemptively squash a repeat of the crash problem.
1 parent 21e35d0 commit 5fd06b4

File tree

7 files changed

+54
-46
lines changed

7 files changed

+54
-46
lines changed

code/mission/missioncampaign.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,30 @@ campaign Campaign;
100100
* In the type field, we return if the campaign is a single player or multiplayer campaign.
101101
* The type field will only be valid if the name returned is non-NULL
102102
*/
103-
int mission_campaign_get_info(const char *filename, char *name, int *type, int *max_players, char **desc, char **first_mission)
103+
bool mission_campaign_get_info(const char *filename, SCP_string &name, int *type, int *max_players, char **desc, char **first_mission)
104104
{
105-
int i, success = 0;
106-
char campaign_type[NAME_LENGTH], fname[MAX_FILENAME_LEN];
105+
int i, success = false;
106+
SCP_string campaign_type;
107+
char fname[MAX_FILENAME_LEN];
107108

108-
Assert( name != NULL );
109109
Assert( type != NULL );
110110

111+
// make sure outputs always have sane values
112+
name.clear();
113+
*type = -1;
114+
115+
if (max_players) {
116+
*max_players = 0;
117+
}
118+
119+
if (desc) {
120+
*desc = nullptr;
121+
}
122+
123+
if (first_mission) {
124+
*first_mission = nullptr;
125+
}
126+
111127
strncpy(fname, filename, MAX_FILENAME_LEN - 1);
112128
auto fname_len = strlen(fname);
113129
if ((fname_len < 4) || stricmp(fname + fname_len - 4, FS_CAMPAIGN_FILE_EXT) != 0){
@@ -116,31 +132,30 @@ int mission_campaign_get_info(const char *filename, char *name, int *type, int *
116132
}
117133
Assert(fname_len < MAX_FILENAME_LEN);
118134

119-
*type = -1;
120135
do {
121136
try
122137
{
123138
read_file_text(fname);
124139
reset_parse();
125140

126141
required_string("$Name:");
127-
stuff_string(name, F_NAME, NAME_LENGTH);
128-
if (name == NULL) {
142+
stuff_string(name, F_NAME);
143+
if (name.empty()) {
129144
nprintf(("Warning", "No name found for campaign file %s\n", filename));
130145
break;
131146
}
132147

133148
required_string("$Type:");
134-
stuff_string(campaign_type, F_NAME, NAME_LENGTH);
149+
stuff_string(campaign_type, F_NAME);
135150

136151
for (i = 0; i < MAX_CAMPAIGN_TYPES; i++) {
137-
if (!stricmp(campaign_type, campaign_types[i])) {
152+
if (!stricmp(campaign_type.c_str(), campaign_types[i])) {
138153
*type = i;
139154
}
140155
}
141156

142-
if (name == NULL) {
143-
Warning(LOCATION, "Invalid campaign type \"%s\"\n", campaign_type);
157+
if (*type < 0) {
158+
Warning(LOCATION, "Invalid campaign type \"%s\"\n", campaign_type.c_str());
144159
break;
145160
}
146161

@@ -166,7 +181,7 @@ int mission_campaign_get_info(const char *filename, char *name, int *type, int *
166181

167182
// if we found a valid campaign type
168183
if ((*type) >= 0) {
169-
success = 1;
184+
success = true;
170185
}
171186
}
172187
catch (const parse::ParseException& e)
@@ -176,7 +191,6 @@ int mission_campaign_get_info(const char *filename, char *name, int *type, int *
176191
}
177192
} while (0);
178193

179-
Assert(success);
180194
return success;
181195
}
182196

@@ -250,7 +264,7 @@ void mission_campaign_free_list()
250264

251265
int mission_campaign_maybe_add(const char *filename)
252266
{
253-
char name[NAME_LENGTH];
267+
SCP_string name;
254268
char *desc = NULL;
255269
int type, max_players;
256270

@@ -261,7 +275,7 @@ int mission_campaign_maybe_add(const char *filename)
261275

262276
if ( mission_campaign_get_info( filename, name, &type, &max_players, &desc) ) {
263277
if ( !MC_multiplayer && (type == CAMPAIGN_TYPE_SINGLE) ) {
264-
Campaign_names[Num_campaigns] = vm_strdup(name);
278+
Campaign_names[Num_campaigns] = vm_strdup(name.c_str());
265279

266280
if (MC_desc)
267281
Campaign_descs[Num_campaigns] = desc;

code/mission/missioncampaign.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ extern void mission_campaign_save_persistent( int type, int index );
213213
// execute the corresponding mission_campaign_savefile functions.
214214

215215
// get name and type of specified campaign file
216-
int mission_campaign_get_info(const char *filename, char *name, int *type, int *max_players, char **desc = nullptr, char **first_mission = nullptr);
216+
bool mission_campaign_get_info(const char *filename, SCP_string &name, int *type, int *max_players, char **desc = nullptr, char **first_mission = nullptr);
217217

218218
// get a listing of missions in a campaign
219219
int mission_campaign_get_mission_list(const char *filename, char **list, int max);

code/mission/missionparse.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6675,7 +6675,11 @@ int get_mission_info(const char *filename, mission *mission_p, bool basic, bool
66756675
{
66766676
static SCP_string real_fname_buf;
66776677
const char *real_fname = nullptr;
6678-
6678+
6679+
if ( !filename || !strlen(filename) ) {
6680+
return -1;
6681+
}
6682+
66796683
if (filename_is_full_path) {
66806684
real_fname = filename;
66816685
} else {

code/network/multi_options.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ void multi_options_process_packet(unsigned char *data, header *hinfo)
750750
// get mission choice options
751751
case MULTI_OPTION_MISSION: {
752752
netgame_info ng;
753-
char title[NAME_LENGTH+1];
753+
SCP_string title;
754754
int campaign_type,max_players;
755755

756756
Assert(Game_mode & GM_STANDALONE_SERVER);
@@ -779,7 +779,6 @@ void multi_options_process_packet(unsigned char *data, header *hinfo)
779779

780780
// set the netgame max players here if the filename has changed
781781
if(strcmp(Netgame.campaign_name,ng.campaign_name) != 0){
782-
memset(title,0,NAME_LENGTH+1);
783782
if(!mission_campaign_get_info(ng.campaign_name,title,&campaign_type,&max_players)){
784783
Netgame.max_players = 0;
785784
} else {

code/network/multiui.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4535,7 +4535,7 @@ void multi_create_list_load_campaigns()
45354535
{
45364536
int idx, file_count;
45374537
int campaign_type,max_players;
4538-
char title[255];
4538+
SCP_string title;
45394539
char wild_card[10];
45404540
char **file_list = NULL;
45414541

@@ -4717,13 +4717,13 @@ void multi_create_list_do()
47174717
// so we can set the data without bothering to check the UI anymore
47184718
void multi_create_list_set_item(int abs_index, int mode) {
47194719

4720-
int campaign_type, max_players;
4721-
char title[NAME_LENGTH + 1];
4720+
int campaign_type = -1, max_players = 0;
4721+
SCP_string title;
47224722
netgame_info ng_temp;
47234723
netgame_info* ng;
47244724
multi_create_info* mcip = NULL;
47254725

4726-
char* campaign_desc;
4726+
char* campaign_desc = nullptr;
47274727

47284728
// if not on the standalone server
47294729
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
@@ -4738,7 +4738,7 @@ void multi_create_list_set_item(int abs_index, int mode) {
47384738
if (mode == MULTI_CREATE_SHOW_MISSIONS) {
47394739
strcpy(ng->mission_name, Multi_create_mission_list[abs_index].filename);
47404740
} else {
4741-
strcpy(ng->mission_name, Multi_create_campaign_list[abs_index].filename);
4741+
strcpy(ng->campaign_name, Multi_create_campaign_list[abs_index].filename);
47424742
}
47434743

47444744
// make sure the netgame type is properly set
@@ -4819,24 +4819,22 @@ void multi_create_list_set_item(int abs_index, int mode) {
48194819

48204820
// if not on the standalone server
48214821
if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
4822-
memset(title, 0, sizeof(title));
48234822
// get the campaign info
48244823
if (!mission_campaign_get_info(ng->campaign_name,
48254824
title,
48264825
&campaign_type,
48274826
&max_players,
48284827
&campaign_desc,
4829-
&first_mission)) {
4828+
&first_mission))
4829+
{
4830+
nprintf(("Network", "MC: Failed to get campaign info for '%s'!\n", ng->campaign_name));
48304831
memset(ng->campaign_name, 0, sizeof(ng->campaign_name));
4831-
ng->max_players = 0;
4832-
}
4833-
// if we successfully got the info
4834-
else {
4835-
memset(ng->title, 0, NAME_LENGTH + 1);
4836-
strcpy_s(ng->title, title);
4837-
ng->max_players = max_players;
48384832
}
48394833

4834+
memset(ng->title, 0, sizeof(ng->title));
4835+
strcpy_s(ng->title, title.c_str());
4836+
ng->max_players = max_players;
4837+
48404838
nprintf(("Network", "MC MAX PLAYERS : %d\n", ng->max_players));
48414839

48424840
// set the information area text

code/playerman/managepilot.cpp

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,26 +170,17 @@ int local_num_campaigns = 0;
170170

171171
int campaign_file_list_filter(const char *filename)
172172
{
173-
char name[NAME_LENGTH];
174-
char *desc = NULL;
173+
SCP_string name;
175174
int type, max_players;
176175

177-
if ( mission_campaign_get_info( filename, name, &type, &max_players, &desc) ) {
176+
if ( mission_campaign_get_info( filename, name, &type, &max_players) ) {
178177
if ( type == CAMPAIGN_TYPE_SINGLE) {
179-
Campaign_names[local_num_campaigns] = vm_strdup(name);
178+
Campaign_names[local_num_campaigns] = vm_strdup(name.c_str());
180179
local_num_campaigns++;
181-
182-
// here we *do* free the campaign description because we are not saving the pointer.
183-
if (desc != NULL)
184-
vm_free(desc);
185-
186180
return 1;
187181
}
188182
}
189183

190-
if (desc != NULL)
191-
vm_free(desc);
192-
193184
return 0;
194185
}
195186

code/scripting/api/libs/mission.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1847,7 +1847,9 @@ ADE_FUNC(loadMission, l_Mission, "string missionName", "Loads a mission", "boole
18471847
gr_post_process_set_defaults();
18481848

18491849
//NOW do the loading stuff
1850-
get_mission_info(s, &The_mission, false);
1850+
if (get_mission_info(s, &The_mission, false))
1851+
return ADE_RETURN_FALSE;
1852+
18511853
game_level_init();
18521854

18531855
if(!mission_load(s))

0 commit comments

Comments
 (0)