From: Mario Date: Sat, 23 May 2020 15:00:11 +0000 (+1000) Subject: If no valid spawnpoints exist in the map, perform another check without targets,... X-Git-Tag: xonotic-v0.8.5~1039 X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=da24db9e038f0039b0cc1f8cd775cf93c94e6cf9;p=xonotic%2Fxonotic-data.pk3dir.git If no valid spawnpoints exist in the map, perform another check without targets, fixes crashes caused by poorly repacked maps containing gamemodes they don't actually support --- diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index 9a12a0486..32fdbba17 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -767,7 +767,7 @@ void trigger_race_checkpoint_verify(entity this) // race only (middle of the race) g_race_qualifying = false; pl_race_place = 0; - if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) { + if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) { error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out")); } @@ -775,7 +775,7 @@ void trigger_race_checkpoint_verify(entity this) // qualifying only g_race_qualifying = 1; pl_race_place = race_lowest_place_spawn; - if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) { + if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) { error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out")); } @@ -783,7 +783,7 @@ void trigger_race_checkpoint_verify(entity this) g_race_qualifying = 0; for (int p = 1; p <= race_highest_place_spawn; ++p) { pl_race_place = p; - if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) { + if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) { error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out")); } } @@ -794,7 +794,7 @@ void trigger_race_checkpoint_verify(entity this) pl_race_checkpoint = race_NextCheckpoint(0); g_race_qualifying = 1; pl_race_place = race_lowest_place_spawn; - if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false)) { + if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) { error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out")); } } else { diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index e2c5ab307..616824bbc 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -222,7 +222,7 @@ spawnfunc(info_player_team4) // Returns: // _x: prio (-1 if unusable) // _y: weight -vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) +vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck, bool targetcheck) { // filter out spots for the wrong team if(teamcheck >= 0) @@ -230,7 +230,7 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) return '-1 0 0'; if(race_spawns) - if(spot.target == "") + if(!spot.target || spot.target == "") return '-1 0 0'; if(IS_REAL_CLIENT(this)) @@ -257,7 +257,7 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) vector spawn_score = prio * '1 0 0' + shortest * '0 1 0'; // filter out spots for assault - if(spot.target && spot.target != "") + if(spot.target && spot.target != "" && targetcheck) { int found = 0; for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain) @@ -283,21 +283,21 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) return spawn_score; } -void Spawn_ScoreAll(entity this, entity firstspot, float mindist, float teamcheck) +void Spawn_ScoreAll(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck) { entity spot; for(spot = firstspot; spot; spot = spot.chain) - spot.spawnpoint_score = Spawn_Score(this, spot, mindist, teamcheck); + spot.spawnpoint_score = Spawn_Score(this, spot, mindist, teamcheck, targetcheck); } -entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck) +entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck) { entity spot, spotlist, spotlistend; spotlist = NULL; spotlistend = NULL; - Spawn_ScoreAll(this, firstspot, mindist, teamcheck); + Spawn_ScoreAll(this, firstspot, mindist, teamcheck, targetcheck); for(spot = firstspot; spot; spot = spot.chain) { @@ -399,7 +399,23 @@ entity SelectSpawnPoint(entity this, bool anypoint) } else { - firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck); + firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck, true); + + // emergency fallback! double check without targets + // fixes some crashes with improperly repacked maps + if(!firstspot) + { + firstspot = IL_FIRST(g_spawnpoints); + prev = NULL; + IL_EACH(g_spawnpoints, true, + { + if(prev) + prev.chain = it; + it.chain = NULL; + prev = it; + }); + firstspot = Spawn_FilterOutBadSpots(this, firstspot, 100, teamcheck, false); + } // there is 50/50 chance of choosing a random spot or the furthest spot // (this means that roughly every other spawn will be furthest, so you diff --git a/qcsrc/server/spawnpoints.qh b/qcsrc/server/spawnpoints.qh index 92af8df66..fe6adaebf 100644 --- a/qcsrc/server/spawnpoints.qh +++ b/qcsrc/server/spawnpoints.qh @@ -9,7 +9,7 @@ const int SPAWN_PRIO_GOOD_DISTANCE = 10; .vector spawnpoint_score; float spawnpoint_nag; bool SpawnEvent_Send(entity this, entity to, int sf); -entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck); +entity Spawn_FilterOutBadSpots(entity this, entity firstspot, float mindist, float teamcheck, bool targetcheck); entity SelectSpawnPoint(entity this, bool anypoint); spawnfunc(info_player_deathmatch); void spawnpoint_use(entity this, entity actor, entity trigger);