-#ifndef NET_H
-#define NET_H
+#pragma once
+
+#include "registry.qh"
+#include "sort.qh"
+#include "yenc.qh"
+
+// netcode mismatch and not sure what caused it? developer_csqcentities 1
+
+.string netname;
+.int m_id;
+.bool(entity this, entity sender, bool isNew) m_read;
+#define NET_HANDLE(id, param) bool Net_Handle_##id(entity this, entity sender, param)
+
+#define NET_GUARD(id) \
+ bool Net_Handle_##id##_guard(entity this, entity sender, bool isNew) { \
+ bool valid = false; \
+ serialize_marker(to, valid); \
+ if (!valid) LOG_FATALF("Last message not fully parsed: %s", _net_prevmsgstr); \
+ _net_prevmsgstr = #id; \
+ return Net_Handle_##id(this, sender, isNew); \
+ }
+
+#ifdef CSQC
+string _net_prevmsgstr;
+ #define REGISTER_NET_TEMP(id) \
+ NET_HANDLE(id, bool); \
+ NET_GUARD(id); \
+ REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) { \
+ this.netname = #id; \
+ this.m_read = Net_Handle_##id##_guard; \
+ }
+#else
+ #define REGISTER_NET_TEMP(id) \
+ const bool NET_##id##_istemp = true; \
+ REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \
+ { \
+ this.netname = #id; \
+ }
+#endif
+#define REGISTER_NET_S2C(id) REGISTER_NET_TEMP(id)
+
+REGISTRY(TempEntities, BITS(8) - 80)
+#define TempEntities_from(i) _TempEntities_from(i, NULL)
+REGISTER_REGISTRY(TempEntities)
+REGISTRY_SORT(TempEntities)
+REGISTRY_CHECK(TempEntities)
+STATIC_INIT(TempEntities_renumber) { FOREACH(TempEntities, true, it.m_id = 80 + i); }
+
+
+
+#ifdef CSQC
+ #define REGISTER_NET_LINKED(id) \
+ ACCUMULATE NET_HANDLE(id, bool isnew) \
+ { \
+ this = __self; \
+ this.sourceLoc = __FILE__ ":" STR(__LINE__); \
+ if (!this) isnew = true; \
+ } \
+ NET_GUARD(id); \
+ REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \
+ { \
+ this.netname = #id; \
+ this.m_read = Net_Handle_##id##_guard; \
+ }
+#else
+ #define REGISTER_NET_LINKED(id) \
+ const bool NET_##id##_istemp = false; \
+ REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \
+ { \
+ this.netname = #id; \
+ }
+#endif
+
+REGISTRY(LinkedEntities, BITS(8) - 1)
+#define LinkedEntities_from(i) _LinkedEntities_from(i, NULL)
+REGISTER_REGISTRY(LinkedEntities)
+REGISTRY_SORT(LinkedEntities)
+REGISTRY_CHECK(LinkedEntities)
+STATIC_INIT(LinkedEntities_renumber) { FOREACH(LinkedEntities, true, it.m_id = 1 + i); }
+
+
+
+#ifdef SVQC
+ #define REGISTER_NET_C2S(id) \
+ NET_HANDLE(id, bool); \
+ REGISTER(C2S_Protocol, NET, id, m_id, new_pure(net_c2s_packet)) \
+ { \
+ this.netname = #id; \
+ this.m_read = Net_Handle_##id; \
+ }
+#else
+ #define REGISTER_NET_C2S(id) \
+ const bool NET_##id##_istemp = true; \
+ REGISTER(C2S_Protocol, NET, id, m_id, new_pure(net_c2s_packet)) \
+ { \
+ this.netname = #id; \
+ }
+#endif
+
+REGISTRY(C2S_Protocol, BITS(8) - 1)
+#define C2S_Protocol_from(i) _C2S_Protocol_from(i, NULL)
+REGISTER_REGISTRY(C2S_Protocol)
+REGISTRY_SORT(C2S_Protocol)
+REGISTRY_CHECK(C2S_Protocol)
+STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); }
#ifdef SVQC
- .int Version; // deprecated, use SendFlags
+ const int MSG_ENTITY = 5;
+
.int SendFlags;
- .bool(entity to, int sendflags) SendEntity;
- /** return false to remove from the client */
- .bool(entity this, entity to, int sendflags) SendEntity3;
- bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); }
+ IntrusiveList g_uncustomizables;
+ STATIC_INIT(g_uncustomizables) { g_uncustomizables = IL_NEW(); }
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
{
if (e.classname == "") e.classname = "net_linked";
- if (e.model == "" || self.modelindex == 0)
+ if (e.model == "" || e.modelindex == 0)
{
vector mi = e.mins;
vector ma = e.maxs;
setsize(e, mi, ma);
}
- e.SendEntity = SendEntity_self;
- e.SendEntity3 = sendfunc;
+ setSendEntity(e, sendfunc);
e.SendFlags = 0xFFFFFF;
if (!docull) e.effects |= EF_NODEPTHTEST;
if (dt)
{
e.nextthink = time + dt;
- e.think = SUB_Remove_self;
+ setthink(e, SUB_Remove);
}
}
void Net_UnlinkEntity(entity e)
{
- e.SendEntity = func_null;
+ setSendEntity(e, func_null);
}
- .void() uncustomizeentityforclient;
+ .void(entity this) uncustomizeentityforclient;
.float uncustomizeentityforclient_set;
- void SetCustomizer(entity e, float() customizer, void() uncustomizer)
+ void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer)
{
- e.customizeentityforclient = customizer;
+ setcefc(e, customizer);
e.uncustomizeentityforclient = uncustomizer;
e.uncustomizeentityforclient_set = !!uncustomizer;
+ if(uncustomizer)
+ IL_PUSH(g_uncustomizables, e);
}
void UncustomizeEntitiesRun()
{
- for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
- WITH(entity, self, e, e.uncustomizeentityforclient());
+ IL_EACH(g_uncustomizables, it.uncustomizeentityforclient_set, it.uncustomizeentityforclient(it));
}
-#endif
+ STRING_ITERATOR(g_buf, string_null, 0);
-#include "registry.qh"
-#include "sort.qh"
+ int ReadByte();
-.string netname;
-.int m_id;
-.bool(entity this, bool isNew) m_read;
+ void Net_ClientCommand(entity sender, string command)
+ {
+ // command matches `c2s "(.+)"`
+ string buf = substring(command, argv_start_index(1) + 1, -2);
+ if (buf == "") return;
+ STRING_ITERATOR_SET(g_buf, buf, 0);
+ for (int C2S; (C2S = ReadByte()) >= 0; )
+ {
+ entity reader = C2S_Protocol_from(C2S);
+ if (reader && reader.m_read && reader.m_read(NULL, sender, true)) continue;
+ LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d", C2S);
+ return;
+ }
+ g_buf_i--;
+ int expected = strlen(buf);
+ if (g_buf_i > expected) LOG_WARNF("Underflow: %d", g_buf_i - expected);
+ if (g_buf_i < expected) LOG_WARNF("Overrflow: %d", expected - g_buf_i);
+ }
+
+#endif
#ifdef CSQC
+ const int MSG_C2S = 0;
+
#define Net_Accept(classname) \
MACRO_BEGIN \
- { \
- if (!this) this = new(classname); \
- } MACRO_END
+ if (!this) this = new(classname); \
+ MACRO_END
#define Net_Reject() \
MACRO_BEGIN \
- { \
- if (this) remove(this); \
- } MACRO_END
- #define NET_HANDLE(id, param) \
- bool Net_Handle_##id(entity this, param)
-#else
+ if (this) delete(this); \
+ MACRO_END
+
+ string g_buf;
+
+ void Net_Flush()
+ {
+ if (g_buf == "") return;
+ localcmd("\ncmd c2s \"", strreplace("$", "$$", g_buf), "\"\n");
+ strfree(g_buf);
+ }
+#endif
+
+#if defined(CSQC)
+ #define WriteHeader(to, id) \
+ WriteByte(to, NET_##id.m_id)
+#elif defined(SVQC)
#define WriteHeader(to, id) \
MACRO_BEGIN \
- { \
if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \
WriteByte(to, NET_##id.m_id); \
- } MACRO_END
+ bool _net_valid = false; serialize_marker(to, _net_valid); \
+ MACRO_END
#endif
-#ifdef CSQC
- #define REGISTER_NET_LINKED(id) \
- [[accumulate]] NET_HANDLE(id, bool isnew) \
- { \
- this = self; \
- this.sourceLocFile = __FILE__; \
- this.sourceLocLine = __LINE__; \
- if (!this) isnew = true; \
- } \
- REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \
- { \
- make_pure(this); \
- this.netname = #id; \
- this.m_read = Net_Handle_##id; \
- }
-#else
- #define REGISTER_NET_LINKED(id) \
- const bool NET_##id##_istemp = false; \
- REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \
- { \
- make_pure(this); \
- this.netname = #id; \
- }
+// serialization: new style
+
+USING(Stream, int);
+#if defined(SVQC)
+ #define stream_reading(stream) false
+ #define stream_writing(stream) true
+#elif defined(CSQC)
+ #define stream_reading(stream) true
+ #define stream_writing(stream) false
#endif
-REGISTRY(LinkedEntities, BITS(8) - 1)
-#define LinkedEntities_from(i) _LinkedEntities_from(i, NULL)
-REGISTER_REGISTRY(LinkedEntities)
-REGISTRY_SORT(LinkedEntities)
-REGISTRY_CHECK(LinkedEntities)
-STATIC_INIT(RegisterLinkedEntities_renumber)
-{
- for (int i = 0; i < LinkedEntities_COUNT; ++i)
- LinkedEntities_from(i).m_id = 1 + i;
-}
+#define serialize(T, stream, ...) \
+MACRO_BEGIN \
+ noref Stream _stream = stream; \
+ serialize_##T(_stream, __VA_ARGS__); \
+MACRO_END
-#ifdef CSQC
- #define REGISTER_NET_TEMP(id) \
- NET_HANDLE(id, bool); \
- REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \
- { \
- make_pure(this); \
- this.netname = #id; \
- this.m_read = Net_Handle_##id; \
- }
-#else
- #define REGISTER_NET_TEMP(id) \
- const bool NET_##id##_istemp = true; \
- REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \
- { \
- make_pure(this); \
- this.netname = #id; \
- }
+#if defined(SVQC)
+ #define serialize_byte(stream, this) \
+ MACRO_BEGIN \
+ WriteByte(stream, this); \
+ MACRO_END
+#elif defined(CSQC)
+ #define serialize_byte(stream, this) \
+ MACRO_BEGIN \
+ this = ReadByte(); \
+ MACRO_END
#endif
-REGISTRY(TempEntities, BITS(8) - 80)
-#define TempEntities_from(i) _TempEntities_from(i, NULL)
-REGISTER_REGISTRY(TempEntities)
-REGISTRY_SORT(TempEntities)
-REGISTRY_CHECK(TempEntities)
-STATIC_INIT(RegisterTempEntities_renumber)
-{
- for (int i = 0; i < TempEntities_COUNT; ++i)
- TempEntities_from(i).m_id = 80 + i;
-}
+#if defined(SVQC)
+ #define serialize_float(stream, this) \
+ MACRO_BEGIN \
+ WriteCoord(stream, this); \
+ MACRO_END
+#elif defined(CSQC)
+ #define serialize_float(stream, this) \
+ MACRO_BEGIN \
+ this = ReadCoord(); \
+ MACRO_END
+#endif
+
+#define serialize_vector(stream, this) \
+MACRO_BEGIN \
+ vector _v = this; \
+ serialize_float(stream, _v.x); \
+ serialize_float(stream, _v.y); \
+ serialize_float(stream, _v.z); \
+ this = _v; \
+MACRO_END
+
+#define serialize_marker(stream, this) \
+MACRO_BEGIN \
+ if (NDEBUG) { \
+ this = true; \
+ } else { \
+ int _de = 0xDE, _ad = 0xAD, _be = 0xBE, _ef = 0xEF; \
+ serialize_byte(stream, _de); \
+ serialize_byte(stream, _ad); \
+ serialize_byte(stream, _be); \
+ serialize_byte(stream, _ef); \
+ this = (_de == 0xDE && _ad == 0xAD && _be == 0xBE && _ef == 0xEF); \
+ } \
+MACRO_END
+
+// serialization: old
+
+#define ReadRegistered(r) r##_from(Read_byte())
+#define WriteRegistered(r, to, it) Write_byte(to, it.m_id)
+
+#define Read_byte() ReadByte()
+#define Write_byte(to, f) WriteByte(to, f)
+
+#if defined(CSQC)
+ int ReadByte();
+ void WriteByte(int to, int b)
+ {
+ assert(to == MSG_C2S);
+ string s = string_null;
+ yenc_single(b, s);
+ string tmp = strcat(g_buf, s);
+ strcpy(g_buf, tmp);
+ }
+ void WriteShort(int to, int b)
+ {
+ WriteByte(to, (b >> 8) & 0xFF);
+ WriteByte(to, b & 0xFF);
+ }
+#elif defined(SVQC)
+ int ReadByte()
+ {
+ int ret = -1;
+ ydec_single(g_buf, ret);
+ return ret;
+ }
+ int ReadShort()
+ {
+ return (ReadByte() << 8) | (ReadByte());
+ }
+ void WriteByte(int to, int b);
+#endif
+
+#define Read_int() ReadInt24_t()
+#define Write_int(to, f) WriteInt24_t(to, f)
-#ifndef MENUQC
+#define Read_float() ReadFloat()
+#define Write_float(to, f) WriteFloat(to, f)
+
+#define Read_string() ReadString()
+#define Write_string(to, f) WriteString(to, f)
+
+#ifdef GAMEQC
const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
- #if defined(CSQC)
- #define Read_float() ReadFloat()
- #define Read_int() ReadInt24_t()
- #define Read_string() ReadString()
- #elif defined(SVQC)
- #define Write_float(to, f) WriteFloat(to, f)
- #define Write_int(to, f) WriteInt24_t(to, f)
- #define Write_string(to, f) WriteString(to, f)
- #endif
-
#ifdef CSQC
- #define ReadRegistry(r) r##_from(ReadByte())
-
+ float servertime;
entity ReadCSQCEntity()
{
int f = ReadShort();
- if (f == 0) return world;
- return findfloat(world, entnum, f);
+ if (f == 0) return NULL;
+ return findfloat(NULL, entnum, f);
}
int ReadInt24_t()
{
v += ReadByte(); // note: this is unsigned
return v;
}
- vector ReadInt48_t()
- {
- vector v;
- v.x = ReadInt24_t();
- v.y = ReadInt24_t();
- v.z = 0;
- return v;
- }
- vector ReadInt72_t()
- {
- vector v;
- v.x = ReadInt24_t();
- v.y = ReadInt24_t();
- v.z = ReadInt24_t();
- return v;
- }
+ #define ReadInt48_t() vec2(ReadInt24_t(), ReadInt24_t())
+ #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t())
- int _ReadSByte;
+ noref int _ReadSByte;
#define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7)))
#define ReadFloat() ReadCoord()
- vector ReadVector() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = ReadFloat(); return v; }
- vector ReadVector2D() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = 0; return v; }
+ #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat())
+ #define ReadVector2D() vec2(ReadFloat(), ReadFloat())
float ReadApproxPastTime()
{
}
#else
- const int MSG_ENTITY = 5;
-
- #define WriteRegistry(r, to, it) WriteByte(to, it.m_id)
-
void WriteInt24_t(float dst, float val)
{
float v;
WriteInt24_t(dst, val.z);
}
- #define WriteFloat(to, f) WriteCoord(to, f)
- #define WriteVector(to, v) MACRO_BEGIN { WriteFloat(to, v.x); WriteFloat(to, v.y); WriteFloat(to, v.z); } MACRO_END
- #define WriteVector2D(to, v) MACRO_BEGIN { WriteFloat(to, v.x); WriteFloat(to, v.y); } MACRO_END
+ #define WriteFloat(to, f) WriteCoord(to, f)
+ #define WriteVector(to, v) MACRO_BEGIN WriteFloat(to, v.x); WriteFloat(to, v.y); WriteFloat(to, v.z); MACRO_END
+ #define WriteVector2D(to, v) MACRO_BEGIN WriteFloat(to, v.x); WriteFloat(to, v.y); MACRO_END
// this will use the value:
// 128
}
// allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
- #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname, statement) \
- entity varname; varname = msg_entity; \
- FOR_EACH_REALCLIENT(msg_entity) \
- if (msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \
- statement msg_entity = varname
- #define WRITESPECTATABLE_MSG_ONE(statement) \
- MACRO_BEGIN \
- { \
- WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement); \
- } MACRO_END
- #define WRITESPECTATABLE(msg, statement) \
- if (msg == MSG_ONE) WRITESPECTATABLE_MSG_ONE(statement); \
- else \
- statement float WRITESPECTATABLE_workaround = 0
+ #define WRITESPECTATABLE_MSG_ONE(to, statement) MACRO_BEGIN \
+ entity prev = msg_entity; \
+ entity dst = to; \
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), { \
+ if (it == dst || (it.classname == STR_SPECTATOR && it.enemy == dst)) \
+ { \
+ msg_entity = it; \
+ LAMBDA(statement); \
+ } \
+ }); \
+ msg_entity = prev; \
+ MACRO_END
#endif
#endif
-
-#endif