libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / net / net_channel.cpp
blob0558c17dd988b7ae85a81a94e666cfc3573df91a
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 "../psim/p_thinker.h"
28 #include "network.h"
29 #include "net_message.h"
31 extern VCvarB net_debug_dump_recv_packets;
32 static VCvarB net_debug_rpc("net_debug_rpc", false, "Dump RPC info?", CVAR_NoShadow);
35 //==========================================================================
37 // VChannel::VChannel
39 //==========================================================================
40 VChannel::VChannel (VNetConnection *AConnection, EChannelType AType, vint32 AIndex, bool AOpenedLocally)
41 : Connection(AConnection)
42 , Index(AIndex)
43 , Type(AType)
44 , OpenedLocally(AOpenedLocally)
45 , OpenAcked(AIndex >= 0 && AIndex < CHANIDX_ObjectMap) // those channels are automatically opened
46 , Closing(false)
47 , InListCount(0)
48 , InListBits(0)
49 , OutListCount(0)
50 , OutListBits(0)
51 , InList(nullptr)
52 , OutList(nullptr)
53 , bSentAnyMessages(false)
55 vassert(Index >= 0 && Index < MAX_CHANNELS);
56 vassert(!Connection->Channels[Index]);
57 Connection->Channels[Index] = this;
58 Connection->OpenChannels.append(this);
62 //==========================================================================
64 // VChannel::~VChannel
66 //==========================================================================
67 VChannel::~VChannel () {
68 // free outgoung queue
69 while (OutList) {
70 VMessageOut *curr = OutList;
71 OutList = curr->Next;
72 delete curr;
74 // free incoming queue
75 while (InList) {
76 VMessageIn *curr = InList;
77 InList = curr->Next;
78 delete curr;
81 // remove from connection's channel table
82 if (Connection && Index >= 0 && Index < MAX_CHANNELS) {
83 vassert(Connection->Channels[Index] == this);
84 Connection->OpenChannels.Remove(this);
85 Connection->Channels[Index] = nullptr;
86 Connection = nullptr;
91 //==========================================================================
93 // VChannel::GetName
95 //==========================================================================
96 VStr VChannel::GetName () const noexcept {
97 return VStr(va("chan #%d(%s)", Index, GetTypeName()));
101 //==========================================================================
103 // VChannel::GetDebugName
105 //==========================================================================
106 VStr VChannel::GetDebugName () const noexcept {
107 return (Connection ? Connection->GetAddress() : VStr("<noip>"))+":"+(IsLocalChannel() ? "l:" : "r:")+GetName();
111 //==========================================================================
113 // VChannel::GetLastOutSeq
115 //==========================================================================
116 vuint32 VChannel::GetLastOutSeq () const noexcept {
117 return (Connection ? Connection->OutReliable[Index] : 0);
121 //==========================================================================
123 // VChannel::IsQueueFull
125 // returns:
126 // -1: oversaturated
127 // 0: ok
128 // 1: full
130 //==========================================================================
131 int VChannel::IsQueueFull () noexcept {
132 //if (OutListCount >= MAX_RELIABLE_BUFFER-2) return false;
133 //return (OutListBits >= (MAX_RELIABLE_BUFFER-(forClose ? 1 : 2))*MAX_MSG_SIZE_BITS);
134 #if 0
135 return
136 OutListBits >= /*(MAX_RELIABLE_BUFFER+13)*MAX_MSG_SIZE_BITS*/14000*8 ? -1 : // oversaturated
137 OutListBits >= /*MAX_RELIABLE_BUFFER*MAX_MSG_SIZE_BITS*/12000*8 ? 1 : // full
138 0; // ok
139 #endif
140 return
141 OutListCount >= MAX_RELIABLE_BUFFER+8 ? -1 : // oversaturated
142 OutListCount >= MAX_RELIABLE_BUFFER-1 ? 1 : // full
143 0; // ok
144 //return (OutListBits >= 33000*8);
148 //==========================================================================
150 // VChannel::CanSendData
152 //==========================================================================
153 bool VChannel::CanSendData () noexcept {
154 // keep some space for close message
155 if (IsQueueFull()) return false;
156 return (Connection ? /*Connection->CanSendData()*/true : false);
160 //==========================================================================
162 // VChannel::CanSendClose
164 //==========================================================================
165 bool VChannel::CanSendClose () noexcept {
166 //return !IsQueueFull(true);
167 return true; // always
171 //==========================================================================
173 // VChannel::SetClosing
175 //==========================================================================
176 void VChannel::SetClosing () {
177 Closing = true;
181 //==========================================================================
183 // VChannel::ReceivedCloseAck
185 // this is called from `ReceivedAcks()`
186 // the channel will be automatically closed and destroyed, so don't do it here
188 //==========================================================================
189 void VChannel::ReceivedCloseAck () {
193 //==========================================================================
195 // VChannel::OutMessageAcked
197 // called by `ReceivedAcks()`, strictly in sequence
199 //==========================================================================
200 void VChannel::OutMessageAcked (VMessageOut &/*Msg*/) {
204 //==========================================================================
206 // VChannel::Close
208 //==========================================================================
209 void VChannel::Close () {
210 // if this channel is already closing, do nothing
211 if (Closing) return;
212 // if the connection is dead, simply set the flag and get out
213 if (!Connection || Connection->IsClosed()) {
214 SetClosing();
215 return;
217 vassert(Connection->Channels[Index] == this);
218 vassert(!Closing);
219 if (net_debug_dump_recv_packets) GCon->Logf(NAME_DevNet, "%s: sending CLOSE %s", *GetDebugName(), (IsLocalChannel() ? "request" : "ack"));
220 // send a close notify, and wait for the ack
221 // we should not have any closing message in the queue (sanity check)
222 for (VMessageOut *Out = OutList; Out; Out = Out->Next) {
223 if (Out->bClose) GCon->Logf(NAME_DevNet, "%s: close flag is not set, yet we already have CLOSE message in queue (pid=%u; stime=%g; time=%g)", *GetDebugName(), Out->PacketId, Out->Time, Sys_Time());
224 vassert(!Out->bClose);
226 //FIXME!
227 if (!bSentAnyMessages && !OpenAcked) GCon->Logf(NAME_DevNet, "WARNING: trying to close the channel %s that wasn't used for anything!", *GetDebugName());
228 // send closing message
229 SendCloseMessageForced();
230 // WARNING! make sure that `SetClosing()` sets `Closing` first, and then does any cleanup!
231 // failing to do so may cause recursive call to `Close()` (in thinker channel, for example)
232 SetClosing();
233 // closing flag should be set, check it
234 vassert(Closing);
238 //==========================================================================
240 // VChannel::PacketLost
242 //==========================================================================
243 void VChannel::PacketLost (vuint32 PacketId) {
244 for (VMessageOut *Out = OutList; Out; Out = Out->Next) {
245 // retransmit reliable messages in the lost packet
246 if (Out->PacketId == PacketId && !Out->bReceivedAck) {
247 vassert(Out->bReliable);
248 Connection->SendMessage(Out);
254 //==========================================================================
256 // VChannel::ReceivedAcks
258 //==========================================================================
259 void VChannel::ReceivedAcks () {
260 vassert(Connection->Channels[Index] == this);
262 // sanity check
263 for (VMessageOut *Out = OutList; Out && Out->Next; Out = Out->Next) vassert(Out->Next->ChanSequence > Out->ChanSequence);
265 // release all acknowledged outgoing queued messages
266 bool doClose = false;
267 while (OutList && OutList->bReceivedAck) {
268 doClose = (doClose || OutList->bClose);
269 VMessageOut *curr = OutList;
270 OutList = OutList->Next;
271 OutListBits -= curr->OutEstimated;
272 vassert(OutListBits >= 0);
273 OutMessageAcked(*curr);
274 delete curr;
275 --OutListCount;
276 vassert(OutListCount >= 0);
279 // if a close has been acknowledged in sequence, we're done
280 if (doClose) {
281 // `OutList` can still contain some packets here for some reason
282 // it looks like a bug in my netcode
283 if (OutList) {
284 GCon->Logf(NAME_DevNet, "!!!! %s: acked close message, but contains some other unacked messages (%d) !!!!", *GetDebugName(), OutListCount);
285 for (VMessageOut *Out = OutList; Out; Out = Out->Next) {
286 vassert(!Out->bReceivedAck);
287 GCon->Logf(NAME_DevNet, " pid=%u; csq=%u; cidx=%u; ctype=%u; open=%d; close=%d; reliable=%d; size=%d",
288 Out->PacketId, Out->ChanSequence, Out->ChanType, Out->ChanIndex, (int)Out->bOpen, (int)Out->bClose,
289 (int)Out->bReliable, Out->GetNumBits());
292 vassert(!OutList);
293 ReceivedCloseAck();
294 delete this;
299 //==========================================================================
301 // VChannel::SendCloseMessageForced
303 // this unconditionally adds "close" message to the
304 // queue, and marks the channel for closing
306 // WARNING! DOES NO CHECKS!
308 //==========================================================================
309 void VChannel::SendCloseMessageForced () {
310 if (!Connection) return;
311 VMessageOut cnotmsg(this, true); // reliable
312 cnotmsg.bClose = true;
313 // this should not happen, but...
314 if (OpenedLocally && !bSentAnyMessages) cnotmsg.bOpen = true;
315 // put into queue without any checks
316 cnotmsg.Next = nullptr;
317 cnotmsg.ChanSequence = ++Connection->OutReliable[Index];
318 VMessageOut *OutMsg = new VMessageOut(cnotmsg);
319 VMessageOut **OutLink;
320 for (OutLink = &OutList; *OutLink; OutLink = &(*OutLink)->Next) {}
321 *OutLink = OutMsg;
322 OutMsg->OutEstimated = OutMsg->EstimateSizeInBits();
323 OutListBits += OutMsg->OutEstimated;
324 ++OutListCount;
325 // send the raw message
326 OutMsg->bReceivedAck = false;
327 Connection->SendMessage(OutMsg);
331 //==========================================================================
333 // VChannel::SendMessage
335 //==========================================================================
336 void VChannel::SendMessage (VMessageOut *Msg) {
337 vassert(Msg);
338 vassert(!Closing);
339 vassert(Connection->Channels[Index] == this);
340 vassert(!Msg->IsError());
342 // set some additional message flags
343 if (OpenedLocally && !bSentAnyMessages) {
344 // first message must be reliable
345 vassert(Msg->bReliable);
346 Msg->bOpen = true;
348 bSentAnyMessages = true;
350 if (Msg->bReliable) {
351 // put outgoing message into send queue
352 //vassert(OutListCount < MAX_RELIABLE_BUFFER-1+(Msg->bClose ? 1 : 0));
353 const int satur = IsQueueFull();
354 if (satur) {
355 if (satur < 0) {
356 GCon->Logf(NAME_DevNet, "NETWORK ERROR: channel %s is highly oversaturated!", *GetDebugName());
357 Connection->AbortChannel(this);
358 return;
359 } else {
360 GCon->Logf(NAME_DevNet, "NETWORK ERROR: channel %s is oversaturated!", *GetDebugName());
363 Msg->Next = nullptr;
364 Msg->ChanSequence = ++Connection->OutReliable[Index];
365 VMessageOut *OutMsg = new VMessageOut(*Msg);
366 VMessageOut **OutLink;
367 for (OutLink = &OutList; *OutLink; OutLink = &(*OutLink)->Next) {}
368 *OutLink = OutMsg;
369 Msg = OutMsg; // use this new message for sending
370 Msg->OutEstimated = Msg->EstimateSizeInBits();
371 OutListBits += Msg->OutEstimated;
372 ++OutListCount;
375 // send the raw message
376 Msg->bReceivedAck = false;
377 Connection->SendMessage(Msg);
378 // if we're closing the channel, mark this channel as dying, so we can reject any new data
379 // note that we can still have some fragments of the data in incoming queue, and it will be
380 // processed normally
381 if (Msg->bClose) SetClosing();
385 //==========================================================================
387 // VChannel::ProcessInMessage
389 //==========================================================================
390 bool VChannel::ProcessInMessage (VMessageIn &Msg) {
391 // fix channel incoming sequence
392 if (Msg.bReliable) Connection->InReliable[Index] = Msg.ChanSequence;
394 // parse a message
395 const bool isCloseMsg = Msg.bClose;
396 if (!Closing) ParseMessage(Msg);
398 // handle a close notify
399 if (isCloseMsg) {
400 if (InList) Sys_Error("ERROR: %s: closing channel #%d with unprocessed incoming queue", *GetDebugName(), Index);
401 delete this;
402 return true;
404 return false;
408 //==========================================================================
410 // VChannel::ReceivedMessage
412 // process a raw, possibly out-of-sequence message
413 // either queue it or dispatch it
414 // the message won't be discarded
416 //==========================================================================
417 void VChannel::ReceivedMessage (VMessageIn &Msg) {
418 vassert(Connection->Channels[Index] == this);
420 if (Msg.bReliable && Msg.ChanSequence != Connection->InReliable[Index]+1) {
421 // if this message is not in a sqeuence, buffer it
422 // out-of-sequence message cannot be open message
423 // actually, we should show channel error, and block all further messaging on it
424 // (or even close the connection, as it looks like broken/malicious)
425 vassert(!Msg.bOpen);
427 // invariant
428 vassert(Msg.ChanSequence > Connection->InReliable[Index]);
430 // put this into incoming queue, keeping the queue ordered
431 VMessageIn *prev = nullptr, *curr = InList;
432 while (curr) {
433 if (Msg.ChanSequence == curr->ChanSequence) return; // duplicate message, ignore it
434 if (Msg.ChanSequence < curr->ChanSequence) break; // insert before `curr`
435 prev = curr;
436 curr = curr->Next;
438 // copy message
439 VMessageIn *newmsg = new VMessageIn(Msg);
440 if (prev) prev->Next = newmsg; else InList = newmsg;
441 newmsg->Next = curr;
442 InListBits += newmsg->GetNumBits();
443 ++InListCount;
444 // just in case
445 for (VMessageIn *m = InList; m && m->Next; m = m->Next) vassert(m->ChanSequence < m->Next->ChanSequence);
446 //vassert(InListCount <= MAX_RELIABLE_BUFFER); //FIXME: signal error here!
447 if (InListBits > /*(MAX_RELIABLE_BUFFER+18)*MAX_MSG_SIZE_BITS*/128000*8) {
448 GCon->Logf(NAME_DevNet, "NETWORK ERROR: channel %s incoming queue overflowed!", *GetDebugName());
449 Connection->AbortChannel(this);
450 return;
452 } else {
453 // this is "in sequence" message, process it
454 bool removed = ProcessInMessage(Msg);
455 if (removed) return;
457 // dispatch any waiting messages
458 while (InList) {
459 if (InList->ChanSequence != Connection->InReliable[Index]+1) break;
460 VMessageIn *curr = InList;
461 InList = InList->Next;
462 InListBits -= curr->GetNumBits();
463 --InListCount;
464 vassert(InListCount >= 0);
465 vassert(InListBits >= 0);
466 removed = ProcessInMessage(*curr);
467 delete curr;
468 if (removed) return;
474 //==========================================================================
476 // VChannel::Tick
478 //==========================================================================
479 void VChannel::Tick () {
483 //==========================================================================
485 // VChannel::WillOverflowMsg
487 //==========================================================================
488 bool VChannel::WillOverflowMsg (const VMessageOut *msg, int addbits) const noexcept {
489 vassert(msg);
490 return msg->WillOverflow(addbits);
494 //==========================================================================
496 // VChannel::WillOverflowMsg
498 //==========================================================================
499 bool VChannel::WillOverflowMsg (const VMessageOut *msg, const VBitStreamWriter &strm) const noexcept {
500 vassert(msg);
501 return msg->WillOverflow(strm);
505 //==========================================================================
507 // VChannel::PutStream
509 // moves steam to msg (sending previous msg if necessary)
511 // returns `true` if something was flushed
513 //==========================================================================
514 bool VChannel::PutStream (VMessageOut *msg, VBitStreamWriter &strm) {
515 vassert(msg);
516 if (strm.GetNumBits() == 0) return false;
517 bool res = false;
518 if (WillOverflowMsg(msg, strm)) {
519 res = true;
520 SendMessage(msg);
521 msg->Reset(this, msg->bReliable);
523 vassert(!WillOverflowMsg(msg, strm));
524 msg->CopyFromWS(strm);
525 strm.Clear();
526 return res;
530 //==========================================================================
532 // VChannel::FlushMsg
534 // sends message if it is not empty
536 // returns `true` if something was flushed
538 //==========================================================================
539 bool VChannel::FlushMsg (VMessageOut *msg) {
540 vassert(msg);
541 if (msg->GetNumBits() || msg->bOpen || msg->bClose) {
542 SendMessage(msg);
543 msg->Reset(this, msg->bReliable);
544 return true;
546 return false;
550 //==========================================================================
552 // VChannel::SendRpc
554 //==========================================================================
555 void VChannel::SendRpc (VMethod *Func, VObject * /*Owner*/) {
556 // we cannot simply get out of here, because we need to pop function arguments
558 //const bool blockSend = !CanSendData();
559 const bool blockSend = (Closing || IsQueueFull() < 0);
560 bool serverSide = Closing; // abuse the flag
562 // check for server-side only
563 if (!serverSide && IsThinker()) {
564 VThinkerChannel *tc = (VThinkerChannel *)this;
565 VThinker *th = tc->GetThinker();
566 if (th && (th->ThinkerFlags&(VThinker::TF_AlwaysRelevant|VThinker::TF_ServerSideOnly)) == VThinker::TF_ServerSideOnly) serverSide = true;
569 VMessageOut Msg(this, !!(Func->Flags&FUNC_NetReliable));
570 //GCon->Logf(NAME_DevNet, "%s: creating RPC: %s", *GetDebugName(), *Func->GetFullName());
571 Msg.WriteUInt((unsigned)Func->NetIndex);
573 // serialise arguments
574 VStack *Param = VObject::VMGetStackPtr()-Func->ParamsSize+1; // skip self
575 for (int i = 0; i < Func->NumParams; ++i) {
576 switch (Func->ParamTypes[i].Type) {
577 case TYPE_Int:
578 case TYPE_Byte:
579 case TYPE_Bool:
580 case TYPE_Name:
581 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&Param->i, Func->ParamTypes[i]);
582 ++Param;
583 break;
584 case TYPE_Float:
585 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&Param->f, Func->ParamTypes[i]);
586 ++Param;
587 break;
588 case TYPE_String:
589 case TYPE_Pointer:
590 case TYPE_Reference:
591 case TYPE_Class:
592 case TYPE_State:
593 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&Param->p, Func->ParamTypes[i]);
594 ++Param;
595 break;
596 case TYPE_Vector:
598 TVec Vec;
599 Vec.x = Param[0].f;
600 Vec.y = Param[1].f;
601 Vec.z = Param[2].f;
602 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&Vec, Func->ParamTypes[i]);
603 Param += 3;
605 break;
606 default:
607 Host_Error("%s: Bad method argument type %d", *GetDebugName(), Func->ParamTypes[i].Type);
609 if (Func->ParamFlags[i]&FPARM_Optional) {
610 Msg.WriteBit(!!Param->i);
611 ++Param;
615 if (serverSide) return; // nothing to do
617 // send it
618 if (blockSend) {
619 if (!(Func->Flags&FUNC_NetReliable)) return; // nobody cares
620 // alas, cannot send reliable RPC, close the channel, and get out of here
621 // if this is non-thinker channel, it is fatal
622 // if this is thinker channel, but it has "always relevant", it is fatal
623 if (!IsThinker()) {
624 GCon->Logf(NAME_DevNet, "%s: cannot send reliable RPC (%s), closing connection (queue: depth=%d; bitsize=%d)", *GetDebugName(), *Func->GetFullName(), OutListCount, OutListBits);
625 Connection->Close();
626 return;
628 // thinker
629 VThinkerChannel *tc = (VThinkerChannel *)this;
630 if (tc->GetThinker() && (tc->GetThinker()->ThinkerFlags&VThinker::TF_AlwaysRelevant)) {
631 GCon->Logf(NAME_DevNet, "%s: cannot send reliable thinker RPC (%s), closing connection (queue: depth=%d; bitsize=%d)", *GetDebugName(), *Func->GetFullName(), OutListCount, OutListBits);
632 Connection->Close();
633 return;
635 GCon->Logf(NAME_DevNet, "%s: cannot send reliable thinker RPC (%s), closing channel (queue: depth=%d; bitsize=%d)", *GetDebugName(), *Func->GetFullName(), OutListCount, OutListBits);
636 Close();
637 return;
640 SendMessage(&Msg);
641 if (net_debug_rpc) GCon->Logf(NAME_DevNet, "%s: created %s RPC: %s (%d bits) (queue: depth=%d; bitsize=%d)", *GetDebugName(), (Func->Flags&FUNC_NetReliable ? "reliable" : "unreliable"), *Func->GetFullName(), Msg.GetNumBits(), OutListCount, OutListBits);
645 //==========================================================================
647 // VChannel::ReadRpc
649 //==========================================================================
650 bool VChannel::ReadRpc (VMessageIn &Msg, int FldIdx, VObject *Owner) {
651 VMethod *Func = nullptr;
652 for (VMethod *CM = Owner->GetClass()->NetMethods; CM; CM = CM->NextNetMethod) {
653 if (CM->NetIndex == FldIdx) {
654 Func = CM;
655 break;
658 if (!Func) return false;
659 if (net_debug_rpc) GCon->Logf(NAME_DevNet, "%s: ...received RPC (%s); method %s", *GetDebugName(), (Connection->IsClient() ? "client" : "server"), *Func->GetFullName());
661 //memset(pr_stackPtr, 0, Func->ParamsSize*sizeof(VStack));
662 VObject::VMCheckAndClearStack(Func->ParamsSize);
663 // push self pointer
664 VObject::PR_PushPtr(Owner);
665 // get arguments
666 for (int i = 0; i < Func->NumParams; ++i) {
667 switch (Func->ParamTypes[i].Type) {
668 case TYPE_Int:
669 case TYPE_Byte:
670 case TYPE_Bool:
671 case TYPE_Name:
672 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&VObject::VMGetStackPtr()->i, Func->ParamTypes[i]);
673 VObject::VMIncStackPtr();
674 break;
675 case TYPE_Float:
676 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&VObject::VMGetStackPtr()->f, Func->ParamTypes[i]);
677 VObject::VMIncStackPtr();
678 break;
679 case TYPE_String:
680 VObject::VMGetStackPtr()->p = nullptr;
681 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&VObject::VMGetStackPtr()->p, Func->ParamTypes[i]);
682 VObject::VMIncStackPtr();
683 break;
684 case TYPE_Pointer:
685 case TYPE_Reference:
686 case TYPE_Class:
687 case TYPE_State:
688 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&VObject::VMGetStackPtr()->p, Func->ParamTypes[i]);
689 VObject::VMIncStackPtr();
690 break;
691 case TYPE_Vector:
693 TVec Vec;
694 VField::NetSerialiseValue(Msg, Connection->ObjMap, (vuint8 *)&Vec, Func->ParamTypes[i]);
695 VObject::PR_Pushv(Vec);
697 break;
698 default:
699 Host_Error("%s: Bad method argument type `%s` for RPC method call `%s`", *GetDebugName(), *Func->ParamTypes[i].GetName(), *Func->GetFullName());
701 if (Func->ParamFlags[i]&FPARM_Optional) {
702 VObject::VMGetStackPtr()->i = Msg.ReadBit();
703 VObject::VMIncStackPtr();
706 // execute it
707 (void)VObject::ExecuteFunction(Func);
708 return true;