X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Flib%2Fnet.qh;h=47d6af7186144f1dfc41a157ffa7867c3358096a;hb=66189615a05cc66dd0ee544a2bbc841c4f426193;hp=9d0cb3a5ff7d3063eed5babc3b894ffc702aaf02;hpb=1245fb3e998e9ace1b301dbe4bfb84b55f25bdf4;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 9d0cb3a5f..47d6af718 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -1,19 +1,113 @@ -#ifndef NET_H -#define NET_H +#pragma once + +#include "registry.qh" +#include "sort.qh" +#include "yenc.qh" + +.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) + + +#ifdef CSQC + #define REGISTER_NET_TEMP(id) \ + NET_HANDLE(id, bool); \ + REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \ + { \ + 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_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(RegisterTempEntities_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; \ + } \ + REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \ + { \ + 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_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(RegisterLinkedEntities_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 + const int MSG_ENTITY = 5; + .int Version; // deprecated, use SendFlags .int SendFlags; - .bool(entity to, int sendflags)SendEntity; - .bool(entity this, entity to, int sendflags)SendEntity3; + .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); } + bool SendEntity_self(entity to, int sendflags) { SELFPARAM(); return this.SendEntity3(this, to, sendflags); } - void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags)sendfunc) + 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; @@ -30,14 +124,19 @@ if (dt) { e.nextthink = time + dt; - e.think = SUB_Remove; + e.think = SUB_Remove_self; } } + void Net_UnlinkEntity(entity e) + { + e.SendEntity = func_null; + } + .void() uncustomizeentityforclient; .float uncustomizeentityforclient_set; - void SetCustomizer(entity e, float(void)customizer, void(void)uncustomizer) + void SetCustomizer(entity e, float() customizer, void() uncustomizer) { e.customizeentityforclient = customizer; e.uncustomizeentityforclient = uncustomizer; @@ -46,123 +145,144 @@ void UncustomizeEntitiesRun() { - for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) - WITH(entity, self, e, e.uncustomizeentityforclient()); + SELFPARAM(); + FOREACH_ENTITY_FLOAT(uncustomizeentityforclient_set, true, WITH(entity, self, it, it.uncustomizeentityforclient())); } -#endif + STRING_ITERATOR(g_buf, string_null, 0); -#include "registry.qh" -#include "sort.qh" + int ReadByte(); -.string netname; -.int m_id; -.void(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\n", C2S); + return; + } + g_buf_i--; + int expected = strlen(buf); + if (g_buf_i > expected) LOG_WARNINGF("Underflow: %d", g_buf_i - expected); + if (g_buf_i < expected) LOG_WARNINGF("Overrflow: %d", expected - g_buf_i); + } + +#endif #ifdef CSQC - #define Net_Accept() \ - do \ - { \ - if (!this) this = spawn(); \ - } \ - while (0) + const int MSG_C2S = 0; + + #define Net_Accept(classname) \ + MACRO_BEGIN { \ + if (!this) this = new(classname); \ + } MACRO_END #define Net_Reject() \ - do \ - { \ + MACRO_BEGIN { \ if (this) remove(this); \ - } \ - while (0) -#else + } MACRO_END + + string g_buf; + + void Net_Flush() + { + if (g_buf == "") return; + localcmd("\ncmd c2s \"", strreplace("$", "$$", g_buf), "\"\n"); + strunzone(g_buf); + g_buf = string_null; + } +#endif + +#if defined(CSQC) #define WriteHeader(to, id) \ - do \ - { \ + MACRO_BEGIN { \ + WriteByte(to, NET_##id.m_id); \ + } MACRO_END +#elif defined(SVQC) + #define WriteHeader(to, id) \ + MACRO_BEGIN { \ if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \ WriteByte(to, NET_##id.m_id); \ - } \ - while (0) + } MACRO_END #endif -#ifdef CSQC - #define REGISTER_NET_LINKED(id, param) \ - void Ent_Read##id(entity this, param) \ - { \ - this = self; \ - } \ - REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) \ - { \ - this.netname = #id; \ - this.m_read = Ent_Read##id; \ - } \ - [[accumulate]] void Ent_Read##id(entity this, param) -#else - #define REGISTER_NET_LINKED(id, param) \ - const bool NET_##id##_istemp = false; \ - REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) \ - { \ - this.netname = #id; \ - } -#endif +#define ReadRegistered(r) r##_from(Read_byte()) +#define WriteRegistered(r, to, it) Write_byte(to, it.m_id) -REGISTRY(LinkedEntities, BIT(0)) -REGISTER_REGISTRY(RegisterLinkedEntities) -REGISTRY_SORT(LinkedEntities, netname, 0) -STATIC_INIT(RegisterLinkedEntities_renumber) -{ - for (int i = 0; i < LinkedEntities_COUNT; ++i) - LinkedEntities[i].m_id = 100 + i; -} +#define Read_byte() ReadByte() +#define Write_byte(to, f) WriteByte(to, f) -#ifdef CSQC - #define REGISTER_NET_TEMP(id, param) \ - void Net_Read##id(entity this, param); \ - REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) \ - { \ - this.netname = #id; \ - this.m_read = Net_Read##id; \ - } \ - void Net_Read##id(entity this, param) -#else - #define REGISTER_NET_TEMP(id, param) \ - const bool NET_##id##_istemp = true; \ - REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) \ - { \ - this.netname = #id; \ - } +#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); + if (g_buf) strunzone(g_buf); + g_buf = strzone(tmp); + } +#elif defined(SVQC) + int ReadByte() + { + int ret = -1; + ydec_single(g_buf, ret); + return ret; + } + void WriteByte(int to, int b); #endif -REGISTRY(TempEntities, BITS(3)) -REGISTER_REGISTRY(RegisterTempEntities) -REGISTRY_SORT(TempEntities, netname, 0) -STATIC_INIT(RegisterTempEntities_renumber) -{ - for (int i = 0; i < TempEntities_COUNT; ++i) - TempEntities[i].m_id = 115 + i; -} +#define Read_int() ReadInt24_t() +#define Write_int(to, f) WriteInt24_t(to, f) + +#define Read_float() ReadFloat() +#define Write_float(to, f) WriteFloat(to, f) + +#define Read_string() ReadString() +#define Write_string(to, f) WriteString(to, f) #ifndef MENUQC + const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; + #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + #ifdef CSQC + entity ReadCSQCEntity() + { + int f = ReadShort(); + if (f == 0) return NULL; + return findfloat(NULL, entnum, f); + } int ReadInt24_t() { int v = ReadShort() << 8; // note: this is signed 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() + #define ReadInt48_t() vec3(ReadInt24_t(), ReadInt24_t(), 0) + #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t()) + + int _ReadSByte; + #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7))) + #define ReadFloat() ReadCoord() + #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat()) + #define ReadVector2D() vec3(ReadFloat(), ReadFloat(), 0) + + float ReadApproxPastTime() { - vector v; - v.x = ReadInt24_t(); - v.y = ReadInt24_t(); - v.z = ReadInt24_t(); - return v; + float dt = ReadByte(); + + // map from range...PPROXPASTTIME_MAX / 256 + dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt)); + + return servertime - dt; } + #else void WriteInt24_t(float dst, float val) { @@ -181,7 +301,45 @@ STATIC_INIT(RegisterTempEntities_renumber) WriteInt24_t(dst, val.y); WriteInt24_t(dst, val.z); } - #endif -#endif + #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 + // accuracy near zero is APPROXPASTTIME_MAX/(256*255) + // accuracy at x is 1/derivative, i.e. + // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536 + void WriteApproxPastTime(float dst, float t) + { + float dt = time - t; + + // warning: this is approximate; do not resend when you don't have to! + // be careful with sendflags here! + // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75 + + // map to range... + dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt)); + + // round... + dt = rint(bound(0, dt, 255)); + + WriteByte(dst, dt); + } + + // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example) + #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