4 #define ENTCS_EXTRAPROPS
6 #define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \
10 /** the engine player name strings are mutable! */
11 #define ENTCS_SET_MUTABLE_STRING(var, x) MACRO_BEGIN \
12 if (var) strunzone(var); \
16 // #define PROP(public, fld, set, sv, cl)
17 #define ENTCS_NETPROPS(ent, PROP) PROP(false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ \
18 PROP(false, origin, ENTCS_SET_NORMAL, \
19 { WriteCoord(chan, ent.origin.x); WriteCoord(chan, ent.origin.y); \
20 WriteCoord(chan, ent.origin.z); }, \
21 { ent.has_sv_origin = true; vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); setorigin(ent, v); }) \
23 PROP(false, angles_y, ENTCS_SET_NORMAL, \
24 { WriteByte(chan, ent.angles.y / 360 * 256); }, \
25 { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; }) \
27 PROP(false, health, ENTCS_SET_NORMAL, \
28 { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ }, \
29 { ent.healthvalue = ReadByte() * 10; }) \
31 PROP(false, armorvalue, ENTCS_SET_NORMAL, \
32 { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, \
33 { ent.armorvalue = ReadByte() * 10; }) \
35 PROP(true, netname, ENTCS_SET_MUTABLE_STRING, \
36 { WriteString(chan, ent.netname); }, \
37 { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); }) \
39 PROP(true, model, ENTCS_SET_NORMAL, \
40 { WriteString(chan, ent.model); }, \
41 { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); }) \
43 PROP(true, skin, ENTCS_SET_NORMAL, \
44 { WriteByte(chan, ent.skin); }, \
45 { ent.skin = ReadByte(); }) \
47 PROP(true, clientcolors, ENTCS_SET_NORMAL, \
48 { WriteByte(chan, ent.clientcolors); }, \
49 { ent.colormap = ReadByte(); }) \
51 PROP(true, frags, ENTCS_SET_NORMAL, \
52 { WriteShort(chan, ent.frags); }, \
53 { ent.frags = ReadShort(); }) \
61 int ENTCS_PUBLICMASK = 0;
62 STATIC_INIT(ENTCS_PUBLICMASK)
65 #define X(public, fld, set, sv, cl) { \
67 ENTCS_PUBLICMASK |= BIT(i); \
71 ENTCS_NETPROPS(this, X);
73 if (i >= BITS(16 - 1)) LOG_FATAL("Exceeded ENTCS_NETPROPS limit");
76 bool _entcs_send(entity this, entity to, int sf, int chan)
78 entity player = this.owner;
79 sf |= BIT(0); // assume private
81 if (IS_PLAYER(player))
83 if (radar_showennemies) break;
84 if (SAME_TEAM(to, player)) break;
85 if (!(IS_PLAYER(to) || to.caplayer) && time > game_starttime) break;
87 sf &= ENTCS_PUBLICMASK; // no private updates
90 sf |= this.m_forceupdate;
91 this.m_forceupdate = 0;
92 if (chan == MSG_ENTITY)
93 WriteHeader(chan, ENT_CLIENT_ENTCS);
95 WriteHeader(chan, CLIENT_ENTCS);
96 WriteByte(chan, etof(player) - 1);
99 #define X(public, fld, set, sv, cl) { \
105 ENTCS_NETPROPS(this, X);
110 bool entcs_send(entity this, entity to, int sf)
112 return _entcs_send(this, to, sf, MSG_ENTITY);
115 void entcs_think(entity this)
117 this.nextthink = time + 0.033333333333; // TODO: increase this to like 0.15 once the client can do smoothing
118 entity o = this.owner;
120 #define X(public, fld, set, sv, cl) { \
121 if (o.fld != this.fld) { \
122 set(this.fld, o.fld); \
123 this.SendFlags |= BIT(i); \
127 ENTCS_NETPROPS(this, X);
129 setorigin(this, this.origin); // relink
132 void entcs_attach(entity player)
134 entity e = player.entcs = new(entcs_sender);
136 setthink(e, entcs_think);
138 Net_LinkEntity(e, false, 0, entcs_send);
139 if (!IS_REAL_CLIENT(player)) return;
140 FOREACH_CLIENT(true, {
142 _entcs_send(it.entcs, msg_entity = player, BITS(23), MSG_ONE);
146 void entcs_detach(entity player)
148 if (!player.entcs) return;
149 delete(player.entcs);
157 void Ent_RemoveEntCS(entity this)
159 int n = this.sv_entnum;
160 entity e = entcs_receiver(n);
161 entcs_receiver(n, NULL);
162 if (e.netname) strunzone(e.netname);
163 e.netname = string_null;
164 if (e.model) strunzone(e.model);
165 e.model = string_null;
166 if (e != this) delete(e);
169 void entcs_think(entity this)
171 entity e = CSQCModel_server2csqc(this.sv_entnum);
174 this.has_origin = this.has_sv_origin;
177 this.has_origin = true;
178 this.origin = e.origin;
179 // `cl_forceplayermodels 1` sounds will be wrong until the player has been in the PVS, but so be it
180 if (this.model != e.model)
182 if (this.model) strunzone(this.model);
183 this.model = strzone(e.model);
187 bool ReadEntcs(entity this)
190 entity e = entcs_receiver(n);
195 e = new_pure(entcs_receiver);
199 setthink(e, entcs_think);
200 entcs_receiver(n, e);
202 else if (e != this && this)
207 setthink(e, entcs_think);
208 entcs_receiver(n, e);
211 InterpolateOrigin_Undo(e);
213 int sf = ReadShort();
214 e.has_sv_origin = false;
215 e.m_entcs_private = boolean(sf & BIT(0));
217 #define X(public, fld, set, sv, cl) { \
223 ENTCS_NETPROPS(e, X);
225 e.iflags |= IFLAG_ORIGIN;
226 InterpolateOrigin_Note(e);
231 NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew)
236 this.classname = "entcs_receiver";
237 this.entremove = Ent_RemoveEntCS;
239 return ReadEntcs(this);
242 NET_HANDLE(CLIENT_ENTCS, bool isnew)
244 return ReadEntcs(NULL);