libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / net / net_channel_level.cpp
blob2ecde30815616228734a3b2a21db5117222e9f02
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 "network.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"
33 #ifdef CLIENT
34 # include "../drawer.h"
35 # include "../client/client.h"
36 # include "../client/cl_local.h"
37 #endif
40 // ////////////////////////////////////////////////////////////////////////// //
41 enum {
42 CMD_Side,
43 CMD_SideTexture,
44 CMD_SideTOffset,
45 CMD_SideROffset,
46 CMD_SideScale,
47 CMD_Sector,
48 CMD_SectorTexture,
49 CMD_SectorLight,
50 CMD_PolyObj,
51 CMD_StaticLight,
52 CMD_NewLevel,
53 CMD_ServerInfo,
54 CMD_ServerInfoEnd,
55 CMD_PreRender,
56 CMD_Line,
57 CMD_CamTex,
58 CMD_LevelTrans,
59 CMD_BodyQueueTrans,
61 CMD_ResetStaticLights,
62 CMD_ResetLevel,
64 CMD_ClientMapLoaded,
68 //==========================================================================
70 // VLevelChannel::VLevelChannel
72 //==========================================================================
73 VLevelChannel::VLevelChannel (VNetConnection *AConnection, vint32 AIndex, vuint8 AOpenedLocally)
74 : VChannel(AConnection, CHANNEL_Level, AIndex, AOpenedLocally)
75 , Level(nullptr)
76 , Lines(nullptr)
77 , Sides(nullptr)
78 , Sectors(nullptr)
80 OpenAcked = true; // this channel is pre-opened
81 serverInfoBuf.clear();
82 csi.mapname.clear();
83 csi.sinfo.clear();
84 csi.maxclients = 1;
85 csi.deathmatch = 0;
86 StaticLightsNext = 0;
87 MapLoadingStartTime = 0;
88 Phase = PhaseDone;
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) {
118 if (Level) {
119 delete[] Lines;
120 delete[] Sides;
121 delete[] Sectors;
122 delete[] PolyObjs;
123 Lines = nullptr;
124 Sides = nullptr;
125 Sectors = nullptr;
126 PolyObjs = nullptr;
127 CameraTextures.Clear();
128 Translations.Clear();
129 BodyQueueTrans.Clear();
132 Level = ALevel;
133 StaticLightsNext = 0;
134 Phase = PhaseServerInfo;
135 MapLoadingStartTime = 0;
137 if (Level) {
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 () {
156 if (Level) {
157 delete[] Lines;
158 delete[] Sides;
159 delete[] Sectors;
160 delete[] PolyObjs;
161 Lines = nullptr;
162 Sides = nullptr;
163 Sectors = nullptr;
164 PolyObjs = nullptr;
165 CameraTextures.Clear();
166 Translations.Clear();
167 BodyQueueTrans.Clear();
168 Level = nullptr;
169 serverInfoBuf.clear();
170 csi.mapname.clear();
171 csi.sinfo.clear();
172 csi.maxclients = 1;
173 csi.deathmatch = 0;
174 StaticLightsNext = 0;
175 Phase = PhaseServerInfo;
180 //==========================================================================
182 // VLevelChannel::SendLevelData
184 //==========================================================================
185 bool VLevelChannel::SendLevelData () {
186 if (Connection->IsClosed()) return false;
188 switch (Phase) {
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());
217 Msg << MapName;
218 Msg << MapHash;
219 Msg << modhash;
220 Msg.WriteUInt(svs.max_clients);
221 Msg.WriteUInt(svs.deathmatch);
222 SendMessage(&Msg);
225 // send server info
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)) {
231 FlushMsg(&Msg);
232 Msg.WriteUInt(CMD_ServerInfo);
234 vuint8 ch = (vuint8)(sinfo[f]&0xff);
235 Msg << ch;
237 FlushMsg(&Msg);
238 Msg.WriteUInt(CMD_ServerInfoEnd);
239 SendMessage(&Msg);
242 if (!Connection->AutoAck) {
243 Phase = PhaseWaitingMapLoaded;
244 MapLoadingStartTime = Connection->Driver->GetNetTime();
245 } else {
246 // demo recording
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());
261 Connection->Close();
266 //==========================================================================
268 // VLevelChannel::SendStaticLights
270 //==========================================================================
271 void VLevelChannel::SendStaticLights () {
272 // just in case
273 if (StaticLightsNext >= Level->StaticLights.length()) {
274 SendPreRender();
275 return;
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;
286 // forced update
287 if (UpdateStaticLight(Msg, strm, i, true)) {
288 PutStream(&Msg, strm);
289 if (!CanSendData()) { FlushMsg(&Msg); return; }
293 FlushMsg(&Msg);
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);
308 SendMessage(&Msg);
310 Phase = PhaseDone;
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);
325 SendMessage(&Msg);
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;
341 // process all lines
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) {
373 strm << Line->alpha;
374 strm.WriteBit(!!(Line->flags&ML_ADDITIVE));
375 RepLine->alpha = Line->alpha;
378 return 1;
382 //==========================================================================
384 // VLevelChannel::ParseLine
386 //==========================================================================
387 bool VLevelChannel::ParseLine (VMessageIn &Msg) {
388 int lidx = 0;
389 Msg << STRM_INDEX_U(lidx);
390 if (Msg.IsError()) {
391 GCon->Logf(NAME_DevNet, "%s: cannot read line index", *GetDebugName());
392 return false;
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);
396 return false;
399 line_t *Line = &Level->Lines[lidx];
400 if (Msg.ReadBit()) {
401 Msg << Line->alpha;
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))
423 return 0;
427 int res = 0;
428 rep_side_t *RepSide = &Sides[sidx];
430 // top texture
431 if (Side->TopTexture != RepSide->TopTexture) {
432 strm.WriteUInt(CMD_SideTexture);
433 strm << STRM_INDEX_U(sidx);
434 // 0
435 strm.WriteBit(false);
436 strm.WriteBit(false);
437 // data
438 Side->TopTexture.Serialise(strm);
440 RepSide->TopTexture = Side->TopTexture;
442 PutStream(&Msg, strm);
443 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return -1; }
444 res = 1;
447 // bottom texture
448 if (Side->BottomTexture != RepSide->BottomTexture) {
449 strm.WriteUInt(CMD_SideTexture);
450 strm << STRM_INDEX_U(sidx);
451 // 1
452 strm.WriteBit(true);
453 strm.WriteBit(false);
454 // data
455 Side->BottomTexture.Serialise(strm);
457 RepSide->BottomTexture = Side->BottomTexture;
459 PutStream(&Msg, strm);
460 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return -1; }
461 res = 1;
464 // middle texture
465 if (Side->MidTexture != RepSide->MidTexture) {
466 strm.WriteUInt(CMD_SideTexture);
467 strm << STRM_INDEX_U(sidx);
468 // 2
469 strm.WriteBit(false);
470 strm.WriteBit(true);
471 // data
472 Side->MidTexture.Serialise(strm);
474 RepSide->MidTexture = Side->MidTexture;
476 PutStream(&Msg, strm);
477 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return -1; }
478 res = 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; }
509 res = 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; }
540 res = 1;
543 // texture scaling
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; }
592 res = 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) {
610 strm << Side->Light;
611 RepSide->Light = Side->Light;
614 PutStream(&Msg, strm);
615 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return -1; }
616 res = 1;
619 return res;
623 //==========================================================================
625 // VLevelChannel::ParseSideTexture
627 //==========================================================================
628 bool VLevelChannel::ParseSideTexture (VMessageIn &Msg) {
629 int sidx = 0;
630 Msg << STRM_INDEX_U(sidx);
631 if (Msg.IsError()) {
632 GCon->Logf(NAME_DevNet, "%s: cannot read side index", *GetDebugName());
633 return false;
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);
637 return false;
640 // get texture number
641 vuint8 tnum = 0;
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);
646 return false;
649 side_t *Side = &Level->Sides[sidx];
650 switch (tnum) {
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
654 default: abort();
657 return !Msg.IsError();
661 //==========================================================================
663 // VLevelChannel::ParseSideTOffset
665 //==========================================================================
666 bool VLevelChannel::ParseSideTOffset (VMessageIn &Msg) {
667 int sidx = 0;
668 Msg << STRM_INDEX_U(sidx);
669 if (Msg.IsError()) {
670 GCon->Logf(NAME_DevNet, "%s: cannot read side index", *GetDebugName());
671 return false;
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);
675 return false;
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) {
693 int sidx = 0;
694 Msg << STRM_INDEX_U(sidx);
695 if (Msg.IsError()) {
696 GCon->Logf(NAME_DevNet, "%s: cannot read side index", *GetDebugName());
697 return false;
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);
701 return false;
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) {
719 int sidx = 0;
720 Msg << STRM_INDEX_U(sidx);
721 if (Msg.IsError()) {
722 GCon->Logf(NAME_DevNet, "%s: cannot read side index", *GetDebugName());
723 return false;
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);
727 return false;
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) {
748 int sidx = 0;
749 Msg << STRM_INDEX_U(sidx);
750 if (Msg.IsError()) {
751 GCon->Logf(NAME_DevNet, "%s: cannot read side index", *GetDebugName());
752 return false;
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);
756 return false;
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);
774 if (vcheck != v) {
775 strm.WriteBit(true);
776 strm << v;
777 vcheck = v;
778 } else {
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))
804 return 0;
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];
816 int res = 0;
818 // floor texture
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; }
829 res = 1;
832 // ceiling texture
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; }
843 res = 1;
846 // floor
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; }
891 res = 1;
894 // ceiling
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; }
939 res = 1;
942 // params
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; }
1000 res = 1;
1003 return res;
1007 //==========================================================================
1009 // VLevelChannel::ParseSectorTexture
1011 //==========================================================================
1012 bool VLevelChannel::ParseSectorTexture (VMessageIn &Msg) {
1013 int sidx = 0;
1014 Msg << STRM_INDEX_U(sidx);
1015 if (Msg.IsError()) {
1016 GCon->Logf(NAME_DevNet, "%s: cannot read sector index", *GetDebugName());
1017 return false;
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);
1021 return false;
1024 sector_t *Sec = &Level->Sectors[sidx];
1026 if (Msg.ReadBit()) {
1027 Sec->ceiling.pic.Serialise(Msg);
1028 } else {
1029 Sec->floor.pic.Serialise(Msg);
1032 return !Msg.IsError();
1036 //==========================================================================
1038 // VLevelChannel::ParseSectorLight
1040 //==========================================================================
1041 bool VLevelChannel::ParseSectorLight (VMessageIn &Msg) {
1042 int sidx = 0;
1043 Msg << STRM_INDEX_U(sidx);
1044 if (Msg.IsError()) {
1045 GCon->Logf(NAME_DevNet, "%s: cannot read sector index", *GetDebugName());
1046 return false;
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);
1050 return false;
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) {
1078 int sidx = 0;
1079 Msg << STRM_INDEX_U(sidx);
1080 if (Msg.IsError()) {
1081 GCon->Logf(NAME_DevNet, "%s: cannot read sector index", *GetDebugName());
1082 return false;
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);
1086 return false;
1089 sector_t *Sec = &Level->Sectors[sidx];
1091 const float PrevFloorDist = Sec->floor.dist;
1092 const float PrevCeilDist = Sec->ceiling.dist;
1094 // `false` is floor
1095 if (Msg.ReadBit()) {
1096 // ceiling
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;
1114 } else {
1115 // floor
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);
1141 return true;
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())) {
1157 needUpdate = true;
1158 break;
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)
1169 return 0;
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;
1187 return 1;
1191 //==========================================================================
1193 // VLevelChannel::ParsePolyObj
1195 //==========================================================================
1196 bool VLevelChannel::ParsePolyObj (VMessageIn &Msg) {
1197 int oidx = 0;
1198 Msg << STRM_INDEX_U(oidx);
1199 if (Msg.IsError()) {
1200 GCon->Logf(NAME_DevNet, "%s: cannot read polyobject index", *GetDebugName());
1201 return false;
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);
1205 return false;
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()) {
1216 float a = 0;
1217 Msg << a;
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();
1238 C.Camera = nullptr;
1239 C.TexNum = -1;
1240 C.FOV = 0;
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;
1250 // send message
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;
1263 return 1;
1267 //==========================================================================
1269 // VLevelChannel::ParseCameraTexture
1271 //==========================================================================
1272 bool VLevelChannel::ParseCameraTexture (VMessageIn &Msg) {
1273 int idx = 0;
1274 Msg << STRM_INDEX_U(idx);
1275 if (Msg.IsError()) {
1276 GCon->Logf(NAME_DevNet, "%s: cannot read camtex index", *GetDebugName());
1277 return false;
1279 if (idx < 0 || idx > 255) {
1280 GCon->Logf(NAME_DevNet, "%s: got invalid camtex index %d (max is %d)", *GetDebugName(), idx, 1023);
1281 return false;
1284 while (Level->CameraTextures.length() <= idx) {
1285 VCameraTextureInfo &C = Level->CameraTextures.Alloc();
1286 C.Camera = nullptr;
1287 C.TexNum = -1;
1288 C.FOV = 0;
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());
1315 if (Eq) {
1316 for (int j = 0; j < Rep.length(); ++j) {
1317 if (memcmp(&Tr->Commands[j], &Rep[j], sizeof(Rep[j]))) {
1318 Eq = false;
1319 break;
1323 if (Eq) return 0;
1325 // send message
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;
1339 Rep[j] = C;
1342 return 1;
1346 //==========================================================================
1348 // VLevelChannel::ParseTranslation
1350 //==========================================================================
1351 bool VLevelChannel::ParseTranslation (VMessageIn &Msg) {
1352 int idx = 0;
1353 Msg << STRM_INDEX_U(idx);
1354 if (Msg.IsError()) {
1355 GCon->Logf(NAME_DevNet, "%s: cannot read translation index", *GetDebugName());
1356 return false;
1358 if (idx < 0 || idx > 4095) {
1359 GCon->Logf(NAME_DevNet, "%s: got invalid translation index %d (max is %d)", *GetDebugName(), idx, 4095);
1360 return false;
1363 while (Level->Translations.length() <= idx) Level->Translations.Append(nullptr);
1364 VTextureTranslation *Tr = Level->Translations[idx];
1365 if (!Tr) {
1366 Tr = new VTextureTranslation;
1367 Level->Translations[idx] = Tr;
1369 Tr->Clear();
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;
1375 if (Type == 0) {
1376 vuint8 Start;
1377 vuint8 End;
1378 vuint8 SrcStart;
1379 vuint8 SrcEnd;
1380 Msg << Start << End << SrcStart << SrcEnd;
1381 if (Msg.IsError()) return false;
1382 Tr->MapToRange(Start, End, SrcStart, SrcEnd);
1383 } else if (Type == 1) {
1384 vuint8 Start;
1385 vuint8 End;
1386 vuint8 R1;
1387 vuint8 G1;
1388 vuint8 B1;
1389 vuint8 R2;
1390 vuint8 G2;
1391 vuint8 B2;
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) {
1396 vuint8 Start;
1397 vuint8 End;
1398 vuint8 R1;
1399 vuint8 G1;
1400 vuint8 B1;
1401 vuint8 R2;
1402 vuint8 G2;
1403 vuint8 B2;
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) {
1408 vuint8 Start;
1409 vuint8 End;
1410 vuint8 R1;
1411 vuint8 G1;
1412 vuint8 B1;
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) {
1417 vuint8 Start;
1418 vuint8 End;
1419 vuint8 R1;
1420 vuint8 G1;
1421 vuint8 B1;
1422 vuint8 Amount;
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;
1450 // send message
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;
1460 return 1;
1464 //==========================================================================
1466 // VLevelChannel::ParseBodyQueueTran
1468 //==========================================================================
1469 bool VLevelChannel::ParseBodyQueueTran (VMessageIn &Msg) {
1470 int idx = 0;
1471 Msg << STRM_INDEX_U(idx);
1472 if (Msg.IsError()) {
1473 GCon->Logf(NAME_DevNet, "%s: cannot read body queue index", *GetDebugName());
1474 return false;
1476 if (idx < 0 || idx > 8191) {
1477 GCon->Logf(NAME_DevNet, "%s: got invalid body queue index %d (max is %d)", *GetDebugName(), idx, 8191);
1478 return false;
1481 while (Level->BodyQueueTrans.length() <= idx) Level->BodyQueueTrans.Append(nullptr);
1482 VTextureTranslation *Tr = Level->BodyQueueTrans[idx];
1483 if (!Tr) {
1484 Tr = new VTextureTranslation;
1485 Level->BodyQueueTrans[idx] = Tr;
1487 Tr->Clear();
1489 vuint8 TrStart = 0;
1490 vuint8 TrEnd = 0;
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);
1525 if (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);
1538 if (flags) {
1539 strm.WriteBit(true);
1540 strm << STRM_INDEX_U(flags);
1541 } else {
1542 strm.WriteBit(false);
1545 if (!forced) L.Flags &= ~rep_light_t::LightChanged;
1547 return 1;
1551 //==========================================================================
1553 // VLevelChannel::ParseStaticLight
1555 //==========================================================================
1556 bool VLevelChannel::ParseStaticLight (VMessageIn &Msg) {
1557 vuint32 owneruid;
1558 TVec Origin;
1559 float Radius;
1560 vuint32 Color;
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;
1567 if (isCone) {
1568 Msg << ConeDir << ConeAngle;
1571 bool isSector = Msg.ReadBit();
1572 vuint32 snum = 0xffffffffu;
1573 float sscale = 0.0f;
1574 if (isSector) {
1575 Msg << STRM_INDEX_U(snum) << sscale;
1576 if (snum >= (unsigned)Level->NumSectors) isSector = false;
1579 vuint32 flags = 0;
1580 bool isFlags = Msg.ReadBit();
1581 if (isFlags) {
1582 Msg << STRM_INDEX_U(flags);
1585 if (Msg.IsError()) {
1586 GCon->Logf(NAME_DevNet, "%s: cannot read static light header", *GetDebugName());
1587 return false;
1590 VLightParams lpar;
1591 lpar.Origin = Origin;
1592 lpar.Radius = Radius;
1593 lpar.Color = Color;
1594 lpar.coneDirection = ConeDir;
1595 lpar.coneAngle = ConeAngle;
1596 lpar.LevelSector = (isSector ? &Level->Sectors[snum] : nullptr);
1597 lpar.LevelScale = sscale;
1598 #ifdef CLIENT
1599 Level->AddStaticLightRGB(owneruid, lpar, flags);
1600 #endif
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; \
1611 if (res > 0) { \
1612 PutStream(&Msg, strm); \
1613 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1616 } while (0)
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); \
1623 if (res == -1) { \
1624 /*GCon->Logf(NAME_DevNet, " item #%d aborted the update", i);*/ \
1625 return; \
1627 if (res > 0) { \
1628 /*GCon->Logf(NAME_DevNet, " flushing item #%d", i);*/ \
1629 PutStream(&Msg, strm); \
1630 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1633 } while (0)
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; \
1641 if (res > 0) { \
1642 PutStream(&Msg, strm); \
1643 if (!CanSendData()) { FlushMsg(&Msg); Connection->NeedsUpdate = true; return; } \
1646 } while (0)
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; }
1659 BuildUpdateSets();
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);
1671 GEN_UPDATE(Line);
1672 GEN_UPDATE(Side);
1673 GEN_UPDATE(Sector);
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; }
1694 FlushMsg(&Msg);
1698 //==========================================================================
1700 // VLevelChannel::ParseMessage
1702 //==========================================================================
1703 void VLevelChannel::ParseMessage (VMessageIn &Msg) {
1704 if (!Connection->IsClient()) {
1705 if (!Msg.AtEnd()) {
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;
1712 return;
1716 GCon->Logf(NAME_DevNet, "%s: client sent some level updates, ignoring", *GetDebugName());
1717 return;
1720 bool err = false;
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());
1725 err = true;
1726 break;
1728 switch (Cmd) {
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
1745 #ifdef CLIENT
1746 Level->ResetStaticLights();
1747 #endif
1748 break;
1749 case CMD_NewLevel:
1750 #ifdef CLIENT
1751 Msg << csi.mapname;
1752 Msg << csi.maphash;
1753 Msg << csi.modhash;
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());
1758 err = true;
1759 break;
1761 if (csi.modhash != FL_GetNetWadsHash()) {
1762 GCon->Logf(NAME_DevNet, "%s: server has different wad set", *GetDebugName());
1763 err = true;
1764 break;
1766 // prepare serveinfo buffer
1767 serverInfoBuf.clear();
1768 GCon->Logf(NAME_DevNet, "%s: received new map request (map name is '%s')", *GetDebugName(), *csi.mapname);
1769 #else
1770 Host_Error("CMD_NewLevel from client");
1771 #endif
1772 break;
1773 case CMD_ServerInfo:
1774 #ifdef CLIENT
1775 while (!Msg.AtEnd()) {
1776 vuint8 ch = 0;
1777 Msg << ch;
1778 if (Msg.IsError()) {
1779 GCon->Logf(NAME_DevNet, "%s: invalid server info packet (got %d server info bytes so far)", *GetDebugName(), serverInfoBuf.length());
1780 err = true;
1781 break;
1783 serverInfoBuf += (char)ch;
1784 if (serverInfoBuf.length() > 1024*1024) {
1785 GCon->Logf(NAME_DevNet, "%s: server info packet too long", *GetDebugName());
1786 err = true;
1787 break;
1790 #else
1791 Host_Error("CMD_ServerInfo from client");
1792 #endif
1793 break;
1794 case CMD_ServerInfoEnd:
1795 #ifdef CLIENT
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();
1801 csi.sinfo.clear();
1802 #else
1803 Host_Error("CMD_ServerInfoEnd from client");
1804 #endif
1805 break;
1806 case CMD_PreRender: // sent by server, received by client
1807 #ifdef CLIENT
1808 GCon->Logf(NAME_DevNet, "%s: received prerender", *GetDebugName());
1809 if (Level->Renderer) Level->Renderer->PreRender();
1810 if (cls.signon) {
1811 GCon->Logf(NAME_DevNet, "%s: Client_Spawn command already sent", *GetDebugName());
1812 err = true;
1813 break;
1815 if (!UserInfoSent) {
1816 cl->eventServerSetUserInfo(cls.userinfo);
1817 UserInfoSent = true;
1819 cls.gotmap = 2;
1820 Connection->SendCommand("PreSpawn\n");
1821 GCmdBuf << "HideConsole\n";
1822 #else
1823 Host_Error("CMD_PreRender from client");
1824 #endif
1825 break;
1826 case CMD_ResetLevel:
1827 #ifdef CLIENT
1828 GCon->Logf(NAME_DevNet, "%s: received level reset", *GetDebugName());
1829 ResetLevel();
1830 #else
1831 Host_Error("CMD_ResetLevel from client");
1832 #endif
1833 break;
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;
1840 break;
1841 default:
1842 GCon->Logf(NAME_DevNet, "%s: received invalid level command from client (%d)", *GetDebugName(), Cmd);
1843 err = true;
1844 break;
1848 if (err) {
1849 GCon->Logf(NAME_DevNet, "%s: clsing connection due to level update errors", *GetDebugName());
1850 Connection->Close();