]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/main.qc
Soft reset: Fix warmup limit and repeatable restart
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / main.qc
index 3859ffa6f7e0b750452b5098db1fcf1232fa4bc2..2b29422b8969362356b6a552106158c9b881d0e7 100644 (file)
@@ -1,35 +1,64 @@
 #include "main.qh"
 
-#include "anticheat.qh"
-#include "hook.qh"
-#include "damage.qh"
-#include "world.qh"
-#include "spawnpoints.qh"
+#include <common/command/generic.qh>
+#include <common/constants.qh>
+#include <common/deathtypes/all.qh>
+#include <common/debug.qh>
+#include <common/mapinfo.qh>
+#include <common/monsters/sv_monsters.qh>
+#include <common/util.qh>
+#include <common/vehicles/all.qh>
+#include <common/weapons/_all.qh>
+#include <lib/csqcmodel/sv_model.qh>
+#include <lib/warpzone/common.qh>
+#include <lib/warpzone/server.qh>
+#include <server/anticheat.qh>
+#include <server/bot/api.qh>
+#include <server/command/common.qh>
+#include <server/compat/quake3.qh>
+#include <server/damage.qh>
 #include <server/gamelog.qh>
-
-#include "bot/api.qh"
-
-#include "command/common.qh"
-
+#include <server/hook.qh>
+#include <server/ipban.qh>
 #include <server/mutators/_mod.qh>
-#include "weapons/csqcprojectile.qh"
+#include <server/spawnpoints.qh>
 #include <server/weapons/common.qh>
-#include <server/compat/quake3.qh>
+#include <server/weapons/csqcprojectile.qh>
+#include <server/world.qh>
 
-#include "../common/constants.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/debug.qh"
-#include "../common/mapinfo.qh"
-#include "../common/util.qh"
-
-#include "../common/vehicles/all.qh"
-#include <common/monsters/sv_monsters.qh>
-#include <common/weapons/_all.qh>
-
-#include "../lib/csqcmodel/sv_model.qh"
-
-#include "../lib/warpzone/common.qh"
-#include "../lib/warpzone/server.qh"
+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)
 {
@@ -302,8 +331,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;
        }
 
@@ -368,6 +397,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
@@ -380,6 +465,37 @@ void WarpZone_PostInitialize_Callback()
        delete(tracetest_ent);
 }
 
+/** engine callback */
+void URI_Get_Callback(float id, float status, string data)
+{
+       if(url_URI_Get_Callback(id, status, data))
+       {
+               // handled
+       }
+       else if (id == URI_GET_DISCARD)
+       {
+               // discard
+       }
+       else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
+       {
+               // sv_cmd curl
+               Curl_URI_Get_Callback(id, status, data);
+       }
+       else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
+       {
+               // online ban list
+               OnlineBanList_URI_Get_Callback(id, status, data);
+       }
+       else if (MUTATOR_CALLHOOK(URI_GetCallback, id, status, data))
+       {
+               // handled by a mutator
+       }
+       else
+       {
+               LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".");
+       }
+}
+
 /*
 ==================
 main