engine: reject mbf21 and shit24 wads. there is no way to know if it is safe to ignore...
[k8vavoom.git] / source / net / net_connection.cpp
blobdef80a27654bf04d1089bcfc79b0ec9c6bb66380
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../text.h"
28 #include "../psim/p_entity.h"
29 #include "../psim/p_player.h"
30 #include "../server/server.h"
31 #include "network.h"
32 #include "net_message.h"
35 static VCvarF net_dbg_send_loss("net_dbg_send_loss", "0", "Emulated sent packet loss percentage (randomly skip sending some packets).", CVAR_PreInit|CVAR_NoShadow);
36 static VCvarF net_dbg_recv_loss("net_dbg_recv_loss", "0", "Emulated received packet loss percentage (randomly skip sending some packets).", CVAR_PreInit|CVAR_NoShadow);
37 static VCvarB net_dbg_conn_show_dgrams("net_dbg_conn_show_dgrams", false, "Show datagram activity?", CVAR_NoShadow);
38 static VCvarB net_dbg_report_stats("net_dbg_report_stats", false, "Report some stats to the console?", CVAR_NoShadow);
40 static VCvarB net_dbg_conn_dump_tick("net_dbg_conn_dump_tick", false, "Dump tick/getmessage calls?", CVAR_NoShadow);
41 static VCvarB net_dbg_conn_dump_acks("net_dbg_conn_dump_acks", false, "Show ack info?", CVAR_NoShadow);
43 static VCvarB net_dbg_detailed_disconnect_stats("net_dbg_detailed_disconnect_stats", false, "Show channels on disconnect?", CVAR_NoShadow/*|CVAR_Archive*/);
45 VCvarB net_debug_dump_recv_packets("net_debug_dump_recv_packets", false, "Dump received packets?", CVAR_NoShadow);
47 //FIXME: autoadjust this according to average ping
48 static VCvarI net_speed_limit("net_speed_limit", "560000", "Network speed limit, bauds (rough).", CVAR_NoShadow/*|CVAR_Archive*/);
49 //static VCvarI net_speed_limit("net_speed_limit", "28000", "Network speed limit, bauds (rough).", CVAR_NoShadow/*|CVAR_Archive*/);
50 // the network layer will force packet sending after this interval
51 static VCvarI net_keepalive("net_keepalive", "60", "Network keepalive time, in milliseconds.", CVAR_NoShadow);
52 static VCvarF net_timeout("net_timeout", "4", "Network timeout, in seconds.", CVAR_NoShadow);
54 // level will be updated twice as more times as this, until i wrote client-side interpolation code
55 static VCvarF sv_fps("sv_fps", "35", "Server update frame rate (the server will use this to send updates to clients).", CVAR_NoShadow/*|CVAR_Archive*/);
58 //==========================================================================
60 // VNetConnection::VNetConnection
62 //==========================================================================
63 VNetConnection::VNetConnection (VSocketPublic *ANetCon, VNetContext *AContext, VBasePlayer *AOwner)
64 : NetCon(ANetCon)
65 , Driver(GNet)
66 , Context(AContext)
67 , Owner(AOwner)
68 , State(NETCON_Open) //FIXME: set to invalid?
69 , NeedsUpdate(false)
70 , AutoAck(false)
71 , LastLevelUpdateTime(0)
72 , LastThinkersUpdateTime(0)
73 , UpdateFrameCounter(0)
74 , UpdateFingerUId(0)
75 , ObjMapSent(false)
76 , LevelInfoSent(LNFO_UNSENT)
77 , Out(MAX_DGRAM_SIZE*8+128, false) // cannot grow
78 //, UpdatePvs(nullptr)
79 //, UpdatePvsSize(0)
80 , LeafPvs(nullptr)
82 OriginField = VEntity::StaticClass()->FindFieldChecked("Origin");
83 DataGameTimeField = VEntity::StaticClass()->FindFieldChecked("DataGameTime");
85 if (ANetCon) {
86 memcpy(AuthKey, ANetCon->AuthKey, VNetUtils::ChaCha20KeySize);
87 } else {
88 // this is for demos
89 memset(AuthKey, 0, VNetUtils::ChaCha20KeySize);
92 InRate = OutRate = 0;
93 InPackets = OutPackets = 0;
94 InMessages = OutMessages = 0;
95 InLoss = OutLoss = 0;
96 InOrder = OutOrder = 0;
97 PrevLag = AvgLag = 0;
99 LagAcc = 0;
100 InLossAcc = OutLossAcc = 0;
101 InPktAcc = OutPktAcc = 0;
102 InMsgAcc = OutMsgAcc = 0;
103 InByteAcc = OutByteAcc = 0;
104 InOrdAcc = 0;
105 LagCount = 0;
106 LastFrameStartTime = FrameDeltaTime = 0;
107 CumulativeTime = AverageFrameTime = 0;
108 StatsFrameCounter = 0;
110 memset((void *)OutLagTime, 0, sizeof(OutLagTime));
111 memset((void *)OutLagPacketId, 0, sizeof(OutLagPacketId));
113 InPacketId = 0;
114 OutPacketId = 1;
115 OutAckPacketId = 0;
116 OutLastWrittenAck = 0;
118 //FIXME: driver time?
119 LastReceiveTime = 0;
120 LastSendTime = 0;
121 LastTickTime = 0;
122 LastStatsUpdateTime = 0;
124 SaturaDepth = 0;
125 //LagAcc = 9999;
126 //PrevLag = 9999;
127 //AvgLag = 9999;
129 ForceFlush = false;
131 for (unsigned f = 0; f < (unsigned)MAX_CHANNELS; ++f) {
132 Channels[f] = nullptr;
133 OutReliable[f] = 0;
134 InReliable[f] = 0;
137 Out.Reinit(MAX_DGRAM_SIZE*8+128, false); // don't grow
139 ObjMap = new VNetObjectsMap(this);
141 // open local channels
142 CreateChannel(CHANNEL_Control, CHANIDX_General, true);
143 CreateChannel(CHANNEL_Player, CHANIDX_Player, true);
144 CreateChannel(CHANNEL_Level, CHANIDX_Level, true);
148 //==========================================================================
150 // VNetConnection::~VNetConnection
152 //==========================================================================
153 VNetConnection::~VNetConnection () {
154 GCon->Logf(NAME_DevNet, "Deleting connection to %s", *GetAddress());
155 // remove all open channels
156 for (auto &&chan : OpenChannels) {
157 chan->Connection = nullptr;
158 chan->Closing = true; // just in case
159 delete chan;
161 OpenChannels.clear();
162 ThinkerChannels.clear();
163 //GCon->Logf(NAME_DevNet, "...all channels deleted");
164 if (NetCon) {
165 //GCon->Logf(NAME_DevNet, "...deleting socket (%p)", NetCon);
166 NetCon->DumpStats();
167 delete NetCon;
169 NetCon = nullptr;
170 if (IsClient()) {
171 vensure(Context->ServerConnection == this);
172 Context->ServerConnection = nullptr;
173 } else {
174 Context->ClientConnections.Remove(this);
177 if (UpdatePvs) {
178 delete[] UpdatePvs;
179 UpdatePvs = nullptr;
182 if (ObjMap) {
183 delete ObjMap;
184 ObjMap = nullptr;
189 //==========================================================================
191 // VNetConnection::IsClient
193 //==========================================================================
194 bool VNetConnection::IsClient () noexcept {
195 return (Context ? Context->IsClient() : false);
199 //==========================================================================
201 // VNetConnection::IsServer
203 //==========================================================================
204 bool VNetConnection::IsServer () noexcept {
205 return (Context ? Context->IsServer() : false);
209 //==========================================================================
211 // VNetConnection::GetNetSpeed
213 //==========================================================================
214 int VNetConnection::GetNetSpeed () const noexcept {
215 if (AutoAck) return 100000000;
216 if (IsLocalConnection()) return 100000000;
217 return max2(2400, net_speed_limit.asInt());
221 //==========================================================================
223 // VNetConnection::IsKeepAliveExceeded
225 //==========================================================================
226 bool VNetConnection::IsKeepAliveExceeded () {
227 if (IsClosed()) return false; // no wai
228 if (AutoAck || IsLocalConnection()) return false;
229 const double kt = clampval(net_keepalive.asInt(), 5, 1000)/1000.0;
230 const double ctt = Driver->GetNetTime();
231 #if 1
232 return (ctt-LastSendTime > kt);
233 #else
234 if (ctt-LastSendTime > kt) return true;
235 // also, if we're getting no data from the other side for a long time, send keepalive too
236 if (LastReceiveTime < 1) LastReceiveTime = Driver->GetNetTime();
237 if (NetCon->LastMessageTime < 1) NetCon->LastMessageTime = Driver->GetNetTime();
238 const double tout = clampval(net_timeout.asFloat(), 0.4f, 20.0f);
239 return (Driver->GetNetTime()-LastReceiveTime >= tout/3.0);
240 #endif
244 //==========================================================================
246 // VNetConnection::IsTimeoutExceeded
248 //==========================================================================
249 bool VNetConnection::IsTimeoutExceeded () {
250 if (IsClosed()) return false; // no wai
251 // for bots and demo playback there's no other end that will send us the ACK
252 // so there's no need to check for timeouts
253 // `AutoAck == true` means "demo recording"
254 if (AutoAck || IsLocalConnection()) return false;
255 if (LastReceiveTime < 1) LastReceiveTime = Driver->GetNetTime();
256 if (NetCon->LastMessageTime < 1) NetCon->LastMessageTime = Driver->GetNetTime();
257 const double tout = clampval(net_timeout.asFloat(), 0.4f, 20.0f);
258 if (Driver->GetNetTime()-LastReceiveTime <= tout) {
259 #if 0
260 if (Driver->GetNetTime()-LastReceiveTime > 2.0) {
261 GCon->Logf(NAME_DevNet, "%s: *** TIMEOUT IS NEAR! gtime=%g; lastrecv=%g; delta=%g", *GetAddress(), Driver->GetNetTime(), LastReceiveTime, Driver->GetNetTime()-LastReceiveTime);
263 #endif
264 return false;
266 // timeout!
267 return true;
271 //==========================================================================
273 // VNetConnection::IsDangerousTimeout
275 //==========================================================================
276 bool VNetConnection::IsDangerousTimeout () {
277 if (IsClosed()) return false; // no wai
278 if (AutoAck || IsLocalConnection()) return false;
279 if (LastReceiveTime < 1) LastReceiveTime = Driver->GetNetTime();
280 if (NetCon->LastMessageTime < 1) NetCon->LastMessageTime = Driver->GetNetTime();
281 //const double tout = clampval(net_timeout.asFloat(), 0.4f, 20.0f);
282 return (Driver->GetNetTime()-LastReceiveTime >= 0.6);
286 //==========================================================================
288 // VNetConnection::Saturate
290 //==========================================================================
291 void VNetConnection::Saturate () noexcept {
292 SaturaDepth = -Out.GetNumBytes();
296 //==========================================================================
298 // VNetConnection::CanSendData
300 //==========================================================================
301 bool VNetConnection::CanSendData () const noexcept {
302 return (AutoAck || SaturaDepth+Out.GetNumBytes() <= 0);
306 //==========================================================================
308 // VNetConnection::IsLocalConnection
310 //==========================================================================
311 bool VNetConnection::IsLocalConnection () const noexcept {
312 // for demo playback NetCon can be `nullptr`
313 return (NetCon ? NetCon->IsLocalConnection() : true);
317 //==========================================================================
319 // VNetConnection::Close
321 // this marks the connection as closed, but doesn't destroy anything
323 //==========================================================================
324 void VNetConnection::Close () {
325 if (IsClosed()) return;
326 ShowTimeoutStats();
327 State = NETCON_Closed;
331 //==========================================================================
333 // VNetConnection::ShowTimeoutStats
335 //==========================================================================
336 void VNetConnection::ShowTimeoutStats () {
337 if (IsClosed()) return;
338 GCon->Logf(NAME_DevNet, "%s: ERROR: Channel timed out; time delta=%g; sent %d packets (%d datagrams), received %d packets (%d datagrams)",
339 *GetAddress(),
340 (Driver->GetNetTime()-LastReceiveTime)*1000.0f,
341 Driver->packetsSent, Driver->UnreliableMessagesSent,
342 Driver->packetsReceived, Driver->UnreliableMessagesReceived);
343 if (net_dbg_detailed_disconnect_stats) {
344 // show extended stats
345 GCon->Logf(NAME_DevNet, " connection saturation: %d", SaturaDepth);
346 GCon->Logf(NAME_DevNet, " active channels: %d", OpenChannels.length());
347 for (int f = 0; f < OpenChannels.length(); ++f) {
348 VChannel *chan = OpenChannels[f];
349 GCon->Logf(NAME_DevNet, " #%d:%s: %s, open is %s%s, saturation:%d", f, *chan->GetName(),
350 (chan->OpenedLocally ? "local" : "remote"),
351 (chan->OpenAcked ? "acked" : "not acked"),
352 (chan->Closing ? ", closing" : ""),
353 chan->IsQueueFull());
354 GCon->Logf(NAME_DevNet, " in packets : %d (estimated bits: %d)", chan->InListCount, chan->InListBits);
355 for (VMessageIn *msg = chan->InList; msg; msg = msg->Next) GCon->Logf(NAME_DevNet, " %s", *msg->toStringDbg());
356 GCon->Logf(NAME_DevNet, " out packets: %d (estimated bits: %d)", chan->OutListCount, chan->OutListBits);
357 for (VMessageOut *msg = chan->OutList; msg; msg = msg->Next) GCon->Logf(NAME_DevNet, " %s", *msg->toStringDbg());
363 //==========================================================================
365 // VNetConnection::CreateChannel
367 //==========================================================================
368 VChannel *VNetConnection::CreateChannel (vuint8 Type, vint32 AIndex, vuint8 OpenedLocally) {
369 // if channel index is -1, find a free channel slot
370 vint32 Index = AIndex;
371 if (Index < 0) {
372 if (Type == CHANNEL_ObjectMap) {
373 vassert(Channels[CHANIDX_ObjectMap] == nullptr);
374 Index = CHANIDX_ObjectMap;
375 } else {
376 vassert(Type == CHANNEL_Thinker);
377 for (int f = CHANIDX_ThinkersStart; f < MAX_CHANNELS; ++f) {
378 if (!Channels[f]) {
379 //if (Index < 0) Index = f;
380 //if (GenRandomU31()) {}
381 Index = f;
382 break;
385 if (Index < 0) return nullptr;
386 vassert(Index >= CHANIDX_ThinkersStart && Index < MAX_CHANNELS);
388 } else if (Type == CHANIDX_ThinkersStart) {
389 // this can happen in client (server requested channel id)
390 if (Index < CHANIDX_ThinkersStart || Index >= MAX_CHANNELS) Sys_Error("trying to allocate thinker channel with invalid index %d", Index);
391 if (Channels[Index]) Sys_Error("trying to allocate already allocated fixed thinker channel with index %d", Index);
393 vassert(Index >= 0 && Index < MAX_CHANNELS);
395 switch (Type) {
396 case CHANNEL_Control: vassert(Index == CHANIDX_General); return new VControlChannel(this, Index, OpenedLocally);
397 case CHANNEL_Level: vassert(Index == CHANIDX_Level); return new VLevelChannel(this, Index, OpenedLocally);
398 case CHANNEL_Player: vassert(Index == CHANIDX_Player); return new VPlayerChannel(this, Index, OpenedLocally);
399 case CHANNEL_Thinker: vassert(Index >= CHANIDX_ThinkersStart); return new VThinkerChannel(this, Index, OpenedLocally);
400 case CHANNEL_ObjectMap: vassert(Index == CHANIDX_ObjectMap); return new VObjectMapChannel(this, Index, OpenedLocally);
401 default: GCon->Logf(NAME_DevNet, "Unknown channel type %d for channel with index %d", Type, Index); return nullptr;
406 //==========================================================================
408 // VNetConnection::GetRawPacket
410 // used in demos
412 //==========================================================================
413 int VNetConnection::GetRawPacket (void *dest, size_t destSize) {
414 vensure(NetCon);
415 return NetCon->GetMessage(dest, destSize);
419 //==========================================================================
421 // VNetConnection::AckEverythingEverywhere
423 // WARNING! this can change channel list!
425 //==========================================================================
426 void VNetConnection::AckEverythingEverywhere () {
427 for (int f = OpenChannels.length()-1; f >= 0; --f) {
428 VChannel *chan = OpenChannels[f];
429 for (VMessageOut *outmsg = chan->OutList; outmsg; outmsg = outmsg->Next) {
430 outmsg->bReceivedAck = true;
431 if (outmsg->bOpen) chan->OpenAcked = true;
433 chan->ReceivedAcks(); // WARNING: channel may delete itself there!
438 //==========================================================================
440 // VNetConnection::GetMessage
442 // read and process incoming network datagram
443 // returns `false` if no message was processed
445 //==========================================================================
446 bool VNetConnection::GetMessage (bool asHearbeat) {
447 Driver->UpdateNetTime();
449 // check for message arrival
450 if (IsClosed()) {
451 // ack all outgoing packets, just in case (this is HACK!)
452 AckEverythingEverywhere();
453 return false;
456 vuint8 msgdata[MAX_DGRAM_SIZE+4];
457 // we need to indirect via `GetRawPacket()` here to allow demo playback
459 #if 0
460 // old code
461 vassert(NetCon);
462 const int msgsize = NetCon->GetMessage(msgdata, sizeof(msgdata));
463 #else
464 // new code
465 const int msgsize = GetRawPacket(msgdata, sizeof(msgdata));
466 #endif
467 if (msgsize == 0) return false;
468 if (msgsize < 0) { Close(); return false; }
470 InByteAcc += msgsize;
471 ++InPktAcc;
473 // received something
474 ++Driver->UnreliableMessagesReceived;
476 // decrypt packet
477 if (msgsize < 8) return true; // too small
479 const vuint32 PacketId =
480 ((vuint32)msgdata[0])|
481 (((vuint32)msgdata[1])<<8)|
482 (((vuint32)msgdata[2])<<16)|
483 (((vuint32)msgdata[3])<<24);
485 vuint32 nonce = PacketId<<1;
486 // client sets bit 0, so if we are server, use client nonce
487 if (IsServer()) nonce |= 1u;
489 // decrypt data
490 VNetUtils::ChaCha20XCrypt(AuthKey, nonce, msgdata+4, msgdata+4, (unsigned)(msgsize-4));
492 // check crc32
493 vuint32 crc32 =
494 ((vuint32)msgdata[4])|
495 (((vuint32)msgdata[5])<<8)|
496 (((vuint32)msgdata[6])<<16)|
497 (((vuint32)msgdata[7])<<24);
499 msgdata[4] = msgdata[5] = msgdata[6] = msgdata[7] = 0;
501 if (crc32 != VNetUtils::CRC32C(0, msgdata, (unsigned)msgsize)) {
502 GCon->Logf(NAME_DevNet, "%s: datagram packet contains invalid data", *GetAddress());
503 return true; // invalid crc, ignore this packet
506 // copy received data to packet stream
507 VBitStreamReader Packet;
508 // nope, this will be set by the server code
509 Packet.SetupFrom(msgdata, msgsize*8, true); // fix the length with the trailing bit
510 if (Packet.IsError()) {
511 GCon->Logf(NAME_DevNet, "%s: datagram packet is missing trailing bit", *GetAddress());
512 Close(); // close connection due to invalid data
513 return false;
515 if (net_dbg_conn_show_dgrams || net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, "%s: got datagram with a packet (%d bits of data)", *GetAddress(), Packet.GetNumBits());
517 if (!asHearbeat) {
518 ReceivedPacket(Packet);
519 } else {
520 LastReceiveTime = Driver->GetNetTime();
521 // in demo reader, `NetCon` is NULL
522 if (NetCon) NetCon->LastMessageTime = LastReceiveTime;
525 return true;
529 //==========================================================================
531 // VNetConnection::GetMessages
533 //==========================================================================
534 void VNetConnection::GetMessages (bool asHearbeat) {
535 if (IsClosed()) {
536 // ack all outgoing packets, just in case (this is HACK!)
537 AckEverythingEverywhere();
538 return;
541 if (net_dbg_conn_dump_tick) GCon->Logf(NAME_DevNet, "%s: GetMessages()", *GetAddress());
542 #if 0
543 if (!GetMessage(asHearbeat)) {
544 const struct timespec sleepTime = {0, 10000}; // 1 millisecond
545 nanosleep(&sleepTime, nullptr);
548 // we can have a lot of small packets queued, so process them all
549 // without this, everything will be delayed
550 if (!GetMessage(asHearbeat)) return;
552 //const double ctt = Sys_Time();
553 int waitCount = 2;
554 for (int f = 0; f < 256 && IsOpen(); ++f) {
555 if (!GetMessage(asHearbeat)) {
556 if (--waitCount == 0) break;
557 const struct timespec sleepTime = {0, 10000/2}; // 0.5 milliseconds
558 nanosleep(&sleepTime, nullptr);
559 continue;
561 //if (Sys_Time()-ctt >= 1.0/1000.0*2.5) break;
563 #else
564 // we can have a lot of small packets queued, so process them all
565 // without this, everything will be delayed
566 if (!GetMessage(asHearbeat)) return; // nothing's here
567 // spend no more than 2 msecs here
568 int count = 0;
569 const double ctt = Sys_Time();
570 for (;;) {
571 if (!GetMessage(asHearbeat)) break;
572 ++count;
573 if (count == 64) {
574 count = 0;
575 if (Sys_Time()-ctt >= 1.0/1000.0*2.0) break;
579 for (int f = 0; f < 128 && IsOpen(); ++f) {
580 if (!GetMessage(asHearbeat)) break;
583 #endif
587 //==========================================================================
589 // VNetConnection::PacketLost
591 //==========================================================================
592 void VNetConnection::PacketLost (vuint32 PacketId) {
593 for (int f = OpenChannels.length()-1; f >= 0; --f) {
594 VChannel *chan = OpenChannels[f];
595 if (!chan) continue; // just in case
597 if (net_dbg_conn_dump_acks) {
598 for (VMessageOut *Out = chan->OutList; Out; Out = Out->Next) {
599 // retransmit reliable messages in the lost packet
600 if (Out->PacketId == PacketId && !Out->bReceivedAck) {
601 vassert(Out->bReliable);
602 GCon->Logf(NAME_DevNet, "%s: LOST: %s", *chan->GetDebugName(), *Out->toStringDbg());
607 chan->PacketLost(PacketId);
612 //==========================================================================
614 // VNetConnection::ReceivedPacket
616 //==========================================================================
617 void VNetConnection::ReceivedPacket (VBitStreamReader &Packet) {
618 Driver->UpdateNetTime();
620 // simulate receiving loss
621 const float lossPrc = net_dbg_recv_loss.asFloat();
622 if (lossPrc > 0.0f && FRandomFull()*100.0f < lossPrc) {
623 //GCon->Logf(NAME_Debug, "%s: simulated packet loss!", *GetAddress());
624 return;
627 // read packet id
628 vuint32 PacketId = 0;
629 Packet << PacketId;
630 if (Packet.IsError()) {
631 GCon->Logf(NAME_DevNet, "%s: got invalid packet, connection dropped", *GetAddress());
632 Close();
633 return;
636 // get crc32
637 vuint32 crc32 = 0xffffffffU;
638 Packet << crc32;
639 if (Packet.IsError() || crc32 != 0) {
640 GCon->Logf(NAME_DevNet, "%s: got invalid packet, connection dropped", *GetAddress());
641 Close();
642 return;
645 // reset timeout timer
646 LastReceiveTime = Driver->GetNetTime();
647 // in demo reader, `NetCon` is NULL
648 if (NetCon) NetCon->LastMessageTime = LastReceiveTime;
649 ++Driver->packetsReceived;
651 if (net_debug_dump_recv_packets) GCon->Logf(NAME_DevNet, "***!!!*** Network Packet (bitcount=%d; pid=%u; inpid=%u)", Packet.GetNum(), PacketId, InPacketId);
653 // check packet ordering
654 if (PacketId > InPacketId) {
655 InLossAcc += PacketId-InPacketId-1;
656 InPacketId = PacketId;
657 } else {
658 ++InOrdAcc;
661 // ack it
662 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, "%s: got packet with pid=%u, sending ack", *GetAddress(), PacketId);
663 SendPacketAck(PacketId);
664 NeedsUpdate = true; // we got *any* activity, update the world!
666 if (Packet.AtEnd()) GCon->Logf(NAME_DevNet, "%s: got empty keepalive packet", *GetAddress());
668 vuint32 lastSeenAck = 0;
670 while (!Packet.AtEnd() && IsOpen()) {
671 if (net_debug_dump_recv_packets) GCon->Logf(NAME_DevNet, " parsing packet: %d bits eaten of %d", Packet.GetPos(), Packet.GetNumBits());
672 //INT StartPos = Reader.GetPosBits();
673 // ack?
674 if (Packet.ReadBit()) {
675 // yep, process it
676 vuint32 AckPacketId = 0;
677 if (lastSeenAck) AckPacketId = Packet.ReadUInt()+lastSeenAck; else Packet << STRM_INDEX_U(AckPacketId);
678 lastSeenAck = AckPacketId;
680 if (Packet.IsError()) {
681 // this is fatal
682 GCon->Logf(NAME_DevNet, "%s: missing ack id", *GetAddress());
683 ++Driver->shortPacketCount;
684 Close();
685 return;
688 // resend any old reliable packets that the receiver hasn't acknowledged
689 if (AckPacketId > OutAckPacketId) {
690 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, " ack: ackpid=%u; outackpid=%u (future)", AckPacketId, OutAckPacketId);
691 for (vuint32 LostPacketId = OutAckPacketId+1; LostPacketId < AckPacketId; ++LostPacketId, ++OutLossAcc) {
692 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, " ack: PACKETLOST: %u", LostPacketId);
693 PacketLost(LostPacketId);
695 OutAckPacketId = AckPacketId;
696 } else if (AckPacketId < OutAckPacketId) {
697 // this is harmless
698 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, " ack: ackpid=%u; outackpid=%u (outdated)", AckPacketId, OutAckPacketId);
699 } else {
700 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, " ack: ackpid=%u; outackpid=%u (current)", AckPacketId, OutAckPacketId);
703 // update lag statistics
704 const unsigned arrIndex = AckPacketId&(ARRAY_COUNT(OutLagPacketId)-1);
705 if (OutLagPacketId[arrIndex] == AckPacketId) {
706 const double NewLag = Driver->GetNetTime()-OutLagTime[arrIndex]-(FrameDeltaTime*0.5);
707 LagAcc += NewLag;
708 ++LagCount;
711 // forward the ack to the respective channel(s)
712 for (int f = OpenChannels.length()-1; f >= 0; --f) {
713 VChannel *chan = OpenChannels[f];
714 for (VMessageOut *outmsg = chan->OutList; outmsg; outmsg = outmsg->Next) {
715 if (outmsg->PacketId == AckPacketId) {
716 outmsg->bReceivedAck = true;
717 if (outmsg->bOpen) chan->OpenAcked = true;
718 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, "%s: ACKED: %s", *chan->GetDebugName(), *outmsg->toStringDbg());
721 chan->ReceivedAcks(); // WARNING: channel may delete itself there!
723 } else {
724 // normal message
726 // read message header
727 VMessageIn Msg(Packet);
728 if (Packet.IsError() || Msg.IsError()) {
729 // this is not fatal
730 GCon->Logf(NAME_DevNet, "%s: packet is missing message header", *GetAddress());
731 ++Driver->shortPacketCount;
732 return;
734 if (net_debug_dump_recv_packets) GCon->Logf(NAME_DevNet, " parsed packet message: %d bits eaten of %d", Packet.GetPos(), Packet.GetNumBits());
736 if (net_debug_dump_recv_packets) {
737 GCon->Logf(NAME_DevNet, " message (channel %u; chantype=%u; chanseq=%u; pktid=%u; open=%d; close=%d; reliable=%d) (len=%d; pos=%d; num=%d; left=%d)",
738 Msg.ChanIndex, Msg.ChanType, Msg.ChanSequence, PacketId, (int)Msg.bOpen, (int)Msg.bClose, (int)Msg.bReliable,
739 Msg.GetNumBits(), Packet.GetPos(), Packet.GetNum(), Packet.GetNum()-Packet.GetPos());
742 if (/*Msg.ChanIndex < 0 ||*/ Msg.ChanIndex >= MAX_CHANNELS) {
743 // this is not fatal
744 GCon->Logf(NAME_DevNet, "%s: got message for channel with invalid index %d", *GetAddress(), Msg.ChanIndex);
745 continue;
748 // ignore already processed reliable packets
749 if (Msg.bReliable && Msg.ChanSequence <= InReliable[Msg.ChanIndex]) {
750 // this is not fatal
751 if (net_debug_dump_recv_packets) GCon->Logf(NAME_DevNet, "%s: got outdated message (channel #%d; msgseq=%u; currseq=%u)", *GetAddress(), Msg.ChanIndex, Msg.ChanSequence, InReliable[Msg.ChanIndex]);
752 continue;
755 // get existing channel
756 VChannel *chan = Channels[Msg.ChanIndex];
758 // discard unreliable message to closed/inexisting channel
759 if (!Msg.bReliable && (!chan || chan->Closing)) {
760 // this is not fatal
761 GCon->Logf(NAME_DevNet, "%s: got unreliable message before open message (channel #%d; msgseq=%u; currseq=%u)", *GetAddress(), Msg.ChanIndex, Msg.ChanSequence, InReliable[Msg.ChanIndex]);
762 continue;
765 // create channel if necessary
766 if (!chan) {
767 if (Msg.ChanType == 0 || Msg.ChanType >= CHANNEL_MAX) {
768 // this is not fatal
769 GCon->Logf(NAME_DevNet, "%s: got message for channel #%d with invalid channel type %u (msgseq=%u; currseq=%u)", *GetAddress(), Msg.ChanIndex, Msg.ChanType, Msg.ChanSequence, InReliable[Msg.ChanIndex]);
770 continue;
773 // reliable (either open or later), so create new channel
774 chan = CreateChannel(Msg.ChanType, Msg.ChanIndex, false); // opened remotely
775 if (!chan) {
776 // cannot create channel, send close packet to notify the remote about it
777 // WTF?! what i was thinking? send close packet with what channel?!
778 // dunno what to do here
779 GCon->Logf(NAME_DevNet, "%s: cannot create channel #%d with type %u", *GetAddress(), Msg.ChanIndex, Msg.ChanType);
780 #if 0
781 VMessageOut refmsg(Msg.ChanType, Msg.ChanIndex, true/*reliable*/);
782 refmsg.bClose = true;
783 chan->SendMessage(&refmsg);
784 Flush();
785 delete chan;
786 vassert(Msg.ChanIndex != 0); // the thing that should not happen
787 #endif
788 continue;
792 if (Msg.bOpen) chan->OpenAcked = true;
794 // let channel process the message
795 chan->ReceivedMessage(Msg); // WARNING: channel may delete itself there!
796 ++InMsgAcc;
802 //==========================================================================
804 // VNetConnection::Prepare
806 // called before sending anything
808 //==========================================================================
809 void VNetConnection::Prepare (int addBits) {
810 vassert(addBits >= 0);
812 // flush if not enough space
813 if (CalcEstimatedByteSize(addBits) > MAX_DGRAM_SIZE) {
814 //GCon->Logf(NAME_DevNet, "*** %s: FLUSHING: %d (%d) (bytes=%d (%d); max=%d)", *GetAddress(), Out.GetNumBits(), addBits, CalcEstimatedByteSize(), CalcEstimatedByteSize(addBits), MAX_DGRAM_SIZE);
815 Flush();
816 //GCon->Logf(NAME_DevNet, "*** %s: FLUSHED: %d (%d) (bytes=%d (%d); max=%d)", *GetAddress(), Out.GetNumBits(), addBits, CalcEstimatedByteSize(), CalcEstimatedByteSize(addBits), MAX_DGRAM_SIZE);
817 } else {
818 //GCon->Logf(NAME_DevNet, "*** %s: collecting: %d (%d) (bytes=%d (%d); max=%d)", *GetAddress(), Out.GetNumBits(), addBits, CalcEstimatedByteSize(), CalcEstimatedByteSize(addBits), MAX_DGRAM_SIZE);
821 // put packet id for new packet
822 if (Out.GetNumBits() == 0) {
823 vuint32 crc32 = 0; // will be fixed later
824 Out << OutPacketId;
825 Out << crc32;
826 vassert(Out.GetNumBits() == MAX_PACKET_HEADER_BITS);
827 OutLastWrittenAck = 0;
830 // make sure there's enough space now
831 if (CalcEstimatedByteSize(addBits) > MAX_DGRAM_SIZE) {
832 Sys_Error("%s: cannot send packet of size %d+%d (newsize is %d, max size is %d)", *GetAddress(), Out.GetNumBits(), addBits, CalcEstimatedByteSize(addBits), MAX_DGRAM_SIZE);
837 //==========================================================================
839 // VNetConnection::PutOneAck
841 // returns argument for `Prepare` if putting the ack will
842 // overflow the output buffer.
843 // i.e. if it returned non-zero, ack is not put.
844 // if `forceSend` is `true`, flush the output buffer if
845 // necessary (always sends, returns 0).
847 //==========================================================================
848 int VNetConnection::PutOneAck (vuint32 ackId, bool forceSend) {
849 if (AutoAck) return 0;
850 if (Out.GetNumBits() == 0) Prepare(0); // put header
851 // we cannot ack packets from the future
852 vassert(ackId <= InPacketId);
853 // dunno, maybe `ResendAcks()` can call it with wrong ack?
854 vassert(ackId >= OutLastWrittenAck);
855 // convert ack to delta, it will take much less room (usually just one byte)
856 unsigned ackIdDelta = ackId-OutLastWrittenAck;
857 int outBits = (OutLastWrittenAck ? BitStreamCalcUIntBits(ackIdDelta) : STRM_INDEX_U_BYTES(ackIdDelta)*8)+1;
858 if (CalcEstimatedByteSize(outBits) > MAX_DGRAM_SIZE) {
859 if (!forceSend) return outBits;
860 Prepare(outBits);
861 // recheck, and reconvert
862 vassert(ackId <= OutPacketId);
863 vassert(OutLastWrittenAck == 0);
864 outBits = STRM_INDEX_U_BYTES(ackIdDelta)*8+1;
865 vassert(CalcEstimatedByteSize(outBits) <= MAX_DGRAM_SIZE);
866 ackIdDelta = ackId;
868 if (net_dbg_conn_dump_acks) GCon->Logf(NAME_DevNet, "%s: putting ack with pid=%u (delta=%u)", *GetAddress(), ackId, ackIdDelta);
869 Out.WriteBit(true); // ack flag
870 if (OutLastWrittenAck) Out.WriteUInt(ackIdDelta); else Out << STRM_INDEX_U(ackIdDelta);
871 vassert(CalcEstimatedByteSize(0) <= MAX_DGRAM_SIZE);
872 OutLastWrittenAck = ackId; // update current ack
873 return 0;
877 //==========================================================================
879 // cmpAcks
881 //==========================================================================
882 static int cmpAcks (const void *aa, const void *bb, void * /*ncptr*/) {
883 const vuint32 a = *(const vuint32 *)aa;
884 const vuint32 b = *(const vuint32 *)bb;
885 return
886 a < b ? -1 :
887 a > b ? 1 :
892 //==========================================================================
894 // VNetConnection::ResendAcks
896 //==========================================================================
897 void VNetConnection::ResendAcks () {
898 if (!AutoAck && AcksToResend.length() > 0) {
899 // make sure that the sequence is right
900 smsort_r(AcksToResend.ptr(), AcksToResend.length(), sizeof(AcksToResend[0]), &cmpAcks, nullptr);
901 if (OutLastWrittenAck > AcksToResend[0]) {
902 GCon->Logf(NAME_Warning, "NET: ResendAcks: OutLastWrittenAck=%u; FirstToResend=%u", OutLastWrittenAck, AcksToResend[0]); // something is VERY wrong here
903 Flush();
904 // `Flush()` can update this queue
905 smsort_r(AcksToResend.ptr(), AcksToResend.length(), sizeof(AcksToResend[0]), &cmpAcks, nullptr);
907 for (auto &&ack : AcksToResend) {
908 PutOneAckForced(ack);
911 AcksToResend.resetNoDtor();
915 //==========================================================================
917 // VNetConnection::SendPacketAck
919 //==========================================================================
920 void VNetConnection::SendPacketAck (vuint32 AckPacketId) {
921 if (AutoAck) { AcksToResend.resetNoDtor(); QueuedAcks.resetNoDtor(); return; }
922 // append current ack to resend queue, so it will be sent too
923 AcksToResend.append(AckPacketId);
924 // this call will clear resend queue
925 ResendAcks();
926 // queue current ack, so it will be sent one more time
927 QueuedAcks.append(AckPacketId);
931 //==========================================================================
933 // VNetConnection::SendMessage
935 //==========================================================================
936 void VNetConnection::SendMessage (VMessageOut *Msg) {
937 Driver->UpdateNetTime();
939 //if (net_dbg_conn_show_dgrams) GCon->Logf(NAME_DevNet, "%s: saving message to outbuf; out=%d; msg=%d", *GetAddress(), Out.GetNumBytes(), Msg.GetNumBytes());
940 vassert(Msg);
941 vassert(!Msg->IsError());
942 vassert(!Msg->bReceivedAck);
943 ++OutMsgAcc;
944 ForceFlush = true;
946 VBitStreamWriter hdr(MAX_MSG_SIZE_BITS+16, false); // no expand
947 Msg->PacketId = OutPacketId;
948 Msg->WriteHeader(hdr);
950 // make sure we have enough room for the message with that header
951 Prepare(hdr.GetNumBits()+Msg->GetNumBits());
953 // outgoing packet id may change
954 if (Msg->PacketId != OutPacketId) {
955 hdr.Reinit(MAX_MSG_SIZE_BITS+16, false); // no expand
956 Msg->PacketId = OutPacketId;
957 Msg->WriteHeader(hdr);
960 Msg->Time = Driver->GetNetTime();
962 Out.SerialiseBits(hdr.GetData(), hdr.GetNumBits());
963 Out.SerialiseBits(Msg->GetData(), Msg->GetNumBits());
965 // send it if it is full, why not
966 if (CalcEstimatedByteSize(0) == MAX_DGRAM_SIZE) {
967 Flush();
968 vassert(Out.GetNumBits() == 0);
973 //==========================================================================
975 // VNetConnection::Flush
977 //==========================================================================
978 void VNetConnection::Flush () {
979 Driver->UpdateNetTime();
981 // if the connection is closed, discard the data
982 if (IsClosed()) {
983 Out.Reinit(MAX_DGRAM_SIZE*8+128, false); // don't expand
984 LastSendTime = Driver->GetNetTime();
985 return;
988 if (Out.IsError()) {
989 GCon->Logf(NAME_DevNet, "!!! %s: out collector errored! bits=%d", *GetAddress(), Out.GetNumBits());
991 vassert(!Out.IsError());
992 ForceFlush = false;
994 // if there is any pending data to send, send it
995 if (Out.GetNumBits() || (!AutoAck && IsKeepAliveExceeded())) {
996 // if sending keepalive packet, still generate the header
997 if (Out.GetNumBits() == 0) {
998 Prepare(0); // write header
999 // this looks like keepalive packet, so resend last acks there too
1000 // this is to avoid client timeout on bad connection
1001 #if 1
1002 if (!AutoAck) {
1003 if (OutLastWrittenAck) GCon->Logf(NAME_Warning, "NET: Flush: OutLastWrittenAck=%u", OutLastWrittenAck); // something is VERY wrong here
1004 // only ticker can call this with empty accumulator, and in this case we have no acks to resend
1005 vassert(AcksToResend.length() == 0);
1006 if (QueuedAcks.length()) {
1007 // sort queued acks, just to be sure that the sequence is right
1008 smsort_r(QueuedAcks.ptr(), QueuedAcks.length(), sizeof(QueuedAcks[0]), &cmpAcks, nullptr);
1009 while (QueuedAcks.length()) {
1010 if (PutOneAck(QueuedAcks[0])) break; // no room
1011 QueuedAcks.removeAt(0);
1013 } else if (InPacketId) {
1014 // `InPacketId` is the last highest packet we've seen, so send ack for it, why not
1015 PutOneAck(InPacketId);
1017 //GCon->Logf(NAME_DevNet, "%s: created keepalive packet with acks; size is %d bits", *GetAddress(), Out.GetNumBits());
1019 #endif
1020 } else {
1022 #ifndef CLIENT
1023 if (!AutoAck && IsKeepAliveExceeded()) {
1024 GCon->Logf(NAME_DevNet, "%s: keex! bits=%d", *GetAddress(), Out.GetNumBits());
1026 #endif
1028 ++Driver->packetsSent;
1031 // add trailing bit so we can find out how many bits the message has
1032 Out.WriteTrailingBit();
1033 vassert(!Out.IsError());
1035 // send the message
1036 const float lossPrc = net_dbg_send_loss.asFloat();
1037 if (lossPrc <= 0.0f || FRandomFull()*100.0f >= lossPrc) {
1038 // fix crc, encrypt the message
1039 vuint8 *msgdata = Out.GetData();
1040 unsigned msgsize = Out.GetNumBytes();
1041 vassert(msgsize >= 4+4);
1043 vuint32 nonce =
1044 ((vuint32)msgdata[0])|
1045 (((vuint32)msgdata[1])<<8)|
1046 (((vuint32)msgdata[2])<<16)|
1047 (((vuint32)msgdata[3])<<24);
1049 nonce <<= 1;
1050 // client sets bit 0, so if we are client, use client nonce
1051 if (IsClient()) nonce |= 1u;
1053 vassert(msgdata[4] == 0);
1054 vassert(msgdata[5] == 0);
1055 vassert(msgdata[6] == 0);
1056 vassert(msgdata[7] == 0);
1058 // write crc32
1059 const vuint32 crc32 = VNetUtils::CRC32C(0, msgdata, msgsize);
1060 msgdata[4] = crc32&0xffU;
1061 msgdata[5] = (crc32>>8)&0xffU;
1062 msgdata[6] = (crc32>>16)&0xffU;
1063 msgdata[7] = (crc32>>24)&0xffU;
1065 // encrypt data
1066 VNetUtils::ChaCha20XCrypt(AuthKey, nonce, msgdata+4, msgdata+4, (unsigned)(msgsize-4));
1068 int res = NetCon->SendMessage(msgdata, (int)msgsize);
1069 if (res < 0) {
1070 GCon->Logf(NAME_DevNet, "%s: error sending datagram", *GetAddress());
1071 Close();
1072 return;
1074 if (res == 0) SaturaDepth = MAX_DGRAM_SIZE; // pause it a little
1075 if (net_dbg_conn_dump_acks) {
1076 vassert(Out.GetNumBytes() >= 4);
1077 //WARNING! invalid for big-endian!
1078 const vuint32 *pid = (const vuint32 *)Out.GetData();
1079 GCon->Logf(NAME_DevNet, "%s: sent packet with pid=%u (size: %d bytes)", *GetAddress(), *pid, Out.GetNumBytes());
1082 LastSendTime = Driver->GetNetTime();
1084 //if (!IsLocalConnection()) ++Driver->MessagesSent;
1085 ++Driver->UnreliableMessagesSent;
1087 const unsigned arrIndex = OutPacketId&(ARRAY_COUNT(OutLagPacketId)-1);
1088 OutLagPacketId[arrIndex] = OutPacketId;
1089 OutLagTime[arrIndex] = Driver->GetNetTime();
1090 ++OutPacketId;
1091 ++OutPktAcc;
1092 SaturaDepth += Out.GetNumBytes();
1093 OutByteAcc += Out.GetNumBytes();
1095 Out.Reinit(MAX_DGRAM_SIZE*8+128, false); // don't expand
1096 OutLastWrittenAck = 0; // just in case
1099 // move queued acks to resend queue
1100 // this way we will send acks twice, just in case they're lost
1101 // (first time ack was sent before it got into queued acks store)
1102 for (auto &&ack : QueuedAcks) AcksToResend.append(ack);
1103 QueuedAcks.resetNoDtor();
1107 //==========================================================================
1109 // VNetConnection::KeepaliveTick
1111 //==========================================================================
1112 void VNetConnection::KeepaliveTick () {
1113 GetMessages(true); // update counters
1114 Tick();
1118 //==========================================================================
1120 // VNetConnection::Tick
1122 //==========================================================================
1123 void VNetConnection::Tick () {
1124 Driver->UpdateNetTime();
1126 if (IsClosed()) {
1127 // ack all outgoing packets, just in case (this is HACK!)
1128 AckEverythingEverywhere();
1129 // don't stop here, though, let channels tick
1132 // for bots and demo playback there's no other end that will send us
1133 // the ACK so just mark all outgoing messages as ACK-ed
1134 // `AutoAck == true` means "demo recording"
1135 if (AutoAck) {
1136 LastReceiveTime = Driver->GetNetTime();
1137 for (int f = OpenChannels.length()-1; f >= 0; --f) {
1138 VChannel *chan = OpenChannels[f];
1139 for (VMessageOut *Msg = chan->OutList; Msg; Msg = Msg->Next) Msg->bReceivedAck = true;
1140 chan->OpenAcked = true;
1141 chan->ReceivedAcks();
1145 // get frame time
1146 double ctt = Sys_Time();
1147 FrameDeltaTime = ctt-LastFrameStartTime;
1148 LastFrameStartTime = ctt;
1149 CumulativeTime += FrameDeltaTime;
1150 ++StatsFrameCounter;
1152 if (CumulativeTime > 1.0f) {
1153 AverageFrameTime = CumulativeTime/StatsFrameCounter;
1154 CumulativeTime = 0;
1155 StatsFrameCounter = 0;
1158 ctt = Driver->GetNetTime();
1160 // update stats roughly once per second
1161 if (ctt-LastStatsUpdateTime > 1) {
1162 const double deltaTime = ctt-LastStatsUpdateTime;
1163 InRate = InByteAcc/deltaTime;
1164 OutRate = OutByteAcc/deltaTime;
1165 InPackets = InPktAcc/deltaTime;
1166 OutPackets = OutPktAcc/deltaTime;
1167 InMessages = InMsgAcc/deltaTime;
1168 OutMessages = OutMsgAcc/deltaTime;
1169 InOrder = InOrdAcc/deltaTime;
1170 InLoss = 100.0*InLossAcc/max2(InPackets+InLossAcc, 1.0);
1171 OutLoss = 100.0*OutLossAcc/max2(OutPackets, 1.0);
1172 if (LagCount) AvgLag = LagAcc/LagCount;
1173 PrevLag = AvgLag;
1175 // if in or out loss is more than... let's say 20, this is high packet loss, show something to the user
1177 NetLagChart[NetLagChartPos] = clampval((int)((PrevLag+1.2*(max2(InLoss, OutLoss)*0.01))*1000), 0, 1000);
1178 NetLagChartPos = (NetLagChartPos+1)%NETLAG_CHART_ITEMS;
1180 if (net_dbg_report_stats) {
1181 GCon->Logf("*** %s: lag:(%d,%d) %d; rate:%g/%g; packets:%g/%g; messages:%g/%g; order:%g/%g; loss:%g/%g", *GetAddress(),
1182 (int)(PrevLag*1000), (int)(AvgLag*1000), (int)((PrevLag+1.2*(max2(InLoss, OutLoss)*0.01))*1000),
1183 InRate, OutRate, InPackets, OutPackets, InMessages, OutMessages, InOrder, OutOrder, InLoss, OutLoss);
1186 // init counters
1187 LagAcc = 0;
1188 InByteAcc = 0;
1189 OutByteAcc = 0;
1190 InPktAcc = 0;
1191 OutPktAcc = 0;
1192 InMsgAcc = 0;
1193 OutMsgAcc = 0;
1194 InLossAcc = 0;
1195 OutLossAcc = 0;
1196 InOrdAcc = 0;
1197 LagCount = 0;
1198 LastStatsUpdateTime = ctt;
1201 // compute time passed since last update
1202 double DeltaTime = ctt-LastTickTime;
1203 LastTickTime = ctt;
1205 if (net_dbg_conn_dump_tick) {
1206 GCon->Logf(NAME_DevNet, "%s: tick: outbits=%d(ffl=%d); lastrecv=%g(%g); lastsend=%g(%g);", *GetAddress(),
1207 Out.GetNumBits(), (int)ForceFlush,
1208 LastReceiveTime, (Driver->GetNetTime()-LastReceiveTime)*1000,
1209 LastSendTime, (Driver->GetNetTime()-LastSendTime)*1000);
1212 // see if this connection has timed out
1213 if (IsTimeoutExceeded()) {
1214 Close();
1215 return;
1218 // tick the channels
1219 // channel should not delete itself in a ticker, but...
1220 // we have to do it from the first opened channel, because
1221 // of update priorities
1222 for (int f = 0; f < OpenChannels.length(); ++f) {
1223 VChannel *chan = OpenChannels[f];
1224 chan->Tick();
1225 // invariant
1226 vassert(OpenChannels[f] == chan);
1229 // if channel 0 has closed, mark the conection as closed
1230 if (!Channels[0] && (OutReliable[0]|InReliable[0])) {
1231 Close();
1232 return;
1235 ResendAcks();
1237 // update queued byte count
1238 // need to be here, because `Flush()` may saturate it
1239 double DeltaBytes = (double)GetNetSpeed()*DeltaTime;
1240 if (DeltaBytes > 0x1fffffff) DeltaBytes = 0x1fffffff;
1241 SaturaDepth -= (int)DeltaBytes;
1242 double AllowedLag = DeltaBytes*2;
1243 if (AllowedLag > 0x3fffffff) AllowedLag = 0x3fffffff;
1244 if (SaturaDepth < -AllowedLag) SaturaDepth = (int)(-AllowedLag);
1246 // also, flush if we have no room for more data in outgoing accumulator
1247 if (ForceFlush || IsKeepAliveExceeded() || CalcEstimatedByteSize() == MAX_DGRAM_SIZE) Flush();
1249 //GCon->Logf(NAME_DevNet, "%s: tick: SaturaDepth=%d; sdd=%d; dt=%g", *GetAddress(), SaturaDepth, SaturaDepth+Out.GetNumBytes(), DeltaTime*1000.0);
1253 //==========================================================================
1255 // VNetConnection::AbortChannel
1257 // this is called by channel send/recv methods on fatal queue overflow
1258 // you can `delete` channel here, as it is guaranteed that call to this
1259 // method is followed by `return`
1260 // you CAN call `chan->Close()` here
1262 //==========================================================================
1263 void VNetConnection::AbortChannel (VChannel *chan) {
1264 vassert(chan);
1265 // if this is some vital channel (control, player, level) -- close connection
1266 // otherwise, just close this channel, and let world updater deal with it
1267 if (!chan->IsThinker()) {
1268 GCon->Logf(NAME_DevNet, "%s: aborting the connection, because vital non-thinker channel %s is oversaturated!", *GetAddress(), *chan->GetDebugName());
1269 Close();
1270 return;
1272 VThinkerChannel *tc = (VThinkerChannel *)chan;
1273 if (tc->GetThinker() && (tc->GetThinker()->ThinkerFlags&VThinker::TF_AlwaysRelevant)) {
1274 GCon->Logf(NAME_DevNet, "%s: aborting the connection, because vital thinker channel %s is oversaturated!", *GetAddress(), *chan->GetDebugName());
1275 Close();
1276 return;
1278 // close this channel, and hope that nothing will go wrong
1279 GCon->Logf(NAME_DevNet, "%s: closing thinker channel %s due oversaturation", *GetAddress(), *chan->GetDebugName());
1280 chan->Close();
1284 //==========================================================================
1286 // VNetConnection::SendCommand
1288 //==========================================================================
1289 void VNetConnection::SendCommand (VStr Str) {
1290 Str = Str.xstrip();
1291 if (Str.length() == 0) return; // no, really
1292 if (Str.length() > 1200) {
1293 GCon->Logf(NAME_Error, "%s: sorry, cannot send command that long (%d bytes): [%s...]", *GetAddress(), Str.length(), *Str.RemoveColors().left(32));
1294 return;
1296 //GCon->Logf(NAME_DevNet, "%s: sending command: \"%s\"", *GetAddress(), *Str.quote());
1297 VMessageOut msg(GetGeneralChannel());
1298 msg << Str;
1299 GetGeneralChannel()->SendMessage(&msg);
1303 //==========================================================================
1305 // VNetConnection::SetupFatPVS
1307 //==========================================================================
1308 void VNetConnection::SetupFatPVS () {
1309 UpdatedSubsectors.reset();
1310 UpdatedSectors.reset();
1312 VLevel *Level = Context->GetLevel();
1313 if (!Level) return;
1315 //LeafPvs = Level->LeafPVS(Owner->MO->SubSector);
1316 LeafPvs = nullptr;
1318 // re-allocate PVS buffer if needed
1320 if (UpdatePvsSize != (Level->NumSubsectors+7)/8) {
1321 if (UpdatePvs) {
1322 delete[] UpdatePvs;
1323 UpdatePvs = nullptr;
1325 UpdatePvsSize = (Level->NumSubsectors+7)/8;
1326 UpdatePvs = new vuint8[UpdatePvsSize];
1330 // build view PVS using view clipper
1331 //memset(UpdatePvs, 0, UpdatePvsSize);
1332 //GCon->Logf("FATPVS: view=(%g,%g,%g)", Owner->ViewOrg.x, Owner->ViewOrg.y, Owner->ViewOrg.z);
1333 Clipper.ClearClipNodes(Owner->ViewOrg, Level);
1334 //Clipper.check2STextures = false;
1335 Clipper.RepSectors = (GetLevelChannel() ? GetLevelChannel()->Sectors : nullptr);
1337 if (Level->NumSubsectors > 1) {
1338 const float bbox[6] = { -99999.0f, -99999.0f, -99999.0f, +99999.0f, +99999.0f, +99999.0f };
1339 SetupPvsNode(Level, Level->NumNodes-1, bbox);
1340 } else if (Level->NumSubsectors == 1) {
1341 SetupPvsSubsector(Level, 0);
1346 //==========================================================================
1348 // VNetConnection::PvsMarkExtra
1350 //==========================================================================
1351 void VNetConnection::PvsMarkExtra (sector_t *sec) {
1352 if (sec->othersecFloor) PvsAddSector(sec->othersecFloor);
1353 if (sec->othersecCeiling) PvsAddSector(sec->othersecCeiling);
1354 if (sec->heightsec) PvsAddSector(sec->heightsec);
1355 for (sec_region_t *reg = sec->eregions->next; reg; reg = reg->next) {
1356 line_t *line = reg->extraline;
1357 if (!line) continue;
1358 if (line->frontsector) PvsAddSector(line->frontsector);
1359 if (line->backsector) PvsAddSector(line->backsector);
1364 //==========================================================================
1366 // VNetConnection::PvsAddSector
1368 //==========================================================================
1369 void VNetConnection::PvsAddSector (sector_t *sec) {
1370 VLevel *Level = Context->GetLevel();
1371 if (UpdatedSectors.put((vint32)(ptrdiff_t)(sec-&Level->Sectors[0]), true)) return;
1372 PvsMarkExtra(sec);
1376 //==========================================================================
1378 // VNetConnection::SetupPvsSubsector
1380 //==========================================================================
1381 void VNetConnection::SetupPvsSubsector (VLevel *Level, int subnum) {
1382 subsector_t *sub = &Level->Subsectors[subnum];
1383 if (sub->isAnyPObj()) return;
1384 if (LeafPvs && !(LeafPvs[subnum>>3]&(1<<(subnum&7)))) return;
1385 if (Clipper.ClipCheckSubsector(sub)) {
1386 //UpdatePvs[subnum>>3] |= 1<<(subnum&7);
1387 UpdatedSubsectors.put(subnum, true);
1388 PvsAddSector(sub->sector);
1390 Clipper.ClipAddSubsectorSegs(sub);
1394 //==========================================================================
1396 // VNetConnection::SetupPvsNode
1398 //==========================================================================
1399 void VNetConnection::SetupPvsNode (VLevel *Level, int BspNum, const float BBox[6]) {
1400 tailcall:
1401 #ifdef VV_CLIPPER_FULL_CHECK
1402 if (Clipper.ClipIsFull()) return;
1403 #endif
1404 if (!Clipper.ClipIsBBoxVisible(BBox)) return;
1406 // found a subsector?
1407 if (BSPIDX_IS_NON_LEAF(BspNum)) {
1408 const node_t *node = &Level->Nodes[BspNum];
1409 // decide which side the view point is on
1410 const unsigned side = (unsigned)node->PointOnSide(Owner->ViewOrg);
1411 // recursively divide front space
1412 SetupPvsNode(Level, node->children[side], node->bbox[side]);
1413 // possibly divide back space
1414 //if (!Clipper.ClipIsBBoxVisible(node->bbox[side^1])) return;
1415 //return SetupPvsNode(node->children[side^1], node->bbox[side^1]);
1416 BspNum = node->children[1u^side];
1417 BBox = node->bbox[1u^side];
1418 goto tailcall;
1419 } else {
1420 const int subnum = (int)BSPIDX_LEAF_SUBSECTOR(BspNum);
1421 return SetupPvsSubsector(Level, subnum);
1426 //==========================================================================
1428 // VNetConnection::CheckFatPVS
1430 //==========================================================================
1431 bool VNetConnection::CheckFatPVS (const subsector_t *Subsector) {
1432 VLevel *Level = Context->GetLevel();
1433 if (!Level) return 0;
1434 //return true; //k8: this returns "always visible" for sector: more data, no door glitches
1435 return UpdatedSubsectors.has((vint32)(ptrdiff_t)(Subsector-&Level->Subsectors[0]));
1437 int ss = (int)(ptrdiff_t)(Subsector-Context->GetLevel()->Subsectors);
1438 return UpdatePvs[ss/8]&(1<<(ss&7));
1443 //==========================================================================
1445 // VNetConnection::SecCheckFatPVS
1447 //==========================================================================
1448 bool VNetConnection::SecCheckFatPVS (const sector_t *Sec) {
1449 VLevel *Level = Context->GetLevel();
1450 if (!Level) return false;
1452 for (subsector_t *Sub = Sec->subsectors; Sub; Sub = Sub->seclink) {
1453 if (CheckFatPVS(Sub)) return true;
1455 return false;
1457 return UpdatedSectors.has((vint32)(ptrdiff_t)(Sec-&Level->Sectors[0]));
1461 //==========================================================================
1463 // VNetConnection::IsRelevant
1465 //==========================================================================
1466 bool VNetConnection::IsRelevant (VThinker *th) {
1467 if (th->IsGoingToDie()) return false; // anyway
1468 if (th->ThinkerFlags&VThinker::TF_AlwaysRelevant) return true; // always
1469 if (th->ThinkerFlags&VThinker::TF_ServerSideOnly) return false; // never
1470 // check if this thinker was detached
1471 if (DetachedThinkers.has(th)) return false;
1472 VEntity *Ent = Cast<VEntity>(th);
1473 if (!Ent) return false;
1474 if (Ent == Owner->MO || Ent->GetTopOwner() == Owner->MO) return true; // inventory
1475 if (!Ent->Sector) return false; // just in case
1476 if (Ent->EntityFlags&(VEntity::EF_NoSector|VEntity::EF_Invisible)) return false;
1477 // if we're in coop, always transmit other players (we need them for the automap)
1478 if (!svs.deathmatch && Ent->IsPlayer() && !Ent->IsRealCorpse() &&
1479 Ent->Player && Ent->Player->MO == Ent && (Ent->Player->PlayerFlags&VBasePlayer::PF_Spawned))
1481 //GCon->Logf(NAME_DevNet, "%s: client #%d", *GetAddress(), Ent->Player->ClientNum);
1482 return true;
1484 //if (Ent->RemoteRole == ROLE_Authority) return false; // this should not end here
1485 if (CheckFatPVS(Ent->BaseSubSector)) return true;
1486 // invisible, but simulated are still relevant
1487 return SimulatedThinkers.has(Ent);
1491 //==========================================================================
1493 // VNetConnection::IsAlwaysRelevant
1495 // inventory is always relevant too
1496 // doesn't check PVS
1497 // call after `IsRelevant()` returned `true`,
1498 // because this does much less checks
1500 //==========================================================================
1501 bool VNetConnection::IsAlwaysRelevant (VThinker *th) {
1502 if (th->ThinkerFlags&VThinker::TF_AlwaysRelevant) return true;
1503 VEntity *Ent = Cast<VEntity>(th);
1504 if (!Ent) return false;
1505 // we, inventory, or other player
1506 return (Ent == Owner->MO || Ent->GetTopOwner() == Owner->MO || Ent->IsPlayer());
1510 //==========================================================================
1512 // VNetConnection::ThinkerSortInfo::ThinkerSortInfo
1514 //==========================================================================
1515 VNetConnection::ThinkerSortInfo::ThinkerSortInfo (VBasePlayer *Owner) noexcept {
1516 MO = Owner->MO;
1517 ViewOrg = Owner->ViewOrg;
1518 ViewAngles = Owner->ViewAngles;
1519 TVec fwd;
1520 #if 0
1521 fwd = AngleVector(ViewAngles);
1522 //GCon->Logf(NAME_DevNet, "angles=(%g,%g,%g); fwd=(%g,%g,%g)", ViewAngles.yaw, ViewAngles.pitch, ViewAngles.roll, fwd.x, fwd.y, fwd.z);
1523 ViewPlane.SetPointNormal3DSafe(ViewOrg, fwd);
1524 #else
1525 fwd = AngleVectorYaw(ViewAngles.yaw);
1526 //GCon->Logf(NAME_Debug, "yaw=%g; fwd=(%g,%g,%g)", ViewAngles.yaw, fwd.x, fwd.y, fwd.z);
1527 //ViewPlane.SetPointDirXY(ViewOrg+fwd*128, fwd);
1528 //ViewPlane.SetPointNormal3DSafe(ViewOrg+fwd*2048, fwd);
1529 // move it back a little, so we can avoid distance check in the other code
1530 ViewPlane.SetPointNormal3DSafe(ViewOrg-fwd*128, fwd);
1531 #endif
1535 //==========================================================================
1537 // cmpPendingThinkers
1538 // cmpPendingGoreEnts
1540 //==========================================================================
1541 extern "C" {
1542 static int cmpPendingThinkers (const void *aa, const void *bb, void *ncptr) {
1543 if (aa == bb) return 0;
1544 const VThinker *ta = *(const VThinker **)aa;
1545 const VThinker *tb = *(const VThinker **)bb;
1546 // "always relewant" first (xor flags to see if they're equal)
1547 if (((ta->ThinkerFlags|tb->ThinkerFlags)&VThinker::TF_AlwaysRelevant) &&
1548 ((ta->ThinkerFlags^tb->ThinkerFlags)&VThinker::TF_AlwaysRelevant))
1550 // only one thinker is "always relevant" here
1551 vassert((ta->ThinkerFlags&VThinker::TF_AlwaysRelevant) != (tb->ThinkerFlags&VThinker::TF_AlwaysRelevant));
1552 return (ta->ThinkerFlags&VThinker::TF_AlwaysRelevant ? -1 : 1);
1554 // entities always wins
1555 if (!ta->IsA(VEntity::StaticClass())) {
1556 // a is not an entity
1557 if (tb->IsA(VEntity::StaticClass())) return 1; // b should come first, a > b
1558 // both aren't entities, sort by object id
1559 if (ta->GetUniqueId() < tb->GetUniqueId()) return -1;
1560 if (ta->GetUniqueId() > tb->GetUniqueId()) return 1;
1561 return 0;
1562 } else if (!tb->IsA(VEntity::StaticClass())) {
1563 // a is entity, b is not; a should come first (a < b)
1564 return -1;
1566 // both are entities
1567 const VNetConnection::ThinkerSortInfo *snfo = (const VNetConnection::ThinkerSortInfo *)ncptr;
1568 /*const*/ VEntity *ea = (/*const*/ VEntity *)ta;
1569 /*const*/ VEntity *eb = (/*const*/ VEntity *)tb;
1571 // player MO always first
1572 if (ea == snfo->MO) {
1573 vassert(eb != snfo->MO);
1574 return -1;
1576 if (eb == snfo->MO) {
1577 vassert(ea != snfo->MO);
1578 return 1;
1581 // inventory should come first
1582 if (ea->GetTopOwner() == snfo->MO) {
1583 // first is in inventory, check second
1584 if (eb->GetTopOwner() != snfo->MO) return -1; // a should come first, a < b
1585 // both are inventories, sort by unique it
1586 if (ta->GetUniqueId() < tb->GetUniqueId()) return -1;
1587 if (ta->GetUniqueId() > tb->GetUniqueId()) return 1;
1588 return 0;
1589 } else {
1590 // first is not in inventory, check second
1591 if (eb->GetTopOwner() == snfo->MO) return 1; // b should come first, a > b
1592 // neither is in inventory, use type/distance sort
1595 // type sorting
1596 //TODO: pickups!
1597 if (((ea->EntityFlags|eb->EntityFlags)&VEntity::EF_IsPlayer)) {
1598 // at least one is player
1599 if ((ea->EntityFlags^eb->EntityFlags)&VEntity::EF_IsPlayer) {
1600 // one is player
1601 return (ea->IsPlayer() ? -1 : 1);
1605 // we moved our plane away, no need to check any distance here
1606 // prefer entities which are before our eyes
1607 const int sidea = snfo->ViewPlane.PointOnSide(ea->Origin);
1608 const int sideb = snfo->ViewPlane.PointOnSide(eb->Origin);
1609 if (sidea^sideb) {
1610 // different sides, prefer one that is before the camera
1611 return (sideb ? -1 : 1); // if b is behind our back, a is first (a < b), otherwise b is first (a > b)
1614 // monsters
1615 if (((ea->FlagsEx|eb->FlagsEx)&VEntity::EFEX_Monster)) {
1616 // at least one is monster
1617 if (((ea->FlagsEx|eb->FlagsEx)^VEntity::EFEX_Monster)) {
1618 // one is monster
1619 return (ea->IsMonster() ? -1 : 1);
1622 // projectiles
1623 if (((ea->EntityFlags|eb->EntityFlags)&VEntity::EF_Missile)) {
1624 // at least one is missile
1625 if ((ea->EntityFlags^eb->EntityFlags)&VEntity::EF_Missile) {
1626 // one is missile
1627 return (ea->IsPlayer() ? -1 : 1);
1630 // solid things
1631 if (((ea->EntityFlags|eb->EntityFlags)&VEntity::EF_Solid)) {
1632 // at least one is solid
1633 if ((ea->EntityFlags^eb->EntityFlags)&VEntity::EF_Solid) {
1634 // one is solid
1635 return (ea->IsPlayer() ? -1 : 1);
1639 // last
1641 // no interaction
1642 if (((ea->FlagsEx|eb->FlagsEx)&VEntity::EFEX_NoInteraction)) {
1643 // at least one is "no interaction" (always last)
1644 if (((ea->FlagsEx|eb->FlagsEx)^VEntity::EFEX_NoInteraction)) {
1645 // one is "no interaction" (always last)
1646 return (ea->IsMonster() ? 1 : -1);
1649 // pseudocorpse
1650 if (((ea->FlagsEx|eb->FlagsEx)&VEntity::EFEX_PseudoCorpse)) {
1651 // at least one is pseudocorpse (corpse decoration, always last)
1652 if (((ea->FlagsEx|eb->FlagsEx)^VEntity::EFEX_PseudoCorpse)) {
1653 // one is pseudocorpse (corpse decoration, always last)
1654 return (ea->IsMonster() ? 1 : -1);
1657 // corpse
1658 if (((ea->EntityFlags|eb->EntityFlags)&VEntity::EF_Corpse)) {
1659 // at least one is corpse
1660 if ((ea->EntityFlags^eb->EntityFlags)&VEntity::EF_Corpse) {
1661 // one is corpse (corpses are always last)
1662 return (ea->IsPlayer() ? 1 : -1);
1666 // the one that is closer to the view origin should come first
1667 const float distaSq = (ea->Origin-snfo->ViewOrg).length2DSquared();
1668 const float distbSq = (eb->Origin-snfo->ViewOrg).length2DSquared();
1669 if (distaSq < distbSq) return -1;
1670 if (distaSq > distbSq) return 1;
1672 // by unique id
1673 if (ta->GetUniqueId() < tb->GetUniqueId()) return -1;
1674 if (ta->GetUniqueId() > tb->GetUniqueId()) return 1;
1675 return 0;
1678 static int cmpPendingGoreEnts (const void *aa, const void *bb, void *ncptr) {
1679 if (aa == bb) return 0;
1680 const VEntity *ea = *(const VEntity **)aa;
1681 const VEntity *eb = *(const VEntity **)bb;
1682 const VNetConnection::ThinkerSortInfo *snfo = (const VNetConnection::ThinkerSortInfo *)ncptr;
1683 // check sides
1684 const int sidea = snfo->ViewPlane.PointOnSide(ea->Origin);
1685 const int sideb = snfo->ViewPlane.PointOnSide(eb->Origin);
1686 if (sidea^sideb) {
1687 // different sides, prefer one that is before the camera
1688 return (sideb ? -1 : 1); // if b is behind our back, a is first (a < b), otherwise b is first (a > b)
1690 // the one that is closer to the view origin should come first
1691 const float distaSq = (ea->Origin-snfo->ViewOrg).length2DSquared();
1692 const float distbSq = (eb->Origin-snfo->ViewOrg).length2DSquared();
1693 if (distaSq < distbSq) return -1;
1694 if (distaSq > distbSq) return 1;
1695 // by unique id
1696 if (ea->GetUniqueId() < eb->GetUniqueId()) return -1;
1697 if (ea->GetUniqueId() > eb->GetUniqueId()) return 1;
1698 return 0;
1703 //==========================================================================
1705 // VNetConnection::CollectAndSortAliveThinkerChans
1707 //==========================================================================
1708 void VNetConnection::CollectAndSortAliveThinkerChans (ThinkerSortInfo *snfo) {
1709 AliveThinkerChans.resetNoDtor();
1710 for (auto &&it : ThinkerChannels.first()) {
1711 VChannel *chan = it.getValue();
1712 if (!chan || !chan->IsThinker() || chan->Closing) continue;
1713 VThinkerChannel *tc = (VThinkerChannel *)chan;
1714 vassert(tc->GetThinker() == it.getKey());
1715 // never evict "always relevant" ones
1716 if (tc->GetThinker()->ThinkerFlags&VThinker::TF_AlwaysRelevant) continue;
1717 VEntity *ent = Cast<VEntity>(it.getKey());
1718 if (!ent) continue;
1719 // never evict players or our own MO
1720 if (ent == Owner->MO || ent->IsPlayer()) continue;
1721 AliveThinkerChans.append(tc->GetThinker());
1723 static_assert(sizeof(AliveThinkerChans[0]) == sizeof(VThinker *), "wtf?!");
1724 // sort them
1725 smsort_r(AliveThinkerChans.ptr(), AliveThinkerChans.length(), sizeof(AliveThinkerChans[0]), &cmpPendingThinkers, (void *)snfo);
1729 //==========================================================================
1731 // VNetConnection::UpdateThinkers
1733 //==========================================================================
1734 void VNetConnection::UpdateThinkers () {
1735 PendingThinkers.resetNoDtor();
1736 PendingGoreEnts.resetNoDtor();
1737 AliveGoreChans.resetNoDtor();
1739 ThinkerSortInfo snfo(Owner);
1741 // use counter trick to mark updated channels
1742 if ((++UpdateFrameCounter) == 0) {
1743 // reset all counters
1744 UpdateFrameCounter = 1;
1745 for (auto &&chan : OpenChannels) {
1746 if (chan->IsThinker()) ((VThinkerChannel *)chan)->LastUpdateFrame = 0;
1751 note: updating the closest object may not be the best strategy.
1752 if our channel is very close to the saturation, we may never update anything that
1753 is far away. basically, we'll be able to update only one or two thinkers, and each
1754 time the same. the game is barely playable in this case anyway, but it still may
1755 be better to "shuffle" things a little based on the last successfull update time.
1756 that is, we may put a "finger" with entity uid, and always start with it after
1757 the players and non-entities.
1758 or we can make LevelInfo the top priority, our MO is next (for predictors), then
1759 other players, then finger.
1762 bool connCanSend = CanSendData();
1764 // send updates to the nearest objects first
1765 // collect all thinkers with channels in `PendingThinkers`, and sort
1766 // also, use finger to update the object
1767 vuint32 minUId = 0xffffffffu, nextUId = 0xffffffffu;
1768 for (auto &&it : ThinkerChannels.first()) {
1769 if (IsRelevant(it.getKey())) {
1770 const vuint32 currUId = it.getKey()->GetUniqueId();
1771 minUId = min2(minUId, currUId);
1772 // finger check
1773 VThinkerChannel *chan = it.getValue();
1774 if (currUId > UpdateFingerUId && nextUId > currUId) nextUId = currUId;
1775 if (UpdateFingerUId) {
1776 // update next uid
1777 if (UpdateFingerUId == currUId && chan->CanSendData()) {
1778 chan->Update();
1779 //GCon->Logf(NAME_DevNet, "%s: FINGER UPDATE", *chan->GetDebugName());
1780 continue;
1783 PendingThinkers.append(it.getKey());
1786 if (minUId == 0xffffffffu) minUId = 0;
1788 // move finger (if we have no next uid, use minimal)
1789 UpdateFingerUId = (nextUId != 0xffffffffu ? nextUId : minUId);
1791 // sort and update existing thinkers first
1792 if (PendingThinkers.length()) {
1793 //GCon->Logf(NAME_DevNet, "000: PendingThinkers.length()=%d", PendingThinkers.length());
1794 smsort_r(PendingThinkers.ptr(), PendingThinkers.length(), sizeof(PendingThinkers[0]), &cmpPendingThinkers, (void *)&snfo);
1795 for (auto &&th : PendingThinkers) {
1796 VThinkerChannel *chan = ThinkerChannels.findptr(th);
1797 if (!chan) continue;
1798 if (connCanSend && chan->CanSendData()) {
1799 chan->Update();
1800 continue;
1802 //GCon->Logf(NAME_DevNet, "000: cannot send PendingThinker! (%d) (%d)", SaturaDepth+Out.GetNumBytes(), chan->IsQueueFull());
1804 //GCon->Logf(NAME_DevNet, "000: PendingThinkers.length()=%d DONE", PendingThinkers.length());
1805 // don't send them twice, lol
1806 PendingThinkers.resetNoDtor();
1809 // if we are starving on channels, don't try to add entities behind our back
1810 const bool starvingOnChannels = (OpenChannels.length() > MAX_CHANNELS-32);
1812 // update mobjs in sight
1813 for (TThinkerIterator<VThinker> th(Context->GetLevel()); th; ++th) {
1814 if (!IsRelevant(*th)) continue;
1815 VThinkerChannel *chan = ThinkerChannels.findptr(*th);
1816 if (!chan) {
1817 //HACK! add gore entities as last ones
1818 if (VStr::startsWith(th->GetClass()->GetName(), "K8Gore_")) {
1819 vassert(th->IsA(VEntity::StaticClass()));
1820 // if we are starving on channels, don't try to add entities behind our back
1821 if (starvingOnChannels) {
1822 VEntity *ent = (VEntity *)(*th);
1823 if (snfo.ViewPlane.PointOnSide(ent->Origin)) continue;
1825 PendingGoreEnts.append((VEntity *)(*th));
1826 continue;
1828 // if we are starving on channels, don't try to add entities behind our back
1829 if (starvingOnChannels && !IsAlwaysRelevant(*th)) {
1830 VEntity *ent = Cast<VEntity>(*th);
1831 if (ent) {
1832 if (snfo.ViewPlane.PointOnSide(ent->Origin)) continue;
1835 // not a gore, remember this thinker; we'll sort them later
1836 PendingThinkers.append(*th);
1837 continue;
1839 // skip, if already updated
1840 if (chan->LastUpdateFrame == UpdateFrameCounter) continue;
1841 // update if we can, but still mark as updated if we cannot (so we won't drop this object)
1842 // TODO: replace unupdated object with new ones according to distance?
1843 if (chan->CanSendData()) {
1844 chan->Update();
1845 } else {
1846 // there's no need to mark for updates here, as client ack will do that for us
1847 //NeedsUpdate = true;
1848 chan->LastUpdateFrame = UpdateFrameCounter;
1852 // close entity channels that were not updated in this frame
1853 // WARNING! `Close()` should not delete a channel!
1854 int closedChanCount = 0; // counts closed thinker channels
1855 for (auto &&chan : OpenChannels) {
1856 if (chan->IsThinker()) {
1857 VThinkerChannel *tc = (VThinkerChannel *)chan;
1858 if (tc->LastUpdateFrame != UpdateFrameCounter) {
1859 if (!chan->Closing) {
1860 if (chan->CanSendClose()) {
1861 chan->Close();
1862 } else {
1863 // there's no need to mark for updates here, as client ack will do that for us
1864 //NeedsUpdate = true;
1868 if (tc->Closing) ++closedChanCount;
1869 // remember alive gore entities
1870 if (!tc->Closing && tc->GetThinker() && VStr::startsWith(tc->GetThinker()->GetClass()->GetName(), "K8Gore_")) {
1871 AliveGoreChans.append(chan->Index);
1876 // if we have some pending thinkers, open channels for them
1877 if (PendingThinkers.length()) {
1878 static_assert(sizeof(PendingThinkers[0]) == sizeof(VThinker *), "wtf?!");
1879 // sort them
1880 smsort_r(PendingThinkers.ptr(), PendingThinkers.length(), sizeof(PendingThinkers[0]), &cmpPendingThinkers, (void *)&snfo);
1882 // if we have not enough free channels, remove gore entities (they will be removed in the future)
1883 if (AliveGoreChans.length() && MAX_CHANNELS-OpenChannels.length() < PendingThinkers.length()) {
1884 int needChans = PendingThinkers.length()-(MAX_CHANNELS-OpenChannels.length());
1885 while (AliveGoreChans.length() && needChans-- > 0) {
1886 // pop index
1887 int idx = AliveGoreChans[AliveGoreChans.length()-1];
1888 AliveGoreChans.removeAt(AliveGoreChans.length()-1);
1889 vassert(idx >= CHANIDX_ThinkersStart && idx < MAX_CHANNELS);
1890 vassert(Channels[idx]);
1891 vassert(Channels[idx]->IsThinker());
1892 // close channel
1893 VThinkerChannel *tc = (VThinkerChannel *)Channels[idx];
1894 vassert(tc->GetThinker() && tc->GetThinker()->IsA(VEntity::StaticClass()));
1895 //PendingGoreEnts.append((VEntity *)(tc->GetThinker()));
1896 if (tc->CanSendClose()) {
1897 tc->Close();
1898 } else {
1899 // there's no need to mark for updates here, as client ack will do that for us
1900 //NeedsUpdate = true;
1902 if (tc->Closing) ++closedChanCount;
1906 bool thinkChansReady = false;
1907 int evictCount = 0;
1909 // append thinkers (do not check network, we need to do this unconditionally)
1910 for (auto &&th : PendingThinkers) {
1911 VThinkerChannel *chan = (VThinkerChannel *)CreateChannel(CHANNEL_Thinker, -1, true); // local channel
1912 if (!chan) {
1914 we have no room for new thinkers. this means that the map is *VERY* crowded (like nuts.wad ;-).
1915 now, check and close any channels further than the current pending thinker, so next update will be able
1916 to add our surroundings.
1917 there's no need to mark for updates here, as client ack will do that for us.
1919 if (!thinkChansReady) {
1920 thinkChansReady = true;
1921 CollectAndSortAliveThinkerChans(&snfo);
1923 if (AliveThinkerChans.length() == 0) break; // nobody to evict anymore
1924 // if this is "always relevant" thinker, do no more checks
1925 if (!(th->ThinkerFlags&VThinker::TF_AlwaysRelevant)) {
1926 // only for entities; it is guaranteed that all "always relevant" thinkers are already processed
1927 // also, it is guaranteed that entities are sorted by the distance
1928 VEntity *ent = Cast<VEntity>(th);
1929 if (!ent) continue;
1930 // do not try to stuff in entities that are behind the camera
1931 // this is not the best way to do it, but we are heavily starving on channels
1932 if (snfo.ViewPlane.PointOnSide(ent->Origin)) {
1933 // everything else should be behind our back, ignore
1935 int n = 0;
1936 while (PendingThinkers[n] != th) ++n;
1937 GCon->Logf(NAME_DevNet, "%s: stopped eviction at %d of %d due to back culling", *GetAddress(), n, PendingThinkers.length());
1939 break;
1941 const float maxDistSq = (ent->Origin-Owner->ViewOrg).length2DSquared();
1942 // evict something that is far away, to make room for this one
1943 ent = Cast<VEntity>(AliveThinkerChans[AliveThinkerChans.length()-1]);
1944 vassert(ent);
1945 const float distSq = (ent->Origin-Owner->ViewOrg).length2DSquared();
1946 // if this one is further than the nearest one, stop
1947 if (distSq <= maxDistSq) break;
1949 // evict furthest thinker
1950 VThinkerChannel *tc = ThinkerChannels.findptr(AliveThinkerChans[AliveThinkerChans.length()-1]);
1951 vassert(tc);
1952 tc->Close();
1953 AliveThinkerChans.removeAt(AliveThinkerChans.length()-1);
1954 ++evictCount;
1955 continue;
1957 chan->SetThinker(th);
1958 chan->Update();
1961 if (evictCount) GCon->Logf(NAME_DevNet, "%s: evicted %d thinker channels", *GetAddress(), evictCount);
1964 // append gore entities if we have any free slots
1965 if (PendingGoreEnts.length() && MAX_CHANNELS-OpenChannels.length() > 0) {
1966 // sort them
1967 smsort_r(PendingGoreEnts.ptr(), PendingGoreEnts.length(), sizeof(PendingGoreEnts[0]), &cmpPendingGoreEnts, (void *)&snfo);
1968 for (auto &&it : PendingGoreEnts) {
1969 if (!CanSendData()) {
1970 // there's no need to mark for updates here, as client ack will do that for us
1971 //NeedsUpdate = true;
1972 break;
1974 VThinkerChannel *chan = (VThinkerChannel *)CreateChannel(CHANNEL_Thinker, -1, true); // local channel
1975 if (!chan) break; // no room
1976 chan->SetThinker(it);
1977 chan->Update();
1983 //==========================================================================
1985 // VNetConnection::UpdateLevel
1987 //==========================================================================
1988 void VNetConnection::UpdateLevel () {
1989 if (IsClient()) return; // client never does this
1990 if (!GetLevelChannel()->Level) return;
1991 vassert(IsServer());
1993 if (!CanSendData()) return;
1995 bool wasAtLeastOneUpdate = false;
1997 const double ctt = Sys_Time();
1999 // update level
2000 if (LastLevelUpdateTime <= ctt) {
2001 LastLevelUpdateTime = ctt+1.0/(double)clampval(sv_fps.asFloat()*2.0f, 5.0f, 70.0f);
2002 //const bool oldUpdateFlag = NeedsUpdate;
2003 NeedsUpdate = false; // note that we already sent an update
2004 SetupFatPVS();
2005 GetLevelChannel()->Update();
2006 wasAtLeastOneUpdate = true;
2009 //GCon->Logf(NAME_DevNet, "CLIENT: LastThinkersUpdateTime=%g; ctt=%g; diff=%g", LastThinkersUpdateTime, ctt, ctt-LastThinkersUpdateTime);
2010 if (LastThinkersUpdateTime <= ctt) {
2011 //LastThinkersUpdateTime = ctt+1.0/(double)clampval(sv_fps.asFloat(), 5.0f, 70.0f);
2012 while (LastThinkersUpdateTime <= ctt) LastThinkersUpdateTime = ctt+1.0/(double)clampval(sv_fps.asFloat(), 5.0f, 70.0f);
2013 //GCon->Logf(NAME_DevNet, "CLIENT: next update timeout=%g", (LastThinkersUpdateTime-ctt)*1000.0);
2014 NeedsUpdate = false; // note that we already sent an update
2015 if (!wasAtLeastOneUpdate) SetupFatPVS();
2016 UpdateThinkers();
2020 NeedsUpdate = false; // note that we already sent an update
2021 SetupFatPVS();
2022 GetLevelChannel()->Update();
2023 UpdateThinkers();
2026 //GCon->Logf(NAME_DevNet, "%s: *** UpdatateLevel done, %d active channels", *GetAddress(), OpenChannels.length());
2030 //==========================================================================
2032 // VNetConnection::SendServerInfo
2034 //==========================================================================
2035 void VNetConnection::SendServerInfo () {
2036 if (!ObjMapSent) return;
2038 // do not send server info if we are in the intermission (let client hang)
2039 if (sv.intermission) return;
2041 if (GetLevelChannel()->Level != GLevel) {
2042 GCon->Logf(NAME_DevNet, "Preparing level for %s", *GetAddress());
2043 GetLevelChannel()->SetLevel(GLevel);
2044 LevelInfoSent = LNFO_UNSENT;
2047 if (LevelInfoSent == LNFO_SENT_COMPLETE) return;
2049 GCon->Logf(NAME_DevNet, "Sending server info for %s", *GetAddress());
2050 // this will load level on client side
2051 LevelInfoSent = (GetLevelChannel()->SendLevelData() ? LNFO_SENT_INPROGRESS : LNFO_SENT_COMPLETE);
2055 //==========================================================================
2057 // VNetConnection::LoadedNewLevel
2059 // always followed by `SendServerInfo()`
2061 //==========================================================================
2062 void VNetConnection::LoadedNewLevel () {
2063 GCon->Log(NAME_DevNet, "LEVEL RESET");
2064 LevelInfoSent = LNFO_UNSENT;
2065 GetLevelChannel()->SetLevel(GLevel);
2069 //==========================================================================
2071 // VNetConnection::ResetLevel
2073 //==========================================================================
2074 void VNetConnection::ResetLevel () {
2075 if (!GetLevelChannel()->Level) return;
2076 // close entity channels
2077 for (int f = OpenChannels.length()-1; f >= 0; --f) {
2078 VChannel *chan = OpenChannels[f];
2079 if (!chan) { OpenChannels.removeAt(f); continue; }
2080 if (chan->IsThinker()) {
2081 chan->Close(); //k8: should we close it, or we can simply kill it?
2083 // we're going to destroy the level anyway, so just drop all actors, nobody cares
2084 chan->Closing = true;
2085 chan->Connection = nullptr;
2086 delete chan;
2087 OpenChannels.removeAt(f);
2088 continue;
2092 //ThinkerChannels.reset();
2093 GetLevelChannel()->ResetLevel();
2094 GetPlayerChannel()->ResetLevel();