From a1372cef7e6aa5fc6f92777b990c8adbf4723d19 Mon Sep 17 00:00:00 2001 From: TimePath Date: Wed, 6 Sep 2017 20:41:59 +1000 Subject: [PATCH] Fix initialization of maxclients Closes #1948 --- qcsrc/client/main.qc | 32 +++++++++++++------------ qcsrc/lib/net.qh | 45 +++++++++++++++++++++++++++++------ qcsrc/lib/registry.qh | 2 +- qcsrc/lib/spawnfunc.qh | 6 +++++ qcsrc/lib/static.qh | 41 ++++++++++++++++++++----------- qcsrc/server/g_world.qc | 24 +++++++++++++------ qcsrc/server/miscfunctions.qc | 27 ++++++++++----------- qcsrc/server/utils.qh | 2 +- 8 files changed, 119 insertions(+), 60 deletions(-) diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index ae624ef85..374096f24 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -609,21 +609,23 @@ NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew) 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; diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 0a3dd8c66..b1f5326a9 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -4,19 +4,30 @@ #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) \ @@ -45,10 +56,11 @@ STATIC_INIT(RegisterTempEntities_renumber) { FOREACH(TempEntities, true, it.m_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) \ @@ -205,6 +217,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } 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 @@ -219,7 +232,11 @@ USING(Stream, int); #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) \ @@ -246,13 +263,27 @@ USING(Stream, int); #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 diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index 8ca07b43f..2d41e5d43 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -187,7 +187,7 @@ void Registry_send(string id, string hash); #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); \ diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index e30e565fd..4638bca2d 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -69,11 +69,17 @@ noref bool require_spawnfunc_prefix; 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) { \ diff --git a/qcsrc/lib/static.qh b/qcsrc/lib/static.qh index e2c5fd4f0..6f511fcec 100644 --- a/qcsrc/lib/static.qh +++ b/qcsrc/lib/static.qh @@ -1,14 +1,5 @@ #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; @@ -25,12 +16,34 @@ void profile(string s) 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() {} diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index c47952c5d..ce25dfa52 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -580,7 +580,15 @@ spawnfunc(__init_dedicated_server) 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(); @@ -597,6 +605,14 @@ void __init_dedicated_server_shutdown() { 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(); @@ -680,12 +696,6 @@ spawnfunc(worldspawn) 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(); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 7bb8e922d..1b631c4b7 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -933,24 +933,21 @@ void InitializeEntitiesRun() .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; } diff --git a/qcsrc/server/utils.qh b/qcsrc/server/utils.qh index da5c7a56a..4a603c022 100644 --- a/qcsrc/server/utils.qh +++ b/qcsrc/server/utils.qh @@ -41,7 +41,7 @@ const string STR_OBSERVER = "observer"; } \ } 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 -- 2.39.2