#include <common/util.qh>
#include <common/vehicles/all.qh>
#include <common/weapons/_all.qh>
+#include <lib/warpzone/common.qh>
#include <server/anticheat.qh>
#include <server/antilag.qh>
#include <server/bot/api.qh>
{
k = bufstr_get(h, i);
-#define BADPREFIX(p) if(substring(k, 0, strlen(p)) == p) continue
-#define BADPRESUFFIX(p,s) if(substring(k, 0, strlen(p)) == p && substring(k, -strlen(s), -1) == s) continue
+#define BADPREFIX_COND(p) (substring(k, 0, strlen(p)) == p)
+#define BADSUFFIX_COND(s) (substring(k, -strlen(s), -1) == s)
+
+#define BADPREFIX(p) if(BADPREFIX_COND(p)) continue
+#define BADPRESUFFIX(p, s) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s)) continue
#define BADCVAR(p) if(k == p) continue
#define BADVALUE(p, val) if (k == p && v == val) continue
+#define BADPRESUFFIXVALUE(p, s, val) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s) && v == val) continue
// general excludes and namespaces for server admin used cvars
BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
BADCVAR("timeformat");
BADCVAR("timestamps");
BADCVAR("g_require_stats");
+ BADCVAR("g_chatban_list");
+ BADCVAR("g_playban_list");
+ BADCVAR("g_playban_minigames");
+ BADCVAR("g_voteban_list");
BADPREFIX("developer_");
BADPREFIX("g_ban_");
BADPREFIX("g_banned_list");
// these can contain player IDs, so better hide
BADPREFIX("g_forced_team_");
- BADCVAR("sv_muteban_list");
- BADCVAR("sv_voteban_list");
BADCVAR("sv_allow_customplayermodels_idlist");
BADCVAR("sv_allow_customplayermodels_speciallist");
BADCVAR("g_keyhunt");
BADCVAR("g_keyhunt_teams");
BADCVAR("g_lms");
+ BADCVAR("g_mayhem");
BADCVAR("g_nexball");
BADCVAR("g_onslaught");
BADCVAR("g_race");
BADCVAR("g_tdm");
BADCVAR("g_tdm_on_dm_maps");
BADCVAR("g_tdm_teams");
+ BADCVAR("g_tka");
+ BADCVAR("g_tka_on_ka_maps");
+ BADCVAR("g_tka_on_tdm_maps");
+ BADCVAR("g_tka_teams");
+ BADCVAR("g_tmayhem");
+ BADCVAR("g_tmayhem_teams");
BADCVAR("g_vip");
BADCVAR("leadlimit");
BADCVAR("nextmap");
BADCVAR("g_spawn_alloweffects");
BADCVAR("g_tdm_point_leadlimit");
BADCVAR("g_tdm_point_limit");
+ BADCVAR("g_mayhem_point_limit");
+ BADCVAR("g_mayhem_point_leadlimit");
+ BADCVAR("g_tmayhem_point_limit");
+ BADCVAR("g_tmayhem_point_leadlimit");
BADCVAR("leadlimit_and_fraglimit");
BADCVAR("leadlimit_override");
BADCVAR("pausable");
BADCVAR("sv_announcer");
+ BADCVAR("sv_autopause");
BADCVAR("sv_checkforpacketsduringsleep");
BADCVAR("sv_damagetext");
BADCVAR("sv_db_saveasdump");
BADCVAR("w_prop_interval");
BADPREFIX("chat_");
BADPREFIX("crypto_");
- BADPREFIX("gameversion");
BADPREFIX("g_chat_");
BADPREFIX("g_ctf_captimerecord_");
BADPREFIX("g_hats_");
BADCVAR("g_ban_sync_uri");
BADCVAR("g_buffs");
BADCVAR("g_ca_teams_override");
+ BADCVAR("g_ca_prevent_stalemate");
BADCVAR("g_ctf_fullbrightflags");
BADCVAR("g_ctf_ignore_frags");
BADCVAR("g_ctf_leaderboard");
BADCVAR("g_keyhunt_point_limit");
BADCVAR("g_keyhunt_teams_override");
BADCVAR("g_lms_lives_override");
+ BADCVAR("g_mayhem_powerups");
BADCVAR("g_maplist");
BADCVAR("g_maxplayers");
BADCVAR("g_mirrordamage");
BADCVAR("g_start_delay");
BADCVAR("g_superspectate");
BADCVAR("g_tdm_teams_override");
+ BADCVAR("g_tmayhem_teams_override");
+ BADCVAR("g_tmayhem_powerups");
BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
BADCVAR("hostname");
BADCVAR("log_file");
BADPREFIX("sv_info_");
BADPREFIX("sv_ready_restart_");
+ BADPRESUFFIXVALUE("g_", "_weaponarena", "most");
+ BADPRESUFFIXVALUE("g_", "_weaponarena", "most_available");
+
// mutators that announce themselves properly to the server browser
BADCVAR("g_instagib");
BADCVAR("g_new_toys");
#undef BADPREFIX
#undef BADCVAR
#undef BADVALUE
+#undef BADPRESUFFIXVALUE
if(pureadding)
{
e = new(info_player_deathmatch); // safeguard against player joining
- // assign reflectively to avoid "assignment to world" warning
- for (int i = 0, n = numentityfields(); i < n; ++i) {
- string k = entityfieldname(i);
- if (k == "classname") {
- // safeguard against various stuff ;)
- putentityfieldstring(i, this, "worldspawn");
- break;
- }
- }
+ // assign reflectively to avoid "assignment to world" warning
+ for (int i = 0, n = numentityfields(); i < n; ++i)
+ {
+ string k = entityfieldname(i);
+ if (k == "classname")
+ {
+ // safeguard against various stuff ;)
+ putentityfieldstring(i, this, "worldspawn");
+ break;
+ }
+ }
// needs to be done so early because of the constants they create
static_init();
int u = AVAILABLE_TEAMS - d;
map_minplayers += (u < d && u + map_minplayers <= m) ? u : -d;
}
- warmup_limit = -1;
}
else
- map_minplayers = 0; // don't display a minimum if it's not used
+ map_minplayers = 0; // don't display a minimum if it's not used (g_maxplayers < 0 && g_warmup >= 0)
}
void InitGameplayMode()
{
- VoteReset();
+ VoteReset(false);
// find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
get_mi_min_max(1);
// assign reflectively to avoid "assignment to world" warning
- int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
- string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
- if (v) {
- putentityfieldstring(i, world, sprintf("%v", v));
- if (++done == 2) break;
- }
+ for (int i = 0, done = 0, n = numentityfields(); i < n; ++i)
+ {
+ string k = entityfieldname(i);
+ vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
+ if (v)
+ {
+ putentityfieldstring(i, world, sprintf("%v", v));
+ if (++done == 2) break;
+ }
}
// currently, NetRadiant's limit is 131072 qu for each side
// distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
{
if (!server_is_dedicated)
{
- // force unloading of server pk3 files when starting a listen server
- // localcmd("\nfs_rescan\n"); // FIXME: does more harm than good, has unintended side effects. What we really want is to unload temporary pk3s only
+ // DP unloads dlcache pk3s before starting a listen server since https://gitlab.com/xonotic/darkplaces/-/merge_requests/134
// restore csqc_progname too
string expect = "csprogs.dat";
wantrestart = cvar_string("csqc_progname") != expect;
GameRules_limit_fallbacks();
- if(warmup_limit == 0)
- warmup_limit = autocvar_timelimit * 60;
-
player_count = 0;
bot_waypoints_for_items = autocvar_g_waypoints_for_items;
if(bot_waypoints_for_items == 1)
WinningConditionHelper(this); // set worldstatus
+ if (autocvar_sv_autopause && server_is_dedicated && !wantrestart)
+ // INITPRIO_LAST is to soon: bots either didn't join yet or didn't leave yet, see: bot_fixcount()
+ defer(this, 5, Pause_TryPause_Dedicated);
+
world_initialized = 1;
__spawnfunc_spawn_all();
}
bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
{
- float m = e.dphitcontentsmask;
- e.dphitcontentsmask = goodcontents | badcontents;
-
- vector org = boundmin;
- vector delta = boundmax - boundmin;
-
- vector start, end;
- start = end = org;
- int j; // used after the loop
- for(j = 0; j < attempts; ++j)
- {
- start.x = org.x + random() * delta.x;
- start.y = org.y + random() * delta.y;
- start.z = org.z + random() * delta.z;
-
- // rule 1: start inside world bounds, and outside
- // solid, and don't start from somewhere where you can
- // fall down to evil
- tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
- if (trace_fraction >= 1)
- continue;
- if (trace_startsolid)
- continue;
- if (trace_dphitcontents & badcontents)
- continue;
- if (trace_dphitq3surfaceflags & badsurfaceflags)
- continue;
-
- // rule 2: if we are too high, lower the point
- if (trace_fraction * delta.z > maxaboveground)
- start = trace_endpos + '0 0 1' * maxaboveground;
- vector enddown = trace_endpos;
-
- // rule 3: make sure we aren't outside the map. This only works
- // for somewhat well formed maps. A good rule of thumb is that
- // the map should have a convex outside hull.
- // these can be traceLINES as we already verified the starting box
- vector mstart = start + 0.5 * (e.mins + e.maxs);
- traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
- if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
- continue;
- traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
- if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
- continue;
- traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
- if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
- continue;
- traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
- if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
- continue;
- traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
- if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
- continue;
+ float m = e.dphitcontentsmask;
+ e.dphitcontentsmask = goodcontents | badcontents;
+
+ vector org = boundmin;
+ vector delta = boundmax - boundmin;
+
+ vector start, end;
+ start = end = org;
+ int j; // used after the loop
+ for(j = 0; j < attempts; ++j)
+ {
+ start.x = org.x + random() * delta.x;
+ start.y = org.y + random() * delta.y;
+ start.z = org.z + random() * delta.z;
+
+ // rule 1: start inside world bounds, and outside
+ // solid, and don't start from somewhere where you can
+ // fall down to evil
+ tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
+ if (trace_fraction >= 1)
+ continue;
+ if (trace_startsolid)
+ continue;
+ if (trace_dphitcontents & badcontents)
+ continue;
+ if (trace_dphitq3surfaceflags & badsurfaceflags)
+ continue;
+
+ // rule 2: if we are too high, lower the point
+ if (trace_fraction * delta.z > maxaboveground)
+ start = trace_endpos + '0 0 1' * maxaboveground;
+ vector enddown = trace_endpos;
+
+ // rule 3: make sure we aren't outside the map. This only works
+ // for somewhat well formed maps. A good rule of thumb is that
+ // the map should have a convex outside hull.
+ // these can be traceLINES as we already verified the starting box
+ vector mstart = start + 0.5 * (e.mins + e.maxs);
+ traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
+ traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
+ if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
+ continue;
// rule 4: we must "see" some spawnpoint or item
entity sp = NULL;
continue;
}
- // find a random vector to "look at"
- end.x = org.x + random() * delta.x;
- end.y = org.y + random() * delta.y;
- end.z = org.z + random() * delta.z;
- end = start + normalize(end - start) * vlen(delta);
-
- // rule 4: start TO end must not be too short
- tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
- if(trace_startsolid)
- continue;
- if(trace_fraction < minviewdistance / vlen(delta))
- continue;
-
- // rule 5: don't want to look at sky
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
- continue;
-
- // rule 6: we must not end up in trigger_hurt
- if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
- continue;
-
- break;
- }
-
- e.dphitcontentsmask = m;
-
- if(j < attempts)
- {
- setorigin(e, start);
- e.angles = vectoangles(end - start);
- LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
- return true;
- }
- return false;
+ // find a random vector to "look at"
+ end.x = org.x + random() * delta.x;
+ end.y = org.y + random() * delta.y;
+ end.z = org.z + random() * delta.z;
+ end = start + normalize(end - start) * vlen(delta);
+
+ // rule 4: start TO end must not be too short
+ tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
+ if(trace_startsolid)
+ continue;
+ if(trace_fraction < minviewdistance / vlen(delta))
+ continue;
+
+ // rule 5: don't want to look at sky
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
+ continue;
+
+ // rule 6: we must not end up in trigger_hurt
+ if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
+ continue;
+
+ break;
+ }
+
+ e.dphitcontentsmask = m;
+
+ if(j < attempts)
+ {
+ setorigin(e, start);
+ e.angles = vectoangles(end - start);
+ LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
+ return true;
+ }
+ return false;
}
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
//pos = FindIntermission ();
- VoteReset();
+ VoteReset(true);
DumpStats(true);
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
- sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
+ serverflags &= ~SERVERFLAG_FORBID_PICKUPTIMER;
+ if(cvar("sv_forbid_pickuptimer"))
+ serverflags |= SERVERFLAG_FORBID_PICKUPTIMER;
- warmup_stage = cvar("g_warmup");
- warmup_limit = cvar("g_warmup_limit");
+ sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
if(cvar("g_campaign"))
warmup_stage = 0; // no warmup during campaign
+ else
+ {
+ warmup_stage = autocvar_g_warmup;
+ if (warmup_stage < 0 || warmup_stage > 1)
+ warmup_limit = -1; // don't start until there's enough players
+ else if (warmup_stage == 1)
+ {
+ // this code is duplicated in ReadyCount()
+ warmup_limit = cvar("g_warmup_limit");
+ if(warmup_limit == 0)
+ warmup_limit = autocvar_timelimit * 60;
+ }
+ }
g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
- g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
- if(!g_weapon_stay)
- g_weapon_stay = cvar("g_weapon_stay");
+ g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
+ if(!g_weapon_stay)
+ g_weapon_stay = cvar("g_weapon_stay");
- MUTATOR_CALLHOOK(ReadLevelCvars);
+ MUTATOR_CALLHOOK(ReadLevelCvars);
if (!warmup_stage && !autocvar_g_campaign)
game_starttime = time + cvar("g_start_delay");
void InitializeEntity(entity e, void(entity this) func, int order)
{
- entity prev, cur;
-
- if (!e || e.initialize_entity)
- {
- // make a proxy initializer entity
- entity e_old = e;
- e = new(initialize_entity);
- e.enemy = e_old;
- }
-
- e.initialize_entity = func;
- e.initialize_entity_order = order;
-
- cur = initialize_entity_first;
- prev = NULL;
- for (;;)
- {
- if (!cur || cur.initialize_entity_order > order)
- {
- // insert between prev and cur
- if (prev)
- prev.initialize_entity_next = e;
- else
- initialize_entity_first = e;
- e.initialize_entity_next = cur;
- return;
- }
- prev = cur;
- cur = cur.initialize_entity_next;
- }
+ entity prev, cur;
+
+ if (!e || e.initialize_entity)
+ {
+ // make a proxy initializer entity
+ entity e_old = e;
+ e = new(initialize_entity);
+ e.enemy = e_old;
+ }
+
+ e.initialize_entity = func;
+ e.initialize_entity_order = order;
+
+ cur = initialize_entity_first;
+ prev = NULL;
+ for (;;)
+ {
+ if (!cur || cur.initialize_entity_order > order)
+ {
+ // insert between prev and cur
+ if (prev)
+ prev.initialize_entity_next = e;
+ else
+ initialize_entity_first = e;
+ e.initialize_entity_next = cur;
+ return;
+ }
+ prev = cur;
+ cur = cur.initialize_entity_next;
+ }
}
void InitializeEntitiesRun()
{
- entity startoflist = initialize_entity_first;
- initialize_entity_first = NULL;
- delete_fn = remove_except_protected;
- for (entity e = startoflist; e; e = e.initialize_entity_next)
- {
+ entity startoflist = initialize_entity_first;
+ initialize_entity_first = NULL;
+ delete_fn = remove_except_protected;
+ for (entity e = startoflist; e; e = e.initialize_entity_next)
+ {
e.remove_except_protected_forbidden = 1;
- }
- for (entity e = startoflist; e; )
- {
+ }
+ for (entity e = startoflist; e; )
+ {
e.remove_except_protected_forbidden = 0;
- e.initialize_entity_order = 0;
- entity next = e.initialize_entity_next;
- e.initialize_entity_next = NULL;
- var void(entity this) func = e.initialize_entity;
- e.initialize_entity = func_null;
- if (e.classname == "initialize_entity")
- {
- entity wrappee = e.enemy;
- builtin_remove(e);
- e = wrappee;
- }
- //dprint("Delayed initialization: ", e.classname, "\n");
- if (func)
- {
- func(e);
- }
- else
- {
- eprint(e);
- backtrace(strcat("Null function in: ", e.classname, "\n"));
- }
- e = next;
- }
- delete_fn = remove_unsafely;
+ e.initialize_entity_order = 0;
+ entity next = e.initialize_entity_next;
+ e.initialize_entity_next = NULL;
+ var void(entity this) func = e.initialize_entity;
+ e.initialize_entity = func_null;
+ if (e.classname == "initialize_entity")
+ {
+ entity wrappee = e.enemy;
+ builtin_remove(e);
+ e = wrappee;
+ }
+ //dprint("Delayed initialization: ", e.classname, "\n");
+ if (func)
+ {
+ func(e);
+ }
+ else
+ {
+ eprint(e);
+ backtrace(strcat("Null function in: ", e.classname, "\n"));
+ }
+ e = next;
+ }
+ delete_fn = remove_unsafely;
}
// deferred dropping
return;
}
- vector end = this.origin - '0 0 256';
+ vector end = this.origin;
+ if (autocvar_sv_mapformat_is_quake3)
+ end.z -= 4096;
+ else if (autocvar_sv_mapformat_is_quake2)
+ end.z -= 128;
+ else
+ end.z -= 256; // Quake, QuakeWorld
- // NOTE: NudgeOutOfSolid support is not added as Xonotic's physics do not use it!
- //if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
- //SV_NudgeOutOfSolid(this);
+ // NOTE: SV_NudgeOutOfSolid is used in the engine here
+ if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+ {
+ _Movetype_UnstickEntity(this);
+ move_out_of_solid(this);
+ }
tracebox(this.origin, this.mins, this.maxs, end, MOVE_NORMAL, this);
else if(trace_fraction < 1)
{
LOG_DEBUGF("DropToFloor_Handler: %v fixed badly placed entity", this.origin);
- //if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
- //SV_NudgeOutOfSolid(this);
setorigin(this, trace_endpos);
+ if(autocvar_sv_gameplayfix_droptofloorstartsolid_nudgetocorrect)
+ {
+ _Movetype_UnstickEntity(this);
+ move_out_of_solid(this);
+ }
SET_ONGROUND(this);
this.groundentity = trace_ent;
// if support is destroyed, keep suspended (gross hack for floating items in various maps)
// if support is destroyed, keep suspended (gross hack for floating items in various maps)
this.move_suspendedinair = true;
}
+ else
+ {
+ // if we can't get the entity out of solid, mark it as on ground so physics doesn't attempt to drop it
+ // hacky workaround for #2774
+ SET_ONGROUND(this);
+ }
}
this.dropped_origin = this.origin;
}