Implement intrusive lists
authorTimePath <andrew.hardaker1995@gmail.com>
Sun, 24 Jul 2016 10:36:12 +0000 (20:36 +1000)
committerTimePath <andrew.hardaker1995@gmail.com>
Tue, 26 Jul 2016 12:04:03 +0000 (22:04 +1000)
38 files changed:
qcsrc/client/commands/cl_cmd.qc
qcsrc/client/hud/panel/radar.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/teamradar.qc
qcsrc/client/view.qc
qcsrc/client/wall.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/debug.qh
qcsrc/common/effects/qc/casings.qc
qcsrc/common/effects/qc/gibs.qc
qcsrc/common/effects/qc/modeleffects.qc
qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc
qcsrc/common/mutators/mutator/damagetext/damagetext.qc
qcsrc/common/mutators/mutator/nades/net.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh
qcsrc/common/t_items.qc
qcsrc/common/triggers/func/conveyor.qc
qcsrc/common/triggers/func/plat.qc
qcsrc/common/triggers/func/pointparticles.qc
qcsrc/common/triggers/func/rainsnow.qc
qcsrc/common/triggers/func/train.qc
qcsrc/common/triggers/misc/laser.qc
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/vehicles/cl_vehicles.qc
qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc
qcsrc/common/vehicles/vehicle/raptor_weapons.qc
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/dpdefs/menudefs.qh
qcsrc/lib/_all.inc
qcsrc/lib/intrusivelist.qh [new file with mode: 0644]
qcsrc/lib/oo.qh
qcsrc/menu/menu.qc
qcsrc/server/g_world.qc

index dce058c..100bebf 100644 (file)
@@ -183,6 +183,7 @@ void LocalCommand_debugmodel(int request, int argc)
                        setorigin(debugmodel_entity, view_origin);
                        debugmodel_entity.angles = view_angles;
                        debugmodel_entity.draw = DrawDebugModel;
+                       IL_PUSH(g_drawables, debugmodel_entity);
 
                        return;
                }
index ad0fa28..3e2a516 100644 (file)
@@ -351,7 +351,7 @@ void HUD_Radar()
 
        draw_teamradar_background(hud_panel_radar_foreground_alpha);
 
-       FOREACH_ENTITY_CLASS("radarlink", true, draw_teamradar_link(it.origin, it.velocity, it.team));
+       IL_EACH(g_radarlinks, true, draw_teamradar_link(it.origin, it.velocity, it.team));
 
        FOREACH_ENTITY_FLAGS(teamradar_icon, 0xFFFFFF, {
                if ( hud_panel_radar_mouse )
index 65ee03a..bd9e82e 100644 (file)
@@ -701,6 +701,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        this.drawmask = MASK_NORMAL;
                        //this.move_movetype = MOVETYPE_NOCLIP;
                        //this.draw = Spawn_Draw;
+                       IL_PUSH(g_drawables, this);
                }*/
                if(autocvar_cl_spawn_point_particles)
                {
@@ -718,6 +719,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
                        else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
 
                        this.draw = Spawn_Draw;
+                       if (is_new) IL_PUSH(g_drawables, this);
                        setpredraw(this, Spawn_PreDraw);
                        this.fade_start = autocvar_cl_spawn_point_dist_min;
                        this.fade_end = autocvar_cl_spawn_point_dist_max;
index 15362aa..ce1b3eb 100644 (file)
@@ -85,11 +85,18 @@ entity teamslots[17];    // 17 teams (including "spectator team")
 .float eliminated;
 
 .void(entity) draw;
+IntrusiveList g_drawables;
+STATIC_INIT(g_drawables) { g_drawables = IL_NEW(); }
 .void(entity) draw2d;
+IntrusiveList g_drawables_2d;
+STATIC_INIT(g_drawables_2d) { g_drawables_2d = IL_NEW(); }
 .void(entity) entremove;
 float drawframetime;
 vector view_origin, view_forward, view_right, view_up;
 
+IntrusiveList g_radarlinks;
+STATIC_INIT(g_radarlinks) { g_radarlinks = IL_NEW(); }
+
 bool button_zoom;
 bool spectatorbutton_zoom;
 bool button_attack2;
index a8708f2..ab14cd3 100644 (file)
@@ -203,6 +203,7 @@ NET_HANDLE(ENT_CLIENT_RADARLINK, bool isnew)
 
        this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
        this.classname = "radarlink";
+       if (isnew) IL_PUSH(g_radarlinks, this);
 
        if(sendflags & 1)
        {
index 200bcfd..cbce1fc 100644 (file)
@@ -363,6 +363,7 @@ STATIC_INIT(Porto)
 {
        entity e = new_pure(porto);
        e.draw = Porto_Draw;
+       IL_PUSH(g_drawables, e);
        e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
 }
 
@@ -1871,10 +1872,7 @@ void CSQC_UpdateView(entity this, float w, float h)
           mousepos = mousepos*0.5 + getmousepos();
         */
 
-       FOREACH_ENTITY_FLOAT(pure_data, false,
-       {
-               if(it.draw) { it.draw(it); }
-       });
+       IL_EACH(g_drawables, it.draw, it.draw(it));
 
        addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
        renderscene();
@@ -2204,10 +2202,7 @@ void CSQC_UpdateView(entity this, float w, float h)
          } else */
 
        // draw 2D entities
-       FOREACH_ENTITY_FLOAT(pure_data, false,
-       {
-               if(it.draw2d) { it.draw2d(it); }
-       });
+       IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it));
        Draw_ShowNames_All();
        Debug_Draw();
 
@@ -2264,6 +2259,8 @@ void CSQC_UpdateView(entity this, float w, float h)
        // let's reset the view back to normal for the end
        setproperty(VF_MIN, '0 0 0');
        setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h);
+
+       IL_ENDFRAME();
 }
 
 
index b94fddd..01a86bb 100644 (file)
@@ -230,5 +230,6 @@ NET_HANDLE(ENT_CLIENT_WALL, bool isnew)
 
        this.entremove = Ent_Wall_Remove;
        this.draw = Ent_Wall_Draw;
+       if (isnew) IL_PUSH(g_drawables, this);
        setpredraw(this, Ent_Wall_PreDraw);
 }
index 56a172b..dd16f0a 100644 (file)
@@ -477,6 +477,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
 
        this.classname = "csqcprojectile";
        this.draw = Projectile_Draw;
+       if (isnew) IL_PUSH(g_drawables, this);
        this.entremove = Ent_RemoveProjectile;
 }
 
index 83cbc30..052e00f 100644 (file)
@@ -309,6 +309,7 @@ STATIC_INIT(TRACE_ENT)
 {
        entity e = TRACE_ENT = new_pure(TRACE_ENT);
        e.draw2d = Trace_draw2d;
+       IL_PUSH(g_drawables_2d, e);
 }
 #endif
 
index 30543ad..a4ade38 100644 (file)
@@ -151,6 +151,7 @@ NET_HANDLE(casings, bool isNew)
     casing.drawmask = MASK_NORMAL;
 
     casing.draw = Casing_Draw;
+    if (isNew) IL_PUSH(g_drawables, casing);
     casing.velocity = casing.velocity + 2 * prandomvec();
     casing.avelocity = '0 250 0' + 100 * prandomvec();
     casing.move_movetype = MOVETYPE_BOUNCE;
index aaecf18..78f1a27 100644 (file)
@@ -180,6 +180,7 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector
        setsize (gib, '-8 -8 -8', '8 8 8');
 
        gib.draw = Gib_Draw;
+       IL_PUSH(g_drawables, gib);
        if(destroyontouch)
                settouch(gib, Gib_Touch);
        else
index f577762..28b8bb4 100644 (file)
@@ -156,6 +156,7 @@ NET_HANDLE(ENT_CLIENT_MODELEFFECT, bool isnew)
        e.cnt = ReadByte() / 255.0; // actually alpha
 
        e.draw = ModelEffect_Draw;
+       if (isnew) IL_PUSH(g_drawables, e);
 
        if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
        return true;
index a22815b..0135f46 100644 (file)
@@ -39,6 +39,7 @@ void ons_generator_ray_spawn(vector org)
        e.move_time = time + 0.05;
        e.drawmask = MASK_NORMAL;
        e.draw = ons_generator_ray_draw;
+       IL_PUSH(g_drawables, e);
 }
 
 void generator_draw(entity this)
index 4694bcb..8de4f2b 100644 (file)
@@ -86,6 +86,7 @@ CLASS(DamageText, Object)
         this.m_group = _group;
         this.m_friendlyfire = _friendlyfire;
         DamageText_update(this, _origin, _health, _armor, _deathtype);
+               IL_PUSH(g_drawables_2d, this);
     }
 ENDCLASS(DamageText)
 #endif
index cbb8e4a..9488e02 100644 (file)
@@ -29,6 +29,7 @@ void orb_setup(entity e)
        e.orb_radius = e.orb_radius/model_radius*0.6;
 
        e.draw = orb_draw;
+       IL_PUSH(g_drawables, e);
        e.health = 255;
        e.move_movetype = MOVETYPE_NONE;
        e.solid = SOLID_NOT;
index 56c4c5b..8ce1094 100644 (file)
@@ -100,9 +100,9 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
 #endif
 
 #ifdef CSQC
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
 NET_HANDLE(waypointsprites, bool isnew) {
-    Ent_WaypointSprite(this);
+    Ent_WaypointSprite(this, isnew);
     return true;
 }
 
@@ -114,7 +114,7 @@ void Ent_RemoveWaypointSprite(entity this)
 }
 
 /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
-void Ent_WaypointSprite(entity this)
+void Ent_WaypointSprite(entity this, bool isnew)
 {
     int sendflags = ReadByte();
     this.wp_extra = ReadByte();
@@ -123,6 +123,7 @@ void Ent_WaypointSprite(entity this)
         this.spawntime = time;
 
     this.draw2d = Draw_WaypointSprite;
+    if (isnew) IL_PUSH(g_drawables_2d, this);
 
     InterpolateOrigin_Undo(this);
     this.iflags |= IFLAG_ORIGIN;
index fa9f4fb..26e4058 100644 (file)
@@ -108,7 +108,7 @@ vector fixrgbexcess(vector rgb);
 
 void Ent_RemoveWaypointSprite(entity this);
 
-void Ent_WaypointSprite(entity this);
+void Ent_WaypointSprite(entity this, bool isnew);
 
 void WaypointSprite_Load_Frames(string ext);
 
index 0de2fe1..ee9babf 100644 (file)
@@ -214,6 +214,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
 
         if(autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI))
         {
+            if (isnew) IL_PUSH(g_drawables, this);
             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
             this.draw = ItemDrawSimple;
 
index 95f9ebc..8bd6016 100644 (file)
@@ -151,6 +151,7 @@ void conveyor_draw(entity this) { conveyor_think(this); }
 void conveyor_init(entity this)
 {
        this.draw = conveyor_draw;
+       IL_PUSH(g_drawables, this);
        this.drawmask = MASK_NORMAL;
 
        this.move_movetype = MOVETYPE_NONE;
index 07709df..341ca46 100644 (file)
@@ -171,6 +171,7 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
                this.move_movetype = MOVETYPE_PUSH;
                this.drawmask = MASK_NORMAL;
                this.draw = plat_draw;
+               if (isnew) IL_PUSH(g_drawables, this);
                this.use = plat_use;
                this.entremove = trigger_remove_generic;
 
index 1705d40..0cf0615 100644 (file)
@@ -378,6 +378,7 @@ NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
        setsize(this, this.mins, this.maxs);
        this.solid = SOLID_NOT;
        this.draw = Draw_PointParticles;
+       if (isnew) IL_PUSH(g_drawables, this);
        this.entremove = Ent_PointParticles_Remove;
 }
 #endif
index d2cf189..cc7dc09 100644 (file)
@@ -134,6 +134,7 @@ NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew)
        setorigin(this, this.origin);
        setsize(this, this.mins, this.maxs);
        this.solid = SOLID_NOT;
+       if (isnew) IL_PUSH(g_drawables, this);
        if(this.impulse)
                this.draw = Draw_Rain;
        else
index 6bf6b0b..29c7d0a 100644 (file)
@@ -310,6 +310,7 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew)
                this.move_movetype = MOVETYPE_PUSH;
                this.drawmask = MASK_NORMAL;
                this.draw = train_draw;
+               if (isnew) IL_PUSH(g_drawables, this);
                this.entremove = trigger_remove_generic;
 
                if(set_platmovetype(this, this.platmovetype))
index 399ba5b..d10ff11 100644 (file)
@@ -377,5 +377,6 @@ NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
 
        InterpolateOrigin_Note(this);
        this.draw = Draw_Laser;
+       if (isnew) IL_PUSH(g_drawables, this);
 }
 #endif
index e2deb39..8fd5cb0 100644 (file)
@@ -238,6 +238,7 @@ void turret_construct(entity this)
        this.tur_head.drawmask                  = MASK_NORMAL;
        this.anim_start_time                    = 0;
        this.draw2d = turret_draw2d;
+       IL_PUSH(g_drawables_2d, this);
        this.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
        this.teamradar_color = '1 0 0';
        this.alpha = 1;
index fd36552..5382a6f 100644 (file)
@@ -71,6 +71,7 @@ NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew)
        axh.colormod_z          = ReadByte() / 255;
        axh.cnt                         = time;
        axh.draw2d                      = AuxiliaryXhair_Draw2D;
+       if (isnew) IL_PUSH(g_drawables_2d, axh);
        return true;
 }
 
index d5a7bce..9cb8b74 100644 (file)
@@ -71,6 +71,7 @@ NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew)
         this.lip = particleeffectnum(EFFECT_BUMBLEBEE_HEAL_IMPACT);
 
         this.draw = bumble_raygun_draw;
+        if (isnew) IL_PUSH(g_drawables, this);
     }
 
 
index 1440546..a97167b 100644 (file)
@@ -246,6 +246,7 @@ void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
     sfrag.solid = SOLID_CORPSE;
 
     sfrag.draw = RaptorCBShellfragDraw;
+    IL_PUSH(g_drawables, sfrag);
 
     sfrag.velocity = _vel;
     sfrag.avelocity = prandomvec() * vlen(sfrag.velocity);
index 1eaf406..3e3af5e 100644 (file)
@@ -1296,6 +1296,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew)
 
                // set other main attributes of the beam
                this.draw = Draw_ArcBeam;
+               IL_PUSH(g_drawables, this);
                this.entremove = Remove_ArcBeam;
                this.move_time = time;
                loopsound(this, CH_SHOTS_SINGLE, SND(ARC_LOOP), VOL_BASE, ATTEN_NORM);
index 8926ffa..01a3497 100644 (file)
@@ -541,6 +541,7 @@ NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew)
        if(bIsNew || !this.teleport_time)
        {
                this.draw = Draw_GrapplingHook;
+               IL_PUSH(g_drawables, this);
                this.entremove = Remove_GrapplingHook;
 
                switch(this.HookType)
index 499453e..2c6d200 100644 (file)
@@ -862,6 +862,7 @@ void Net_ReadShockwaveParticle()
        entity shockwave;
        shockwave = spawn();
        shockwave.draw = Draw_Shockwave;
+       IL_PUSH(g_drawables, shockwave);
 
        shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord();
        shockwave.sw_shotdir_x = ReadCoord(); shockwave.sw_shotdir_y = ReadCoord(); shockwave.sw_shotdir_z = ReadCoord();
index 362209f..8a8e858 100644 (file)
@@ -140,6 +140,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
        setthink(this, SUB_Remove);
        this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10);
        this.draw = VaporizerBeam_Draw;
+       if (isNew) IL_PUSH(g_drawables, this);
        this.drawmask = MASK_NORMAL;
 
        this.vorg1_x = ReadCoord(); this.vorg1_y = ReadCoord(); this.vorg1_z = ReadCoord();
index c0e6d3b..939a86a 100644 (file)
@@ -40,6 +40,8 @@ int(string s1, string s2, int len) _strncasecmp = #230;
 int() _buf_create = #440;
 #define buf_create _buf_create
 
+bool(entity ent) wasfreed = #353;
+
 #pragma noref 0
 
 #endif
index 61755fe..37093d2 100644 (file)
@@ -77,6 +77,7 @@ void    isnt_bool(float this) { print(ftos(this)); }
 #include "file.qh"
 #include "functional.qh"
 #include "i18n.qh"
+#include "intrusivelist.qh"
 #include "iter.qh"
 #include "json.qc"
 #include "lazy.qh"
diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh
new file mode 100644 (file)
index 0000000..1d7b251
--- /dev/null
@@ -0,0 +1,245 @@
+#pragma once
+
+#include "iter.qh"
+
+const int IL_MAX = 128;
+
+void IL_INIT(entity this);
+void IL_DTOR(entity this);
+void IL_ENDFRAME();
+
+/**
+ * limitations:
+ *   NULL cannot be present
+ *   elements can only be present once
+ *   a maximum of `IL_MAX` lists can exist at one time
+ *   freed entities must be removed from the list
+ */
+CLASS(IntrusiveList, Object)
+       ATTRIB(IntrusiveList, il_head, entity, NULL);
+       ATTRIB(IntrusiveList, il_tail, entity, NULL);
+       ATTRIB(IntrusiveList, il_nextfld, .entity, nil);
+       ATTRIB(IntrusiveList, il_prevfld, .entity, nil);
+       INIT(IntrusiveList) { IL_INIT(this); }
+       DESTRUCTOR(IntrusiveList) { IL_DTOR(this); }
+ENDCLASS(IntrusiveList)
+
+// bitflags
+.vector il_lists;
+// bitflags
+.vector il_listmask;
+
+#define IL_NEW() NEW(IntrusiveList)
+
+#define IL_EMPTY(this) (this.il_head == NULL)
+
+#define IL_FIRST(this) (this.il_head)
+#define IL_LAST(this) (this.il_tail)
+#define IL_PEEK(this) (this.il_tail)
+
+bool IL_CONTAINS(IntrusiveList this, entity it)
+{
+       assert(this, return false);
+       return it.(this.il_nextfld) || this.il_head == it || this.il_tail == it;
+}
+
+/**
+ * Push to tail
+ */
+entity IL_PUSH(IntrusiveList this, entity it)
+{
+       assert(this, return NULL);
+       assert(it, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       assert(!IL_CONTAINS(this, it), return NULL);
+
+       entity tail = it.(il_prev) = this.il_tail;
+       tail ? (tail.(il_next) = it) : this.il_head = it;
+       this.il_tail = it;
+       it.il_lists |= this.il_listmask;
+       return it;
+}
+
+/**
+ * Push to head
+ */
+entity IL_UNSHIFT(IntrusiveList this, entity it)
+{
+       assert(this, return NULL);
+       assert(it, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       assert(!IL_CONTAINS(this, it), return NULL);
+
+       entity head = it.(il_next) = this.il_head;
+       head ? (head.(il_prev) = it) : this.il_tail = it;
+       this.il_head = it;
+       it.il_lists |= this.il_listmask;
+       return it;
+}
+
+/**
+ * Pop from tail
+ */
+entity IL_POP(IntrusiveList this)
+{
+       assert(this, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+
+       if (!this.il_tail) return NULL;
+       entity it = this.il_tail;
+       entity prev = it.(il_prev);
+       if (prev) (this.il_tail = prev).(il_next) = NULL;
+       else this.il_head = this.il_tail = NULL;
+       return it;
+}
+
+/**
+ * Pop from head
+ */
+entity IL_SHIFT(IntrusiveList this)
+{
+       assert(this, return NULL);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+
+       if (!this.il_head) return NULL;
+       entity it = this.il_head;
+       entity next = it.(il_next);
+       if (next) (this.il_head = next).(il_prev) = NULL;
+       else this.il_head = this.il_tail = NULL;
+       return it;
+}
+
+/**
+ * Remove any element, anywhere in the list
+ */
+void IL_REMOVE(IntrusiveList this, entity it)
+{
+       assert(this, return);
+       .entity il_next = this.il_nextfld;
+       .entity il_prev = this.il_prevfld;
+       entity next = it.(il_next);
+       entity prev = it.(il_prev);
+       entity ohead = this.il_head;
+       entity otail = this.il_tail;
+       next ? next.(il_prev) = prev : this.il_tail = prev;
+       prev ? prev.(il_next) = next : this.il_head = next;
+       LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i\n", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail);
+       it.(il_next) = it.(il_prev) = NULL;
+}
+
+/**
+ * Remove all elements
+ */
+#define IL_CLEAR(this) \
+       MACRO_BEGIN \
+       { \
+               IntrusiveList __il = this; \
+               assert(__il); \
+               .entity il_prev = __il.il_prevfld; \
+               IL_EACH(__il, true, it.(il_next) = it.(il_prev) = NULL); \
+               __il.il_head = __il.il_tail = NULL; \
+       } MACRO_END
+
+/**
+ * Delete the list
+ */
+#define IL_DELETE(this, dtor) \
+       MACRO_BEGIN \
+       { \
+               remove(this); \
+               this = NULL; \
+       } MACRO_END
+
+#define IL_EACH(this, cond, body) \
+       MACRO_BEGIN \
+       { \
+               IntrusiveList _il = this; \
+               assert(_il); \
+               .entity il_next = _il.il_nextfld; \
+               noref int i = 0; \
+               for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \
+               { \
+                       const noref entity it = _it; \
+                       _next = it.(il_next); \
+                       if (cond) { LAMBDA(body) } \
+               } \
+       } MACRO_END
+
+.int il_id;
+IntrusiveList il_links[IL_MAX];
+.entity il_links_flds[IL_MAX * 2];
+int il_links_ptr;
+
+#define IL_FLOOR(n) ((n) | 0)
+#define IL_CEIL(n)  IL_FLOOR((n) + 0.5)
+
+#define IL_LISTS_PER_BIT IL_CEIL(IL_MAX / (3 * 24))
+
+void IL_INIT(IntrusiveList this)
+{
+       .entity nextfld, prevfld;
+       for (int i = il_links_ptr; i < il_links_ptr + IL_MAX; ++i) {
+               int idx = i;
+               if (idx >= IL_MAX) idx -= IL_MAX;
+               int id = idx;
+               idx *= 2;
+               if (!il_links[idx]) {
+                       il_links[idx] = this;
+                       nextfld = il_links_flds[idx + 0];
+                       prevfld = il_links_flds[idx + 1];
+                       this.il_id = id;
+                       int bit = IL_FLOOR(id / IL_LISTS_PER_BIT);
+                       if (bit < (1 * 24)) this.il_listmask = '1 0 0' * (1 << (bit - (0 * 24)));
+                       else if (bit < (2 * 24)) this.il_listmask = '0 1 0' * (1 << (bit - (1 * 24)));
+                       else if (bit < (3 * 24)) this.il_listmask = '0 0 1' * (1 << (bit - (2 * 24)));
+                       else assert(false);
+                       il_links_ptr = id + 1;
+                       if (il_links_ptr >= IL_MAX) il_links_ptr -= IL_MAX;
+                       this.il_nextfld = nextfld;
+                       this.il_prevfld = prevfld;
+                       return;
+               }
+       }
+       LOG_WARNINGF("IntrusiveList overflow");
+}
+
+void IL_DTOR(IntrusiveList this)
+{
+       IL_CLEAR(this);
+       il_links[this.il_id] = NULL;
+}
+
+void IL_ENDFRAME()
+{
+#if 0
+       // incompatible with CSQC, remove() clears entities
+       for (int i = 0; i < IL_MAX; ++i) {
+               IntrusiveList list = il_links[i];
+               if (list) {
+                       .entity nextfld = list.il_nextfld;
+                       for (entity next, it = list.il_head; it; it = next) {
+                               next = it.(nextfld);
+                               if (wasfreed(it)) {
+                                       IL_REMOVE(list, it);
+                               }
+                       }
+               }
+       }
+#endif
+}
+
+void ONREMOVE(entity this)
+{
+       if (this.il_lists) {
+               for (int i = 0; i < IL_MAX; ++i) {
+                       IntrusiveList list = il_links[i];
+                       if (this.il_lists & list.il_listmask && IL_CONTAINS(list, this)) {
+                               IL_REMOVE(list, this);
+                       }
+               }
+       }
+}
index 3708552..f47e558 100644 (file)
@@ -71,9 +71,12 @@ entity __spawn(string _classname, string _sourceLoc, bool pure)
 #define new_pure(class) _new(class, true)
 #define spawn() __spawn("entity", __FILE__ ":" STR(__LINE__), false)
 
+[[accumulate]] void ONREMOVE(entity this) {}
+
 #define delete(this) MACRO_BEGIN { \
     entity _this = (this); \
     void(entity) _dtor = _this.dtor; \
+    ONREMOVE(this); \
     if (_dtor) _dtor(_this); else remove(_this); \
     /* this = NULL; */  \
 } MACRO_END
index 9b617ab..f32b852 100644 (file)
@@ -849,6 +849,7 @@ void m_draw(float width, float height)
        postMenuDraw();
 
        frametime = 0;
+       IL_ENDFRAME();
 }
 
 void m_display()
index 409e33c..064a69e 100644 (file)
@@ -2058,6 +2058,7 @@ void EndFrame()
                PlayerState s = PS(it);
                s.ps_push(s, it);
        });
+       IL_ENDFRAME();
 }