1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: net_channel_level.cpp 3884 2008-12-01 20:47:53Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "network.h"
30 #include "cl_local.h"
31 #include "sv_local.h"
32 
33 // MACROS ------------------------------------------------------------------
34 
35 // TYPES -------------------------------------------------------------------
36 
37 enum
38 {
39 	CMD_Side,
40 	CMD_Sector,
41 	CMD_PolyObj,
42 	CMD_StaticLight,
43 	CMD_NewLevel,
44 	CMD_PreRender,
45 	CMD_Line,
46 	CMD_CamTex,
47 	CMD_LevelTrans,
48 	CMD_BodyQueueTrans,
49 
50 	CMD_MAX
51 };
52 
53 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
54 
55 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
56 
57 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
58 
59 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
60 
61 // PUBLIC DATA DEFINITIONS -------------------------------------------------
62 
63 // PRIVATE DATA DEFINITIONS ------------------------------------------------
64 
65 // CODE --------------------------------------------------------------------
66 
67 //==========================================================================
68 //
69 //	VLevelChannel::VLevelChannel
70 //
71 //==========================================================================
72 
VLevelChannel(VNetConnection * AConnection,vint32 AIndex,vuint8 AOpenedLocally)73 VLevelChannel::VLevelChannel(VNetConnection* AConnection, vint32 AIndex,
74 	vuint8 AOpenedLocally)
75 : VChannel(AConnection, CHANNEL_Player, AIndex, AOpenedLocally)
76 , Level(NULL)
77 , Lines(NULL)
78 , Sides(NULL)
79 , Sectors(NULL)
80 {
81 }
82 
83 //==========================================================================
84 //
85 //	VLevelChannel::~VLevelChannel
86 //
87 //==========================================================================
88 
~VLevelChannel()89 VLevelChannel::~VLevelChannel()
90 {
91 	SetLevel(NULL);
92 }
93 
94 //==========================================================================
95 //
96 //	VLevelChannel::SetLevel
97 //
98 //==========================================================================
99 
SetLevel(VLevel * ALevel)100 void VLevelChannel::SetLevel(VLevel* ALevel)
101 {
102 	guard(VLevelChannel::SetLevel);
103 	if (Level)
104 	{
105 		delete[] Lines;
106 		delete[] Sides;
107 		delete[] Sectors;
108 		delete[] PolyObjs;
109 		Lines = NULL;
110 		Sides = NULL;
111 		Sectors = NULL;
112 		PolyObjs = NULL;
113 		CameraTextures.Clear();
114 		Translations.Clear();
115 		BodyQueueTrans.Clear();
116 	}
117 
118 	Level = ALevel;
119 
120 	if (Level)
121 	{
122 		Lines = new rep_line_t[Level->NumLines];
123 		memcpy(Lines, Level->BaseLines, sizeof(rep_line_t) * Level->NumLines);
124 		Sides = new rep_side_t[Level->NumSides];
125 		memcpy(Sides, Level->BaseSides, sizeof(rep_side_t) * Level->NumSides);
126 		Sectors = new rep_sector_t[Level->NumSectors];
127 		memcpy(Sectors, Level->BaseSectors, sizeof(rep_sector_t) * Level->NumSectors);
128 		PolyObjs = new rep_polyobj_t[Level->NumPolyObjs];
129 		memcpy(PolyObjs, Level->BasePolyObjs, sizeof(rep_polyobj_t) * Level->NumPolyObjs);
130 	}
131 	unguard;
132 }
133 
134 //==========================================================================
135 //
136 //	VLevelChannel::SendNewLevel
137 //
138 //==========================================================================
139 
SendNewLevel()140 void VLevelChannel::SendNewLevel()
141 {
142 	guard(VLevelChannel::SendNewLevel);
143 	guardSlow(NewLevel);
144 	VMessageOut Msg(this);
145 	Msg.bReliable = true;
146 	Msg.WriteInt(CMD_NewLevel, CMD_MAX);
147 	VStr MapName = *Level->MapName;
148 	Msg << svs.serverinfo << MapName;
149 	Msg.WriteInt(svs.max_clients, MAXPLAYERS + 1);
150 	Msg.WriteInt(deathmatch, 256);
151 	SendMessage(&Msg);
152 	unguardSlow;
153 
154 	guardSlow(StaticLights);
155 	SendStaticLights();
156 	unguardSlow;
157 
158 	guardSlow(PreRender);
159 	VMessageOut Msg(this);
160 	Msg.bReliable = true;
161 	Msg.WriteInt(CMD_PreRender, CMD_MAX);
162 	SendMessage(&Msg);
163 	unguardSlow;
164 	unguard;
165 }
166 
167 //==========================================================================
168 //
169 //	VLevelChannel::Update
170 //
171 //==========================================================================
172 
Update()173 void VLevelChannel::Update()
174 {
175 	guard(VLevelChannel::Update);
176 	VMessageOut Msg(this);
177 	Msg.bReliable = true;
178 
179 	for (int i = 0; i < Level->NumLines; i++)
180 	{
181 		line_t* Line = &Level->Lines[i];
182 		//if (!Connection->SecCheckFatPVS(Line->sector))
183 		//	continue;
184 
185 		rep_line_t* RepLine = &Lines[i];
186 		if (Line->alpha == RepLine->alpha)
187 			continue;
188 
189 		Msg.WriteInt(CMD_Line, CMD_MAX);
190 		Msg.WriteInt(i, Level->NumLines);
191 		Msg.WriteBit(Line->alpha != RepLine->alpha);
192 		if (Line->alpha != RepLine->alpha)
193 		{
194 			Msg << Line->alpha;
195 			Msg.WriteBit(!!(Line->flags & ML_ADDITIVE));
196 			RepLine->alpha = Line->alpha;
197 		}
198 	}
199 
200 	for (int i = 0; i < Level->NumSides; i++)
201 	{
202 		side_t* Side = &Level->Sides[i];
203 		if (!Connection->SecCheckFatPVS(Side->Sector))
204 			continue;
205 
206 		rep_side_t* RepSide = &Sides[i];
207 		if (Side->TopTexture == RepSide->TopTexture &&
208 			Side->BottomTexture == RepSide->BottomTexture &&
209 			Side->MidTexture == RepSide->MidTexture &&
210 			Side->TopTextureOffset == RepSide->TopTextureOffset &&
211 			Side->BotTextureOffset == RepSide->BotTextureOffset &&
212 			Side->MidTextureOffset == RepSide->MidTextureOffset &&
213 			Side->TopRowOffset == RepSide->TopRowOffset &&
214 			Side->BotRowOffset == RepSide->BotRowOffset &&
215 			Side->MidRowOffset == RepSide->MidRowOffset &&
216 			Side->Flags == RepSide->Flags &&
217 			Side->Light == RepSide->Light)
218 			continue;
219 
220 		Msg.WriteInt(CMD_Side, CMD_MAX);
221 		Msg.WriteInt(i, Level->NumSides);
222 		Msg.WriteBit(Side->TopTexture != RepSide->TopTexture);
223 		if (Side->TopTexture != RepSide->TopTexture)
224 		{
225 			Msg.WriteInt(Side->TopTexture, MAX_VUINT16);
226 			RepSide->TopTexture = Side->TopTexture;
227 		}
228 		Msg.WriteBit(Side->BottomTexture != RepSide->BottomTexture);
229 		if (Side->BottomTexture != RepSide->BottomTexture)
230 		{
231 			Msg.WriteInt(Side->BottomTexture, MAX_VUINT16);
232 			RepSide->BottomTexture = Side->BottomTexture;
233 		}
234 		Msg.WriteBit(Side->MidTexture != RepSide->MidTexture);
235 		if (Side->MidTexture != RepSide->MidTexture)
236 		{
237 			Msg.WriteInt(Side->MidTexture, MAX_VUINT16);
238 			RepSide->MidTexture = Side->MidTexture;
239 		}
240 		Msg.WriteBit(Side->TopTextureOffset != RepSide->TopTextureOffset);
241 		if (Side->TopTextureOffset != RepSide->TopTextureOffset)
242 		{
243 			Msg << Side->TopTextureOffset;
244 			RepSide->TopTextureOffset = Side->TopTextureOffset;
245 		}
246 		Msg.WriteBit(Side->BotTextureOffset != RepSide->BotTextureOffset);
247 		if (Side->BotTextureOffset != RepSide->BotTextureOffset)
248 		{
249 			Msg << Side->BotTextureOffset;
250 			RepSide->BotTextureOffset = Side->BotTextureOffset;
251 		}
252 		Msg.WriteBit(Side->MidTextureOffset != RepSide->MidTextureOffset);
253 		if (Side->MidTextureOffset != RepSide->MidTextureOffset)
254 		{
255 			Msg << Side->MidTextureOffset;
256 			RepSide->MidTextureOffset = Side->MidTextureOffset;
257 		}
258 		Msg.WriteBit(Side->TopRowOffset != RepSide->TopRowOffset);
259 		if (Side->TopRowOffset != RepSide->TopRowOffset)
260 		{
261 			Msg << Side->TopRowOffset;
262 			RepSide->TopRowOffset = Side->TopRowOffset;
263 		}
264 		Msg.WriteBit(Side->BotRowOffset != RepSide->BotRowOffset);
265 		if (Side->BotRowOffset != RepSide->BotRowOffset)
266 		{
267 			Msg << Side->BotRowOffset;
268 			RepSide->BotRowOffset = Side->BotRowOffset;
269 		}
270 		Msg.WriteBit(Side->MidRowOffset != RepSide->MidRowOffset);
271 		if (Side->MidRowOffset != RepSide->MidRowOffset)
272 		{
273 			Msg << Side->MidRowOffset;
274 			RepSide->MidRowOffset = Side->MidRowOffset;
275 		}
276 		Msg.WriteBit(Side->Flags != RepSide->Flags);
277 		if (Side->Flags != RepSide->Flags)
278 		{
279 			Msg.WriteInt(Side->Flags, 0x000f);
280 			RepSide->Flags = Side->Flags;
281 		}
282 		Msg.WriteBit(Side->Light != RepSide->Light);
283 		if (Side->Light != RepSide->Light)
284 		{
285 			Msg << Side->Light;
286 			RepSide->Light = Side->Light;
287 		}
288 	}
289 
290 	for (int i = 0; i < Level->NumSectors; i++)
291 	{
292 		sector_t* Sec = &Level->Sectors[i];
293 		if (!Connection->SecCheckFatPVS(Sec) &&
294 			!(Sec->SectorFlags & sector_t::SF_ExtrafloorSource) &&
295 			!(Sec->SectorFlags & sector_t::SF_TransferSource))
296 			continue;
297 
298 		VEntity* FloorSkyBox = Sec->floor.SkyBox;
299 		if (FloorSkyBox && !Connection->ObjMap->CanSerialiseObject(FloorSkyBox))
300 		{
301 			FloorSkyBox = NULL;
302 		}
303 		VEntity* CeilSkyBox = Sec->ceiling.SkyBox;
304 		if (CeilSkyBox && !Connection->ObjMap->CanSerialiseObject(CeilSkyBox))
305 		{
306 			CeilSkyBox = NULL;
307 		}
308 
309 		rep_sector_t* RepSec = &Sectors[i];
310 		bool FloorChanged = RepSec->floor_dist != Sec->floor.dist ||
311 			mround(RepSec->floor_xoffs) != mround(Sec->floor.xoffs) ||
312 			mround(RepSec->floor_yoffs) != mround(Sec->floor.yoffs) ||
313 			RepSec->floor_XScale != Sec->floor.XScale ||
314 			RepSec->floor_YScale != Sec->floor.YScale ||
315 			mround(RepSec->floor_Angle) != mround(Sec->floor.Angle) ||
316 			mround(RepSec->floor_BaseAngle) != mround(Sec->floor.BaseAngle) ||
317 			mround(RepSec->floor_BaseYOffs) != mround(Sec->floor.BaseYOffs) ||
318 			RepSec->floor_SkyBox != FloorSkyBox;
319 		bool CeilChanged = RepSec->ceil_dist != Sec->ceiling.dist ||
320 			mround(RepSec->ceil_xoffs) != mround(Sec->ceiling.xoffs) ||
321 			mround(RepSec->ceil_yoffs) != mround(Sec->ceiling.yoffs) ||
322 			RepSec->ceil_XScale != Sec->ceiling.XScale ||
323 			RepSec->ceil_YScale != Sec->ceiling.YScale ||
324 			mround(RepSec->ceil_Angle) != mround(Sec->ceiling.Angle) ||
325 			mround(RepSec->ceil_BaseAngle) != mround(Sec->ceiling.BaseAngle) ||
326 			mround(RepSec->ceil_BaseYOffs) != mround(Sec->ceiling.BaseYOffs) ||
327 			RepSec->ceil_SkyBox != CeilSkyBox;
328 		bool LightChanged = abs(RepSec->lightlevel - Sec->params.lightlevel) >= 4;
329 		bool FadeChanged = RepSec->Fade != Sec->params.Fade;
330 		bool SkyChanged = RepSec->Sky != Sec->Sky;
331 		bool MirrorChanged = RepSec->floor_MirrorAlpha != Sec->floor.MirrorAlpha ||
332 			RepSec->ceil_MirrorAlpha != Sec->ceiling.MirrorAlpha;
333 		if (RepSec->floor_pic == Sec->floor.pic &&
334 			RepSec->ceil_pic == Sec->ceiling.pic &&
335 			!FloorChanged && !CeilChanged && !LightChanged && !FadeChanged &&
336 			!SkyChanged && !MirrorChanged)
337 			continue;
338 
339 		Msg.WriteInt(CMD_Sector, CMD_MAX);
340 		Msg.WriteInt(i, Level->NumSectors);
341 		Msg.WriteBit(RepSec->floor_pic != Sec->floor.pic);
342 		if (RepSec->floor_pic != Sec->floor.pic)
343 		{
344 			Msg.WriteInt(Sec->floor.pic, MAX_VUINT16);
345 		}
346 		Msg.WriteBit(RepSec->ceil_pic != Sec->ceiling.pic);
347 		if (RepSec->ceil_pic != Sec->ceiling.pic)
348 		{
349 			Msg.WriteInt(Sec->ceiling.pic, MAX_VUINT16);
350 		}
351 		Msg.WriteBit(FloorChanged);
352 		if (FloorChanged)
353 		{
354 			Msg.WriteBit(RepSec->floor_dist != Sec->floor.dist);
355 			if (RepSec->floor_dist != Sec->floor.dist)
356 			{
357 				Msg << Sec->floor.dist;
358 				Msg << Sec->floor.TexZ;
359 			}
360 			Msg.WriteBit(mround(RepSec->floor_xoffs) != mround(Sec->floor.xoffs));
361 			if (mround(RepSec->floor_xoffs) != mround(Sec->floor.xoffs))
362 				Msg.WriteInt(mround(Sec->floor.xoffs) & 63, 64);
363 			Msg.WriteBit(mround(RepSec->floor_yoffs) != mround(Sec->floor.yoffs));
364 			if (mround(RepSec->floor_yoffs) != mround(Sec->floor.yoffs))
365 				Msg.WriteInt(mround(Sec->floor.yoffs) & 63, 64);
366 			Msg.WriteBit(RepSec->floor_XScale != Sec->floor.XScale);
367 			if (RepSec->floor_XScale != Sec->floor.XScale)
368 				Msg << Sec->floor.XScale;
369 			Msg.WriteBit(RepSec->floor_YScale != Sec->floor.YScale);
370 			if (RepSec->floor_YScale != Sec->floor.YScale)
371 				Msg << Sec->floor.YScale;
372 			Msg.WriteBit(mround(RepSec->floor_Angle) != mround(Sec->floor.Angle));
373 			if (mround(RepSec->floor_Angle) != mround(Sec->floor.Angle))
374 				Msg.WriteInt((int)AngleMod(Sec->floor.Angle), 360);
375 			Msg.WriteBit(mround(RepSec->floor_BaseAngle) != mround(Sec->floor.BaseAngle));
376 			if (mround(RepSec->floor_BaseAngle) != mround(Sec->floor.BaseAngle))
377 				Msg.WriteInt((int)AngleMod(Sec->floor.BaseAngle), 360);
378 			Msg.WriteBit(mround(RepSec->floor_BaseYOffs) != mround(Sec->floor.BaseYOffs));
379 			if (mround(RepSec->floor_BaseYOffs) != mround(Sec->floor.BaseYOffs))
380 				Msg.WriteInt(mround(Sec->floor.BaseYOffs) & 63, 64);
381 			Msg.WriteBit(RepSec->floor_SkyBox != FloorSkyBox);
382 			if (RepSec->floor_SkyBox != FloorSkyBox)
383 				Msg << FloorSkyBox;
384 		}
385 		Msg.WriteBit(CeilChanged);
386 		if (CeilChanged)
387 		{
388 			Msg.WriteBit(RepSec->ceil_dist != Sec->ceiling.dist);
389 			if (RepSec->ceil_dist != Sec->ceiling.dist)
390 			{
391 				Msg << Sec->ceiling.dist;
392 				Msg << Sec->ceiling.TexZ;
393 			}
394 			Msg.WriteBit(mround(RepSec->ceil_xoffs) != mround(Sec->ceiling.xoffs));
395 			if (mround(RepSec->ceil_xoffs) != mround(Sec->ceiling.xoffs))
396 				Msg.WriteInt(mround(Sec->ceiling.xoffs) & 63, 64);
397 			Msg.WriteBit(mround(RepSec->ceil_yoffs) != mround(Sec->ceiling.yoffs));
398 			if (mround(RepSec->ceil_yoffs) != mround(Sec->ceiling.yoffs))
399 				Msg.WriteInt(mround(Sec->ceiling.yoffs) & 63, 64);
400 			Msg.WriteBit(RepSec->ceil_XScale != Sec->ceiling.XScale);
401 			if (RepSec->ceil_XScale != Sec->ceiling.XScale)
402 				Msg << Sec->ceiling.XScale;
403 			Msg.WriteBit(RepSec->ceil_YScale != Sec->ceiling.YScale);
404 			if (RepSec->ceil_YScale != Sec->ceiling.YScale)
405 				Msg << Sec->ceiling.YScale;
406 			Msg.WriteBit(mround(RepSec->ceil_Angle) != mround(Sec->ceiling.Angle));
407 			if (mround(RepSec->ceil_Angle) != mround(Sec->ceiling.Angle))
408 				Msg.WriteInt((int)AngleMod(Sec->ceiling.Angle), 360);
409 			Msg.WriteBit(mround(RepSec->ceil_BaseAngle) != mround(Sec->ceiling.BaseAngle));
410 			if (mround(RepSec->ceil_BaseAngle) != mround(Sec->ceiling.BaseAngle))
411 				Msg.WriteInt((int)AngleMod(Sec->ceiling.BaseAngle), 360);
412 			Msg.WriteBit(mround(RepSec->ceil_BaseYOffs) != mround(Sec->ceiling.BaseYOffs));
413 			if (mround(RepSec->ceil_BaseYOffs) != mround(Sec->ceiling.BaseYOffs))
414 				Msg.WriteInt(mround(Sec->ceiling.BaseYOffs) & 63, 64);
415 			Msg.WriteBit(RepSec->ceil_SkyBox != CeilSkyBox);
416 			if (RepSec->ceil_SkyBox != CeilSkyBox)
417 				Msg << CeilSkyBox;
418 		}
419 		Msg.WriteBit(LightChanged);
420 		if (LightChanged)
421 		{
422 			Msg.WriteInt(Sec->params.lightlevel >> 2, 256);
423 		}
424 		Msg.WriteBit(FadeChanged);
425 		if (FadeChanged)
426 		{
427 			Msg << Sec->params.Fade;
428 		}
429 		Msg.WriteBit(SkyChanged);
430 		if (SkyChanged)
431 		{
432 			Msg.WriteInt(Sec->Sky, MAX_VUINT16);
433 		}
434 		Msg.WriteBit(MirrorChanged);
435 		if (MirrorChanged)
436 		{
437 			Msg << Sec->floor.MirrorAlpha;
438 			Msg << Sec->ceiling.MirrorAlpha;
439 		}
440 
441 		RepSec->floor_pic = Sec->floor.pic;
442 		RepSec->floor_dist = Sec->floor.dist;
443 		RepSec->floor_xoffs = Sec->floor.xoffs;
444 		RepSec->floor_yoffs = Sec->floor.yoffs;
445 		RepSec->floor_XScale = Sec->floor.XScale;
446 		RepSec->floor_YScale = Sec->floor.YScale;
447 		RepSec->floor_Angle = Sec->floor.Angle;
448 		RepSec->floor_BaseAngle = Sec->floor.BaseAngle;
449 		RepSec->floor_BaseYOffs = Sec->floor.BaseYOffs;
450 		RepSec->floor_SkyBox = FloorSkyBox;
451 		RepSec->floor_MirrorAlpha = Sec->floor.MirrorAlpha;
452 		RepSec->ceil_pic = Sec->ceiling.pic;
453 		RepSec->ceil_dist = Sec->ceiling.dist;
454 		RepSec->ceil_xoffs = Sec->ceiling.xoffs;
455 		RepSec->ceil_yoffs = Sec->ceiling.yoffs;
456 		RepSec->ceil_XScale = Sec->ceiling.XScale;
457 		RepSec->ceil_YScale = Sec->ceiling.YScale;
458 		RepSec->ceil_Angle = Sec->ceiling.Angle;
459 		RepSec->ceil_BaseAngle = Sec->ceiling.BaseAngle;
460 		RepSec->ceil_BaseYOffs = Sec->ceiling.BaseYOffs;
461 		RepSec->ceil_SkyBox = CeilSkyBox;
462 		RepSec->ceil_MirrorAlpha = Sec->ceiling.MirrorAlpha;
463 		RepSec->lightlevel = Sec->params.lightlevel;
464 		RepSec->Fade = Sec->params.Fade;
465 	}
466 
467 	for (int i = 0; i < Level->NumPolyObjs; i++)
468 	{
469 		polyobj_t* Po = &Level->PolyObjs[i];
470 		if (!Connection->CheckFatPVS(Po->subsector))
471 			continue;
472 
473 		rep_polyobj_t* RepPo = &PolyObjs[i];
474 		if (RepPo->startSpot.x == Po->startSpot.x &&
475 			RepPo->startSpot.y == Po->startSpot.y &&
476 			RepPo->angle == Po->angle)
477 			continue;
478 
479 		Msg.WriteInt(CMD_PolyObj, CMD_MAX);
480 		Msg.WriteInt(i, Level->NumPolyObjs);
481 		Msg.WriteBit(RepPo->startSpot.x != Po->startSpot.x);
482 		if (RepPo->startSpot.x != Po->startSpot.x)
483 		{
484 			Msg << Po->startSpot.x;
485 		}
486 		Msg.WriteBit(RepPo->startSpot.y != Po->startSpot.y);
487 		if (RepPo->startSpot.y != Po->startSpot.y)
488 		{
489 			Msg << Po->startSpot.y;
490 		}
491 		Msg.WriteBit(RepPo->angle != Po->angle);
492 		if (RepPo->angle != Po->angle)
493 		{
494 			Msg << Po->angle;
495 		}
496 
497 		RepPo->startSpot = Po->startSpot;
498 		RepPo->angle = Po->angle;
499 	}
500 
501 	for (int i = 0; i < Level->CameraTextures.Num(); i++)
502 	{
503 		//	Grow replication array if needed.
504 		if (CameraTextures.Num() == i)
505 		{
506 			VCameraTextureInfo& C = CameraTextures.Alloc();
507 			C.Camera = NULL;
508 			C.TexNum = -1;
509 			C.FOV = 0;
510 		}
511 
512 		VCameraTextureInfo& Cam = Level->CameraTextures[i];
513 		VCameraTextureInfo& RepCam = CameraTextures[i];
514 		VEntity* CamEnt = Cam.Camera;
515 		if (CamEnt && !Connection->ObjMap->CanSerialiseObject(CamEnt))
516 		{
517 			CamEnt = NULL;
518 		}
519 		if (CamEnt == RepCam.Camera && Cam.TexNum == RepCam.TexNum &&
520 			Cam.FOV == RepCam.FOV)
521 		{
522 			continue;
523 		}
524 
525 		//	Send message
526 		Msg.WriteInt(CMD_CamTex, CMD_MAX);
527 		Msg.WriteInt(i, 0xff);
528 		Connection->ObjMap->SerialiseObject(Msg, *(VObject**)&CamEnt);
529 		Msg.WriteInt(Cam.TexNum, 0xffff);
530 		Msg.WriteInt(Cam.FOV, 360);
531 
532 		//	Update replication info.
533 		RepCam.Camera = CamEnt;
534 		RepCam.TexNum = Cam.TexNum;
535 		RepCam.FOV = Cam.FOV;
536 	}
537 
538 	for (int i = 0; i < Level->Translations.Num(); i++)
539 	{
540 		//	Grow replication array if needed.
541 		if (Translations.Num() == i)
542 		{
543 			Translations.Alloc();
544 		}
545 		if (!Level->Translations[i])
546 		{
547 			continue;
548 		}
549 		VTextureTranslation* Tr = Level->Translations[i];
550 		TArray<VTextureTranslation::VTransCmd>& Rep = Translations[i];
551 		bool Eq = Tr->Commands.Num() == Rep.Num();
552 		if (Eq)
553 		{
554 			for (int j = 0; j < Rep.Num(); j++)
555 			{
556 				if (memcmp(&Tr->Commands[j], &Rep[j], sizeof(Rep[j])))
557 				{
558 					Eq = false;
559 					break;
560 				}
561 			}
562 		}
563 		if (Eq)
564 		{
565 			continue;
566 		}
567 
568 		//	Send message
569 		Msg.WriteInt(CMD_LevelTrans, CMD_MAX);
570 		Msg.WriteInt(i, MAX_LEVEL_TRANSLATIONS);
571 		Msg.WriteInt(Tr->Commands.Num(), 0xff);
572 		Rep.SetNum(Tr->Commands.Num());
573 		for (int j = 0; j < Tr->Commands.Num(); j++)
574 		{
575 			VTextureTranslation::VTransCmd& C = Tr->Commands[j];
576 			Msg.WriteInt(C.Type, 2);
577 			if (C.Type == 0)
578 			{
579 				Msg << C.Start << C.End << C.R1 << C.R2;
580 			}
581 			else if (C.Type == 1)
582 			{
583 				Msg << C.Start << C.End << C.R1 << C.G1 << C.B1 << C.R2 <<
584 					C.G2 << C.B2;
585 			}
586 			Rep[j] = C;
587 		}
588 	}
589 
590 	for (int i = 0; i < Level->BodyQueueTrans.Num(); i++)
591 	{
592 		//	Grow replication array if needed.
593 		if (BodyQueueTrans.Num() == i)
594 		{
595 			BodyQueueTrans.Alloc().TranslStart = 0;
596 		}
597 		if (!Level->BodyQueueTrans[i])
598 		{
599 			continue;
600 		}
601 		VTextureTranslation* Tr = Level->BodyQueueTrans[i];
602 		if (!Tr->TranslStart)
603 		{
604 			continue;
605 		}
606 		VBodyQueueTrInfo& Rep = BodyQueueTrans[i];
607 		if (Tr->TranslStart == Rep.TranslStart &&
608 			Tr->TranslEnd == Rep.TranslEnd && Tr->Colour == Rep.Colour)
609 		{
610 			continue;
611 		}
612 
613 		//	Send message
614 		Msg.WriteInt(CMD_BodyQueueTrans, CMD_MAX);
615 		Msg.WriteInt(i, MAX_BODY_QUEUE_TRANSLATIONS);
616 		Msg << Tr->TranslStart << Tr->TranslEnd;
617 		Msg.WriteInt(Tr->Colour, 0x00ffffff);
618 		Rep.TranslStart = Tr->TranslStart;
619 		Rep.TranslEnd = Tr->TranslEnd;
620 		Rep.Colour = Tr->Colour;
621 	}
622 
623 	if (Msg.GetNumBits())
624 	{
625 		SendMessage(&Msg);
626 	}
627 	unguard;
628 }
629 
630 //==========================================================================
631 //
632 //	VLevelChannel::SendStaticLights
633 //
634 //==========================================================================
635 
SendStaticLights()636 void VLevelChannel::SendStaticLights()
637 {
638 	guard(VLevelChannel::SendStaticLights);
639 	for (int i = 0; i < Level->NumStaticLights; i++)
640 	{
641 		rep_light_t& L = Level->StaticLights[i];
642 		VMessageOut Msg(this);
643 		Msg.bReliable = true;
644 		Msg.WriteInt(CMD_StaticLight, CMD_MAX);
645 		Msg << L.Origin << L.Radius << L.Colour;
646 		SendMessage(&Msg);
647 	}
648 	unguard;
649 }
650 
651 //==========================================================================
652 //
653 //	VLevelChannel::ParsePacket
654 //
655 //==========================================================================
656 
ParsePacket(VMessageIn & Msg)657 void VLevelChannel::ParsePacket(VMessageIn& Msg)
658 {
659 	guard(VLevelChannel::ParsePacket);
660 	while (!Msg.AtEnd())
661 	{
662 		int Cmd = Msg.ReadInt(CMD_MAX);
663 		switch (Cmd)
664 		{
665 		case CMD_Side:
666 			{
667 				side_t* Side = &Level->Sides[Msg.ReadInt(Level->NumSides)];
668 				if (Msg.ReadBit())
669 					Side->TopTexture = Msg.ReadInt(MAX_VUINT16);
670 				if (Msg.ReadBit())
671 					Side->BottomTexture = Msg.ReadInt(MAX_VUINT16);
672 				if (Msg.ReadBit())
673 					Side->MidTexture = Msg.ReadInt(MAX_VUINT16);
674 				if (Msg.ReadBit())
675 					Msg << Side->TopTextureOffset;
676 				if (Msg.ReadBit())
677 					Msg << Side->BotTextureOffset;
678 				if (Msg.ReadBit())
679 					Msg << Side->MidTextureOffset;
680 				if (Msg.ReadBit())
681 					Msg << Side->TopRowOffset;
682 				if (Msg.ReadBit())
683 					Msg << Side->BotRowOffset;
684 				if (Msg.ReadBit())
685 					Msg << Side->MidRowOffset;
686 				if (Msg.ReadBit())
687 					Side->Flags = Msg.ReadInt(0x000f);
688 				if (Msg.ReadBit())
689 					Msg << Side->Light;
690 			}
691 			break;
692 
693 		case CMD_Sector:
694 			{
695 				sector_t* Sec = &Level->Sectors[Msg.ReadInt(Level->NumSectors)];
696 				float PrevFloorDist = Sec->floor.dist;
697 				float PrevCeilDist = Sec->ceiling.dist;
698 				if (Msg.ReadBit())
699 				{
700 					Sec->floor.pic = Msg.ReadInt(MAX_VUINT16);
701 				}
702 				if (Msg.ReadBit())
703 				{
704 					Sec->ceiling.pic = Msg.ReadInt(MAX_VUINT16);
705 				}
706 				if (Msg.ReadBit())
707 				{
708 					if (Msg.ReadBit())
709 					{
710 						Msg << Sec->floor.dist;
711 						Msg << Sec->floor.TexZ;
712 					}
713 					if (Msg.ReadBit())
714 						Sec->floor.xoffs = Msg.ReadInt(64);
715 					if (Msg.ReadBit())
716 						Sec->floor.yoffs = Msg.ReadInt(64);
717 					if (Msg.ReadBit())
718 						Msg << Sec->floor.XScale;
719 					if (Msg.ReadBit())
720 						Msg << Sec->floor.YScale;
721 					if (Msg.ReadBit())
722 						Sec->floor.Angle = Msg.ReadInt(360);
723 					if (Msg.ReadBit())
724 						Sec->floor.BaseAngle = Msg.ReadInt(360);
725 					if (Msg.ReadBit())
726 						Sec->floor.BaseYOffs = Msg.ReadInt(64);
727 					if (Msg.ReadBit())
728 						Msg << Sec->floor.SkyBox;
729 				}
730 				if (Msg.ReadBit())
731 				{
732 					if (Msg.ReadBit())
733 					{
734 						Msg << Sec->ceiling.dist;
735 						Msg << Sec->ceiling.TexZ;
736 					}
737 					if (Msg.ReadBit())
738 						Sec->ceiling.xoffs = Msg.ReadInt(64);
739 					if (Msg.ReadBit())
740 						Sec->ceiling.yoffs = Msg.ReadInt(64);
741 					if (Msg.ReadBit())
742 						Msg << Sec->ceiling.XScale;
743 					if (Msg.ReadBit())
744 						Msg << Sec->ceiling.YScale;
745 					if (Msg.ReadBit())
746 						Sec->ceiling.Angle = Msg.ReadInt(360);
747 					if (Msg.ReadBit())
748 						Sec->ceiling.BaseAngle = Msg.ReadInt(360);
749 					if (Msg.ReadBit())
750 						Sec->ceiling.BaseYOffs = Msg.ReadInt(64);
751 					if (Msg.ReadBit())
752 						Msg << Sec->ceiling.SkyBox;
753 				}
754 				if (Msg.ReadBit())
755 				{
756 					Sec->params.lightlevel = Msg.ReadInt(256) << 2;
757 				}
758 				if (Msg.ReadBit())
759 				{
760 					Msg << Sec->params.Fade;
761 				}
762 				if (Msg.ReadBit())
763 				{
764 					Sec->Sky = Msg.ReadInt(MAX_VUINT16);
765 				}
766 				if (Msg.ReadBit())
767 				{
768 					Msg << Sec->floor.MirrorAlpha;
769 					Msg << Sec->ceiling.MirrorAlpha;
770 				}
771 				if (PrevFloorDist != Sec->floor.dist ||
772 					PrevCeilDist != Sec->ceiling.dist)
773 				{
774 					CalcSecMinMaxs(Sec);
775 				}
776 			}
777 			break;
778 
779 		case CMD_PolyObj:
780 			{
781 				polyobj_t* Po = &Level->PolyObjs[Msg.ReadInt(Level->NumPolyObjs)];
782 				TVec Pos = Po->startSpot;
783 				if (Msg.ReadBit())
784 					Msg << Pos.x;
785 				if (Msg.ReadBit())
786 					Msg << Pos.y;
787 				if (Pos != Po->startSpot)
788 				{
789 					Level->MovePolyobj(Po->tag, Pos.x - Po->startSpot.x,
790 						Pos.y - Po->startSpot.y);
791 				}
792 				if (Msg.ReadBit())
793 				{
794 					float a;
795 					Msg << a;
796 					Level->RotatePolyobj(Po->tag, a - Po->angle);
797 				}
798 			}
799 			break;
800 
801 		case CMD_StaticLight:
802 			{
803 				TVec		Origin;
804 				float		Radius;
805 				vuint32		Colour;
806 				Msg << Origin << Radius << Colour;
807 				Level->RenderData->AddStaticLight(Origin, Radius, Colour);
808 			}
809 			break;
810 
811 		case CMD_NewLevel:
812 #ifdef CLIENT
813 			CL_ParseServerInfo(Msg);
814 #endif
815 			break;
816 
817 		case CMD_PreRender:
818 			Level->RenderData->PreRender();
819 #ifdef CLIENT
820 			if (cls.signon)
821 			{
822 				Host_Error("Spawn command already sent");
823 			}
824 			if (!UserInfoSent)
825 			{
826 				cl->eventServerSetUserInfo(cls.userinfo);
827 				UserInfoSent = true;
828 			}
829 #endif
830 			Connection->SendCommand("PreSpawn\n");
831 			GCmdBuf << "HideConsole\n";
832 			break;
833 
834 		case CMD_Line:
835 			{
836 				line_t* Line = &Level->Lines[Msg.ReadInt(Level->NumLines)];
837 				if (Msg.ReadBit())
838 				{
839 					Msg << Line->alpha;
840 					if (Msg.ReadBit())
841 					{
842 						Line->flags |= ML_ADDITIVE;
843 					}
844 					else
845 					{
846 						Line->flags &= ~ML_ADDITIVE;
847 					}
848 				}
849 			}
850 			break;
851 
852 		case CMD_CamTex:
853 			{
854 				int i = Msg.ReadInt(0xff);
855 				while (Level->CameraTextures.Num() <= i)
856 				{
857 					VCameraTextureInfo& C = Level->CameraTextures.Alloc();
858 					C.Camera = NULL;
859 					C.TexNum = -1;
860 					C.FOV = 0;
861 				}
862 				VCameraTextureInfo& Cam = Level->CameraTextures[i];
863 				Connection->ObjMap->SerialiseObject(Msg, *(VObject**)&Cam.Camera);
864 				Cam.TexNum = Msg.ReadInt(0xffff);
865 				Cam.FOV = Msg.ReadInt(360);
866 			}
867 			break;
868 
869 		case CMD_LevelTrans:
870 			{
871 				int i = Msg.ReadInt(MAX_LEVEL_TRANSLATIONS);
872 				while (Level->Translations.Num() <= i)
873 				{
874 					Level->Translations.Append(NULL);
875 				}
876 				VTextureTranslation* Tr = Level->Translations[i];
877 				if (!Tr)
878 				{
879 					Tr = new VTextureTranslation;
880 					Level->Translations[i] = Tr;
881 				}
882 				Tr->Clear();
883 				int Count = Msg.ReadInt(0xff);
884 				for (int j = 0; j < Count; j++)
885 				{
886 					vuint8 Type = Msg.ReadInt(2);
887 					if (Type == 0)
888 					{
889 						vuint8 Start;
890 						vuint8 End;
891 						vuint8 SrcStart;
892 						vuint8 SrcEnd;
893 						Msg << Start << End << SrcStart << SrcEnd;
894 						Tr->MapToRange(Start, End, SrcStart, SrcEnd);
895 					}
896 					else if (Type == 1)
897 					{
898 						vuint8 Start;
899 						vuint8 End;
900 						vuint8 R1;
901 						vuint8 G1;
902 						vuint8 B1;
903 						vuint8 R2;
904 						vuint8 G2;
905 						vuint8 B2;
906 						Msg << Start << End << R1 << G1 << B1 << R2 << G2 << B2;
907 						Tr->MapToColours(Start, End, R1, G1, B1, R2, G2, B2);
908 					}
909 				}
910 			}
911 			break;
912 
913 		case CMD_BodyQueueTrans:
914 			{
915 				int i = Msg.ReadInt(MAX_BODY_QUEUE_TRANSLATIONS);
916 				while (Level->BodyQueueTrans.Num() <= i)
917 				{
918 					Level->BodyQueueTrans.Append(NULL);
919 				}
920 				VTextureTranslation* Tr = Level->BodyQueueTrans[i];
921 				if (!Tr)
922 				{
923 					Tr = new VTextureTranslation;
924 					Level->BodyQueueTrans[i] = Tr;
925 				}
926 				Tr->Clear();
927 				vuint8 TrStart;
928 				vuint8 TrEnd;
929 				Msg << TrStart << TrEnd;
930 				vint32 Col = Msg.ReadInt(0x00ffffff);
931 				Tr->BuildPlayerTrans(TrStart, TrEnd, Col);
932 			}
933 			break;
934 
935 		default:
936 			Sys_Error("Invalid level update command %d", Cmd);
937 		}
938 	}
939 	unguard;
940 }
941