NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew)
{
make_pure(this);
- int i, j, b, f;
-
- int sf = ReadByte();
- if(sf & 1)
- {
- for(j = 0; j < maxclients; ++j)
- if(playerslots[j])
- playerslots[j].eliminated = 1;
- for(i = 1; i <= maxclients; i += 8)
- {
- f = ReadByte();
- for(j = i-1, b = 1; b < 256; b *= 2, ++j)
- if (!(f & b))
- if(playerslots[j])
- playerslots[j].eliminated = 0;
+ int sf; serialize(byte, 0, sf);
+ if (sf & 1) {
+ for (int j = 0; j < maxclients; ++j) {
+ if (playerslots[j]) {
+ playerslots[j].eliminated = 1;
+ }
+ }
+ for (int i = 1; i <= maxclients; i += 8) {
+ int f = 0;
+ serialize(byte, 0, f);
+ for (int b = 0; b < 8; ++b) {
+ if (f & BIT(b)) continue;
+ int j = i - 1 + b;
+ if (playerslots[j]) {
+ playerslots[j].eliminated = 0;
+ }
+ }
}
}
return true;
#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); \
- REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \
- { \
+ NET_GUARD(id); \
+ REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) { \
this.netname = #id; \
- this.m_read = Net_Handle_##id; \
+ this.m_read = Net_Handle_##id##_guard; \
}
#else
#define REGISTER_NET_TEMP(id) \
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; \
+ this.m_read = Net_Handle_##id##_guard; \
}
#else
#define REGISTER_NET_LINKED(id) \
MACRO_BEGIN { \
if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \
WriteByte(to, NET_##id.m_id); \
+ bool _net_valid = false; serialize_marker(to, _net_valid); \
} MACRO_END
#endif
#define stream_writing(stream) false
#endif
-#define serialize(T, stream, ...) serialize_##T(stream, __VA_ARGS__)
+#define serialize(T, stream, ...) \
+MACRO_BEGIN \
+ noref Stream _stream = stream; \
+ serialize_##T(_stream, __VA_ARGS__); \
+MACRO_END
#if defined(SVQC)
#define serialize_byte(stream, this) \
#endif
#define serialize_vector(stream, this) \
- MACRO_BEGIN \
+MACRO_BEGIN \
vector _v = this; \
serialize_float(stream, _v.x); \
serialize_float(stream, _v.y); \
serialize_float(stream, _v.z); \
this = _v; \
- MACRO_END
+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 EVAL_REGISTER_REGISTRY(...) __VA_ARGS__
#define REGISTER_REGISTRY_1(id) REGISTER_REGISTRY_2(id, #id)
#define REGISTER_REGISTRY_2(id, str) \
- ACCUMULATE_FUNCTION(__static_init, Register##id) \
+ ACCUMULATE_FUNCTION(__static_init_1, Register##id) \
CLASS(id##Registry, Object) \
ATTRIB(id##Registry, m_name, string, str); \
ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first); \
e.__spawnfunc_constructor(e);
}
+ noref bool __spawnfunc_first;
+
#define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION)
#define spawnfunc_2(id, whitelist) \
void __spawnfunc_##id(entity this); \
[[accumulate]] void spawnfunc_##id(entity this) \
{ \
+ if (!__spawnfunc_first) { \
+ __spawnfunc_first = true; \
+ static_init_early(); \
+ } \
bool dospawn = true; \
if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = false; } \
else if (__spawnfunc_expecting) { \
#pragma once
-void __static_init() {}
-#define static_init() CALL_ACCUMULATED_FUNCTION(__static_init)
-void __static_init_late() {}
-#define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_late)
-void __static_init_precache() {}
-#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_precache)
-void __shutdown() {}
-#define shutdownhooks() CALL_ACCUMULATED_FUNCTION(__shutdown)
-
#define GETTIME_REALTIME 1
#ifdef MENUQC
float(int tmr) _gettime = #67;
LOG_TRACEF("[%f] %s", rt - g_starttime, s);
}
-#define _STATIC_INIT(where, func) \
+#define _STATIC_INIT(func, where) \
[[accumulate]] void _static_##func() { profile(#func); } \
ACCUMULATE_FUNCTION(where, _static_##func) \
void _static_##func()
-#define STATIC_INIT(func) _STATIC_INIT(__static_init, func)
-#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late)
-#define PRECACHE(func) _STATIC_INIT(__static_init_precache, func##_precache)
-#define SHUTDOWN(func) _STATIC_INIT(__shutdown, func##_shutdown)
+/** before worldspawn */
+#define STATIC_INIT_EARLY(func) _STATIC_INIT(func##_0, __static_init_0)
+#define static_init_early() CALL_ACCUMULATED_FUNCTION(__static_init_0)
+void __static_init_0() {}
+
+/** during worldspawn */
+#define STATIC_INIT(func) _STATIC_INIT(func##_1, __static_init_1)
+#define static_init() CALL_ACCUMULATED_FUNCTION(__static_init_1)
+void __static_init_1() {}
+
+/** directly after STATIC_INIT */
+#define STATIC_INIT_LATE(func) _STATIC_INIT(func##_2, __static_init_2)
+#define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_2)
+void __static_init_2() {}
+
+/** directly after STATIC_INIT_LATE */
+#define PRECACHE(func) _STATIC_INIT(func##_3, __static_init_3)
+#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_3)
+void __static_init_3() {}
+
+/* other map entities spawn now */
+
+/** before shutdown */
+#define SHUTDOWN(func) _STATIC_INIT(func##_shutdown, __shutdown)
+#define shutdownhooks() CALL_ACCUMULATED_FUNCTION( __shutdown)
+void __shutdown() {}
e = new(info_player_deathmatch); // safeguard against player joining
- this.classname = "worldspawn"; // safeguard against various stuff ;)
+ // assign reflectively to avoid "assignment to world" warning
+ for (int i = 0, n = numentityfields(); i < n; ++i) {
+ string k = entityfieldname(i);
+ if (k == "classname") {
+ // safeguard against various stuff ;)
+ putentityfieldstring(i, this, "worldspawn");
+ break;
+ }
+ }
// needs to be done so early because of the constants they create
static_init();
MapInfo_Shutdown();
}
+STATIC_INIT_EARLY(maxclients)
+{
+ maxclients = 0;
+ for (entity head = nextent(NULL); head; head = nextent(head)) {
+ ++maxclients;
+ }
+}
+
void Map_MarkAsRecent(string m);
float world_already_spawned;
void Nagger_Init();
cvar_changes_init(); // do this very early now so it REALLY matches the server config
- maxclients = 0;
- for (entity head = nextent(NULL); head; head = nextent(head))
- {
- ++maxclients;
- }
-
// needs to be done so early because of the constants they create
static_init();
.float(entity) isEliminated;
bool EliminatedPlayers_SendEntity(entity this, entity to, float sendflags)
{
- float i, f, b;
- entity e;
- WriteHeader(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
- WriteByte(MSG_ENTITY, sendflags);
-
- if(sendflags & 1)
- {
- for(i = 1; i <= maxclients; i += 8)
- {
- for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
- {
- if(eliminatedPlayers.isEliminated(e))
- f |= b;
+ Stream out = MSG_ENTITY;
+ WriteHeader(out, ENT_CLIENT_ELIMINATEDPLAYERS);
+ serialize(byte, out, sendflags);
+ if (sendflags & 1) {
+ for (int i = 1; i <= maxclients; i += 8) {
+ int f = 0;
+ entity e = edict_num(i);
+ for (int b = 0; b < 8; ++b, e = nextent(e)) {
+ if (eliminatedPlayers.isEliminated(e)) {
+ f |= BIT(b);
+ }
}
- WriteByte(MSG_ENTITY, f);
+ serialize(byte, out, f);
}
}
-
return true;
}
} \
} MACRO_END
-#define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), body)
+#define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), LAMBDA(body))
// using the "inside out" version of knuth-fisher-yates shuffle
// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle