From: TimePath Date: Sun, 24 Jul 2016 10:36:12 +0000 (+1000) Subject: Implement intrusive lists X-Git-Tag: xonotic-v0.8.2~700^2~18^2~4 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=709b073f1b5e22a56368c391191f85c9a91ba0af;p=xonotic%2Fxonotic-data.pk3dir.git Implement intrusive lists --- diff --git a/qcsrc/client/commands/cl_cmd.qc b/qcsrc/client/commands/cl_cmd.qc index dce058c81..100bebf5f 100644 --- a/qcsrc/client/commands/cl_cmd.qc +++ b/qcsrc/client/commands/cl_cmd.qc @@ -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; } diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index ad0fa28f1..3e2a516f9 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -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 ) diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 65ee03aaf..bd9e82ed2 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -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; diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 15362aad3..ce1b3eb23 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -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; diff --git a/qcsrc/client/teamradar.qc b/qcsrc/client/teamradar.qc index a8708f246..ab14cd3bf 100644 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@ -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) { diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 200bcfde7..cbce1fcb5 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -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(); } diff --git a/qcsrc/client/wall.qc b/qcsrc/client/wall.qc index b94fddd8d..01a86bb1e 100644 --- a/qcsrc/client/wall.qc +++ b/qcsrc/client/wall.qc @@ -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); } diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc index 56a172b10..dd16f0a9d 100644 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@ -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; } diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 83cbc302d..052e00f07 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -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 diff --git a/qcsrc/common/effects/qc/casings.qc b/qcsrc/common/effects/qc/casings.qc index 30543adee..a4ade3836 100644 --- a/qcsrc/common/effects/qc/casings.qc +++ b/qcsrc/common/effects/qc/casings.qc @@ -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; diff --git a/qcsrc/common/effects/qc/gibs.qc b/qcsrc/common/effects/qc/gibs.qc index aaecf18f9..78f1a27cc 100644 --- a/qcsrc/common/effects/qc/gibs.qc +++ b/qcsrc/common/effects/qc/gibs.qc @@ -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 diff --git a/qcsrc/common/effects/qc/modeleffects.qc b/qcsrc/common/effects/qc/modeleffects.qc index f57776290..28b8bb496 100644 --- a/qcsrc/common/effects/qc/modeleffects.qc +++ b/qcsrc/common/effects/qc/modeleffects.qc @@ -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; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc index a22815b16..0135f467e 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc @@ -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) diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc index 4694bcbf7..8de4f2b85 100644 --- a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc @@ -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 diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc index cbb8e4abf..9488e02a5 100644 --- a/qcsrc/common/mutators/mutator/nades/net.qc +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index 56c4c5b5d..8ce10947f 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index fa9f4fbdf..26e4058d2 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -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); diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 0de2fe1cf..ee9babfb3 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -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; diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc index 95f9ebc69..8bd6016f2 100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@ -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; diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index 07709df33..341ca460f 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -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; diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc index 1705d4081..0cf0615da 100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@ -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 diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc index d2cf18963..cc7dc0922 100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@ -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 diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc index 6bf6b0b1a..29c7d0a7b 100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@ -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)) diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc index 399ba5baf..d10ff1128 100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@ -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 diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index e2deb3980..8fd5cb08c 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -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; diff --git a/qcsrc/common/vehicles/cl_vehicles.qc b/qcsrc/common/vehicles/cl_vehicles.qc index fd3655211..5382a6f7a 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@ -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; } diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc index d5a7bcedf..9cb8b74c2 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc @@ -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); } diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc index 14405460e..a97167b2e 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc @@ -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); diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index 1eaf4062d..3e3af5e0f 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -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); diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index 8926ffa30..01a3497d0 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -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) diff --git a/qcsrc/common/weapons/weapon/shockwave.qc b/qcsrc/common/weapons/weapon/shockwave.qc index 499453ecd..2c6d20066 100644 --- a/qcsrc/common/weapons/weapon/shockwave.qc +++ b/qcsrc/common/weapons/weapon/shockwave.qc @@ -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(); diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 362209f91..8a8e858ec 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -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(); diff --git a/qcsrc/dpdefs/menudefs.qh b/qcsrc/dpdefs/menudefs.qh index c0e6d3ba7..939a86adb 100644 --- a/qcsrc/dpdefs/menudefs.qh +++ b/qcsrc/dpdefs/menudefs.qh @@ -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 diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 61755fe94..37093d27d 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -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 index 000000000..1d7b25168 --- /dev/null +++ b/qcsrc/lib/intrusivelist.qh @@ -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); + } + } + } +} diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index 3708552d5..f47e558a8 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -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 diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 9b617ab8a..f32b85298 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -849,6 +849,7 @@ void m_draw(float width, float height) postMenuDraw(); frametime = 0; + IL_ENDFRAME(); } void m_display() diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 409e33cd0..064a69e47 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -2058,6 +2058,7 @@ void EndFrame() PlayerState s = PS(it); s.ps_push(s, it); }); + IL_ENDFRAME(); }