]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Provisions for unit testing
authorTimePath <andrew.hardaker1995@gmail.com>
Wed, 23 Mar 2016 11:17:59 +0000 (22:17 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Wed, 23 Mar 2016 11:27:25 +0000 (22:27 +1100)
25 files changed:
qcsrc/common/state.qc
qcsrc/common/state.qh
qcsrc/dpdefs/doc.md
qcsrc/dpdefs/progsdefs.qh
qcsrc/lib/csqcmodel/cl_model.qc
qcsrc/lib/csqcmodel/sv_model.qc
qcsrc/lib/test.qc
qcsrc/server/autocvars.qh
qcsrc/server/campaign.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_client.qh
qcsrc/server/command/cmd.qc
qcsrc/server/defs.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/mutator/gamemode_lms.qc
qcsrc/server/progs.inc
qcsrc/server/spawnpoints.qc
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/tests.qc [new file with mode: 0644]
qcsrc/server/tests.qh [new file with mode: 0644]
qcsrc/server/weapons/common.qc
qcsrc/tools/compilationunits.sh

index 1e7582d79f673fe863fa809dd67af42670d18c41..21e009022145aa9bbcef21d404190f8e5abed328 100644 (file)
@@ -20,7 +20,7 @@ void PlayerState_detach(entity this)
     Inventory_delete(self);
 }
 
-void GetCvars(int);
+void GetCvars(entity this, int);
 void DecodeLevelParms(entity this);
 void PlayerScore_Attach(entity this);
 void ClientData_Attach(entity this);
@@ -35,7 +35,7 @@ void ClientState_attach(entity this)
 {
        this._cs = NEW(ClientState, this);
 
-    GetCvars(0);  // get other cvars from player
+    GetCvars(this, 0);  // get other cvars from player
 
        // TODO: xonstat elo.txt support, until then just 404s
        if (false && IS_REAL_CLIENT(this)) { PlayerStats_PlayerBasic_CheckUpdate(this); }
@@ -69,7 +69,7 @@ void ClientState_detach(entity this)
        remove(CS(this));
        this._cs = NULL;
 
-    GetCvars(-1);  // free cvars
+    GetCvars(this, -1);  // free cvars
 
     bot_clientdisconnect(this);
 
index 4e80bfd2cd971338acba75cf50b55de0d9e74859..110df1c575840421fb05a47ebb66cc16c89f8efa 100644 (file)
@@ -25,7 +25,7 @@ CLASS(PlayerState, Object)
 ENDCLASS(PlayerState)
 
 .PlayerState _ps;
-#define PS(this) (this._ps)
+#define PS(this) ((this)._ps)
 
 // TODO: renew on death
 void PlayerState_attach(entity this);
@@ -50,7 +50,7 @@ ENDCLASS(ClientState)
 #if NDEBUG
        #define CS(this) (this._cs)
 #else
-       ClientState CS(entity this) { assert(IS_CLIENT(this)); assert(this._cs); return this._cs; }
+       ClientState CS(Client this) { TC(Client, this); assert(this._cs); return this._cs; }
 #endif
 
 void ClientState_attach(entity this);
index fa25604402652d987de3a2e942f29010db146792..d279ced478d0c48df33ffc0cc8e43837687c73ee 100644 (file)
@@ -124,6 +124,7 @@ void SV_OnEntityPostSpawnFunction();
 //   parm1..n
 void SetNewParms();
 
+// Runs every frame
 // input:
 //
 .bool customizeentityforclient();
index 535c3b6f7c3504e8ba3113a385ec7e489bcb29fa..783ac036faf07c3e645cce833b78c3bb6ab5e1d1 100644 (file)
 #undef spawn
 #undef setmodel
 
+#define stuffcmd(cl, ...) MACRO_BEGIN \
+    entity _cl = (cl); \
+    if (IS_REAL_CLIENT(_cl)) stuffcmd(_cl, __VA_ARGS__); \
+MACRO_END
+
 #pragma noref 0
 
 #endif
index 4c7d2da726ddaf01cfbbc4c144e8961ee25b3535..789f115d14ac960b3c361bac06ce8229e7960e0b 100644 (file)
@@ -220,14 +220,14 @@ NET_HANDLE(ENT_CLIENT_MODEL, bool isnew)
        int sf = ReadInt24_t();
 
        // some nice flags for CSQCMODEL_IF and the hooks
-       bool isplayer = (this.entnum >= 1 && this.entnum <= maxclients);
+       bool isplayer = ReadByte() || (this.entnum >= 1 && this.entnum <= maxclients);
        if (isnew && isplayer)
        {
                CSQCModel_players[this.entnum - 1] = this;
                this.entremove = CSQCModel_remove;
        }
        bool islocalplayer = (this.entnum == player_localnum + 1);
-       noref bool isnolocalplayer = (isplayer && (this.entnum != player_localnum + 1));
+       noref bool isnolocalplayer = (isplayer && !islocalplayer);
 
        this.classname = "csqcmodel";
        this.iflags |= IFLAG_ORIGIN; // interpolate origin too
index 60ad7252f9b853b11d742b1cf4ac14bbb0bd7e3d..dc2e4021fbc405283e1de904b13b9546ebbc648a 100644 (file)
@@ -34,23 +34,20 @@ bool CSQCModel_Send(entity to, int sf)
 {
     SELFPARAM();
        // some nice flags for CSQCMODEL_IF
-       float isplayer = (IS_CLIENT(self));
-       float islocalplayer = (self == to);
-       float isnolocalplayer = (isplayer && (self != to));
-
-       unused_float = isplayer;
-       unused_float = islocalplayer;
-       unused_float = isnolocalplayer;
+       noref bool isplayer = IS_CLIENT(this);
+       noref bool islocalplayer = (this == to);
+       noref bool isnolocalplayer = (isplayer && (this != to));
 
        WriteHeader(MSG_ENTITY, ENT_CLIENT_MODEL);
        WriteInt24_t(MSG_ENTITY, sf);
+       WriteByte(MSG_ENTITY, isplayer);
 
 #define CSQCMODEL_IF(cond) if(cond) {
 #define CSQCMODEL_ENDIF }
 #define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
        if(sf & flag) \
        { \
-               w(MSG_ENTITY, self.csqcmodel_##f); \
+               w(MSG_ENTITY, this.csqcmodel_##f); \
        }
 #define CSQCMODEL_PROPERTY_SCALED(flag,t,r,w,f,s,mi,ma) CSQCMODEL_PROPERTY(flag,t,r,w,f)
        ALLPROPERTIES
@@ -68,7 +65,7 @@ bool CSQCModel_Send(entity to, int sf)
 void CSQCModel_CheckUpdate(entity e)
 {
        // some nice flags for CSQCMODEL_IF
-       float isplayer = (IS_CLIENT(e));
+       float isplayer = IS_CLIENT(e);
        float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
        float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags
 
index 41e1f294ae4c7eb0fc48684490e02615eba4c9e7..0997e53a4964fe1657c045ce01f4f698f1a7259b 100644 (file)
@@ -22,7 +22,8 @@ bool TEST_Run(string s)
        TEST_failed = 0;
        TEST_fatal = 0;
        TEST_ok = false;
-       callfunction(strcat("_TEST_", s));
+       string fn = strcat("_TEST_", s);
+       if (isfunction(fn)) callfunction(fn);
        if (TEST_failed > 0)
        {
                LOG_INFOF("%s: %d items failed.\n", s, TEST_failed);
index 26fa625af1cfffe4e25a8c1f39dd954d63f4b840..893bb647859e8da75b80b26b05e028c59e1ab530 100644 (file)
@@ -263,7 +263,7 @@ bool autocvar_g_spawn_alloweffects;
 float autocvar_g_spawn_furthest;
 bool autocvar_g_spawn_useallspawns;
 bool autocvar_g_spawnpoints_auto_move_out_of_solid;
-#define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
+float autocvar_g_spawnshieldtime;
 float autocvar_g_spawnshield_blockdamage;
 float autocvar_g_teamdamage_resetspeed;
 float autocvar_g_teamdamage_threshold;
index 8be26fef86dea36b6ab1d252692fb7fb398f50f3..6feb07c4541965366aa850ee8aaf2b984200e253 100644 (file)
@@ -12,6 +12,8 @@ void CampaignPostIntermission(); // must change map
 
 void CampaignLevelWarp(float n);
 
-float campaign_bots_may_start;
-// campaign mode: bots shall spawn but wait for the player to spawn before they do anything
-// in other game modes, this is ignored
+/**
+ * campaign mode: bots shall spawn but wait for the player to spawn before they do anything
+ * in other game modes, this is ignored
+ */
+bool campaign_bots_may_start;
index 44e1e41617b09d41080a4271423d920b25724152..8a6eefe86ce153670d8af307cdb9d594396852f7 100644 (file)
 
 #include "../lib/warpzone/server.qh"
 
+STATIC_METHOD(Client, Add, void(Client this, int _team))
+{
+    WITH(entity, self, this, ClientConnect());
+    TRANSMUTE(Player, this);
+    this.frame = 12; // 7
+    this.team = _team;
+    WITH(entity, self, this, PutClientInServer());
+}
+
+void PutObserverInServer();
+void ClientDisconnect();
+
+STATIC_METHOD(Client, Remove, void(Client this))
+{
+    TRANSMUTE(Observer, this);
+    WITH(entity, self, this, PutClientInServer(); ClientDisconnect());
+}
 
 void send_CSQC_teamnagger() {
        WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
@@ -89,14 +106,14 @@ bool ClientData_Send(entity this, entity to, int sf)
 void ClientData_Attach(entity this)
 {
        Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send);
-       self.clientdata.drawonlytoclient = this;
-       self.clientdata.owner = this;
+       this.clientdata.drawonlytoclient = this;
+       this.clientdata.owner = this;
 }
 
 void ClientData_Detach(entity this)
 {
        remove(this.clientdata);
-       self.clientdata = NULL;
+       this.clientdata = NULL;
 }
 
 void ClientData_Touch(entity e)
@@ -988,6 +1005,7 @@ void ClientConnect()
        SELFPARAM();
        if (Ban_MaybeEnforceBanOnce(this)) return;
        assert(!IS_CLIENT(this), return);
+       this.flags |= FL_CLIENT;
        assert(player_count >= 0, player_count = 0);
 
 #ifdef WATERMARK
@@ -1035,16 +1053,14 @@ void ClientConnect()
            JoinBestTeam(this, false, false); // if the team number is valid, keep it
            this.playerid = id;
     }
+
        if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) {
                TRANSMUTE(Observer, this);
        } else {
-               if (!teamplay || autocvar_g_balance_teams)
-               {
+               if (!teamplay || autocvar_g_balance_teams) {
                        TRANSMUTE(Player, this);
-                       campaign_bots_may_start = 1;
-               }
-               else
-               {
+                       campaign_bots_may_start = true;
+               } else {
                        TRANSMUTE(Observer, this); // do it anyway
                }
        }
@@ -1759,7 +1775,7 @@ void LeaveSpectatorMode()
                                { JoinBestTeam(self, false, true); }
 
                        if(autocvar_g_campaign)
-                               { campaign_bots_may_start = 1; }
+                               { campaign_bots_may_start = true; }
 
                        Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CPID_PREVENT_JOIN);
 
index 36f33219c6d9f4cc871470a2c9122a7fbf9ca295..337c4db838c3e73ed0b95b60fd58658d22315cb2 100644 (file)
@@ -28,19 +28,30 @@ CLASS(Client, Object)
 
     // custom
 
-    ATTRIB(Client, flags, int, this.flags)
     ATTRIB(Client, playerid, int, this.playerid)
 
     METHOD(Client, m_unwind, bool(Client this));
 
+    STATIC_METHOD(Client, Add, void(Client this, int _team));
+    STATIC_METHOD(Client, Remove, void(Client this));
+
     INIT(Client) {
         if (this.m_unwind(this)) return this;
+        make_impure(this);
         this.classname = "player_joining";
-        this.flags = FL_CLIENT;
         static int playerid_last;
         this.playerid = ++playerid_last;
         ClientState_attach(this);
     }
+    DESTRUCTOR(Client) {
+        Client_Remove(this);
+    }
+    CONSTRUCTOR(Client, string name) {
+        CONSTRUCT(Client);
+        this.netname = name;
+        this.netaddress = "local";
+        this.playermodel = "models/player/megaerebus.iqm";
+    }
 ENDCLASS(Client)
 
 CLASS(Observer, Client)
index 56ba45c61240a07c8b6bc67f8cbe77099a653c65..4533e4f5b21cdb14cfe97aa0d39eeb7d014dc981 100644 (file)
@@ -177,7 +177,7 @@ void ClientCommand_join(float request)
                                        if (self.caplayer) return;
                                        if (nJoinAllowed(self, self))
                                        {
-                                               if (autocvar_g_campaign)   campaign_bots_may_start = 1;
+                                               if (autocvar_g_campaign)   campaign_bots_may_start = true;
                                                TRANSMUTE(Player, self);
                                                PlayerScore_Clear(self);
                                                Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CPID_PREVENT_JOIN);
@@ -471,7 +471,7 @@ void ClientCommand_sentcvar(float request, float argc, string command)
                                        tokenize_console(s);
                                }
 
-                               GetCvars(1);
+                               GetCvars(this, 1);
 
                                return;
                        }
index 4b1874f89fdd7e679bc2394ab319a074f9964bad..843eeb99e16e99dc85b9105d6c7cbf71ee5dfb7c 100644 (file)
@@ -378,7 +378,7 @@ float client_cefc_accumulatortime;
 
 string deathmessage;
 
-.float just_joined;
+.bool just_joined;
 
 .float cvar_cl_weaponimpulsemode;
 .float selectweapon; // last selected weapon of the player
index 4814c408022374fcfd84bf36cdc9f2f75043aea0..65d38c8aa90ec3bb672e3edab877bf61d5422a4e 100644 (file)
@@ -410,8 +410,8 @@ REPLICATE(cvar_g_xonoticversion, string, "g_xonoticversion");
 /**
  * @param f -1: cleanup, 0: request, 1: receive
  */
-void GetCvars(int f)
-{SELFPARAM();
+void GetCvars(entity this, int f)
+{
        string s = string_null;
 
        if (f > 0)
index 4a6829a74306df60412c366ab5e3a08eaf3e9959..4d4bf4a616967dc0c3a8549f160fa09b046c4af4 100644 (file)
@@ -76,7 +76,7 @@ void GameLogInit();
 
 void GameLogClose();
 
-void GetCvars(float f);
+void GetCvars(entity this, float f);
 
 string GetMapname();
 
index 7ecb290ab64f0c8bad066a0f355f06acc3381c92..4f84c5abbcb6c71994884d3a68e13f2ef703c5c8 100644 (file)
@@ -215,7 +215,7 @@ MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
 MUTATOR_HOOKFUNCTION(lms, ClientConnect)
 {SELFPARAM();
        TRANSMUTE(Player, self);
-       campaign_bots_may_start = 1;
+       campaign_bots_may_start = true;
 
        if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
        {
index 0665f807c14c9e30b415d135ac4ab4aa6951cd75..a26f56f8e4e42605f0a599d40635e788f7542ec1 100644 (file)
@@ -34,6 +34,8 @@
 #include "t_quake3.qc"
 #include "t_quake.qc"
 
+#include "tests.qc"
+
 #include "bot/_all.inc"
 
 #include "command/all.qc"
index d2c632294e2e7fd3106aee5ee81540aafb817f25..344f05846f742cdb4d59b2c98ac053be715d1b8d 100644 (file)
@@ -30,13 +30,13 @@ bool SpawnEvent_Send(entity this, entity to, int sf)
 
        if(autocvar_g_spawn_alloweffects)
        {
-               WriteByte(MSG_ENTITY, etof(self.owner));
-               WriteCoord(MSG_ENTITY, self.owner.origin.x);
-               WriteCoord(MSG_ENTITY, self.owner.origin.y);
-               WriteCoord(MSG_ENTITY, self.owner.origin.z);
+               WriteByte(MSG_ENTITY, etof(this.owner));
+               WriteCoord(MSG_ENTITY, this.owner.origin.x);
+               WriteCoord(MSG_ENTITY, this.owner.origin.y);
+               WriteCoord(MSG_ENTITY, this.owner.origin.z);
                send = true;
        }
-       else if((to == self.owner) || (IS_SPEC(to) && (to.enemy == self.owner)) )
+       else if((to == this.owner) || (IS_SPEC(to) && (to.enemy == this.owner)) )
        {
                WriteByte(MSG_ENTITY, 0);
                send = true;
index 4aa404d75979f85c4c7e320f19e4b8a5f6228e98..cf090833a5ff61c165eb160007f3070bb038d2e0 100644 (file)
@@ -154,8 +154,13 @@ float game_delay_last;
 
 bool autocvar_sv_autopause = true;
 float RedirectionThink();
+void PM_Main(Client this);
 void StartFrame()
 {
+    // TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
+    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_NOT_A_CLIENT(it), PM_Main(it));
+    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_NOT_A_CLIENT(it), WITH(entity, self, it, PlayerPreThink()));
+
        execute_next_frame();
        if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true);
 
@@ -230,6 +235,7 @@ void StartFrame()
        MUTATOR_CALLHOOK(SV_StartFrame);
 
     FOREACH_CLIENT(true, LAMBDA(GlobalStats_update(it)));
+    FOREACH_ENTITY_CLASS(STR_PLAYER, IS_NOT_A_CLIENT(it), WITH(entity, self, it, PlayerPostThink()));
 }
 
 .vector originjitter;
index 34ee3277a5fa6ce755520cfe520db6f29e030fe9..fabeaeced96f4e41b2476aaad022b9e4176eac82 100644 (file)
@@ -499,7 +499,7 @@ float FindSmallestTeam(entity pl, float ignore_pl)
        return RandomSelection_chosen_float;
 }
 
-float JoinBestTeam(entity pl, float only_return_best, float forcebestteam)
+int JoinBestTeam(entity pl, bool only_return_best, bool forcebestteam)
 {SELFPARAM();
        float smallest, selectedteam;
 
index 5a2fe7b12e950322efd833c821d490f880b5d557..e24005a3211336bc53a8c082751df9bf82eeac74 100644 (file)
@@ -47,7 +47,7 @@ float TeamSmallerEqThanTeam(float ta, float tb, entity e);
 // NOTE: Assumes CheckAllowedTeams has already been called!
 float FindSmallestTeam(entity pl, float ignore_pl);
 
-float JoinBestTeam(entity pl, float only_return_best, float forcebestteam);
+int JoinBestTeam(entity pl, bool only_return_best, bool forcebestteam);
 
 //void() ctf_playerchanged;
 void SV_ChangeTeam(float _color);
diff --git a/qcsrc/server/tests.qc b/qcsrc/server/tests.qc
new file mode 100644 (file)
index 0000000..0b153ff
--- /dev/null
@@ -0,0 +1,38 @@
+#include "tests.qh"
+
+void test_weapons_hurt() {
+    SELFPARAM();
+    EXPECT_NE(100, this.health);
+    remove(this.enemy);
+    remove(this);
+}
+
+TEST(Weapons, Hurt)
+{
+    entity it;
+
+    Client a = it = NEW(Client, "A");
+    WITH(float, autocvar_g_spawnshieldtime, 0, Client_Add(it, NUM_TEAM_1));
+    it.origin = '-100 0 0';
+    it.angles = '0 0 0';
+
+    Client b = it = NEW(Client, "B");
+    WITH(float, autocvar_g_spawnshieldtime, 0, Client_Add(it, NUM_TEAM_2));
+    it.origin = '100 0 0';
+    it.angles = '0 180 0';
+
+    it = a;
+    PHYS_INPUT_BUTTON_ATCK(it) = true;
+    it.items |= IT_UNLIMITED_AMMO;
+    Weapon wep = WEP_VORTEX;
+    W_GiveWeapon(it, wep.m_id);
+    W_SwitchWeapon_Force(it, wep);
+
+    it = b;
+    PHYS_INPUT_BUTTON_JUMP(it) = true;
+    it.enemy = a;
+
+    defer(it, wep.switchdelay_raise + 0.1, test_weapons_hurt);
+
+    SUCCEED();
+}
diff --git a/qcsrc/server/tests.qh b/qcsrc/server/tests.qh
new file mode 100644 (file)
index 0000000..05c8d37
--- /dev/null
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "autocvars.qh"
+#include "cl_client.qh"
+#include "command/all.qh"
+#include "weapons/common.qh"
+#include "weapons/selection.qh"
+#include <common/items/item.qh>
+#include <common/physics/player.qh>
+#include <common/weapons/all.qh>
index 85e8820f38c9dc21cc15f4ff3fe8581357af8576..2a439f3d9eeb26d5763b85349995a12937f65f63 100644 (file)
@@ -8,20 +8,15 @@
 #include <common/weapons/all.qh>
 #include <common/items/all.qc>
 
-void W_GiveWeapon (entity e, float wep)
-{SELFPARAM();
-
-       if (!wep)
-               return;
+void W_GiveWeapon(entity e, int wep)
+{
+       if (!wep) return;
 
        e.weapons |= WepSet_FromWeapon(Weapons_from(wep));
 
-       setself(e);
-
-       if(IS_PLAYER(other))
-               { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); }
-
-       setself(this);
+       if (IS_PLAYER(e)) {
+           Send_Notification(NOTIF_ONE, e, MSG_MULTI, ITEM_WEAPON_GOT, wep);
+    }
 }
 
 void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound
index 658fe8682581933e7629259049157738aac2559e..676c3f315a79d440c9f0e9e0aca14d73ead96df7 100755 (executable)
@@ -33,14 +33,21 @@ QCCFLAGS="${QCCFLAGS[@]} ${NOWARN[@]}"
 . qcc.sh
 cd ..
 
-function check() {
+function check1() {
     declare -l base="${1}"
     MODE=${2}
-    find ${base} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do
-        qpp ${file} test.dat \
+    declare -l file="${3}"
+    qpp ${file} test.dat \
             -include lib/_all.inc -include ${base}/_all.qh \
             -I. ${QCCIDENT} ${QCCDEFS} -D${MODE} > ${WORKDIR}/${MODE}.qc
-        qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${MODE}.qc >/dev/null
+    qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${MODE}.qc >/dev/null
+}
+
+function check() {
+    declare -l base="${1}"
+    MODE=${2}
+    find ${base} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do
+        check1 ${base} ${MODE} ${file}
     done
 }