X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmain.qc;h=8a2da54aa4a721c2312017b0bd30ae52bd72e561;hb=e9a12973bad78d9c87dab26cdd2e6fd23c0578b0;hp=13574bf0edcf2347f87610da627a7f6c8ff40e6b;hpb=6a501068766094b93c1d14663caf4895f2e9c446;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/main.qc b/qcsrc/server/main.qc index 13574bf0e..8a2da54aa 100644 --- a/qcsrc/server/main.qc +++ b/qcsrc/server/main.qc @@ -26,6 +26,40 @@ #include #include +void dropclient_do(entity this) +{ + if (this.owner) + dropclient(this.owner); + delete(this); +} +/** + * Schedules dropclient for a player and returns true; + * if dropclient is already scheduled (for that player) it does nothing and returns false. + * + * NOTE: this function exists only to allow sending a message to the kicked player with + * Send_Notification, which doesn't work if called together with dropclient + */ +bool dropclient_schedule(entity this) +{ + bool scheduled = false; + FOREACH_ENTITY_CLASS("dropclient_handler", true, + { + if(it.owner == this) + { + scheduled = true; + break; // can't use return here, compiler shows a warning + } + }); + if (scheduled) + return false; + + entity e = new_pure(dropclient_handler); + setthink(e, dropclient_do); + e.owner = this; + e.nextthink = time + 0.1; + return true; +} + void CreatureFrame_hotliquids(entity this) { if (this.contents_damagetime >= time) @@ -152,20 +186,35 @@ void CreatureFrame_All() }); } -void Pause_TryPause(bool ispaused) +void Pause_TryPause_Dedicated(entity this) { - int n = 0; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if (PHYS_INPUT_BUTTON_CHAT(it) != ispaused) return; + if (player_count == 0) + setpause(1); +} + +void Pause_TryPause() +{ + int n = 0, p = 0; + FOREACH_CLIENT(IS_REAL_CLIENT(it), { + if (PHYS_INPUT_BUTTON_CHAT(it)) ++p; ++n; }); if (!n) return; - setpause(ispaused); + if (n == p) + setpause(1); + else + setpause(0); } void SV_PausedTic(float elapsedtime) { - if (!server_is_dedicated) Pause_TryPause(false); + if (!server_is_dedicated) + { + if (autocvar_sv_autopause) + Pause_TryPause(); + else + setpause(0); + } } void dedicated_print(string input) @@ -234,7 +283,6 @@ Called before each frame by the server bool game_delay_last; -bool autocvar_sv_autopause = false; void systems_update(); void sys_phys_update(entity this, float dt); void StartFrame() @@ -244,7 +292,7 @@ void StartFrame() IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPreThink(it)); execute_next_frame(); - if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true); + if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(); delete_fn = remove_unsafely; // not during spawning! serverprevtime = servertime; @@ -297,8 +345,8 @@ void StartFrame() CreatureFrame_All(); CheckRules_World(); - if (warmup_stage && !game_stopped && warmup_limit > 0 && time >= warmup_limit) { - ReadyRestart(); + if (warmup_stage && !game_stopped && warmup_limit > 0 && time - game_starttime >= warmup_limit) { + ReadyRestart(true); return; } @@ -331,7 +379,7 @@ void SV_OnEntityPreSpawnFunction(entity this) return; } - if (DoesQ3ARemoveThisEntity(this)) { + if (q3compat && DoesQ3ARemoveThisEntity(this)) { delete(this); return; } @@ -363,6 +411,62 @@ void SV_OnEntityPreSpawnFunction(entity this) } } +string GetField_fullspawndata(entity e, string f, ...) +/* Retrieves the value of a map entity field from fullspawndata + * This bypasses field value changes made by the engine, + * eg string-to-float and escape sequence substitution. + * + * Avoids the need to declare fields just to read them once :) + * + * Returns the last instance of the field to match DarkPlaces behaviour. + * Path support: converts \ to / and tests the file if a third (bool, true) arg is passed. + * Returns string_null if the entity does not have the field, or the file is not in the VFS. + * + * FIXME: entities with //comments are not supported. + */ +{ + string v = string_null; + + if (!e.fullspawndata) + { + //LOG_WARNF("^1EDICT %s (classname %s) has no fullspawndata, engine lacks support?", ftos(num_for_edict(e)), e.classname); + return v; + } + + if (strstrofs(e.fullspawndata, "//", 0) >= 0) + { + // tokenize and tokenize_console return early if "//" is reached, + // which can leave an odd number of tokens and break key:value pairing. + LOG_WARNF("^1EDICT %s fullspawndata contains unsupported //comment^7%s", ftos(num_for_edict(e)), e.fullspawndata); + return v; + } + + //print(sprintf("%s(EDICT %s, FIELD %s)\n", __FUNC__, ftos(num_for_edict(e)), f)); + //print(strcat("FULLSPAWNDATA:", e.fullspawndata, "\n")); + + // tokenize treats \ as an escape, but tokenize_console returns the required literal + for (int t = tokenize_console(e.fullspawndata) - 3; t > 0; t -= 2) + { + //print(sprintf("\tTOKEN %s:%s\t%s:%s\n", ftos(t), ftos(t + 1), argv(t), argv(t + 1))); + if (argv(t) == f) + { + v = argv(t + 1); + break; + } + } + + //print(strcat("RESULT: ", v, "\n\n")); + + if (v && ...(0, bool) == true) + { + v = strreplace("\\", "/", v); + if (whichpack(v) == "") + return string_null; + } + + return v; +} + void WarpZone_PostInitialize_Callback() { // create waypoint links for warpzones