From 2d79d9ed30444579f4a1e3205280eec945201b9f Mon Sep 17 00:00:00 2001 From: TimePath Date: Mon, 16 Nov 2015 20:58:02 +1100 Subject: [PATCH] ent_cs: rewrite --- qcsrc/client/hud/panel/radar.qc | 2 + qcsrc/client/main.qc | 43 +--- qcsrc/client/main.qh | 2 - qcsrc/client/miscfunctions.qc | 54 +---- qcsrc/client/miscfunctions.qh | 3 - qcsrc/client/progs.inc | 1 + qcsrc/client/shownames.qc | 321 +++++++++++-------------- qcsrc/client/view.qc | 2 +- qcsrc/common/constants.qh | 1 - qcsrc/common/effects/qc/globalsound.qc | 28 ++- qcsrc/common/ent_cs.qc | 165 +++++++++++++ qcsrc/common/ent_cs.qh | 49 ++++ qcsrc/common/weapons/weapon/arc.qc | 2 +- qcsrc/lib/warpzone/common.qc | 2 - qcsrc/server/cl_client.qc | 6 +- qcsrc/server/ent_cs.qc | 87 ------- qcsrc/server/ent_cs.qh | 26 -- qcsrc/server/progs.inc | 2 +- 18 files changed, 388 insertions(+), 408 deletions(-) create mode 100644 qcsrc/common/ent_cs.qc create mode 100644 qcsrc/common/ent_cs.qh delete mode 100644 qcsrc/server/ent_cs.qc delete mode 100644 qcsrc/server/ent_cs.qh diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index ad3e79ce5..c06fcdbfe 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -359,6 +359,8 @@ void HUD_Radar() } for(tm = world; (tm = find(tm, classname, "entcs_receiver")); ) { + if (!tm.m_entcs_private) continue; + if (entcs_is_self(tm)) continue; color2 = GetPlayerColor(tm.sv_entnum); //if(color == NUM_SPECTATOR || color == color2) draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2)); diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 985489b81..4b4f5a68b 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -327,47 +327,6 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) // -------------------------------------------------------------------------- // BEGIN OPTIONAL CSQC FUNCTIONS -void Ent_RemoveEntCS() -{ - SELFPARAM(); - entcs_receiver[this.sv_entnum] = NULL; -} - -NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew) -{ - make_pure(this); - this.classname = "entcs_receiver"; - InterpolateOrigin_Undo(); - int sf = ReadByte(); - - if(sf & BIT(0)) - this.sv_entnum = ReadByte(); - if (sf & BIT(1)) - { - this.origin_x = ReadShort(); - this.origin_y = ReadShort(); - this.origin_z = ReadShort(); - setorigin(this, this.origin); - } - if (sf & BIT(2)) - { - this.angles_y = ReadByte() * 360.0 / 256; - this.angles_x = this.angles_z = 0; - } - if (sf & BIT(3)) - this.healthvalue = ReadByte() * 10; - if (sf & BIT(4)) - this.armorvalue = ReadByte() * 10; - - return = true; - - entcs_receiver[this.sv_entnum] = this; - this.entremove = Ent_RemoveEntCS; - this.iflags |= IFLAG_ORIGIN; - - InterpolateOrigin_Note(); -} - void Ent_Remove(); void Ent_RemovePlayerScore() @@ -795,7 +754,7 @@ void CSQC_Ent_Update(float bIsNewEntity) this.enttype = t; bool done = false; FOREACH(LinkedEntities, it.m_id == t, LAMBDA( - this.classname = it.netname; + if (bIsNewEntity) this.classname = it.netname; if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Update(%d) with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", bIsNewEntity, this, this.entnum, this.enttype, it.netname, t); done = it.m_read(this, bIsNewEntity); diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 48f762187..9229b718a 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -141,8 +141,6 @@ float g_balance_electro_secondary_bouncefactor; float g_balance_electro_secondary_bouncestop; float g_trueaim_minrange; -entity entcs_receiver[255]; // 255 is the engine limit on maxclients - float hud; float view_quality; int framecount; diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index ab4cb1f15..bbe0722fd 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -482,55 +482,25 @@ void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector } } -vector getplayerorigin(int pl) +// TODO: entcs +float getplayeralpha(int pl) { - entity e; - - e = CSQCModel_server2csqc(pl + 1); - if(e) - return e.origin; - - e = entcs_receiver[pl]; - if(e) - return e.origin; - - return GETPLAYERORIGIN_ERROR; -} - -float getplayeralpha(float pl) -{ - entity e; - - e = CSQCModel_server2csqc(pl + 1); - if(e) - return e.alpha; - - return 1; + entity e = CSQCModel_server2csqc(pl + 1); + return (e) ? e.alpha : 1; } -vector getcsqcplayercolor(float pl) +// TODO: entcs +vector getcsqcplayercolor(int pl) { - entity e; - - e = CSQCModel_server2csqc(pl); - if(e) - { - if(e.colormap > 0) - return colormapPaletteColor(((e.colormap >= 1024) ? e.colormap : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 0x0F, true); - } - - return '1 1 1'; + entity e = CSQCModel_server2csqc(pl); + return (e && e.colormap > 0) ? colormapPaletteColor(((e.colormap >= 1024) ? e.colormap : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 0x0F, true) : '1 1 1'; } -float getplayerisdead(float pl) +// TODO: entcs +bool getplayerisdead(int pl) { - entity e; - - e = CSQCModel_server2csqc(pl + 1); - if(e) - return e.csqcmodel_isdead; - - return false; + entity e = CSQCModel_server2csqc(pl + 1); + return e ? e.csqcmodel_isdead : false; } /** engine callback */ diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index 60048d049..520b5e47d 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -143,9 +143,6 @@ void PolyDrawModel(entity e); void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag); -const vector GETPLAYERORIGIN_ERROR = '1123581321 2357111317 3141592653'; // way out of bounds for anything on the map -vector getplayerorigin(int pl); - float getplayeralpha(float pl); vector getcsqcplayercolor(float pl); diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index 1e939b770..4a6b58788 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -27,6 +27,7 @@ #include "../common/animdecide.qc" #include "../common/effects/effectinfo.qc" +#include "../common/ent_cs.qc" #include "../common/mapinfo.qc" #include "../common/movetypes/include.qc" #include "../common/net_notice.qc" diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 300521545..0dbffb874 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -8,8 +8,10 @@ #include "../lib/csqcmodel/cl_model.qh" +entity shownames_ent[255]; + // self.isactive = player is in range and coordinates/status (health and armor) are up to date -// self.origin = player origin TODO: should maybe move this so it's the origin of the shownames tag already in SSQC for culling? +// self.origin = player origin // self.healthvalue // self.armorvalue // self.sameteam = player is on same team as local client @@ -19,215 +21,166 @@ const float SHOWNAMES_FADESPEED = 4; const float SHOWNAMES_FADEDELAY = 0.4; -void Draw_ShowNames(entity ent) +void Draw_ShowNames(entity this) { - if(!autocvar_hud_shownames) - return; - - if(ent.sv_entnum == player_localentnum) // ent is me or person i'm spectating - if(!(autocvar_hud_shownames_self && autocvar_chase_active)) - return; - - if(ent.sameteam || (!ent.sameteam && autocvar_hud_shownames_enemies)) + if (!autocvar_hud_shownames) return; + if (this.sv_entnum == player_localentnum) // self or spectatee + if (!(autocvar_hud_shownames_self && autocvar_chase_active)) return; + if (!this.sameteam && !autocvar_hud_shownames_enemies) return; + bool hit; + if (!autocvar_hud_shownames_crosshairdistance && this.sameteam) { - ent.origin_z += autocvar_hud_shownames_offset; - - float hit; - if(ent.sameteam && !autocvar_hud_shownames_crosshairdistance) - { - hit = 1; - } - else - { - traceline(view_origin, ent.origin, MOVE_NORMAL, ent); - if(trace_fraction < 1 && (trace_networkentity != ent.sv_entnum && trace_ent.entnum != ent.sv_entnum)) - hit = 0; - else - hit = 1; - } - - // handle tag fading - float overlap = false, onscreen, crosshairdistance; - vector o, eo; - - o = project_3d_to_2d(ent.origin); - - if(autocvar_hud_shownames_antioverlap) + hit = true; + } + else + { + traceline(view_origin, this.origin, MOVE_NORMAL, this); + hit = !(trace_fraction < 1 && (trace_networkentity != this.sv_entnum && trace_ent.entnum != this.sv_entnum)); + } + // handle tag fading + bool overlap = false; + vector o = project_3d_to_2d(this.origin + eZ * autocvar_hud_shownames_offset); + float dist = vlen(this.origin - view_origin); + if (autocvar_hud_shownames_antioverlap) + { + // fade tag out if another tag that is closer to you overlaps + for (entity e = world; (e = find(e, classname, "shownames_tag")); ) { - // fade tag out if another tag that is closer to you overlaps - entity e; - for(e = world; (e = find(e, classname, "shownames_tag")); ) + if (e == this) continue; + vector eo = project_3d_to_2d(e.origin); + if (eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight) continue; + eo.z = 0; + if (vlen((eX * o.x + eY * o.y) - eo) < autocvar_hud_shownames_antioverlap_distance + && dist > vlen(e.origin - view_origin)) { - if(e == ent) - continue; - eo = project_3d_to_2d(e.origin); - if (!(eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight)) - { - eo.z = 0; - if(vlen((eX * o.x + eY * o.y) - eo) < autocvar_hud_shownames_antioverlap_distance && vlen(ent.origin - view_origin) > vlen(e.origin - view_origin)) - { - overlap = true; - break; - } - } - } - } - - onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight); - crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) ); - - if(autocvar_hud_shownames_crosshairdistance) - { - if(autocvar_hud_shownames_crosshairdistance > crosshairdistance) - ent.pointtime = time; - - if (ent.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true; - else - overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar. - } - - if(!ent.fadedelay) - ent.fadedelay = time + SHOWNAMES_FADEDELAY; - - if(!ent.sameteam && (!onscreen || !hit)) // out of view, fade out - { - ent.alpha = max(0, ent.alpha - SHOWNAMES_FADESPEED * frametime); - ent.fadedelay = 0; // reset fade in delay, enemy has left the view - } - else if(ent.csqcmodel_isdead) // dead player, fade out slowly - ent.alpha = max(0, ent.alpha - SHOWNAMES_FADESPEED * 0.25 * frametime); - else if(overlap) // tag overlap detected, fade out - ent.alpha = max(0, ent.alpha - SHOWNAMES_FADESPEED * frametime); - else if(ent.sameteam) // fade in for team mates - ent.alpha = min(1, ent.alpha + SHOWNAMES_FADESPEED * frametime); - else if(time > ent.fadedelay) // fade in for enemies - ent.alpha = min(1, ent.alpha + SHOWNAMES_FADESPEED * frametime); - - // multiply by player alpha - if(!ent.sameteam || (ent.sv_entnum == player_localentnum)) - ent.alpha *= getplayeralpha(ent.sv_entnum-1); - - if(ent.alpha < ALPHA_MIN_VISIBLE && gametype != MAPINFO_TYPE_CTS) - return; - - float dist; - dist = vlen(ent.origin - view_origin); - - float a; - a = autocvar_hud_shownames_alpha; - a *= ent.alpha; - if(autocvar_hud_shownames_maxdistance) - { - if(dist >= autocvar_hud_shownames_maxdistance) - return; - a *= ((autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance) - max(0, dist - autocvar_hud_shownames_mindistance)) / (autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance); + break; + } } - - if(!a) - return; - - float resize; - resize = 1; - if(autocvar_hud_shownames_resize) // limit resize so its never smaller than 0.5... gets unreadable - resize = 0.5 + 0.5 * ((autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance) - max(0, dist - autocvar_hud_shownames_mindistance)) / (autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance); - - // draw the sprite image - if(o.z >= 0) + } + bool onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight); + float crosshairdistance = sqrt(pow(o.x - vid_conwidth / 2, 2) + pow(o.y - vid_conheight / 2, 2)); + if (autocvar_hud_shownames_crosshairdistance) + { + if (autocvar_hud_shownames_crosshairdistance > crosshairdistance) this.pointtime = time; + if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true; + else overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar. + } + if (!this.fadedelay) this.fadedelay = time + SHOWNAMES_FADEDELAY; + if (this.csqcmodel_isdead) // dead player, fade out slowly + { + this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * 0.25 * frametime); + } + else if (!this.sameteam && (!onscreen || !hit)) // out of view, fade out + { + this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime); + this.fadedelay = 0; // reset fade in delay, enemy has left the view + } + else if (overlap) // tag overlap detected, fade out + { + this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime); + } + else if (this.sameteam) // fade in for team mates + { + this.alpha = min(1, this.alpha + SHOWNAMES_FADESPEED * frametime); + } + else if (time > this.fadedelay) // fade in for enemies + { + this.alpha = min(1, this.alpha + SHOWNAMES_FADESPEED * frametime); + } + float a = autocvar_hud_shownames_alpha * this.alpha; + // multiply by player alpha + if (!this.sameteam || (this.sv_entnum == player_localentnum)) + { + float f = getplayeralpha(this.sv_entnum - 1); + if (f == 0) f = 1; + if (f < 0) f = 0; + // FIXME: alpha is negative when dead, breaking death fade + if (!this.csqcmodel_isdead) a *= f; + } + if (a < ALPHA_MIN_VISIBLE && gametype != MAPINFO_TYPE_CTS) return; + if (autocvar_hud_shownames_maxdistance) + { + if (dist >= autocvar_hud_shownames_maxdistance) return; + float f = autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance; + a *= (f - max(0, dist - autocvar_hud_shownames_mindistance)) / f; + } + if (!a) return; + float resize = 1; + if (autocvar_hud_shownames_resize) // limit resize so its never smaller than 0.5... gets unreadable + { + float f = autocvar_hud_shownames_maxdistance - autocvar_hud_shownames_mindistance; + resize = 0.5 + 0.5 * (f - max(0, dist - autocvar_hud_shownames_mindistance)) / f; + } + // draw the sprite image + if (o.z >= 0) + { + o.z = 0; + vector mySize = (eX * autocvar_hud_shownames_aspect + eY) * autocvar_hud_shownames_fontsize; + vector myPos = o - '0.5 0 0' * mySize.x - '0 1 0' * mySize.y; + // size scaling + mySize.x *= resize; + mySize.y *= resize; + myPos.x += 0.5 * (mySize.x / resize - mySize.x); + myPos.y += (mySize.y / resize - mySize.y); + // this is where the origin of the string + vector namepos = myPos; + float namewidth = mySize.x; + if (autocvar_hud_shownames_status && this.sameteam) { - o.z = 0; - - vector myPos, mySize; - mySize = (eX * autocvar_hud_shownames_aspect + eY) * autocvar_hud_shownames_fontsize; - myPos = o - '0.5 0 0' * mySize.x - '0 1 0' * mySize.y; - - // size scaling - mySize.x *= resize; - mySize.y *= resize; - - myPos.x += 0.5 * (mySize.x / resize - mySize.x); - myPos.y += (mySize.y / resize - mySize.y); - - vector namepos; // this is where the origin of the string - float namewidth; - - namepos = myPos; - namewidth = mySize.x; - - if(autocvar_hud_shownames_status && teamplay) + vector v = namepos + '0 1 0' * autocvar_hud_shownames_fontsize * resize; + vector s = eX * 0.5 * mySize.x + eY * resize * autocvar_hud_shownames_statusbar_height; + if (this.healthvalue > 0) { - if(ent.sameteam) - { - if(ent.healthvalue > 0) - { - HUD_Panel_DrawProgressBar(namepos + '0 1 0' * autocvar_hud_shownames_fontsize * resize, eX * 0.5 * mySize.x + eY * resize * autocvar_hud_shownames_statusbar_height, "nametag_statusbar", ent.healthvalue/autocvar_hud_panel_healtharmor_maxhealth, 0, 1, '1 0 0', a, DRAWFLAG_NORMAL); - - if(ent.armorvalue > 0) - HUD_Panel_DrawProgressBar(namepos + '0 1 0' * autocvar_hud_shownames_fontsize * resize + eX * 0.5 * mySize.x, eX * 0.5 * mySize.x + eY * resize * autocvar_hud_shownames_statusbar_height, "nametag_statusbar", ent.armorvalue/autocvar_hud_panel_healtharmor_maxarmor, 0, 0, '0 1 0', a, DRAWFLAG_NORMAL); - } - } + HUD_Panel_DrawProgressBar(v, s, "nametag_statusbar", + this.healthvalue / autocvar_hud_panel_healtharmor_maxhealth, false, 1, '1 0 0', a, + DRAWFLAG_NORMAL); + } + if (this.armorvalue > 0) + { + HUD_Panel_DrawProgressBar(v + eX * 0.5 * mySize.x, s, "nametag_statusbar", + this.armorvalue / autocvar_hud_panel_healtharmor_maxarmor, false, 0, '0 1 0', a, + DRAWFLAG_NORMAL); } - - string s; - s = GetPlayerName(ent.sv_entnum-1); - if((autocvar_hud_shownames_decolorize == 1 && teamplay) || autocvar_hud_shownames_decolorize == 2) - s = playername(s, GetPlayerColor(ent.sv_entnum-1)); - - drawfontscale = '1 1 0' * resize; - s = textShortenToWidth(s, namewidth, '1 1 0' * autocvar_hud_shownames_fontsize, stringwidth_colors); - - float width; - width = stringwidth(s, true, '1 1 0' * autocvar_hud_shownames_fontsize); - - if (width != namewidth) - namepos.x += (namewidth - width) / 2; - drawcolorcodedstring(namepos, s, '1 1 0' * autocvar_hud_shownames_fontsize, a, DRAWFLAG_NORMAL); - drawfontscale = '1 1 0'; } + string s = GetPlayerName(this.sv_entnum - 1); + if ((autocvar_hud_shownames_decolorize == 1 && teamplay) + || autocvar_hud_shownames_decolorize == 2) s = playername(s, GetPlayerColor(this.sv_entnum - 1)); + drawfontscale = '1 1 0' * resize; + s = textShortenToWidth(s, namewidth, '1 1 0' * autocvar_hud_shownames_fontsize, stringwidth_colors); + float width = stringwidth(s, true, '1 1 0' * autocvar_hud_shownames_fontsize); + if (width != namewidth) namepos.x += (namewidth - width) / 2; + drawcolorcodedstring(namepos, s, '1 1 0' * autocvar_hud_shownames_fontsize, a, DRAWFLAG_NORMAL); + drawfontscale = '1 1 0'; } } -entity shownames_ent[255]; void Draw_ShowNames_All() { - int i; - for(i = 0; i < maxclients; ++i) + for (int i = 0; i < maxclients; ++i) { - float t; - t = GetPlayerColor(i); - if(t == NUM_SPECTATOR) - continue; - - entity e; - e = shownames_ent[i]; - if(!e) + entity e = shownames_ent[i]; + if (!e) { - e = new(shownames_tag); - e.sv_entnum = i+1; - shownames_ent[i] = e; + e = shownames_ent[i] = new(shownames_tag); + e.sv_entnum = i + 1; } - - entity entcs; - entcs = entcs_receiver[i]; - if(entcs) + entity entcs = entcs_receiver[i]; + if (entcs.m_entcs_private) { e.healthvalue = entcs.healthvalue; e.armorvalue = entcs.armorvalue; - e.sameteam = 1; /* (teamplay && (t == myteam)); */ + e.sameteam = true; } else { - e.healthvalue = 2342; + e.healthvalue = 0; e.armorvalue = 0; - e.sameteam = 0; + e.sameteam = false; } - - setorigin(e, getplayerorigin(i)); - if(e.origin == GETPLAYERORIGIN_ERROR) - continue; - - e.csqcmodel_isdead = getplayerisdead(i); - + bool dead = getplayerisdead(i); + if (!e.csqcmodel_isdead && entcs.has_origin) setorigin(e, entcs.origin); + e.csqcmodel_isdead = dead; Draw_ShowNames(e); } } diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 699e2d8ad..11c8f431f 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -359,7 +359,7 @@ float TrueAimCheck() break; } - vector traceorigin = getplayerorigin(player_localentnum-1) + (eZ * getstati(STAT_VIEWHEIGHT)); + vector traceorigin = entcs_receiver[player_localentnum - 1].origin + (eZ * getstati(STAT_VIEWHEIGHT)); vecs = decompressShotOrigin(getstati(STAT_SHOTORG)); diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 8d8e8df19..1ea5a8bff 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -55,7 +55,6 @@ NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { return true; } /** Sent as a temp entity from a persistent linked entity */ REGISTER_NET_TEMP(ENT_CLIENT_INIT) -REGISTER_NET_LINKED(ENT_CLIENT_ENTCS) REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO) REGISTER_NET_LINKED(ENT_CLIENT_SCORES) REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES) diff --git a/qcsrc/common/effects/qc/globalsound.qc b/qcsrc/common/effects/qc/globalsound.qc index 301b0215b..413df981f 100644 --- a/qcsrc/common/effects/qc/globalsound.qc +++ b/qcsrc/common/effects/qc/globalsound.qc @@ -1,4 +1,7 @@ #include "globalsound.qh" + +#include "../common/ent_cs.qh" + #ifdef IMPLEMENTATION #include "../../animdecide.qh" @@ -46,6 +49,7 @@ WriteByte(channel, fabs(chan)); WriteByte(channel, floor(vol * 255)); WriteByte(channel, floor(atten * 64)); + entcs_force_origin(from); vector o = from.origin + 0.5 * (from.mins + from.maxs); WriteCoord(channel, o.x); WriteCoord(channel, o.y); @@ -88,16 +92,10 @@ NET_HANDLE(playersound, bool isnew) { - entity ps; - ps = PlayerSounds_from(ReadByte()); + entity ps = PlayerSounds_from(ReadByte()); float r = ReadByte() / 255; int who = ReadByte(); - entity e = findfloat(world, entnum, who); - if (autocvar_cl_forceplayermodels) - { - entity me = findfloat(world, entnum, player_currententnum); - if (me.model != "null") e = me; - } + entity e = entcs_receiver[who - 1]; UpdatePlayerSounds(e); string s = e.(ps.m_playersoundfld); string sample = GlobalSound_sample(s, r); @@ -108,13 +106,16 @@ o.x = ReadCoord(); o.y = ReadCoord(); o.z = ReadCoord(); - if (who == player_currententnum) + if (e) { - // client knows better, play at current position to unlag + // TODO: for non-visible players, origin should probably continue to be updated as long as the sound is playing + e.origin = o; sound7(e, chan, sample, vol, atten, 0, 0); } else { + LOG_WARNINGF("Missing entcs data for player %i\n", e); + // Can this happen? entity e = new(playersound); e.origin = o; sound8(e, o, chan, sample, vol, atten, 0, 0); @@ -269,15 +270,16 @@ return true; } - .int modelindex_for_playersound; + .string model_for_playersound; .int skin_for_playersound; bool autocvar_g_debug_defaultsounds; void UpdatePlayerSounds(entity this) { - if (this.modelindex == this.modelindex_for_playersound && this.skin == this.skin_for_playersound) return; - this.modelindex_for_playersound = this.modelindex; + if (this.model == this.model_for_playersound && this.skin == this.skin_for_playersound) return; + if (this.model_for_playersound) strunzone(this.model_for_playersound); + this.model_for_playersound = strzone(this.model); this.skin_for_playersound = this.skin; ClearPlayerSounds(this); LoadPlayerSounds(this, "sound/player/default.sounds", true); diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc new file mode 100644 index 000000000..e016dd32d --- /dev/null +++ b/qcsrc/common/ent_cs.qc @@ -0,0 +1,165 @@ +#include "ent_cs.qh" + +// #define PROP(public, fld, sv, cl) +#define ENTCS_NETPROPS(PROP) \ + PROP(true, sv_entnum, \ + { WriteByte(MSG_ENTITY, etof(player) - 1); }, \ + { this.sv_entnum = ReadByte(); }) \ + \ + PROP(false, origin, \ + { WriteShort(MSG_ENTITY, this.origin.x); WriteShort(MSG_ENTITY, this.origin.y); \ + WriteShort(MSG_ENTITY, this.origin.z); }, \ + { this.has_origin = true; vector v; v.x = ReadShort(); v.y = ReadShort(); v.z = ReadShort(); setorigin(this, v); }) \ + \ + PROP(false, angles_y, \ + { WriteByte(MSG_ENTITY, this.angles.y / 360 * 256); }, \ + { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; this.angles = v; }) \ + \ + PROP(false, health, \ + { WriteByte(MSG_ENTITY, this.health / 10); /* FIXME: use a better scale? */ }, \ + { this.healthvalue = ReadByte() * 10; }) \ + \ + PROP(false, armorvalue, \ + { WriteByte(MSG_ENTITY, this.armorvalue / 10); /* FIXME: use a better scale? */ }, \ + { this.armorvalue = ReadByte() * 10; }) \ + \ + PROP(true, netname, \ + { WriteString(MSG_ENTITY, this.netname); }, \ + { if (this.netname) strunzone(this.netname); this.netname = strzone(ReadString()); }) \ + \ + PROP(true, model, \ + { WriteString(MSG_ENTITY, this.model); }, \ + { if (this.model) strunzone(this.model); this.model = strzone(ReadString()); }) \ + \ + PROP(true, skin, \ + { WriteByte(MSG_ENTITY, this.skin); }, \ + { this.skin = ReadByte(); }) \ + \ + /**/ + +#ifdef SVQC + + int ENTCS_PUBLICMASK = 0; + STATIC_INIT(ENTCS_PUBLICMASK) + { + int i = 1; + #define X(public, fld, sv, cl) { if (public) ENTCS_PUBLICMASK |= BIT(i); } i += 1; + ENTCS_NETPROPS(X); + #undef X + if (i >= BITS(16 - 1)) LOG_FATAL("Exceeded ENTCS_NETPROPS limit"); + } + + bool entcs_customize() + { + SELFPARAM(); + entity player = this.owner; + return IS_PLAYER(player) // player must be active + && player.deadflag == DEAD_NO // player must be alive + ; + } + + bool entcs_send(entity this, entity to, int sf) + { + entity player = this.owner; + sf |= 1; + if (IS_PLAYER(to) || to.caplayer) // unless spectating, + { + bool same_team = (to == player) || (teamplay && player.team == to.team); + if (!same_team && !radar_showennemies) sf &= ENTCS_PUBLICMASK; // no private updates + } + sf |= this.m_forceupdate; + this.m_forceupdate = 0; + WriteHeader(MSG_ENTITY, ENT_CLIENT_ENTCS); + WriteShort(MSG_ENTITY, sf); + int i = 1; + #define X(public, fld, sv, cl) { if (sf & BIT(i)) sv; } i += 1; + ENTCS_NETPROPS(X); + #undef X + return true; + } + + void entcs_think() + { + SELFPARAM(); + this.nextthink = time + 0.033333333333; // TODO: increase this to like 0.15 once the client can do smoothing + entity o = this.owner; + int i = 1; + #define X(public, fld, sv, cl) \ + if (o.fld != this.fld) \ + { \ + this.fld = o.fld; \ + this.SendFlags |= BIT(i); \ + } \ + i += 1; + ENTCS_NETPROPS(X); + #undef X + } + + void entcs_attach(entity player) + { + entity e = player.entcs = new(entcs_sender); + make_pure(e); + e.owner = player; + e.think = entcs_think; + e.nextthink = time; + Net_LinkEntity(e, false, 0, entcs_send); + e.customizeentityforclient = entcs_customize; + } + + void entcs_detach(entity player) + { + if (!player.entcs) return; + remove(player.entcs); + player.entcs = NULL; + } + +#endif + +#ifdef CSQC + + void Ent_RemoveEntCS() + { + SELFPARAM(); + entcs_receiver[this.sv_entnum] = NULL; + } + + void entcs_think() + { + SELFPARAM(); + this.nextthink = time; + entity e = CSQCModel_server2csqc(this.sv_entnum + 1); + bool exists = !!e; + if (exists) + { + this.has_origin = true; + this.origin = e.origin; + // `cl_forceplayermodels 1` sounds will be wrong until the player has been in the PVS, but so be it + this.model = e.model; + } + } + + NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew) + { + if (isnew) + { + make_pure(this); + this.classname = "entcs_receiver"; + this.entremove = Ent_RemoveEntCS; + this.think = entcs_think; + this.nextthink = time; + } + InterpolateOrigin_Undo(); + int sf = ReadShort(); + this.has_origin = false; + this.m_entcs_private = boolean(sf & 1); + int i = 1; + #define X(public, fld, sv, cl) { if (sf & BIT(i)) cl; } i += 1; + ENTCS_NETPROPS(X); + #undef X + entcs_receiver[this.sv_entnum] = this; + this.iflags |= IFLAG_ORIGIN; + InterpolateOrigin_Note(); + return true; + } + +#endif diff --git a/qcsrc/common/ent_cs.qh b/qcsrc/common/ent_cs.qh new file mode 100644 index 000000000..598ff0514 --- /dev/null +++ b/qcsrc/common/ent_cs.qh @@ -0,0 +1,49 @@ +#ifndef ENT_CS_H +#define ENT_CS_H + +REGISTER_NET_LINKED(ENT_CLIENT_ENTCS) + +/** True when private information such as origin is available */ +.bool m_entcs_private; + +/** True when a recent origin is available */ +.bool has_origin; + +#ifdef SVQC +/** + * The point of these entities is to avoid the problems + * with clientprediction. + * If you add SendEntity to players, the engine will not + * do any prediction anymore, and you'd have to write the whole + * prediction code in CSQC, you want that? :P + * Data can depend on gamemode. For now, it serves as GPS entities + * in onslaught... YAY ;) + */ + +.entity entcs; + +bool entcs_customize(); + +bool entcs_send(entity this, entity to, int sf); + +void entcs_think(); + +void entcs_attach(entity e); + +void entcs_detach(entity e); + +.int m_forceupdate; + +/** Force an origin update, for player sounds */ +#define entcs_force_origin(e) ((e).entcs.m_forceupdate = BIT(2)) + +#endif + +#ifdef CSQC + +entity entcs_receiver[255]; // 255 is the engine limit on maxclients +#define entcs_is_self(e) ((e).sv_entnum + 1 == player_localentnum) + +#endif + +#endif diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index b873e6238..749237fa3 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -1227,7 +1227,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) else { // use player origin so that third person display still works - self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT)); + self.origin = entcs_receiver[player_localnum].origin + ('0 0 1' * getstati(STAT_VIEWHEIGHT)); } } diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index 50339a730..0f898c340 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -576,8 +576,6 @@ bool WarpZoneLib_BadEntity(entity e) if (is_pure(e)) return true; switch (s) { - case "entcs_sender": - case "entcs_receiver": // case "net_linked": // actually some real entities are linked without classname, fail case "": return true; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 454ddc903..26d089245 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -3,7 +3,6 @@ #include "anticheat.qh" #include "cl_impulse.qh" #include "cl_player.qh" -#include "ent_cs.qh" #include "ipban.qh" #include "miscfunctions.qh" #include "portals.qh" @@ -23,6 +22,7 @@ #include "bot/bot.qh" #include "bot/navigation.qh" +#include "../common/ent_cs.qh" #include "../common/vehicles/all.qh" #include "../common/triggers/teleporters.qh" @@ -1178,7 +1178,7 @@ void ClientConnect () else stuffcmd(self, "set _teams_available 0\n"); - attach_entcs(self); + entcs_attach(self); bot_relinkplayerlist(); @@ -1264,7 +1264,7 @@ void ClientDisconnect () bot_clientdisconnect(); - detach_entcs(self); + entcs_detach(self); if(autocvar_sv_eventlog) GameLogEcho(strcat(":part:", ftos(self.playerid))); diff --git a/qcsrc/server/ent_cs.qc b/qcsrc/server/ent_cs.qc deleted file mode 100644 index 9c98e7d99..000000000 --- a/qcsrc/server/ent_cs.qc +++ /dev/null @@ -1,87 +0,0 @@ -#include "ent_cs.qh" - -float entcs_customize() -{ - SELFPARAM(); - entity o = self.owner; - if(o.deadflag != DEAD_NO) - return false; - if (!IS_PLAYER(o)) - return false; - if(other == o) - return false; - if((IS_PLAYER(other)) || other.caplayer) - if(!teamplay || o.team != other.team) - if (!radar_showennemies) - return false; - return true; -} - -bool entcs_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_ENTCS); - WriteByte(MSG_ENTITY, sf); - if(sf & BIT(0)) - WriteByte(MSG_ENTITY, num_for_edict(self.owner) - 1); - if(sf & BIT(1)) - { - WriteShort(MSG_ENTITY, self.origin.x); - WriteShort(MSG_ENTITY, self.origin.y); - WriteShort(MSG_ENTITY, self.origin.z); - } - if(sf & BIT(2)) - WriteByte(MSG_ENTITY, self.angles.y * 256.0 / 360); - if(sf & BIT(3)) - WriteByte(MSG_ENTITY, self.health / 10); // FIXME use a better scale? - if(sf & BIT(4)) - WriteByte(MSG_ENTITY, self.armorvalue / 10); // FIXME use a better scale? - return true; -} - -void entcs_think() -{ - SELFPARAM(); - self.nextthink = time + 0.033333333333; // increase this to like 0.15 once the client can do smoothing - entity o = self.owner; - if (o.origin != self.origin) - { - setorigin(self, o.origin); - self.SendFlags |= BIT(1); - } - if (o.angles.y != self.angles.y) - { - self.angles = o.angles; - self.SendFlags |= BIT(2); - } - if (o.health != self.health) - { - self.health = o.health; - self.SendFlags |= BIT(3); - } - if (o.armorvalue != self.armorvalue) - { - self.armorvalue = o.armorvalue; - self.SendFlags |= BIT(4); - } -} - -entity attach_entcs(entity e) -{ - entity ent = e.entcs = new(entcs_sender); - make_pure(ent); - ent.owner = e; - ent.think = entcs_think; - ent.nextthink = time; - - Net_LinkEntity(ent, false, 0, entcs_send); - ent.customizeentityforclient = entcs_customize; - - return ent; -} - -void detach_entcs(entity e) -{ - if (!e.entcs) return; - remove(e.entcs); - e.entcs = NULL; -} diff --git a/qcsrc/server/ent_cs.qh b/qcsrc/server/ent_cs.qh deleted file mode 100644 index 1cfc854c0..000000000 --- a/qcsrc/server/ent_cs.qh +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef ENT_CS_H -#define ENT_CS_H - -/** - * The point of these entities is to avoid the problems - * with clientprediction. - * If you add SendEntity to players, the engine will not - * do any prediction anymore, and you'd have to write the whole - * prediction code in CSQC, you want that? :P - * Data can depend on gamemode. For now, it serves as GPS entities - * in onslaught... YAY ;) - */ - -.entity entcs; - -float entcs_customize(); - -bool entcs_send(entity this, entity to, int sf); - -void entcs_think(); - -entity attach_entcs(entity e); - -void detach_entcs(entity e); - -#endif diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index 08a9d4d22..e23909812 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -10,7 +10,6 @@ #include "cl_client.qc" #include "cl_impulse.qc" #include "cl_player.qc" -#include "ent_cs.qc" #include "g_damage.qc" #include "g_hook.qc" // #include "g_lights.qc" // TODO: was never used @@ -58,6 +57,7 @@ #include "../common/campaign_file.qc" #include "../common/campaign_setup.qc" #include "../common/effects/effectinfo.qc" +#include "../common/ent_cs.qc" #include "../common/mapinfo.qc" #include "../common/minigames/minigames.qc" #include "../common/minigames/sv_minigames.qc" -- 2.39.2