From ccde836b2e7ed3a89479e5e98474a51c581a9fb4 Mon Sep 17 00:00:00 2001 From: TimePath Date: Mon, 5 Oct 2015 16:44:12 +1100 Subject: [PATCH] Linked entities: simplify registration --- qcsrc/client/main.qc | 15 +++-- qcsrc/common/constants.qh | 1 - qcsrc/common/nades.qc | 115 ++++++++++++++++------------------ qcsrc/common/nades.qh | 4 -- qcsrc/common/util.qc | 72 --------------------- qcsrc/common/util.qh | 5 -- qcsrc/lib/_all.inc | 2 + qcsrc/lib/net.qh | 81 ++++++++++++++++++++++++ qcsrc/lib/oo.qh | 1 - qcsrc/lib/registry.qh | 23 +++++++ qcsrc/lib/sort.qh | 75 ++++++++++++++++++++++ qcsrc/server/defs.qh | 2 - qcsrc/server/miscfunctions.qc | 43 ------------- qcsrc/server/miscfunctions.qh | 5 -- 14 files changed, 245 insertions(+), 199 deletions(-) create mode 100644 qcsrc/lib/net.qh create mode 100644 qcsrc/lib/sort.qh diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index ca31f32f7..898b10e0b 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -796,15 +796,13 @@ void Ent_Init(); void Ent_ScoresInfo(); void CSQC_Ent_Update(float bIsNewEntity) {SELFPARAM(); - float t; - float savetime; - t = ReadByte(); + int t = ReadByte(); if(autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t); // set up the "time" global for received entities to be correct for interpolation purposes - savetime = time; + float savetime = time; if(servertime) { time = servertime; @@ -821,7 +819,6 @@ void CSQC_Ent_Update(float bIsNewEntity) { if(t != self.enttype || bIsNewEntity) { - //print("A CSQC entity changed its type!\n"); LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t); Ent_Remove(); clearentity(self); @@ -838,6 +835,13 @@ void CSQC_Ent_Update(float bIsNewEntity) } #endif self.enttype = t; + bool done = false; + FOREACH(Linked, it.m_id == t, LAMBDA( + it.m_read(self, bIsNewEntity); + done = true; + break; + )); + if (!done) switch(t) { case ENT_CLIENT_MUTATOR: { @@ -884,7 +888,6 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break; case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; - case ENT_CLIENT_HEALING_ORB: ent_healer(); break; case ENT_CLIENT_MINIGAME: ent_read_minigame(); break; case ENT_CLIENT_VIEWLOC: ent_viewloc(); break; case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break; diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index fe04e910c..2fcd63c24 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -127,7 +127,6 @@ const int ENT_CLIENT_EFFECT = 74; const int ENT_CLIENT_MINIGAME = 75; const int ENT_CLIENT_VIEWLOC = 78; const int ENT_CLIENT_VIEWLOC_TRIGGER = 79; -const int ENT_CLIENT_HEALING_ORB = 80; const int ENT_CLIENT_MUTATOR = TE_CSQC_MUTATOR; // 99 diff --git a/qcsrc/common/nades.qc b/qcsrc/common/nades.qc index f361730ba..f0e72b7ec 100644 --- a/qcsrc/common/nades.qc +++ b/qcsrc/common/nades.qc @@ -15,29 +15,6 @@ #endif -#ifdef SVQC -float healer_send(entity to, int sf) -{SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_HEALING_ORB); - WriteByte(MSG_ENTITY, sf); - - if(sf & 1) - { - WriteCoord(MSG_ENTITY, self.origin.x); - WriteCoord(MSG_ENTITY, self.origin.y); - WriteCoord(MSG_ENTITY, self.origin.z); - - WriteByte(MSG_ENTITY, self.healer_lifetime); - //WriteByte(MSG_ENTITY, self.ltime - time + 1); - WriteShort(MSG_ENTITY, self.healer_radius); - // round time delta to a 1/10th of a second - WriteByte(MSG_ENTITY, (self.ltime - time)*10.0+0.5); - } - - return true; -} -#endif // SVQC - #ifdef CSQC .float ltime; void healer_draw() @@ -49,48 +26,66 @@ void healer_draw() self.alpha = (self.ltime - time) / self.healer_lifetime; self.scale = min((1 - self.alpha)*self.healer_lifetime*4,1)*self.healer_radius; - } -void healer_setup() -{SELFPARAM(); - setmodel(self, MDL_NADE_HEAL); - - setorigin(self, self.origin); - - float model_radius = self.maxs.x; - vector size = '1 1 1' * self.healer_radius / 2; - setsize(self,-size,size); - self.healer_radius = self.healer_radius/model_radius*0.6; - - self.draw = healer_draw; - self.health = 255; - self.movetype = MOVETYPE_NONE; - self.solid = SOLID_NOT; - self.drawmask = MASK_NORMAL; - self.scale = 0.01; - self.avelocity = self.move_avelocity = '7 0 11'; - self.colormod = '1 0 0'; - self.renderflags |= RF_ADDITIVE; +void healer_setup(entity e) +{ + setmodel(e, MDL_NADE_HEAL); + + setorigin(e, e.origin); + + float model_radius = e.maxs.x; + vector size = '1 1 1' * e.healer_radius / 2; + setsize(e,-size,size); + e.healer_radius = e.healer_radius/model_radius*0.6; + + e.draw = healer_draw; + e.health = 255; + e.movetype = MOVETYPE_NONE; + e.solid = SOLID_NOT; + e.drawmask = MASK_NORMAL; + e.scale = 0.01; + e.avelocity = e.move_avelocity = '7 0 11'; + e.colormod = '1 0 0'; + e.renderflags |= RF_ADDITIVE; } +#endif // CSQC -void ent_healer() -{SELFPARAM(); +REGISTER_LINKED(Nade_Heal, bool isNew) +#ifdef CSQC +{ int sf = ReadByte(); + if (sf & 1) { + this.origin_x = ReadCoord(); + this.origin_y = ReadCoord(); + this.origin_z = ReadCoord(); + setorigin(this, this.origin); + this.healer_lifetime = ReadByte(); + this.healer_radius = ReadShort(); + this.ltime = time + ReadByte()/10.0; + // this.ltime = time + this.healer_lifetime; + healer_setup(this); + } +} +#endif - if(sf & TNSF_SETUP) - { - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - setorigin(self, self.origin); - - self.healer_lifetime = ReadByte(); - self.healer_radius = ReadShort(); - self.ltime = time + ReadByte()/10.0; - //self.ltime = time + self.healer_lifetime; - - healer_setup(); +#ifdef SVQC +float healer_send(entity to, int sf) +{ + SELFPARAM(); + WriteByte(MSG_ENTITY, Linked_Nade_Heal.m_id); + WriteByte(MSG_ENTITY, sf); + if (sf & 1) { + WriteCoord(MSG_ENTITY, this.origin.x); + WriteCoord(MSG_ENTITY, this.origin.y); + WriteCoord(MSG_ENTITY, this.origin.z); + + WriteByte(MSG_ENTITY, this.healer_lifetime); + //WriteByte(MSG_ENTITY, this.ltime - time + 1); + WriteShort(MSG_ENTITY, this.healer_radius); + // round time delta to a 1/10th of a second + WriteByte(MSG_ENTITY, (this.ltime - time)*10.0+0.5); } + return true; } -#endif // CSQC +#endif // SVQC diff --git a/qcsrc/common/nades.qh b/qcsrc/common/nades.qh index 1ec1b9ec7..5a7f5d49d 100644 --- a/qcsrc/common/nades.qh +++ b/qcsrc/common/nades.qh @@ -137,8 +137,4 @@ string Nade_TrailEffect(int proj, float nade_team) float healer_send(entity to, int sf); #endif -#ifdef CSQC -// misc functions -void ent_healer(); -#endif // CSQC #endif diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index a506a59fc..4b6a346ff 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1500,26 +1500,6 @@ float isGametypeInFilter(float gt, float tp, float ts, string pattern) return 1; } -void shuffle(float n, swapfunc_t swap, entity pass) -{ - float i, j; - for(i = 1; i < n; ++i) - { - // swap i-th item at a random position from 0 to i - // proof for even distribution: - // n = 1: obvious - // n -> n+1: - // item n+1 gets at any position with chance 1/(n+1) - // all others will get their 1/n chance reduced by factor n/(n+1) - // to be on place n+1, their chance will be 1/(n+1) - // 1/n * n/(n+1) = 1/(n+1) - // q.e.d. - j = floor(random() * (i + 1)); - if(j != i) - swap(j, i, pass); - } -} - string substring_range(string s, float b, float e) { return substring(s, b, e - b); @@ -1750,58 +1730,6 @@ vector decompressShotOrigin(int f) return v; } -void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass) -{ - float start, end, root, child; - - // heapify - start = floor((n - 2) / 2); - while(start >= 0) - { - // siftdown(start, count-1); - root = start; - while(root * 2 + 1 <= n-1) - { - child = root * 2 + 1; - if(child < n-1) - if(cmp(child, child+1, pass) < 0) - ++child; - if(cmp(root, child, pass) < 0) - { - swap(root, child, pass); - root = child; - } - else - break; - } - // end of siftdown - --start; - } - - // extract - end = n - 1; - while(end > 0) - { - swap(0, end, pass); - --end; - // siftdown(0, end); - root = 0; - while(root * 2 + 1 <= end) - { - child = root * 2 + 1; - if(child < end && cmp(child, child+1, pass) < 0) - ++child; - if(cmp(root, child, pass) < 0) - { - swap(root, child, pass); - root = child; - } - else - break; - } - // end of siftdown - } -} void RandomSelection_Init() { diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index d43136c67..4af16801d 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -130,11 +130,6 @@ string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw); float isGametypeInFilter(float gt, float tp, float ts, string pattern); -typedef void(float i1, float i2, entity pass) swapfunc_t; // is only ever called for i1 < i2 -typedef float(float i1, float i2, entity pass) comparefunc_t; // <0 for <, ==0 for ==, >0 for > (like strcmp) -void shuffle(float n, swapfunc_t swap, entity pass); -void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass); - string swapwords(string str, float i, float j); string shufflewords(string str); diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index e7ee2746f..7dc607405 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -12,6 +12,7 @@ #include "lazy.qh" #include "log.qh" #include "math.qh" +#include "net.qh" #include "nil.qh" #include "noise.qc" #include "oo.qh" @@ -21,6 +22,7 @@ #include "progname.qh" #include "registry.qh" #include "replicate.qh" +#include "sort.qh" #include "sortlist.qc" #include "spawnfunc.qh" #include "static.qh" diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh new file mode 100644 index 000000000..a41fae144 --- /dev/null +++ b/qcsrc/lib/net.qh @@ -0,0 +1,81 @@ +#ifndef NET_H +#define NET_H + +#ifdef SVQC +.int SendFlags; +.bool(entity to, int sendflags) SendEntity; + +void Net_LinkEntity(entity e, bool docull, float dt, bool(entity to, int sendflags) sendfunc) +{ + if (!e.classname) e.classname = "net_linked"; + + if (!e.model || !self.modelindex) { + vector mi = e.mins; + vector ma = e.maxs; + _setmodel(e, "null"); + setsize(e, mi, ma); + } + + e.SendEntity = sendfunc; + e.SendFlags = 0xFFFFFF; + + if (!docull) e.effects |= EF_NODEPTHTEST; + + if (dt) { + e.nextthink = time + dt; + e.think = SUB_Remove; + } +} + +.void() uncustomizeentityforclient; +.float uncustomizeentityforclient_set; + +void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer) +{ + e.customizeentityforclient = customizer; + e.uncustomizeentityforclient = uncustomizer; + e.uncustomizeentityforclient_set = !!uncustomizer; +} + +void UncustomizeEntitiesRun() +{ + for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) { + WITH(entity, self, e, e.uncustomizeentityforclient()); + } +} + +#endif + +#include "registry.qh" +#include "sort.qh" + +REGISTRY(Linked, 24) + +.string netname; +.int m_id; +.void(entity this, bool isNew) m_read; + +#ifdef CSQC + #define REGISTER_LINKED(id, param) \ + void Ent_Read##id(entity this, param) { this = self; } \ + REGISTER(RegisterLinked, Linked, Linked, Linked_COUNT, id, m_id, spawn()) { \ + this.netname = #id; \ + this.m_read = Ent_Read##id; \ + } \ + [[accumulate]] void Ent_Read##id(entity this, param) +#else + #define REGISTER_LINKED(id, param) \ + REGISTER(RegisterLinked, Linked, Linked, Linked_COUNT, id, m_id, spawn()) { \ + this.netname = #id; \ + } +#endif + +REGISTER_REGISTRY(RegisterLinked) +REGISTRY_SORT(Linked, netname, 0) +STATIC_INIT(RegisterLinked_renumber) { + for (int i = 0; i < Linked_COUNT; ++i) { + Linked[i].m_id = 100 + i; + } +} + +#endif diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index 88c0ef8a4..fce18cbfd 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -2,7 +2,6 @@ #define OO_H #include "nil.qh" -#include "registry.qh" #ifdef MENUQC #define NULL (null_entity) diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index c751fc793..b74c4783b 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -6,6 +6,12 @@ #define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this) #define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this) +#define REGISTRY(id, max) \ + void Register##id() {} \ + const int id##_MAX = max; \ + noref entity id[id##_MAX], id##_first, id##_last; \ + int id##_COUNT; + /** * Register a new entity with a global constructor. * Must be followed by a semicolon or a function body with a `this` parameter. @@ -48,4 +54,21 @@ ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \ REGISTER_INIT(ns, id) +#define REGISTRY_SORT(id, field, skip) \ + void _REGISTRY_SWAP_##id(int i, int j, entity pass) { \ + i += skip; j += skip; \ + entity e = id[i]; \ + id[i] = id[j]; \ + id[j] = e; \ + } \ + float _REGISTRY_CMP_##id(int i, int j, entity pass) { \ + i += skip; j += skip; \ + string a = id[i].field; \ + string b = id[j].field; \ + return strcasecmp(a, b); \ + } \ + STATIC_INIT(Registry_sort_##id) { \ + heapsort(id##_COUNT, _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \ + } + #endif diff --git a/qcsrc/lib/sort.qh b/qcsrc/lib/sort.qh new file mode 100644 index 000000000..0d3177a98 --- /dev/null +++ b/qcsrc/lib/sort.qh @@ -0,0 +1,75 @@ +#ifndef SORT_H +#define SORT_H + +/** is only ever called for i1 < i2 */ +typedef void(float i1, float i2, entity pass) swapfunc_t; +/** <0 for <, ==0 for ==, >0 for > (like strcmp) */ +typedef float(float i1, float i2, entity pass) comparefunc_t; + +void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass) +{ + int root, child; + + // heapify + int start = floor((n - 2) / 2); + while (start >= 0) { + // siftdown(start, n - 1); + root = start; + while (root * 2 + 1 <= n - 1) { + child = root * 2 + 1; + if (child < n - 1 && cmp(child, child + 1, pass) < 0) { + child += 1; + } + if (cmp(root, child, pass) < 0) { + swap(root, child, pass); + root = child; + } else { + break; + } + } + // end of siftdown + --start; + } + + // extract + int end = n - 1; + while (end > 0) { + swap(0, end, pass); + end -= 1; + // siftdown(0, end); + root = 0; + while (root * 2 + 1 <= end) { + child = root * 2 + 1; + if (child < end && cmp(child, child+1, pass) < 0) { + child += 1; + } + if (cmp(root, child, pass) < 0) { + swap(root, child, pass); + root = child; + } else { + break; + } + } + // end of siftdown + } +} + +void shuffle(float n, swapfunc_t swap, entity pass) +{ + for (int i = 1; i < n; ++i) { + // swap i-th item at a random position from 0 to i + // proof for even distribution: + // n = 1: obvious + // n -> n+1: + // item n+1 gets at any position with chance 1/(n+1) + // all others will get their 1/n chance reduced by factor n/(n+1) + // to be on place n+1, their chance will be 1/(n+1) + // 1/n * n/(n+1) = 1/(n+1) + // q.e.d. + int j = floor(random() * (i + 1)); + if (j != i) + swap(j, i, pass); + } +} + +#endif diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 723b137a1..c55ae50f3 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -304,8 +304,6 @@ float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) float next_pingtime; .float Version; -.int SendFlags; -.bool(entity to, int sendflags) SendEntity; // player sounds, voice messages // TODO implemented fall and falling diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index a6164747d..58239a38a 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -1155,49 +1155,6 @@ void InitializeEntitiesRun() remove = remove_unsafely; } -void UncustomizeEntitiesRun() -{SELFPARAM(); - for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) - { - WITH(entity, self, e, e.uncustomizeentityforclient()); - } -} -void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer) -{ - e.customizeentityforclient = customizer; - e.uncustomizeentityforclient = uncustomizer; - e.uncustomizeentityforclient_set = !!uncustomizer; -} - -void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc) -{SELFPARAM(); - vector mi, ma; - - if (e.classname == "") - e.classname = "net_linked"; - - if (e.model == "" || self.modelindex == 0) - { - mi = e.mins; - ma = e.maxs; - setmodel(e, MDL_Null); - setsize(e, mi, ma); - } - - e.SendEntity = sendfunc; - e.SendFlags = 0xFFFFFF; - - if (!docull) - e.effects |= EF_NODEPTHTEST; - - if (dt) - { - e.nextthink = time + dt; - e.think = SUB_Remove; - } -} - - .float(entity) isEliminated; float EliminatedPlayers_SendEntity(entity to, float sendflags) { diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index fc3583bb1..bdd6dc6a6 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -29,8 +29,6 @@ float cvar_normal(string n) #define cvar_set_normal builtin_cvar_set .vector dropped_origin; -.void(void) uncustomizeentityforclient; -.float uncustomizeentityforclient_set; .float nottargeted; entity eliminatedPlayers; @@ -61,9 +59,6 @@ void precache_all_playermodels(string pattern); void soundat(entity e, vector o, float chan, string samp, float vol, float _atten); -void defer(float fdelay, void() func); - -void UncustomizeEntitiesRun(); void InitializeEntitiesRun(); void stopsoundto(float _dest, entity e, float chan); -- 2.39.2