1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
26 #include "../gamedefs.h"
28 #include "net_message.h"
29 #include "../psim/p_entity.h"
30 #include "../psim/p_player.h"
31 #include "../server/server.h"
32 #include "../filesys/files.h"
34 # include "../drawer.h"
35 # include "../client/client.h"
36 # include "../client/cl_local.h"
40 // ////////////////////////////////////////////////////////////////////////// //
61 CMD_ResetStaticLights
,
68 //==========================================================================
70 // VLevelChannel::VLevelChannel
72 //==========================================================================
73 VLevelChannel::VLevelChannel (VNetConnection
*AConnection
, vint32 AIndex
, vuint8 AOpenedLocally
)
74 : VChannel(AConnection
, CHANNEL_Level
, AIndex
, AOpenedLocally
)
80 OpenAcked
= true; // this channel is pre-opened
81 serverInfoBuf
.clear();
87 MapLoadingStartTime
= 0;
92 //==========================================================================
94 // VLevelChannel::~VLevelChannel
96 //==========================================================================
97 VLevelChannel::~VLevelChannel () {
98 if (Connection
) SetLevel(nullptr);
102 //==========================================================================
104 // VLevelChannel::GetName
106 //==========================================================================
107 VStr
VLevelChannel::GetName () const noexcept
{
108 return VStr(va("lvlchan #%d(%s)", Index
, GetTypeName()));
112 //==========================================================================
114 // VLevelChannel::SetLevel
116 //==========================================================================
117 void VLevelChannel::SetLevel (VLevel
*ALevel
) {
127 CameraTextures
.Clear();
128 Translations
.Clear();
129 BodyQueueTrans
.Clear();
133 StaticLightsNext
= 0;
134 Phase
= PhaseServerInfo
;
135 MapLoadingStartTime
= 0;
138 Lines
= new rep_line_t
[Level
->NumLines
];
139 memcpy(Lines
, Level
->BaseLines
, sizeof(rep_line_t
)*Level
->NumLines
);
140 Sides
= new rep_side_t
[Level
->NumSides
];
141 memcpy(Sides
, Level
->BaseSides
, sizeof(rep_side_t
)*Level
->NumSides
);
142 Sectors
= new rep_sector_t
[Level
->NumSectors
];
143 memcpy(Sectors
, Level
->BaseSectors
, sizeof(rep_sector_t
)*Level
->NumSectors
);
144 PolyObjs
= new rep_polyobj_t
[Level
->NumPolyObjs
+1];
145 if (Level
->NumPolyObjs
) memcpy((void *)PolyObjs
, Level
->BasePolyObjs
, sizeof(rep_polyobj_t
)*Level
->NumPolyObjs
);
150 //==========================================================================
152 // VLevelChannel::ResetLevel
154 //==========================================================================
155 void VLevelChannel::ResetLevel () {
165 CameraTextures
.Clear();
166 Translations
.Clear();
167 BodyQueueTrans
.Clear();
169 serverInfoBuf
.clear();
174 StaticLightsNext
= 0;
175 Phase
= PhaseServerInfo
;
180 //==========================================================================
182 // VLevelChannel::SendLevelData
184 //==========================================================================
185 bool VLevelChannel::SendLevelData () {
186 if (Connection
->IsClosed()) return false;
189 case PhaseServerInfo
: SendServerInfo(); break;
190 case PhaseWaitingMapLoaded
: WaitForMapLoaded(); break;
191 case PhaseStaticLights
: SendStaticLights(); break;
192 case PhasePrerender
: SendPreRender(); break;
193 case PhaseDone
: break;
194 default: abort(); // the thing that should not be
196 return (Phase
!= PhaseDone
);
200 //==========================================================================
202 // VLevelChannel::SendServerInfo
204 //==========================================================================
205 void VLevelChannel::SendServerInfo () {
206 GCon
->Logf(NAME_DevNet
, "sending initial level data to %s", *Connection
->GetAddress());
208 //FIXME: fragment overlong server info
209 VStr sinfo
= svs
.serverinfo
;
211 VMessageOut
Msg(this);
212 Msg
.WriteUInt(CMD_NewLevel
);
213 VStr MapName
= *Level
->MapName
;
214 VStr MapHash
= *Level
->MapHash
;
215 vuint32 modhash
= FL_GetNetWadsHash();
216 vassert(!Msg
.IsLoading());
220 Msg
.WriteUInt(svs
.max_clients
);
221 Msg
.WriteUInt(svs
.deathmatch
);
226 if (!sinfo
.isEmpty()) {
227 VMessageOut
Msg(this);
228 Msg
.WriteUInt(CMD_ServerInfo
);
229 for (int f
= 0; f
< sinfo
.length(); ++f
) {
230 if (WillOverflowMsg(&Msg
, 8)) {
232 Msg
.WriteUInt(CMD_ServerInfo
);
234 vuint8 ch
= (vuint8
)(sinfo
[f
]&0xff);
238 Msg
.WriteUInt(CMD_ServerInfoEnd
);
242 if (!Connection
->AutoAck
) {
243 Phase
= PhaseWaitingMapLoaded
;
244 MapLoadingStartTime
= Connection
->Driver
->GetNetTime();
247 Phase
= PhaseStaticLights
;
252 //==========================================================================
254 // VLevelChannel::WaitForMapLoaded
256 //==========================================================================
257 void VLevelChannel::WaitForMapLoaded () {
258 vassert(Phase
== PhaseWaitingMapLoaded
);
259 if (Connection
->Driver
->GetNetTime()-MapLoadingStartTime
> 5*60) {
260 GCon
->Logf(NAME_DevNet
, "%s: client-side level loading takes too long", *GetDebugName());
266 //==========================================================================
268 // VLevelChannel::SendStaticLights
270 //==========================================================================
271 void VLevelChannel::SendStaticLights () {
273 if (StaticLightsNext
>= Level
->StaticLights
.length()) {
278 GCon
->Logf(NAME_DevNet
, "sending static lights to %s", *Connection
->GetAddress());
279 VMessageOut
Msg(this);
280 VBitStreamWriter
strm(MAX_MSG_SIZE_BITS
+64, false); // no expand
282 if (StaticLightsNext
== 0) strm
.WriteUInt(CMD_ResetStaticLights
);
284 for (int i
= 0; i
< Level
->StaticLights
.length(); ++i
) {
285 StaticLightsNext
= i
+1;
287 if (UpdateStaticLight(Msg
, strm
, i
, true)) {
288 PutStream(&Msg
, strm
);
289 if (!CanSendData()) { FlushMsg(&Msg
); return; }
295 Phase
= PhasePrerender
;
299 //==========================================================================
301 // VLevelChannel::SendPreRender
303 //==========================================================================
304 void VLevelChannel::SendPreRender () {
305 GCon
->Logf(NAME_DevNet
, "sending prerender to %s (%d of %d static lights sent)", *Connection
->GetAddress(), StaticLightsNext
, Level
->StaticLights
.length());
306 VMessageOut
Msg(this);
307 Msg
.WriteUInt(CMD_PreRender
);
314 //==========================================================================
316 // VLevelChannel::SendMapLoaded
318 // used by the client
320 //==========================================================================
321 void VLevelChannel::SendMapLoaded () {
322 GCon
->Logf(NAME_DevNet
, "%s: sending 'map loaded'", *Connection
->GetAddress());
323 VMessageOut
Msg(this);
324 Msg
.WriteUInt(CMD_ClientMapLoaded
);
329 //==========================================================================
331 // VLevelChannel::BuildUpdateSets
333 //==========================================================================
334 void VLevelChannel::BuildUpdateSets () {
335 UpdatedLines
.reset();
336 UpdatedSides
.reset();
337 if (!Connection
|| Connection
->IsClosed()) return; // just in case
338 for (auto &&it
: Connection
->UpdatedSectors
.first()) {
339 sector_t
*sec
= &Level
->Sectors
[it
.getKey()];
340 if (sec
->isOriginalPObj()) continue;
342 line_t
**lines
= sec
->lines
;
343 for (int f
= sec
->linecount
; f
> 0; --f
, ++lines
) {
344 line_t
*line
= *lines
;
345 if (!UpdatedLines
.put((vint32
)(ptrdiff_t)(line
-&Level
->Lines
[0]), true)) {
346 // new line, add sides
347 if (line
->sidenum
[0] >= 0 && line
->sidenum
[0] < Level
->NumSides
) UpdatedSides
.put(line
->sidenum
[0], true);
348 if (line
->sidenum
[1] >= 0 && line
->sidenum
[1] < Level
->NumSides
) UpdatedSides
.put(line
->sidenum
[1], true);
352 //GCon->Logf(NAME_Debug, "optimised: %d/%d sectors, %d/%d lines, %d/%d sides", Connection->UpdatedSectors.length(), Level->NumSectors, UpdatedLines.length(), Level->NumLines, UpdatedSides.length(), Level->NumSides);
356 //==========================================================================
358 // VLevelChannel::UpdateLine
360 //==========================================================================
361 int VLevelChannel::UpdateLine (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int lidx
) {
362 vassert(lidx
>= 0 && lidx
< Level
->NumLines
);
364 line_t
*Line
= &Level
->Lines
[lidx
];
366 rep_line_t
*RepLine
= &Lines
[lidx
];
367 if (Line
->alpha
== RepLine
->alpha
) return 0;
369 strm
.WriteUInt(CMD_Line
);
370 strm
<< STRM_INDEX_U(lidx
);
371 strm
.WriteBit(Line
->alpha
!= RepLine
->alpha
);
372 if (Line
->alpha
!= RepLine
->alpha
) {
374 strm
.WriteBit(!!(Line
->flags
&ML_ADDITIVE
));
375 RepLine
->alpha
= Line
->alpha
;
382 //==========================================================================
384 // VLevelChannel::ParseLine
386 //==========================================================================
387 bool VLevelChannel::ParseLine (VMessageIn
&Msg
) {
389 Msg
<< STRM_INDEX_U(lidx
);
391 GCon
->Logf(NAME_DevNet
, "%s: cannot read line index", *GetDebugName());
394 if (lidx
< 0 || lidx
>= Level
->NumLines
) {
395 GCon
->Logf(NAME_DevNet
, "%s: got invalid line index %d (max is %d)", *GetDebugName(), lidx
, Level
->NumLines
-1);
399 line_t
*Line
= &Level
->Lines
[lidx
];
402 if (Msg
.ReadBit()) Line
->flags
|= ML_ADDITIVE
; else Line
->flags
&= ~ML_ADDITIVE
;
405 return !Msg
.IsError();
409 //==========================================================================
411 // VLevelChannel::UpdateSide
413 //==========================================================================
414 int VLevelChannel::UpdateSide (VMessageOut
&Msg
, VBitStreamWriter
&strm
, int sidx
) {
415 vassert(sidx
>= 0 && sidx
< Level
->NumSides
);
417 side_t
*Side
= &Level
->Sides
[sidx
];
420 if (!(Side->Sector->SectorFlags&(sector_t::SF_ExtrafloorSource|sector_t::SF_TransferSource)) &&
421 !Connection->SecCheckFatPVS(Side->Sector))
428 rep_side_t
*RepSide
= &Sides
[sidx
];
431 if (Side
->TopTexture
!= RepSide
->TopTexture
) {
432 strm
.WriteUInt(CMD_SideTexture
);
433 strm
<< STRM_INDEX_U(sidx
);
435 strm
.WriteBit(false);
436 strm
.WriteBit(false);
438 Side
->TopTexture
.Serialise(strm
);
440 RepSide
->TopTexture
= Side
->TopTexture
;
442 PutStream(&Msg
, strm
);
443 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
448 if (Side
->BottomTexture
!= RepSide
->BottomTexture
) {
449 strm
.WriteUInt(CMD_SideTexture
);
450 strm
<< STRM_INDEX_U(sidx
);
453 strm
.WriteBit(false);
455 Side
->BottomTexture
.Serialise(strm
);
457 RepSide
->BottomTexture
= Side
->BottomTexture
;
459 PutStream(&Msg
, strm
);
460 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
465 if (Side
->MidTexture
!= RepSide
->MidTexture
) {
466 strm
.WriteUInt(CMD_SideTexture
);
467 strm
<< STRM_INDEX_U(sidx
);
469 strm
.WriteBit(false);
472 Side
->MidTexture
.Serialise(strm
);
474 RepSide
->MidTexture
= Side
->MidTexture
;
476 PutStream(&Msg
, strm
);
477 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
481 // horizontal texture offsets
482 if (Side
->Top
.TextureOffset
!= RepSide
->Top
.TextureOffset
||
483 Side
->Bot
.TextureOffset
!= RepSide
->Bot
.TextureOffset
||
484 Side
->Mid
.TextureOffset
!= RepSide
->Mid
.TextureOffset
)
486 strm
.WriteUInt(CMD_SideTOffset
);
487 strm
<< STRM_INDEX_U(sidx
);
489 strm
.WriteBit(Side
->Top
.TextureOffset
!= RepSide
->Top
.TextureOffset
);
490 if (Side
->Top
.TextureOffset
!= RepSide
->Top
.TextureOffset
) {
491 strm
<< Side
->Top
.TextureOffset
;
492 RepSide
->Top
.TextureOffset
= Side
->Top
.TextureOffset
;
495 strm
.WriteBit(Side
->Bot
.TextureOffset
!= RepSide
->Bot
.TextureOffset
);
496 if (Side
->Bot
.TextureOffset
!= RepSide
->Bot
.TextureOffset
) {
497 strm
<< Side
->Bot
.TextureOffset
;
498 RepSide
->Bot
.TextureOffset
= Side
->Bot
.TextureOffset
;
501 strm
.WriteBit(Side
->Mid
.TextureOffset
!= RepSide
->Mid
.TextureOffset
);
502 if (Side
->Mid
.TextureOffset
!= RepSide
->Mid
.TextureOffset
) {
503 strm
<< Side
->Mid
.TextureOffset
;
504 RepSide
->Mid
.TextureOffset
= Side
->Mid
.TextureOffset
;
507 PutStream(&Msg
, strm
);
508 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
512 // vertical texture offsets
513 if (Side
->Top
.RowOffset
!= RepSide
->Top
.RowOffset
||
514 Side
->Bot
.RowOffset
!= RepSide
->Bot
.RowOffset
||
515 Side
->Mid
.RowOffset
!= RepSide
->Mid
.RowOffset
)
517 strm
.WriteUInt(CMD_SideROffset
);
518 strm
<< STRM_INDEX_U(sidx
);
520 strm
.WriteBit(Side
->Top
.RowOffset
!= RepSide
->Top
.RowOffset
);
521 if (Side
->Top
.RowOffset
!= RepSide
->Top
.RowOffset
) {
522 strm
<< Side
->Top
.RowOffset
;
523 RepSide
->Top
.RowOffset
= Side
->Top
.RowOffset
;
526 strm
.WriteBit(Side
->Bot
.RowOffset
!= RepSide
->Bot
.RowOffset
);
527 if (Side
->Bot
.RowOffset
!= RepSide
->Bot
.RowOffset
) {
528 strm
<< Side
->Bot
.RowOffset
;
529 RepSide
->Bot
.RowOffset
= Side
->Bot
.RowOffset
;
532 strm
.WriteBit(Side
->Mid
.RowOffset
!= RepSide
->Mid
.RowOffset
);
533 if (Side
->Mid
.RowOffset
!= RepSide
->Mid
.RowOffset
) {
534 strm
<< Side
->Mid
.RowOffset
;
535 RepSide
->Mid
.RowOffset
= Side
->Mid
.RowOffset
;
538 PutStream(&Msg
, strm
);
539 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
544 if (Side
->Top
.ScaleX
!= RepSide
->Top
.ScaleX
||
545 Side
->Top
.ScaleY
!= RepSide
->Top
.ScaleY
||
546 Side
->Bot
.ScaleX
!= RepSide
->Bot
.ScaleX
||
547 Side
->Bot
.ScaleY
!= RepSide
->Bot
.ScaleY
||
548 Side
->Mid
.ScaleX
!= RepSide
->Mid
.ScaleX
||
549 Side
->Mid
.ScaleY
!= RepSide
->Mid
.ScaleY
)
551 strm
.WriteUInt(CMD_SideScale
);
552 strm
<< STRM_INDEX_U(sidx
);
554 strm
.WriteBit(Side
->Top
.ScaleX
!= RepSide
->Top
.ScaleX
);
555 if (Side
->Top
.ScaleX
!= RepSide
->Top
.ScaleX
) {
556 strm
<< Side
->Top
.ScaleX
;
557 RepSide
->Top
.ScaleX
= Side
->Top
.ScaleX
;
560 strm
.WriteBit(Side
->Top
.ScaleY
!= RepSide
->Top
.ScaleY
);
561 if (Side
->Top
.ScaleY
!= RepSide
->Top
.ScaleY
) {
562 strm
<< Side
->Top
.ScaleY
;
563 RepSide
->Top
.ScaleY
= Side
->Top
.ScaleY
;
566 strm
.WriteBit(Side
->Bot
.ScaleX
!= RepSide
->Bot
.ScaleX
);
567 if (Side
->Bot
.ScaleX
!= RepSide
->Bot
.ScaleX
) {
568 strm
<< Side
->Bot
.ScaleX
;
569 RepSide
->Bot
.ScaleX
= Side
->Bot
.ScaleX
;
572 strm
.WriteBit(Side
->Bot
.ScaleY
!= RepSide
->Bot
.ScaleY
);
573 if (Side
->Bot
.ScaleY
!= RepSide
->Bot
.ScaleY
) {
574 strm
<< Side
->Bot
.ScaleY
;
575 RepSide
->Bot
.ScaleY
= Side
->Bot
.ScaleY
;
578 strm
.WriteBit(Side
->Mid
.ScaleX
!= RepSide
->Mid
.ScaleX
);
579 if (Side
->Mid
.ScaleX
!= RepSide
->Mid
.ScaleX
) {
580 strm
<< Side
->Mid
.ScaleX
;
581 RepSide
->Mid
.ScaleX
= Side
->Mid
.ScaleX
;
584 strm
.WriteBit(Side
->Mid
.ScaleY
!= RepSide
->Mid
.ScaleY
);
585 if (Side
->Mid
.ScaleY
!= RepSide
->Mid
.ScaleY
) {
586 strm
<< Side
->Mid
.ScaleY
;
587 RepSide
->Mid
.ScaleY
= Side
->Mid
.ScaleY
;
590 PutStream(&Msg
, strm
);
591 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
595 // lighting and flags
596 if (Side
->Flags
!= RepSide
->Flags
||
597 Side
->Light
!= RepSide
->Light
)
599 strm
.WriteUInt(CMD_Side
);
600 strm
<< STRM_INDEX_U(sidx
);
602 strm
.WriteBit(Side
->Flags
!= RepSide
->Flags
);
603 if (Side
->Flags
!= RepSide
->Flags
) {
604 strm
.WriteUInt((vuint32
)Side
->Flags
);
605 RepSide
->Flags
= Side
->Flags
;
608 strm
.WriteBit(Side
->Light
!= RepSide
->Light
);
609 if (Side
->Light
!= RepSide
->Light
) {
611 RepSide
->Light
= Side
->Light
;
614 PutStream(&Msg
, strm
);
615 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
623 //==========================================================================
625 // VLevelChannel::ParseSideTexture
627 //==========================================================================
628 bool VLevelChannel::ParseSideTexture (VMessageIn
&Msg
) {
630 Msg
<< STRM_INDEX_U(sidx
);
632 GCon
->Logf(NAME_DevNet
, "%s: cannot read side index", *GetDebugName());
635 if (sidx
< 0 || sidx
>= Level
->NumSides
) {
636 GCon
->Logf(NAME_DevNet
, "%s: got invalid side index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSides
-1);
640 // get texture number
642 if (Msg
.ReadBit()) tnum
|= 1u;
643 if (Msg
.ReadBit()) tnum
|= 2u;
644 if (Msg
.IsError() || tnum
> 2) {
645 GCon
->Logf(NAME_DevNet
, "%s: got invalid side texture index %u", *GetDebugName(), tnum
);
649 side_t
*Side
= &Level
->Sides
[sidx
];
651 case 0: Side
->TopTexture
.Serialise(Msg
); break; // top
652 case 1: Side
->BottomTexture
.Serialise(Msg
); break; // bottom
653 case 2: Side
->MidTexture
.Serialise(Msg
); break; // middle
657 return !Msg
.IsError();
661 //==========================================================================
663 // VLevelChannel::ParseSideTOffset
665 //==========================================================================
666 bool VLevelChannel::ParseSideTOffset (VMessageIn
&Msg
) {
668 Msg
<< STRM_INDEX_U(sidx
);
670 GCon
->Logf(NAME_DevNet
, "%s: cannot read side index", *GetDebugName());
673 if (sidx
< 0 || sidx
>= Level
->NumSides
) {
674 GCon
->Logf(NAME_DevNet
, "%s: got invalid side index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSides
-1);
678 side_t
*Side
= &Level
->Sides
[sidx
];
679 if (Msg
.ReadBit()) Msg
<< Side
->Top
.TextureOffset
;
680 if (Msg
.ReadBit()) Msg
<< Side
->Bot
.TextureOffset
;
681 if (Msg
.ReadBit()) Msg
<< Side
->Mid
.TextureOffset
;
683 return !Msg
.IsError();
687 //==========================================================================
689 // VLevelChannel::ParseSideROffset
691 //==========================================================================
692 bool VLevelChannel::ParseSideROffset (VMessageIn
&Msg
) {
694 Msg
<< STRM_INDEX_U(sidx
);
696 GCon
->Logf(NAME_DevNet
, "%s: cannot read side index", *GetDebugName());
699 if (sidx
< 0 || sidx
>= Level
->NumSides
) {
700 GCon
->Logf(NAME_DevNet
, "%s: got invalid side index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSides
-1);
704 side_t
*Side
= &Level
->Sides
[sidx
];
705 if (Msg
.ReadBit()) Msg
<< Side
->Top
.RowOffset
;
706 if (Msg
.ReadBit()) Msg
<< Side
->Bot
.RowOffset
;
707 if (Msg
.ReadBit()) Msg
<< Side
->Mid
.RowOffset
;
709 return !Msg
.IsError();
713 //==========================================================================
715 // VLevelChannel::ParseSideScale
717 //==========================================================================
718 bool VLevelChannel::ParseSideScale (VMessageIn
&Msg
) {
720 Msg
<< STRM_INDEX_U(sidx
);
722 GCon
->Logf(NAME_DevNet
, "%s: cannot read side index", *GetDebugName());
725 if (sidx
< 0 || sidx
>= Level
->NumSides
) {
726 GCon
->Logf(NAME_DevNet
, "%s: got invalid side index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSides
-1);
730 side_t
*Side
= &Level
->Sides
[sidx
];
731 if (Msg
.ReadBit()) Msg
<< Side
->Top
.ScaleX
;
732 if (Msg
.ReadBit()) Msg
<< Side
->Top
.ScaleY
;
733 if (Msg
.ReadBit()) Msg
<< Side
->Bot
.ScaleX
;
734 if (Msg
.ReadBit()) Msg
<< Side
->Bot
.ScaleY
;
735 if (Msg
.ReadBit()) Msg
<< Side
->Mid
.ScaleX
;
736 if (Msg
.ReadBit()) Msg
<< Side
->Mid
.ScaleY
;
738 return !Msg
.IsError();
742 //==========================================================================
744 // VLevelChannel::ParseSide
746 //==========================================================================
747 bool VLevelChannel::ParseSide (VMessageIn
&Msg
) {
749 Msg
<< STRM_INDEX_U(sidx
);
751 GCon
->Logf(NAME_DevNet
, "%s: cannot read side index", *GetDebugName());
754 if (sidx
< 0 || sidx
>= Level
->NumSides
) {
755 GCon
->Logf(NAME_DevNet
, "%s: got invalid side index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSides
-1);
759 side_t
*Side
= &Level
->Sides
[sidx
];
760 if (Msg
.ReadBit()) Side
->Flags
= Msg
.ReadUInt();
761 if (Msg
.ReadBit()) Msg
<< Side
->Light
;
763 return !Msg
.IsError();
767 //==========================================================================
769 // WriteFloatWithFlag
771 //==========================================================================
772 static inline void WriteFloatWithFlag (VBitStreamWriter
&strm
, float &vcheck
, float v
, const bool rounded
) {
773 if (rounded
) v
= mround(v
);
779 strm
.WriteBit(false);
783 #define WRITE_FLOAT_WITH_FLAG_ROUNDED(pfx,name) WriteFloatWithFlag(strm, RepSec->pfx##_##name, Sec->pfx.name, true)
784 #define WRITE_FLOAT_WITH_FLAG(pfx,name) WriteFloatWithFlag(strm, RepSec->pfx##_##name, Sec->pfx.name, false)
786 #define CHECK_FLOAT(pfx,name) RepSec->pfx##_##name != Sec->pfx.name
787 #define CHECK_FLOAT_ROUNDED(pfx,name) RepSec->pfx##_##name != mround(Sec->pfx.name)
790 //==========================================================================
792 // VLevelChannel::UpdateSector
794 //==========================================================================
795 int VLevelChannel::UpdateSector (VMessageOut
&Msg
, VBitStreamWriter
&strm
, int sidx
) {
796 vassert(sidx
>= 0 && sidx
< Level
->NumSectors
);
798 sector_t
*Sec
= &Level
->Sectors
[sidx
];
801 if (!(Sec->SectorFlags&(sector_t::SF_ExtrafloorSource|sector_t::SF_TransferSource)) &&
802 !Connection->SecCheckFatPVS(Sec))
808 VEntity
*FloorSkyBox
= Sec
->floor
.SkyBox
;
809 if (FloorSkyBox
&& !Connection
->ObjMap
->CanSerialiseObject(FloorSkyBox
)) FloorSkyBox
= nullptr;
811 VEntity
*CeilSkyBox
= Sec
->ceiling
.SkyBox
;
812 if (CeilSkyBox
&& !Connection
->ObjMap
->CanSerialiseObject(CeilSkyBox
)) CeilSkyBox
= nullptr;
814 rep_sector_t
*RepSec
= &Sectors
[sidx
];
819 if (RepSec
->floor_pic
!= Sec
->floor
.pic
) {
820 strm
.WriteUInt(CMD_SectorTexture
);
821 strm
<< STRM_INDEX_U(sidx
);
822 strm
.WriteBit(false); // floor
823 Sec
->floor
.pic
.Serialise(strm
);
825 RepSec
->floor_pic
= Sec
->floor
.pic
;
827 PutStream(&Msg
, strm
);
828 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
833 if (RepSec
->ceiling_pic
!= Sec
->ceiling
.pic
) {
834 strm
.WriteUInt(CMD_SectorTexture
);
835 strm
<< STRM_INDEX_U(sidx
);
836 strm
.WriteBit(true); // ceiling
837 Sec
->floor
.pic
.Serialise(strm
);
839 RepSec
->ceiling_pic
= Sec
->ceiling
.pic
;
841 PutStream(&Msg
, strm
);
842 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
847 if (RepSec
->floor_dist
!= Sec
->floor
.dist
||
848 CHECK_FLOAT_ROUNDED(floor
, xoffs
) ||
849 CHECK_FLOAT_ROUNDED(floor
, yoffs
) ||
850 CHECK_FLOAT(floor
, XScale
) ||
851 CHECK_FLOAT(floor
, YScale
) ||
852 CHECK_FLOAT_ROUNDED(floor
, Angle
) ||
853 CHECK_FLOAT_ROUNDED(floor
, BaseAngle
) ||
854 CHECK_FLOAT_ROUNDED(floor
, BaseXOffs
) ||
855 CHECK_FLOAT_ROUNDED(floor
, BaseYOffs
) ||
856 CHECK_FLOAT_ROUNDED(floor
, PObjCX
) ||
857 CHECK_FLOAT_ROUNDED(floor
, PObjCY
) ||
858 CHECK_FLOAT_ROUNDED(floor
, MirrorAlpha
) ||
859 RepSec
->floor_SkyBox
!= FloorSkyBox
)
861 strm
.WriteUInt(CMD_Sector
);
862 strm
<< STRM_INDEX_U(sidx
);
863 strm
.WriteBit(false); // floor
865 strm
.WriteBit(RepSec
->floor_dist
!= Sec
->floor
.dist
);
866 if (RepSec
->floor_dist
!= Sec
->floor
.dist
) {
867 strm
<< Sec
->floor
.dist
;
868 strm
<< Sec
->floor
.TexZ
;
869 //GCon->Logf(NAME_DevNet, "%s: sent floor distance change (sector=%d; dist=%g)", *GetDebugName(), sidx, Sec->floor.dist);
871 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, xoffs
);
872 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, yoffs
);
873 WRITE_FLOAT_WITH_FLAG(floor
, XScale
);
874 WRITE_FLOAT_WITH_FLAG(floor
, YScale
);
875 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, Angle
);
876 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, BaseAngle
);
877 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, BaseXOffs
);
878 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, BaseYOffs
);
879 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, PObjCX
);
880 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, PObjCY
);
881 WRITE_FLOAT_WITH_FLAG_ROUNDED(floor
, MirrorAlpha
);
883 strm
.WriteBit(RepSec
->floor_SkyBox
!= FloorSkyBox
);
884 if (RepSec
->floor_SkyBox
!= FloorSkyBox
) strm
<< FloorSkyBox
;
886 RepSec
->floor_dist
= Sec
->floor
.dist
;
887 RepSec
->floor_SkyBox
= FloorSkyBox
;
889 PutStream(&Msg
, strm
);
890 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
895 if (RepSec
->ceiling_dist
!= Sec
->ceiling
.dist
||
896 CHECK_FLOAT_ROUNDED(ceiling
, xoffs
) ||
897 CHECK_FLOAT_ROUNDED(ceiling
, yoffs
) ||
898 CHECK_FLOAT(ceiling
, XScale
) ||
899 CHECK_FLOAT(ceiling
, YScale
) ||
900 CHECK_FLOAT_ROUNDED(ceiling
, Angle
) ||
901 CHECK_FLOAT_ROUNDED(ceiling
, BaseAngle
) ||
902 CHECK_FLOAT_ROUNDED(ceiling
, BaseXOffs
) ||
903 CHECK_FLOAT_ROUNDED(ceiling
, BaseYOffs
) ||
904 CHECK_FLOAT_ROUNDED(ceiling
, PObjCX
) ||
905 CHECK_FLOAT_ROUNDED(ceiling
, PObjCY
) ||
906 CHECK_FLOAT_ROUNDED(ceiling
, MirrorAlpha
) ||
907 RepSec
->ceiling_SkyBox
!= CeilSkyBox
)
909 strm
.WriteUInt(CMD_Sector
);
910 strm
<< STRM_INDEX_U(sidx
);
911 strm
.WriteBit(true); // ceiling
913 strm
.WriteBit(RepSec
->ceiling_dist
!= Sec
->ceiling
.dist
);
914 if (RepSec
->ceiling_dist
!= Sec
->ceiling
.dist
) {
915 strm
<< Sec
->ceiling
.dist
;
916 strm
<< Sec
->ceiling
.TexZ
;
917 //GCon->Logf(NAME_DevNet, "%s: sent ceiling distance change (sector=%d; dist=%g)", *GetDebugName(), sidx, Sec->ceiling.dist);
919 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, xoffs
);
920 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, yoffs
);
921 WRITE_FLOAT_WITH_FLAG(ceiling
, XScale
);
922 WRITE_FLOAT_WITH_FLAG(ceiling
, YScale
);
923 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, Angle
);
924 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, BaseAngle
);
925 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, BaseXOffs
);
926 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, BaseYOffs
);
927 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, PObjCX
);
928 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, PObjCY
);
929 WRITE_FLOAT_WITH_FLAG_ROUNDED(ceiling
, MirrorAlpha
);
931 strm
.WriteBit(RepSec
->ceiling_SkyBox
!= CeilSkyBox
);
932 if (RepSec
->ceiling_SkyBox
!= CeilSkyBox
) strm
<< CeilSkyBox
;
934 RepSec
->ceiling_dist
= Sec
->ceiling
.dist
;
935 RepSec
->ceiling_SkyBox
= CeilSkyBox
;
937 PutStream(&Msg
, strm
);
938 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
943 if (RepSec
->Sky
!= Sec
->Sky
|| /* sorry */
944 RepSec
->params
.lightlevel
!= Sec
->params
.lightlevel
||
945 RepSec
->params
.LightColor
!= Sec
->params
.LightColor
||
946 RepSec
->params
.Fade
!= Sec
->params
.Fade
||
947 RepSec
->params
.contents
!= Sec
->params
.contents
||
948 RepSec
->params
.lightFCFlags
!= Sec
->params
.lightFCFlags
||
949 RepSec
->params
.lightFloor
!= Sec
->params
.lightFloor
||
950 RepSec
->params
.lightCeiling
!= Sec
->params
.lightCeiling
||
951 RepSec
->params
.glowFloor
!= Sec
->params
.glowFloor
||
952 RepSec
->params
.glowCeiling
!= Sec
->params
.glowCeiling
||
953 RepSec
->params
.glowFloorHeight
!= Sec
->params
.glowFloorHeight
||
954 RepSec
->params
.glowCeilingHeight
!= Sec
->params
.glowCeilingHeight
)
956 strm
.WriteUInt(CMD_SectorLight
);
957 strm
<< STRM_INDEX_U(sidx
);
959 strm
.WriteBit(RepSec
->Sky
!= Sec
->Sky
);
960 if (RepSec
->Sky
!= Sec
->Sky
) strm
.WriteInt(Sec
->Sky
);
962 strm
.WriteBit(RepSec
->params
.lightlevel
!= Sec
->params
.lightlevel
);
963 if (RepSec
->params
.lightlevel
!= Sec
->params
.lightlevel
) strm
.WriteUInt((vuint32
)Sec
->params
.lightlevel
); // 256
965 strm
.WriteBit(RepSec
->params
.LightColor
!= Sec
->params
.LightColor
);
966 if (RepSec
->params
.LightColor
!= Sec
->params
.LightColor
) strm
<< STRM_INDEX_U(Sec
->params
.LightColor
);
968 strm
.WriteBit(RepSec
->params
.Fade
!= Sec
->params
.Fade
);
969 if (RepSec
->params
.Fade
!= Sec
->params
.Fade
) strm
<< STRM_INDEX_U(Sec
->params
.Fade
);
971 strm
.WriteBit(RepSec
->params
.contents
!= Sec
->params
.contents
);
972 if (RepSec
->params
.contents
!= Sec
->params
.contents
) strm
<< STRM_INDEX_U(Sec
->params
.contents
);
974 strm
.WriteBit(RepSec
->params
.lightFCFlags
!= Sec
->params
.lightFCFlags
);
975 if (RepSec
->params
.lightFCFlags
!= Sec
->params
.lightFCFlags
) strm
<< STRM_INDEX_U(Sec
->params
.lightFCFlags
);
977 strm
.WriteBit(RepSec
->params
.lightFloor
!= Sec
->params
.lightFloor
);
978 if (RepSec
->params
.lightFloor
!= Sec
->params
.lightFloor
) strm
<< STRM_INDEX_U(Sec
->params
.lightFloor
);
980 strm
.WriteBit(RepSec
->params
.lightCeiling
!= Sec
->params
.lightCeiling
);
981 if (RepSec
->params
.lightCeiling
!= Sec
->params
.lightCeiling
) strm
<< STRM_INDEX_U(Sec
->params
.lightCeiling
);
983 strm
.WriteBit(RepSec
->params
.glowFloor
!= Sec
->params
.glowFloor
);
984 if (RepSec
->params
.glowFloor
!= Sec
->params
.glowFloor
) strm
<< STRM_INDEX_U(Sec
->params
.glowFloor
);
986 strm
.WriteBit(RepSec
->params
.glowCeiling
!= Sec
->params
.glowCeiling
);
987 if (RepSec
->params
.glowCeiling
!= Sec
->params
.glowCeiling
) strm
<< STRM_INDEX_U(Sec
->params
.glowCeiling
);
989 strm
.WriteBit(RepSec
->params
.glowFloorHeight
!= Sec
->params
.glowFloorHeight
);
990 if (RepSec
->params
.glowFloorHeight
!= Sec
->params
.glowFloorHeight
) strm
<< Sec
->params
.glowFloorHeight
;
992 strm
.WriteBit(RepSec
->params
.glowCeilingHeight
!= Sec
->params
.glowCeilingHeight
);
993 if (RepSec
->params
.glowCeilingHeight
!= Sec
->params
.glowCeilingHeight
) strm
<< Sec
->params
.glowCeilingHeight
;
995 RepSec
->Sky
= Sec
->Sky
;
996 RepSec
->params
= Sec
->params
;
998 PutStream(&Msg
, strm
);
999 if (!CanSendData()) { FlushMsg(&Msg
); Connection
->NeedsUpdate
= true; return -1; }
1007 //==========================================================================
1009 // VLevelChannel::ParseSectorTexture
1011 //==========================================================================
1012 bool VLevelChannel::ParseSectorTexture (VMessageIn
&Msg
) {
1014 Msg
<< STRM_INDEX_U(sidx
);
1015 if (Msg
.IsError()) {
1016 GCon
->Logf(NAME_DevNet
, "%s: cannot read sector index", *GetDebugName());
1019 if (sidx
< 0 || sidx
>= Level
->NumSectors
) {
1020 GCon
->Logf(NAME_DevNet
, "%s: got invalid sector index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSectors
-1);
1024 sector_t
*Sec
= &Level
->Sectors
[sidx
];
1026 if (Msg
.ReadBit()) {
1027 Sec
->ceiling
.pic
.Serialise(Msg
);
1029 Sec
->floor
.pic
.Serialise(Msg
);
1032 return !Msg
.IsError();
1036 //==========================================================================
1038 // VLevelChannel::ParseSectorLight
1040 //==========================================================================
1041 bool VLevelChannel::ParseSectorLight (VMessageIn
&Msg
) {
1043 Msg
<< STRM_INDEX_U(sidx
);
1044 if (Msg
.IsError()) {
1045 GCon
->Logf(NAME_DevNet
, "%s: cannot read sector index", *GetDebugName());
1048 if (sidx
< 0 || sidx
>= Level
->NumSectors
) {
1049 GCon
->Logf(NAME_DevNet
, "%s: got invalid sector index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSectors
-1);
1053 sector_t
*Sec
= &Level
->Sectors
[sidx
];
1055 if (Msg
.ReadBit()) Sec
->Sky
= Msg
.ReadInt();
1056 if (Msg
.ReadBit()) Sec
->params
.lightlevel
= Msg
.ReadUInt();
1057 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.LightColor
);
1058 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.Fade
);
1059 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.contents
);
1060 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.lightFCFlags
);
1061 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.lightFloor
);
1062 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.lightCeiling
);
1063 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.glowFloor
);
1064 if (Msg
.ReadBit()) Msg
<< STRM_INDEX_U(Sec
->params
.glowCeiling
);
1065 if (Msg
.ReadBit()) Msg
<< Sec
->params
.glowFloorHeight
;
1066 if (Msg
.ReadBit()) Msg
<< Sec
->params
.glowCeilingHeight
;
1068 return !Msg
.IsError();
1072 //==========================================================================
1074 // VLevelChannel::ParseSector
1076 //==========================================================================
1077 bool VLevelChannel::ParseSector (VMessageIn
&Msg
) {
1079 Msg
<< STRM_INDEX_U(sidx
);
1080 if (Msg
.IsError()) {
1081 GCon
->Logf(NAME_DevNet
, "%s: cannot read sector index", *GetDebugName());
1084 if (sidx
< 0 || sidx
>= Level
->NumSectors
) {
1085 GCon
->Logf(NAME_DevNet
, "%s: got invalid sector index %d (max is %d)", *GetDebugName(), sidx
, Level
->NumSectors
-1);
1089 sector_t
*Sec
= &Level
->Sectors
[sidx
];
1091 const float PrevFloorDist
= Sec
->floor
.dist
;
1092 const float PrevCeilDist
= Sec
->ceiling
.dist
;
1095 if (Msg
.ReadBit()) {
1097 if (Msg
.ReadBit()) {
1098 Msg
<< Sec
->ceiling
.dist
;
1099 Msg
<< Sec
->ceiling
.TexZ
;
1100 //GCon->Logf(NAME_DevNet, "%s: got ceiling distance change (sector=%d; dist=%g)", *GetDebugName(), sidx, Sec->ceiling.dist);
1102 if (Msg
.ReadBit()) Sec
->ceiling
.xoffs
= Msg
.ReadInt();
1103 if (Msg
.ReadBit()) Sec
->ceiling
.yoffs
= Msg
.ReadInt();
1104 if (Msg
.ReadBit()) Msg
<< Sec
->ceiling
.XScale
;
1105 if (Msg
.ReadBit()) Msg
<< Sec
->ceiling
.YScale
;
1106 if (Msg
.ReadBit()) Sec
->ceiling
.Angle
= Msg
.ReadInt();
1107 if (Msg
.ReadBit()) Sec
->ceiling
.BaseAngle
= Msg
.ReadInt();
1108 if (Msg
.ReadBit()) Sec
->ceiling
.BaseXOffs
= Msg
.ReadInt();
1109 if (Msg
.ReadBit()) Sec
->ceiling
.BaseYOffs
= Msg
.ReadInt();
1110 if (Msg
.ReadBit()) Sec
->ceiling
.PObjCX
= Msg
.ReadInt();
1111 if (Msg
.ReadBit()) Sec
->ceiling
.PObjCY
= Msg
.ReadInt();
1112 if (Msg
.ReadBit()) Msg
<< Sec
->ceiling
.SkyBox
;
1113 if (Msg
.ReadBit()) Msg
<< Sec
->ceiling
.MirrorAlpha
;
1116 if (Msg
.ReadBit()) {
1117 Msg
<< Sec
->floor
.dist
;
1118 Msg
<< Sec
->floor
.TexZ
;
1120 if (Msg
.ReadBit()) Sec
->floor
.xoffs
= Msg
.ReadInt();
1121 if (Msg
.ReadBit()) Sec
->floor
.yoffs
= Msg
.ReadInt();
1122 if (Msg
.ReadBit()) Msg
<< Sec
->floor
.XScale
;
1123 if (Msg
.ReadBit()) Msg
<< Sec
->floor
.YScale
;
1124 if (Msg
.ReadBit()) Sec
->floor
.Angle
= Msg
.ReadInt();
1125 if (Msg
.ReadBit()) Sec
->floor
.BaseAngle
= Msg
.ReadInt();
1126 if (Msg
.ReadBit()) Sec
->floor
.BaseXOffs
= Msg
.ReadInt();
1127 if (Msg
.ReadBit()) Sec
->floor
.BaseYOffs
= Msg
.ReadInt();
1128 if (Msg
.ReadBit()) Sec
->floor
.PObjCX
= Msg
.ReadInt();
1129 if (Msg
.ReadBit()) Sec
->floor
.PObjCY
= Msg
.ReadInt();
1130 if (Msg
.ReadBit()) Msg
<< Sec
->floor
.SkyBox
;
1131 if (Msg
.ReadBit()) Msg
<< Sec
->floor
.MirrorAlpha
;
1134 if (Msg
.IsError()) return false;
1136 if (PrevFloorDist
!= Sec
->floor
.dist
|| PrevCeilDist
!= Sec
->ceiling
.dist
) {
1137 //GCon->Logf("updating sector #%d", (int)(ptrdiff_t)(Sec-&GClLevel->Sectors[0]));
1138 Level
->CalcSecMinMaxs(Sec
);
1145 //==========================================================================
1147 // VLevelChannel::UpdatePolyObj
1149 //==========================================================================
1150 int VLevelChannel::UpdatePolyObj (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int oidx
) {
1151 vassert(oidx
>= 0 && oidx
< Level
->NumPolyObjs
);
1153 polyobj_t
*Po
= Level
->PolyObjs
[oidx
];
1154 bool needUpdate
= false;
1155 for (auto &&it
: Po
->SubFirst()) {
1156 if (Connection
->CheckFatPVS(it
.sub())) {
1161 if (!needUpdate
) return 0;
1163 rep_polyobj_t
*RepPo
= &PolyObjs
[oidx
];
1164 if (RepPo
->startSpot
.x
== Po
->startSpot
.x
&&
1165 RepPo
->startSpot
.y
== Po
->startSpot
.y
&&
1166 RepPo
->startSpot
.z
== Po
->startSpot
.z
&&
1167 RepPo
->angle
== Po
->angle
)
1172 strm
.WriteUInt(CMD_PolyObj
);
1173 strm
<< STRM_INDEX_U(oidx
);
1175 strm
.WriteBit(RepPo
->startSpot
.x
!= Po
->startSpot
.x
);
1176 if (RepPo
->startSpot
.x
!= Po
->startSpot
.x
) strm
<< Po
->startSpot
.x
;
1177 strm
.WriteBit(RepPo
->startSpot
.y
!= Po
->startSpot
.y
);
1178 if (RepPo
->startSpot
.y
!= Po
->startSpot
.y
) strm
<< Po
->startSpot
.y
;
1179 strm
.WriteBit(RepPo
->startSpot
.z
!= Po
->startSpot
.z
);
1180 if (RepPo
->startSpot
.z
!= Po
->startSpot
.z
) strm
<< Po
->startSpot
.z
;
1181 strm
.WriteBit(RepPo
->angle
!= Po
->angle
);
1182 if (RepPo
->angle
!= Po
->angle
) strm
<< Po
->angle
;
1184 RepPo
->startSpot
= Po
->startSpot
;
1185 RepPo
->angle
= Po
->angle
;
1191 //==========================================================================
1193 // VLevelChannel::ParsePolyObj
1195 //==========================================================================
1196 bool VLevelChannel::ParsePolyObj (VMessageIn
&Msg
) {
1198 Msg
<< STRM_INDEX_U(oidx
);
1199 if (Msg
.IsError()) {
1200 GCon
->Logf(NAME_DevNet
, "%s: cannot read polyobject index", *GetDebugName());
1203 if (oidx
< 0 || oidx
>= Level
->NumPolyObjs
) {
1204 GCon
->Logf(NAME_DevNet
, "%s: got invalid polyobject index %d (max is %d)", *GetDebugName(), oidx
, Level
->NumPolyObjs
-1);
1208 polyobj_t
*Po
= Level
->PolyObjs
[oidx
];
1209 TVec Pos
= Po
->startSpot
;
1210 if (Msg
.ReadBit()) Msg
<< Pos
.x
;
1211 if (Msg
.ReadBit()) Msg
<< Pos
.y
;
1212 if (Msg
.ReadBit()) Msg
<< Pos
.z
;
1213 if (Msg
.IsError()) return false;
1214 if (Pos
!= Po
->startSpot
) Level
->MovePolyobj(Po
->tag
, Pos
.x
-Po
->startSpot
.x
, Pos
.y
-Po
->startSpot
.y
, Pos
.z
-Po
->startSpot
.z
, VLevel::POFLAG_FORCED
|VLevel::POFLAG_NOLINK
);
1215 if (Msg
.ReadBit()) {
1218 if (Msg
.IsError()) return false;
1219 Level
->RotatePolyobj(Po
->tag
, a
-Po
->angle
, VLevel::POFLAG_FORCED
|VLevel::POFLAG_NOLINK
);
1222 return !Msg
.IsError();
1226 //==========================================================================
1228 // VLevelChannel::UpdateCameraTexture
1230 //==========================================================================
1231 int VLevelChannel::UpdateCameraTexture (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int idx
) {
1232 if (idx
> 255) return 0; // oops
1233 vassert(idx
>= 0 && idx
< Level
->CameraTextures
.length());
1235 // grow replication array if needed
1236 if (CameraTextures
.length() == idx
) {
1237 VCameraTextureInfo
&C
= CameraTextures
.Alloc();
1243 VCameraTextureInfo
&Cam
= Level
->CameraTextures
[idx
];
1244 VCameraTextureInfo
&RepCam
= CameraTextures
[idx
];
1245 VEntity
*CamEnt
= Cam
.Camera
;
1247 if (CamEnt
&& !Connection
->ObjMap
->CanSerialiseObject(CamEnt
)) CamEnt
= nullptr;
1248 if (CamEnt
== RepCam
.Camera
&& Cam
.TexNum
== RepCam
.TexNum
&& Cam
.FOV
== RepCam
.FOV
) return 0;
1251 strm
.WriteUInt(CMD_CamTex
);
1252 strm
<< STRM_INDEX_U(idx
);
1254 Connection
->ObjMap
->SerialiseObject(strm
, *(VObject
**)&CamEnt
);
1255 strm
.WriteInt(Cam
.TexNum
);
1256 strm
.WriteInt(Cam
.FOV
);
1258 // update replication info
1259 RepCam
.Camera
= CamEnt
;
1260 RepCam
.TexNum
= Cam
.TexNum
;
1261 RepCam
.FOV
= Cam
.FOV
;
1267 //==========================================================================
1269 // VLevelChannel::ParseCameraTexture
1271 //==========================================================================
1272 bool VLevelChannel::ParseCameraTexture (VMessageIn
&Msg
) {
1274 Msg
<< STRM_INDEX_U(idx
);
1275 if (Msg
.IsError()) {
1276 GCon
->Logf(NAME_DevNet
, "%s: cannot read camtex index", *GetDebugName());
1279 if (idx
< 0 || idx
> 255) {
1280 GCon
->Logf(NAME_DevNet
, "%s: got invalid camtex index %d (max is %d)", *GetDebugName(), idx
, 1023);
1284 while (Level
->CameraTextures
.length() <= idx
) {
1285 VCameraTextureInfo
&C
= Level
->CameraTextures
.Alloc();
1290 VCameraTextureInfo
&Cam
= Level
->CameraTextures
[idx
];
1291 Connection
->ObjMap
->SerialiseObject(Msg
, *(VObject
**)&Cam
.Camera
);
1292 Cam
.TexNum
= Msg
.ReadInt();
1293 Cam
.FOV
= Msg
.ReadInt();
1295 return !Msg
.IsError();
1299 //==========================================================================
1301 // VLevelChannel::UpdateTranslation
1303 //==========================================================================
1304 int VLevelChannel::UpdateTranslation (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int idx
) {
1305 if (idx
> 4095) return 0; // artificial limit
1306 vassert(idx
>= 0 && idx
< Level
->Translations
.length());
1308 // grow replication array if needed
1309 if (Translations
.length() == idx
) Translations
.Alloc();
1310 if (!Level
->Translations
[idx
]) return 0;
1312 VTextureTranslation
*Tr
= Level
->Translations
[idx
];
1313 TArray
<VTextureTranslation::VTransCmd
> &Rep
= Translations
[idx
];
1314 bool Eq
= (Tr
->Commands
.length() == Rep
.length());
1316 for (int j
= 0; j
< Rep
.length(); ++j
) {
1317 if (memcmp(&Tr
->Commands
[j
], &Rep
[j
], sizeof(Rep
[j
]))) {
1326 strm
.WriteUInt(CMD_LevelTrans
);
1327 strm
<< STRM_INDEX_U(idx
);
1329 strm
.WriteUInt((vuint32
)Tr
->Commands
.length());
1330 Rep
.setLength(Tr
->Commands
.length());
1331 for (int j
= 0; j
< Tr
->Commands
.length(); ++j
) {
1332 VTextureTranslation::VTransCmd
&C
= Tr
->Commands
[j
];
1333 strm
.WriteUInt(C
.Type
);
1334 if (C
.Type
== 0) strm
<< C
.Start
<< C
.End
<< C
.R1
<< C
.R2
;
1335 else if (C
.Type
== 1) strm
<< C
.Start
<< C
.End
<< C
.R1
<< C
.G1
<< C
.B1
<< C
.R2
<< C
.G2
<< C
.B2
;
1336 else if (C
.Type
== 2) strm
<< C
.Start
<< C
.End
<< C
.R1
<< C
.G1
<< C
.B1
<< C
.R2
<< C
.G2
<< C
.B2
;
1337 else if (C
.Type
== 3) strm
<< C
.Start
<< C
.End
<< C
.R1
<< C
.G1
<< C
.B1
;
1338 else if (C
.Type
== 4) strm
<< C
.Start
<< C
.End
<< C
.R1
<< C
.G1
<< C
.B1
<< C
.R2
;
1346 //==========================================================================
1348 // VLevelChannel::ParseTranslation
1350 //==========================================================================
1351 bool VLevelChannel::ParseTranslation (VMessageIn
&Msg
) {
1353 Msg
<< STRM_INDEX_U(idx
);
1354 if (Msg
.IsError()) {
1355 GCon
->Logf(NAME_DevNet
, "%s: cannot read translation index", *GetDebugName());
1358 if (idx
< 0 || idx
> 4095) {
1359 GCon
->Logf(NAME_DevNet
, "%s: got invalid translation index %d (max is %d)", *GetDebugName(), idx
, 4095);
1363 while (Level
->Translations
.length() <= idx
) Level
->Translations
.Append(nullptr);
1364 VTextureTranslation
*Tr
= Level
->Translations
[idx
];
1366 Tr
= new VTextureTranslation
;
1367 Level
->Translations
[idx
] = Tr
;
1371 int Count
= (int)Msg
.ReadUInt();
1372 for (int j
= 0; j
< Count
; ++j
) {
1373 vuint8 Type
= Msg
.ReadUInt();
1374 if (Msg
.IsError()) return false;
1380 Msg
<< Start
<< End
<< SrcStart
<< SrcEnd
;
1381 if (Msg
.IsError()) return false;
1382 Tr
->MapToRange(Start
, End
, SrcStart
, SrcEnd
);
1383 } else if (Type
== 1) {
1392 Msg
<< Start
<< End
<< R1
<< G1
<< B1
<< R2
<< G2
<< B2
;
1393 if (Msg
.IsError()) return false;
1394 Tr
->MapToColors(Start
, End
, R1
, G1
, B1
, R2
, G2
, B2
);
1395 } else if (Type
== 2) {
1404 Msg
<< Start
<< End
<< R1
<< G1
<< B1
<< R2
<< G2
<< B2
;
1405 if (Msg
.IsError()) return false;
1406 Tr
->MapDesaturated(Start
, End
, R1
/128.0f
, G1
/128.0f
, B1
/128.0f
, R2
/128.0f
, G2
/128.0f
, B2
/128.0f
);
1407 } else if (Type
== 3) {
1413 Msg
<< Start
<< End
<< R1
<< G1
<< B1
;
1414 if (Msg
.IsError()) return false;
1415 Tr
->MapBlended(Start
, End
, R1
, G1
, B1
);
1416 } else if (Type
== 4) {
1423 Msg
<< Start
<< End
<< R1
<< G1
<< B1
<< Amount
;
1424 if (Msg
.IsError()) return false;
1425 Tr
->MapTinted(Start
, End
, R1
, G1
, B1
, Amount
);
1429 return !Msg
.IsError();
1433 //==========================================================================
1435 // VLevelChannel::UpdateBodyQueueTran
1437 //==========================================================================
1438 int VLevelChannel::UpdateBodyQueueTran (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int idx
) {
1439 if (idx
> 8191) return 0; // artificial limit
1440 vassert(idx
>= 0 && idx
< Level
->BodyQueueTrans
.length());
1442 // grow replication array if needed
1443 if (BodyQueueTrans
.length() == idx
) BodyQueueTrans
.Alloc().TranslStart
= 0;
1444 if (!Level
->BodyQueueTrans
[idx
]) return 0;
1445 VTextureTranslation
*Tr
= Level
->BodyQueueTrans
[idx
];
1446 if (!Tr
->TranslStart
) return 0;
1447 VBodyQueueTrInfo
&Rep
= BodyQueueTrans
[idx
];
1448 if (Tr
->TranslStart
== Rep
.TranslStart
&& Tr
->TranslEnd
== Rep
.TranslEnd
&& Tr
->Color
== Rep
.Color
) return 0;
1451 strm
.WriteUInt(CMD_BodyQueueTrans
);
1452 strm
<< STRM_INDEX_U(idx
);
1453 strm
<< Tr
->TranslStart
<< Tr
->TranslEnd
;
1454 strm
.WriteUInt((vuint32
)Tr
->Color
);
1456 Rep
.TranslStart
= Tr
->TranslStart
;
1457 Rep
.TranslEnd
= Tr
->TranslEnd
;
1458 Rep
.Color
= Tr
->Color
;
1464 //==========================================================================
1466 // VLevelChannel::ParseBodyQueueTran
1468 //==========================================================================
1469 bool VLevelChannel::ParseBodyQueueTran (VMessageIn
&Msg
) {
1471 Msg
<< STRM_INDEX_U(idx
);
1472 if (Msg
.IsError()) {
1473 GCon
->Logf(NAME_DevNet
, "%s: cannot read body queue index", *GetDebugName());
1476 if (idx
< 0 || idx
> 8191) {
1477 GCon
->Logf(NAME_DevNet
, "%s: got invalid body queue index %d (max is %d)", *GetDebugName(), idx
, 8191);
1481 while (Level
->BodyQueueTrans
.length() <= idx
) Level
->BodyQueueTrans
.Append(nullptr);
1482 VTextureTranslation
*Tr
= Level
->BodyQueueTrans
[idx
];
1484 Tr
= new VTextureTranslation
;
1485 Level
->BodyQueueTrans
[idx
] = Tr
;
1491 Msg
<< TrStart
<< TrEnd
;
1492 vint32 Col
= Msg
.ReadUInt();
1493 Tr
->BuildPlayerTrans(TrStart
, TrEnd
, Col
);
1495 return !Msg
.IsError();
1499 //==========================================================================
1501 // VLevelChannel::UpdateStaticLight
1503 //==========================================================================
1504 int VLevelChannel::UpdateStaticLight (VMessageOut
&/*Msg*/, VBitStreamWriter
&strm
, int idx
, bool forced
) {
1505 if (idx
> 65535) return 0; // arbitrary limit
1506 vassert(idx
>= 0 && idx
< Level
->StaticLights
.length());
1508 rep_light_t
&L
= Level
->StaticLights
[idx
];
1510 //FIXME: send deactivation too
1511 if (!L
.IsActive()) return 0;
1513 if (!forced
&& !(L
.Flags
&rep_light_t::LightChanged
)) return 0;
1515 strm
.WriteUInt(CMD_StaticLight
);
1517 TVec lOrigin
= L
.Origin
;
1518 float lRadius
= L
.Radius
;
1519 vuint32 lColor
= L
.Color
;
1520 vuint32 ouid
= L
.OwnerUId
;
1522 strm
<< STRM_INDEX_U(ouid
) << lOrigin
<< lRadius
<< lColor
;
1524 strm
.WriteBit(!!L
.ConeAngle
);
1526 TVec lConeDir
= L
.ConeDir
;
1527 float lConeAngle
= L
.ConeAngle
;
1528 strm
<< lConeDir
<< lConeAngle
;
1531 strm
.WriteBit(!!L
.LevelSector
);
1532 if (L
.LevelSector
) {
1533 const vuint32 sidx
= (vuint32
)(ptrdiff_t)(L
.LevelSector
-&Level
->Sectors
[0]);
1534 strm
<< STRM_INDEX_U(sidx
) << L
.LevelScale
;
1537 vuint32 flags
= L
.Flags
&~(rep_light_t::LightChanged
|rep_light_t::LightActive
);
1539 strm
.WriteBit(true);
1540 strm
<< STRM_INDEX_U(flags
);
1542 strm
.WriteBit(false);
1545 if (!forced
) L
.Flags
&= ~rep_light_t::LightChanged
;
1551 //==========================================================================
1553 // VLevelChannel::ParseStaticLight
1555 //==========================================================================
1556 bool VLevelChannel::ParseStaticLight (VMessageIn
&Msg
) {
1562 Msg
<< STRM_INDEX_U(owneruid
) << Origin
<< Radius
<< Color
;
1564 bool isCone
= Msg
.ReadBit();
1565 TVec
ConeDir(0.0f
, 0.0f
, 0.0f
);
1566 float ConeAngle
= 0.0f
;
1568 Msg
<< ConeDir
<< ConeAngle
;
1571 bool isSector
= Msg
.ReadBit();
1572 vuint32 snum
= 0xffffffffu
;
1573 float sscale
= 0.0f
;
1575 Msg
<< STRM_INDEX_U(snum
) << sscale
;
1576 if (snum
>= (unsigned)Level
->NumSectors
) isSector
= false;
1580 bool isFlags
= Msg
.ReadBit();
1582 Msg
<< STRM_INDEX_U(flags
);
1585 if (Msg
.IsError()) {
1586 GCon
->Logf(NAME_DevNet
, "%s: cannot read static light header", *GetDebugName());
1591 lpar
.Origin
= Origin
;
1592 lpar
.Radius
= Radius
;
1594 lpar
.coneDirection
= ConeDir
;
1595 lpar
.coneAngle
= ConeAngle
;
1596 lpar
.LevelSector
= (isSector
? &Level
->Sectors
[snum
] : nullptr);
1597 lpar
.LevelScale
= sscale
;
1599 Level
->AddStaticLightRGB(owneruid
, lpar
, flags
);
1602 return !Msg
.IsError();
1606 #define GEN_FAST_UPDATE(name_,hashname_) do { \
1607 /*GCon->Log(NAME_DevNet, "VLevelChannel::Update -- " #name_ "s");*/ \
1608 for (auto &&it : hashname_.first()) { \
1609 int res = Update##name_(Msg, strm, it.getKey()); \
1610 if (res == -1) return; \
1612 PutStream(&Msg, strm); \
1613 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1619 #define GEN_UPDATE(name_) do { \
1620 /*GCon->Log(NAME_DevNet, "VLevelChannel::Update -- " #name_ "s");*/ \
1621 for (int i = 0; i < Level->Num ## name_ ## s; ++i) { \
1622 int res = Update##name_(Msg, strm, i); \
1624 /*GCon->Logf(NAME_DevNet, " item #%d aborted the update", i);*/ \
1628 /*GCon->Logf(NAME_DevNet, " flushing item #%d", i);*/ \
1629 PutStream(&Msg, strm); \
1630 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1636 #define GEN_UPDATE_ARR(name_) do { \
1637 /*GCon->Log(NAME_DevNet, "VLevelChannel::Update -- " #name_ "s");*/ \
1638 for (int i = 0; i < name_ ## s.length(); ++i) { \
1639 int res = Update##name_(Msg, strm, i); \
1640 if (res == -1) return; \
1642 PutStream(&Msg, strm); \
1643 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1649 //==========================================================================
1651 // VLevelChannel::Update
1653 //==========================================================================
1654 void VLevelChannel::Update () {
1655 if (Closing
) return; // just in case
1656 // if network connection is saturated, do nothing
1657 if (!CanSendData()) { Connection
->NeedsUpdate
= true; return; }
1661 if (UpdatedLines
.length() == 0 && UpdatedSides
.length() == 0) return;
1663 VMessageOut
Msg(this);
1664 VBitStreamWriter
strm(MAX_MSG_SIZE_BITS
+64, false); // no expand
1666 GEN_FAST_UPDATE(Sector
, Connection
->UpdatedSectors
);
1667 GEN_FAST_UPDATE(Side
, UpdatedSides
);
1668 GEN_FAST_UPDATE(Line
, UpdatedLines
);
1676 GEN_UPDATE(PolyObj
);
1677 GEN_UPDATE_ARR(CameraTexture
);
1678 GEN_UPDATE_ARR(Translation
);
1679 GEN_UPDATE_ARR(BodyQueueTran
);
1681 // do not send static light updates yet
1682 // we need to move static light replication info here first
1684 for (int i = 0; i < Level->NumStaticLights; ++i) {
1685 const int oldsize = strm.GetNumBits();
1686 UpdateStaticLight(strm, i, false); // not forced
1687 if (strm.GetNumBits() != oldsize) {
1688 PutStream(&Msg, strm);
1689 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; }
1698 //==========================================================================
1700 // VLevelChannel::ParseMessage
1702 //==========================================================================
1703 void VLevelChannel::ParseMessage (VMessageIn
&Msg
) {
1704 if (!Connection
->IsClient()) {
1706 int Cmd
= (int)Msg
.ReadUInt();
1707 if (!Msg
.IsError() && Cmd
== CMD_ClientMapLoaded
) {
1708 if (Phase
== PhaseWaitingMapLoaded
) {
1709 GCon
->Logf(NAME_DevNet
, "%s: client loaded the map", *GetDebugName());
1710 // advance to the next phase
1711 Phase
= PhaseStaticLights
;
1716 GCon
->Logf(NAME_DevNet
, "%s: client sent some level updates, ignoring", *GetDebugName());
1721 while (!err
&& !Msg
.AtEnd()) {
1722 int Cmd
= (int)Msg
.ReadUInt();
1723 if (Msg
.IsError()) {
1724 GCon
->Logf(NAME_DevNet
, "%s: cannot read command", *GetDebugName());
1729 case CMD_Line
: err
= !ParseLine(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading line update", *GetDebugName()); break;
1730 case CMD_Side
: err
= !ParseSide(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading side update", *GetDebugName()); break;
1731 case CMD_SideTexture
: err
= !ParseSideTexture(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading side update", *GetDebugName()); break;
1732 case CMD_SideTOffset
: err
= !ParseSideTOffset(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading side update", *GetDebugName()); break;
1733 case CMD_SideROffset
: err
= !ParseSideROffset(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading side update", *GetDebugName()); break;
1734 case CMD_SideScale
: err
= !ParseSideScale(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading side update", *GetDebugName()); break;
1735 case CMD_Sector
: err
= !ParseSector(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading sector update", *GetDebugName()); break;
1736 case CMD_SectorTexture
: err
= !ParseSectorTexture(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading sector update", *GetDebugName()); break;
1737 case CMD_SectorLight
: err
= !ParseSectorLight(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading sector update", *GetDebugName()); break;
1738 case CMD_PolyObj
: err
= !ParsePolyObj(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading polyobject update", *GetDebugName()); break;
1739 case CMD_CamTex
: err
= !ParseCameraTexture(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading camtex update", *GetDebugName()); break;
1740 case CMD_LevelTrans
: err
= !ParseTranslation(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading translation update", *GetDebugName()); break;
1741 case CMD_BodyQueueTrans
: err
= !ParseBodyQueueTran(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading body queue update", *GetDebugName()); break;
1742 case CMD_StaticLight
: err
= !ParseStaticLight(Msg
); if (err
) GCon
->Logf(NAME_DevNet
, "%s: error reading static light update", *GetDebugName()); break;
1743 case CMD_ResetStaticLights
: //TODO
1744 // currently, there is nothing to do here, because client level is spawned without entities anyway
1746 Level
->ResetStaticLights();
1754 csi
.maxclients
= Msg
.ReadUInt();
1755 csi
.deathmatch
= Msg
.ReadUInt();
1756 if (Msg
.IsError() || csi
.maxclients
< 1 || csi
.maxclients
> MAXPLAYERS
) {
1757 GCon
->Logf(NAME_DevNet
, "%s: invalid level handshake sequence", *GetDebugName());
1761 if (csi
.modhash
!= FL_GetNetWadsHash()) {
1762 GCon
->Logf(NAME_DevNet
, "%s: server has different wad set", *GetDebugName());
1766 // prepare serveinfo buffer
1767 serverInfoBuf
.clear();
1768 GCon
->Logf(NAME_DevNet
, "%s: received new map request (map name is '%s')", *GetDebugName(), *csi
.mapname
);
1770 Host_Error("CMD_NewLevel from client");
1773 case CMD_ServerInfo
:
1775 while (!Msg
.AtEnd()) {
1778 if (Msg
.IsError()) {
1779 GCon
->Logf(NAME_DevNet
, "%s: invalid server info packet (got %d server info bytes so far)", *GetDebugName(), serverInfoBuf
.length());
1783 serverInfoBuf
+= (char)ch
;
1784 if (serverInfoBuf
.length() > 1024*1024) {
1785 GCon
->Logf(NAME_DevNet
, "%s: server info packet too long", *GetDebugName());
1791 Host_Error("CMD_ServerInfo from client");
1794 case CMD_ServerInfoEnd
:
1796 GCon
->Logf(NAME_DevNet
, "%s: received server info: \"%s\"", *GetDebugName(), *serverInfoBuf
.quote());
1797 csi
.sinfo
= serverInfoBuf
;
1798 serverInfoBuf
.clear();
1799 CL_ParseServerInfo(&csi
); // this loads map
1800 csi
.mapname
.clear();
1803 Host_Error("CMD_ServerInfoEnd from client");
1806 case CMD_PreRender
: // sent by server, received by client
1808 GCon
->Logf(NAME_DevNet
, "%s: received prerender", *GetDebugName());
1809 if (Level
->Renderer
) Level
->Renderer
->PreRender();
1811 GCon
->Logf(NAME_DevNet
, "%s: Client_Spawn command already sent", *GetDebugName());
1815 if (!UserInfoSent
) {
1816 cl
->eventServerSetUserInfo(cls
.userinfo
);
1817 UserInfoSent
= true;
1820 Connection
->SendCommand("PreSpawn\n");
1821 GCmdBuf
<< "HideConsole\n";
1823 Host_Error("CMD_PreRender from client");
1826 case CMD_ResetLevel
:
1828 GCon
->Logf(NAME_DevNet
, "%s: received level reset", *GetDebugName());
1831 Host_Error("CMD_ResetLevel from client");
1834 case CMD_ClientMapLoaded
:
1835 if (Phase
== PhaseWaitingMapLoaded
) {
1836 GCon
->Logf(NAME_DevNet
, "%s: client loaded the map", *GetDebugName());
1837 // advance to the next phase
1838 Phase
= PhaseStaticLights
;
1842 GCon
->Logf(NAME_DevNet
, "%s: received invalid level command from client (%d)", *GetDebugName(), Cmd
);
1849 GCon
->Logf(NAME_DevNet
, "%s: clsing connection due to level update errors", *GetDebugName());
1850 Connection
->Close();