1 /** @file d_netcl.cpp Common code related to netgames (client-side).
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "common.h"
22 #include "d_netcl.h"
23
24 #include <cstdio>
25 #include <cstring>
26 #include "d_netsv.h" ///< @todo remove me
27 #include "g_common.h"
28 #include "g_defs.h"
29 #include "gamesession.h"
30 #include "hu_inventory.h"
31 #include "p_actor.h"
32 #include "p_inventory.h"
33 #include "p_map.h"
34 #include "p_saveg.h"
35 #include "p_start.h"
36 #include "r_common.h"
37 #include "player.h"
38 #include "st_stuff.h"
39
40 using namespace de;
41 using namespace common;
42
NetCl_UpdateGameState(reader_s * msg)43 void NetCl_UpdateGameState(reader_s *msg)
44 {
45 BusyMode_FreezeGameForBusyMode();
46
47 byte gsFlags = Reader_ReadByte(msg);
48
49 // Game identity key.
50 AutoStr *gsGameId = AutoStr_NewStd();
51 Str_Read(gsGameId, msg);
52
53 // Current map.
54 uri_s *gsMapUri = Uri_FromReader(msg);
55 Uri_SetScheme(gsMapUri, "Maps");
56
57 // Current episode.
58 AutoStr *gsEpisodeId = AutoStr_NewStd();
59 Str_Read(gsEpisodeId, msg);
60
61 /*uint gsMap =*/ Reader_ReadByte(msg);
62
63 /// @todo Not communicated to clients??
64 //uint gsMapEntrance = ??;
65
66 byte configFlags = Reader_ReadByte(msg);
67
68 GameRules gsRules(gfw_Session()->rules()); // Initialize with a copy of the current rules.
69 GameRules_Set(gsRules, deathmatch, configFlags & 0x3);
70 GameRules_Set(gsRules, noMonsters, !(configFlags & 0x4? true : false));
71 #if !__JHEXEN__
72 GameRules_Set(gsRules, respawnMonsters, (configFlags & 0x8? true : false));
73 #endif
74 /// @todo Not applied??
75 //byte gsJumping = (configFlags & 0x10? true : false);
76
77 GameRules_Set(gsRules, skill, skillmode_t(Reader_ReadByte(msg)));
78
79 // Interpret skill modes outside the normal range as "spawn no things".
80 if(gsRules.values.skill < SM_BABY || gsRules.values.skill >= NUM_SKILL_MODES)
81 {
82 GameRules_Set(gsRules, skill, SM_NOTHINGS);
83 }
84
85 coord_t gsGravity = Reader_ReadFloat(msg);
86
87 LOGDEV_MAP_NOTE("NetCl_UpdateGameState: Flags=%x") << gsFlags;
88
89 // Demo game state changes are only effective during demo playback.
90 if(gsFlags & GSF_DEMO && !Get(DD_PLAYBACK))
91 {
92 Uri_Delete(gsMapUri);
93 return;
94 }
95
96 // Check for a game mode mismatch.
97 /// @todo Automatically load the server's game if it is available.
98 /// However, note that this can only occur if the server changes its game
99 /// while a netgame is running (which currently will end the netgame).
100 if (gfw_GameId().compare(Str_Text(gsGameId)))
101 {
102 LOG_NET_ERROR("Game mismatch: server's identity key (%s) is different to yours (%s)")
103 << gsGameId << gfw_GameId();
104 DD_Execute(false, "net disconnect");
105 Uri_Delete(gsMapUri);
106 return;
107 }
108
109 // Some statistics.
110 LOG_NOTE("%s - %s\n %s")
111 << gsRules.description()
112 << Str_Text(Uri_ToString(gsMapUri))
113 << gsRules.asText();
114
115 // Do we need to change the map?
116 if(gsFlags & GSF_CHANGE_MAP)
117 {
118 gfw_Session()->end();
119 gfw_Session()->begin(gsRules, Str_Text(gsEpisodeId),
120 *reinterpret_cast<de::Uri *>(gsMapUri),
121 gfw_Session()->mapEntryPoint() /*gsMapEntrance*/);
122 }
123 else
124 {
125 /// @todo Breaks session management logic; rules cannot change once the session has
126 /// begun and setting the current map and/or entrance is illogical at this point.
127 DENG2_ASSERT(!Str_Compare(gsEpisodeId, gfw_Session()->episodeId().toLatin1().constData()));
128 DENG2_ASSERT(*reinterpret_cast<de::Uri *>(gsMapUri) == gfw_Session()->mapUri());
129
130 gfw_Session()->applyNewRules(gsRules);
131 }
132
133 // Set gravity.
134 /// @todo This is a map-property, not a global property.
135 DD_SetVariable(DD_MAP_GRAVITY, &gsGravity);
136
137 // Camera init included?
138 if(gsFlags & GSF_CAMERA_INIT)
139 {
140 player_t *pl = &players[CONSOLEPLAYER];
141 if(mobj_t *mo = pl->plr->mo)
142 {
143 P_MobjUnlink(mo);
144 mo->origin[VX] = Reader_ReadFloat(msg);
145 mo->origin[VY] = Reader_ReadFloat(msg);
146 mo->origin[VZ] = Reader_ReadFloat(msg);
147 P_MobjLink(mo);
148 mo->angle = Reader_ReadUInt32(msg);
149 // Update floorz and ceilingz.
150 #if __JDOOM__ || __JDOOM64__
151 P_CheckPosition(mo, mo->origin);
152 #else
153 P_CheckPositionXY(mo, mo->origin[VX], mo->origin[VY]);
154 #endif
155 mo->floorZ = tmFloorZ;
156 mo->ceilingZ = tmCeilingZ;
157 }
158 else
159 {
160 float mx = Reader_ReadFloat(msg);
161 float my = Reader_ReadFloat(msg);
162 float mz = Reader_ReadFloat(msg);
163 angle_t angle = Reader_ReadUInt32(msg);
164
165 LOGDEV_NET_WARNING("NetCl_UpdateGameState: Got camera init, but player has no mobj; "
166 "pos=%f,%f,%f Angle=%x") << mx << my << mz << angle;
167 }
168 }
169
170 // Tell the server we're ready to begin receiving frames.
171 Net_SendPacket(0, DDPT_OK, 0, 0);
172
173 Uri_Delete(gsMapUri);
174 }
175
NetCl_MobjImpulse(reader_s * msg)176 void NetCl_MobjImpulse(reader_s *msg)
177 {
178 mobj_t *mo = players[CONSOLEPLAYER].plr->mo;
179 mobj_t *clmo = ClPlayer_ClMobj(CONSOLEPLAYER);
180
181 if(!mo || !clmo) return;
182
183 thid_t id = Reader_ReadUInt16(msg);
184 if(id != clmo->thinker.id)
185 {
186 // Not applicable; wrong mobj.
187 return;
188 }
189
190 App_Log(DE2_DEV_MAP_VERBOSE, "NetCl_MobjImpulse: Player %i, clmobj %i", CONSOLEPLAYER, id);
191
192 // Apply to the local mobj.
193 mo->mom[MX] += Reader_ReadFloat(msg);
194 mo->mom[MY] += Reader_ReadFloat(msg);
195 mo->mom[MZ] += Reader_ReadFloat(msg);
196 }
197
NetCl_PlayerSpawnPosition(reader_s * msg)198 void NetCl_PlayerSpawnPosition(reader_s *msg)
199 {
200 player_t *p = &players[CONSOLEPLAYER];
201
202 coord_t x = Reader_ReadFloat(msg);
203 coord_t y = Reader_ReadFloat(msg);
204 coord_t z = Reader_ReadFloat(msg);
205 angle_t angle = Reader_ReadUInt32(msg);
206
207 App_Log(DE2_DEV_MAP_NOTE, "Got player spawn position (%g, %g, %g) facing %x",
208 x, y, z, angle);
209
210 mobj_t *mo = p->plr->mo;
211 DENG2_ASSERT(mo != 0);
212
213 P_TryMoveXYZ(mo, x, y, z);
214 mo->angle = angle;
215 }
216
NetCl_UpdatePlayerState2(reader_s * msg,int plrNum)217 void NetCl_UpdatePlayerState2(reader_s *msg, int plrNum)
218 {
219 player_t *pl = &players[plrNum];
220
221 if(!Get(DD_GAME_READY))
222 {
223 App_Log(DE2_DEV_NET_WARNING, "NetCl_UpdatePlayerState2: game isn't ready yet!");
224 return;
225 }
226
227 if(plrNum < 0)
228 {
229 // Player number included in the message.
230 plrNum = Reader_ReadByte(msg);
231 }
232 uint flags = Reader_ReadUInt32(msg);
233
234 if(flags & PSF2_OWNED_WEAPONS)
235 {
236 int k = Reader_ReadUInt16(msg);
237 for(int i = 0; i < NUM_WEAPON_TYPES; ++i)
238 {
239 bool owned = CPP_BOOL(k & (1 << i));
240
241 // Maybe unhide the HUD?
242 if(owned && pl->weapons[i].owned == false)
243 {
244 ST_HUDUnHide(pl - players, HUE_ON_PICKUP_WEAPON);
245 }
246
247 pl->weapons[i].owned = owned;
248 }
249 }
250
251 if(flags & PSF2_STATE)
252 {
253 int oldPlayerState = pl->playerState;
254
255 byte b = Reader_ReadByte(msg);
256 pl->playerState = playerstate_t(b & 0xf);
257 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
258 pl->armorType = b >> 4;
259 #endif
260
261 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState2: New player state = %s",
262 pl->playerState == PST_LIVE? "PST_LIVE" :
263 pl->playerState == PST_DEAD? "PST_DEAD" : "PST_REBORN");
264
265 // Player state changed?
266 if(oldPlayerState != pl->playerState)
267 {
268 // Set or clear the DEAD flag for this player.
269 if(pl->playerState == PST_LIVE)
270 {
271 // Becoming alive again...
272 // After being reborn, the server will tell us the new weapon.
273 pl->plr->flags |= DDPF_UNDEFINED_WEAPON;
274
275 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState2: Player %i: Marking weapon as undefined",
276 (int)(pl - players));
277
278 pl->plr->flags &= ~DDPF_DEAD;
279 }
280 else
281 {
282 pl->plr->flags |= DDPF_DEAD;
283 }
284 }
285
286 pl->cheats = Reader_ReadByte(msg);
287
288 // Set or clear the NOCLIP flag.
289 if(P_GetPlayerCheats(pl) & CF_NOCLIP)
290 pl->plr->flags |= DDPF_NOCLIP;
291 else
292 pl->plr->flags &= ~DDPF_NOCLIP;
293 }
294 }
295
NetCl_UpdatePlayerState(reader_s * msg,int plrNum)296 void NetCl_UpdatePlayerState(reader_s *msg, int plrNum)
297 {
298 int i;
299 byte b;
300 int s;
301
302 if(!Get(DD_GAME_READY))
303 return;
304
305 if(plrNum < 0)
306 {
307 plrNum = Reader_ReadByte(msg);
308 }
309 player_t *pl = &players[plrNum];
310
311 int flags = Reader_ReadUInt16(msg);
312
313 if(flags & PSF_STATE) // and armor type (the same bit)
314 {
315 byte b = Reader_ReadByte(msg);
316 pl->playerState = playerstate_t(b & 0xf);
317 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
318 pl->armorType = b >> 4;
319 #endif
320 // Set or clear the DEAD flag for this player.
321 if(pl->playerState == PST_LIVE)
322 pl->plr->flags &= ~DDPF_DEAD;
323 else
324 pl->plr->flags |= DDPF_DEAD;
325
326 //if(oldstate != pl->playerState) // && oldstate == PST_DEAD)
327 {
328 P_SetupPsprites(pl);
329 }
330 }
331
332 if(flags & PSF_HEALTH)
333 {
334 int health = Reader_ReadByte(msg);
335
336 if(health < pl->health)
337 ST_HUDUnHide(plrNum, HUE_ON_DAMAGE);
338
339 pl->health = health;
340 if(pl->plr->mo)
341 {
342 pl->plr->mo->health = pl->health;
343 }
344 else
345 {
346 App_Log(DE2_DEV_MAP_ERROR, "NetCl_UpdatePlayerState: Player mobj not yet allocated at this time");
347 }
348 }
349
350 if(flags & PSF_ARMOR_POINTS)
351 {
352 byte ap;
353 #if __JHEXEN__
354 for(i = 0; i < NUMARMOR; ++i)
355 {
356 ap = Reader_ReadByte(msg);
357
358 // Maybe unhide the HUD?
359 if(ap >= pl->armorPoints[i] &&
360 pl == &players[CONSOLEPLAYER])
361 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_ARMOR);
362
363 pl->armorPoints[i] = ap;
364 }
365 #else
366 ap = Reader_ReadByte(msg);
367
368 // Maybe unhide the HUD?
369 if(ap >= pl->armorPoints)
370 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_ARMOR);
371
372 pl->armorPoints = ap;
373 #endif
374
375 }
376
377 #if __JHERETIC__ || __JHEXEN__ || __JDOOM64__
378 if(flags & PSF_INVENTORY)
379 {
380 for(uint i = 0; i < NUM_INVENTORYITEM_TYPES; ++i)
381 {
382 inventoryitemtype_t type = inventoryitemtype_t(IIT_FIRST + i);
383 uint count = P_InventoryCount(plrNum, type);
384
385 for(uint j = 0; j < count; ++j)
386 {
387 P_InventoryTake(plrNum, type, true);
388 }
389 }
390
391 uint count = Reader_ReadByte(msg);
392 for(uint i = 0; i < count; ++i)
393 {
394 s = Reader_ReadUInt16(msg);
395
396 inventoryitemtype_t const type = inventoryitemtype_t(s & 0xff);
397 uint const num = s >> 8;
398
399 for(uint j = 0; j < num; ++j)
400 {
401 P_InventoryGive(plrNum, type, true);
402 }
403 }
404 }
405 #endif
406
407 if(flags & PSF_POWERS)
408 {
409 b = Reader_ReadByte(msg);
410
411 // Only the non-zero powers are included in the message.
412 #if __JHEXEN__ || __JSTRIFE__
413 for(i = 0; i < NUM_POWER_TYPES - 1; ++i)
414 {
415 byte val = ((b & (1 << i))? (Reader_ReadByte(msg) * 35) : 0);
416
417 // Maybe unhide the HUD?
418 if(val > pl->powers[i])
419 ST_HUDUnHide(pl - players, HUE_ON_PICKUP_POWER);
420
421 pl->powers[i + 1] = val;
422 }
423 #else
424 for(i = 0; i < NUM_POWER_TYPES; ++i)
425 {
426 # if __JDOOM__ || __JDOOM64__
427 if(i == PT_IRONFEET || i == PT_STRENGTH)
428 continue;
429 # endif
430 {
431 int val = ((b & (1 << i))? (Reader_ReadByte(msg) * 35) : 0);
432
433 /**
434 * @todo This function duplicates logic in P_GivePower(). The
435 * redundancy should be removed for instance by adding a new
436 * game packet GPT_GIVE_POWER that calls the appropriate
437 * P_GivePower() on clientside after it has been called on the
438 * server. -jk
439 */
440
441 // Maybe unhide the HUD?
442 if(val > pl->powers[i])
443 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_POWER);
444
445 pl->powers[i] = val;
446
447 if(val && i == PT_FLIGHT && pl->plr->mo)
448 {
449 pl->plr->mo->flags2 |= MF2_FLY;
450 pl->plr->mo->flags |= MF_NOGRAVITY;
451 pl->flyHeight = 10;
452 pl->powers[i] = val;
453
454 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: Local mobj flight enabled");
455 }
456
457 // Should we reveal the map?
458 if(val && i == PT_ALLMAP && plrNum == CONSOLEPLAYER)
459 {
460 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: Revealing automap");
461
462 ST_RevealAutomap(plrNum, true);
463 }
464 }
465 }
466 #endif
467 }
468
469 if(flags & PSF_KEYS)
470 {
471 b = Reader_ReadByte(msg);
472 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
473 for(i = 0; i < NUM_KEY_TYPES; ++i)
474 {
475 dd_bool val = (b & (1 << i)) != 0;
476
477 // Maybe unhide the HUD?
478 if(val && !pl->keys[i])
479 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_KEY);
480
481 pl->keys[i] = val;
482 }
483 #endif
484 #if __JHEXEN__
485 if((pl->keys & b) != 0)
486 {
487 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_KEY);
488 }
489 pl->keys = b;
490 #endif
491 }
492
493 if(flags & PSF_FRAGS)
494 {
495 de::zap(pl->frags);
496 // First comes the number of frag counts included.
497 for(i = Reader_ReadByte(msg); i > 0; i--)
498 {
499 s = Reader_ReadUInt16(msg);
500 pl->frags[s >> 12] = s & 0xfff;
501 }
502 }
503
504 if(flags & PSF_OWNED_WEAPONS)
505 {
506 b = Reader_ReadByte(msg);
507 for(i = 0; i < NUM_WEAPON_TYPES; ++i)
508 {
509 bool owned = CPP_BOOL(b & (1 << i));
510
511 // Maybe unhide the HUD?
512 if(owned && pl->weapons[i].owned == false)
513 {
514 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_WEAPON);
515 }
516
517 pl->weapons[i].owned = owned;
518 }
519 }
520
521 if(flags & PSF_AMMO)
522 {
523 for(i = 0; i < NUM_AMMO_TYPES; ++i)
524 {
525 int val = Reader_ReadInt16(msg);
526
527 // Maybe unhide the HUD?
528 if(val > pl->ammo[i].owned)
529 {
530 ST_HUDUnHide(plrNum, HUE_ON_PICKUP_AMMO);
531 }
532
533 pl->ammo[i].owned = val;
534 }
535 }
536
537 if(flags & PSF_MAX_AMMO)
538 {
539 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__ // Hexen has no use for max ammo.
540 for(i = 0; i < NUM_AMMO_TYPES; i++)
541 pl->ammo[i].max = Reader_ReadInt16(msg);
542 #endif
543 }
544
545 if(flags & PSF_COUNTERS)
546 {
547 pl->killCount = Reader_ReadInt16(msg);
548 pl->itemCount = Reader_ReadByte(msg);
549 pl->secretCount = Reader_ReadByte(msg);
550
551 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: kills=%i, items=%i, secrets=%i",
552 pl->killCount, pl->itemCount, pl->secretCount);
553 }
554
555 if(flags & PSF_PENDING_WEAPON || flags & PSF_READY_WEAPON)
556 {
557 dd_bool wasUndefined = (pl->plr->flags & DDPF_UNDEFINED_WEAPON) != 0;
558
559 b = Reader_ReadByte(msg);
560 if(flags & PSF_PENDING_WEAPON)
561 {
562 if(!wasUndefined)
563 {
564 int weapon = b & 0xf;
565 if(weapon != WT_NOCHANGE)
566 {
567 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: Weapon already known, "
568 "using an impulse to switch to %i", weapon);
569
570 P_Impulse(pl - players, CTL_WEAPON1 + weapon);
571 }
572 }
573 else
574 {
575 pl->pendingWeapon = weapontype_t(b & 0xf);
576
577 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: pendingweapon=%i", pl->pendingWeapon);
578 }
579
580 pl->plr->flags &= ~DDPF_UNDEFINED_WEAPON;
581 }
582
583 if(flags & PSF_READY_WEAPON)
584 {
585 if(wasUndefined)
586 {
587 pl->readyWeapon = weapontype_t(b >> 4);
588 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: readyweapon=%i", pl->readyWeapon);
589 }
590 else
591 {
592 App_Log(DE2_DEV_MAP_NOTE, "NetCl_UpdatePlayerState: Readyweapon already known (%i), "
593 "not setting server's value %i", pl->readyWeapon, b >> 4);
594 }
595
596 pl->plr->flags &= ~DDPF_UNDEFINED_WEAPON;
597 }
598
599 if(!(pl->plr->flags & DDPF_UNDEFINED_WEAPON) && wasUndefined)
600 {
601 App_Log(DE2_DEV_MAP_NOTE, "NetCl_UpdatePlayerState: Weapon was undefined, bringing it up now");
602
603 // Bring it up now.
604 P_BringUpWeapon(pl);
605 }
606 }
607
608 if(flags & PSF_VIEW_HEIGHT)
609 {
610 pl->viewHeight = (float) Reader_ReadByte(msg);
611 }
612
613 #if __JHERETIC__ || __JHEXEN__ || __JSTRIFE__
614 if(flags & PSF_MORPH_TIME)
615 {
616 pl->morphTics = Reader_ReadByte(msg) * 35;
617 App_Log(DE2_DEV_MAP_MSG, "NetCl_UpdatePlayerState: Player %i morphtics = %i", plrNum, pl->morphTics);
618 }
619 #endif
620
621 #if defined(HAVE_EARTHQUAKE) || __JSTRIFE__
622 if(flags & PSF_LOCAL_QUAKE)
623 {
624 localQuakeHappening[plrNum] = Reader_ReadByte(msg);
625 }
626 #endif
627 }
628
NetCl_UpdatePSpriteState(reader_s *)629 void NetCl_UpdatePSpriteState(reader_s * /*msg*/)
630 {
631 // Not used.
632 /*
633 unsigned short s;
634
635 NetCl_SetReadBuffer(data);
636 s = NetCl_ReadShort();
637 P_SetPsprite(&players[CONSOLEPLAYER], ps_weapon, s);
638 */
639 }
640
NetCl_Intermission(reader_s * msg)641 void NetCl_Intermission(reader_s *msg)
642 {
643 int flags = Reader_ReadByte(msg);
644
645 if(flags & IMF_BEGIN)
646 {
647 // Close any HUDs left open at the end of the previous map.
648 for(uint i = 0; i < MAXPLAYERS; ++i)
649 {
650 ST_CloseAll(i, true/*fast*/);
651 }
652
653 G_ResetViewEffects();
654
655 #if __JHEXEN__
656 SN_StopAllSequences();
657 #endif
658
659 /// @todo jHeretic does not transmit the intermission info!
660 #if !defined(__JHERETIC__)
661 # if __JDOOM__ || __JDOOM64__
662 ::wmInfo.maxKills = de::max<int>(1, Reader_ReadUInt16(msg));
663 ::wmInfo.maxItems = de::max<int>(1, Reader_ReadUInt16(msg));
664 ::wmInfo.maxSecret = de::max<int>(1, Reader_ReadUInt16(msg));
665 # endif
666 Uri_Read(reinterpret_cast<uri_s *>(&::wmInfo.nextMap), msg);
667 # if __JHEXEN__
668 ::wmInfo.nextMapEntryPoint = Reader_ReadByte(msg);
669 # else
670 Uri_Read(reinterpret_cast<uri_s *>(&::wmInfo.currentMap), msg);
671 # endif
672 # if __JDOOM__ || __JDOOM64__
673 ::wmInfo.didSecret = Reader_ReadByte(msg);
674
675 G_PrepareWIData();
676 # endif
677 #endif
678
679 IN_Begin(::wmInfo);
680
681 #if __JDOOM64__
682 S_StartMusic("dm2int", true);
683 #elif __JDOOM__
684 S_StartMusic((gameModeBits & GM_ANY_DOOM2)? "dm2int" : "inter", true);
685 #elif __JHERETIC__
686 S_StartMusic("intr", true);
687 #elif __JHEXEN__
688 S_StartMusic("hub", true);
689 #endif
690 G_ChangeGameState(GS_INTERMISSION);
691 }
692
693 if(flags & IMF_END)
694 {
695 IN_End();
696 }
697
698 if(flags & IMF_STATE)
699 {
700 #if __JDOOM__ || __JDOOM64__
701 IN_SetState(interludestate_t(Reader_ReadInt16(msg)));
702 #elif __JHERETIC__ || __JHEXEN__
703 IN_SetState(Reader_ReadInt16(msg));
704 #endif
705 }
706
707 #if __JHERETIC__
708 if(flags & IMF_TIME)
709 {
710 IN_SetTime(Reader_ReadUInt16(msg));
711 }
712 #endif
713 }
714
715 #if 0 // MOVED INTO THE ENGINE
716 /**
717 * This is where clients start their InFine interludes.
718 */
719 void NetCl_Finale(int packetType, reader_s *msg)
720 {
721 int flags, len, numConds, i;
722 byte *script = NULL;
723
724 flags = Reader_ReadByte(msg);
725 if(flags & FINF_SCRIPT)
726 {
727 // First read the values of the conditions.
728 if(packetType == GPT_FINALE2)
729 {
730 numConds = Reader_ReadByte(msg);
731 for(i = 0; i < numConds; ++i)
732 {
733 FI_SetCondition(i, Reader_ReadByte(msg));
734 }
735 }
736
737 // Read the script into map-scope memory. It will be freed
738 // when the next map is loaded.
739 len = Reader_ReadUInt32(msg);
740 script = Z_Malloc(len + 1, PU_MAP, 0);
741 Reader_Read(msg, script, len);
742 script[len] = 0;
743 }
744
745 if(flags & FINF_BEGIN && script)
746 {
747 // Start the script.
748 FI_Start((char*)script,
749 (flags & FINF_AFTER) ? FIMODE_AFTER : (flags & FINF_OVERLAY) ?
750 FIMODE_OVERLAY : FIMODE_BEFORE);
751 }
752
753 if(flags & FINF_END)
754 { // Stop InFine.
755 FI_End();
756 }
757
758 if(flags & FINF_SKIP)
759 {
760 FI_SkipRequest();
761 }
762 }
763 #endif
764
NetCl_UpdatePlayerInfo(reader_s * msg)765 void NetCl_UpdatePlayerInfo(reader_s *msg)
766 {
767 int num = Reader_ReadByte(msg);
768 cfg.playerColor[num] = Reader_ReadByte(msg);
769 players[num].colorMap = cfg.playerColor[num];
770 #if __JHEXEN__ || __JHERETIC__
771 cfg.playerClass[num] = playerclass_t(Reader_ReadByte(msg));
772 players[num].class_ = cfg.playerClass[num];
773 #endif
774
775 #if __JDOOM__ || __JDOOM64__
776 App_Log(DE2_MAP_VERBOSE, "Player %i color set to %i", num, cfg.playerColor[num]);
777 #else
778 App_Log(DE2_MAP_VERBOSE, "Player %i color set to %i and class to %i", num, cfg.playerColor[num], cfg.playerClass[num]);
779 #endif
780 }
781
782 /**
783 * Send CONSOLEPLAYER's settings to the server.
784 */
NetCl_SendPlayerInfo()785 void NetCl_SendPlayerInfo()
786 {
787 if(!IS_CLIENT) return;
788
789 writer_s *msg = D_NetWrite();
790
791 Writer_WriteByte(msg, cfg.common.netColor);
792 #ifdef __JHEXEN__
793 Writer_WriteByte(msg, cfg.netClass);
794 #else
795 Writer_WriteByte(msg, PCLASS_PLAYER);
796 #endif
797
798 Net_SendPacket(0, GPT_PLAYER_INFO, Writer_Data(msg), Writer_Size(msg));
799 }
800
NetCl_SaveGame(reader_s * msg)801 void NetCl_SaveGame(reader_s *msg)
802 {
803 #if __JHEXEN__
804 DENG2_UNUSED(msg);
805 #endif
806
807 if(Get(DD_PLAYBACK)) return;
808
809 #if !__JHEXEN__
810 SV_SaveGameClient(Reader_ReadUInt32(msg));
811 #endif
812 #if __JDOOM__ || __JDOOM64__
813 P_SetMessageWithFlags(&players[CONSOLEPLAYER], TXT_GAMESAVED, LMF_NO_HIDE);
814 #endif
815 }
816
NetCl_LoadGame(reader_s * msg)817 void NetCl_LoadGame(reader_s *msg)
818 {
819 #if __JHEXEN__
820 DENG2_UNUSED(msg);
821 #endif
822
823 if(!IS_CLIENT || Get(DD_PLAYBACK)) return;
824
825 #if !__JHEXEN__
826 SV_LoadGameClient(Reader_ReadUInt32(msg));
827 #endif
828 #if __JDOOM__ || __JDOOM64__
829 P_SetMessage(&players[CONSOLEPLAYER], GET_TXT(TXT_CLNETLOAD));
830 #endif
831 }
832
NetCl_CheatRequest(char const * command)833 void NetCl_CheatRequest(char const *command)
834 {
835 writer_s *msg = D_NetWrite();
836
837 Writer_WriteUInt16(msg, uint16_t(strlen(command)));
838 Writer_Write(msg, command, strlen(command));
839
840 if(IS_CLIENT)
841 {
842 Net_SendPacket(0, GPT_CHEAT_REQUEST, Writer_Data(msg), Writer_Size(msg));
843 }
844 else
845 {
846 NetSv_ExecuteCheat(CONSOLEPLAYER, command);
847 }
848 }
849
NetCl_UpdateJumpPower(reader_s * msg)850 void NetCl_UpdateJumpPower(reader_s *msg)
851 {
852 netJumpPower = Reader_ReadFloat(msg);
853
854 App_Log(DE2_LOG_VERBOSE, "Jump power: %g", netJumpPower);
855 }
856
NetCl_DismissHUDs(reader_s * msg)857 void NetCl_DismissHUDs(reader_s *msg)
858 {
859 dd_bool fast = Reader_ReadByte(msg)? true : false;
860 ST_CloseAll(CONSOLEPLAYER, fast);
861 }
862
NetCl_FloorHitRequest(player_t * player)863 void NetCl_FloorHitRequest(player_t *player)
864 {
865 writer_s *msg;
866 mobj_t *mo;
867
868 if(!IS_CLIENT || !player->plr->mo)
869 return;
870
871 mo = player->plr->mo;
872 msg = D_NetWrite();
873
874 App_Log(DE2_DEV_MAP_VERBOSE, "NetCl_FloorHitRequest: Player %i", (int)(player - players));
875
876 // Include the position and momentum of the hit.
877 Writer_WriteFloat(msg, mo->origin[VX]);
878 Writer_WriteFloat(msg, mo->origin[VY]);
879 Writer_WriteFloat(msg, mo->origin[VZ]);
880 Writer_WriteFloat(msg, mo->mom[MX]);
881 Writer_WriteFloat(msg, mo->mom[MY]);
882 Writer_WriteFloat(msg, mo->mom[MZ]);
883
884 Net_SendPacket(0, GPT_FLOOR_HIT_REQUEST, Writer_Data(msg), Writer_Size(msg));
885 }
886
NetCl_PlayerActionRequest(player_t * player,int actionType,int actionParam)887 void NetCl_PlayerActionRequest(player_t *player, int actionType, int actionParam)
888 {
889 writer_s *msg;
890
891 if(!IS_CLIENT)
892 return;
893
894 msg = D_NetWrite();
895
896 App_Log(DE2_DEV_NET_VERBOSE, "NetCl_PlayerActionRequest: Player %i, action %i",
897 (int)(player - players), actionType);
898
899 // Type of the request.
900 Writer_WriteInt32(msg, actionType);
901
902 // Position of the action.
903 if(G_GameState() == GS_MAP)
904 {
905 Writer_WriteFloat(msg, player->plr->mo->origin[VX]);
906 Writer_WriteFloat(msg, player->plr->mo->origin[VY]);
907 Writer_WriteFloat(msg, player->plr->mo->origin[VZ]);
908
909 // Which way is the player looking at?
910 Writer_WriteUInt32(msg, player->plr->mo->angle);
911 Writer_WriteFloat(msg, player->plr->lookDir);
912 }
913 else
914 {
915 // Not in a map, so can't provide position/direction.
916 Writer_WriteFloat(msg, 0);
917 Writer_WriteFloat(msg, 0);
918 Writer_WriteFloat(msg, 0);
919 Writer_WriteUInt32(msg, 0);
920 Writer_WriteFloat(msg, 0);
921 }
922
923 Writer_WriteInt32(msg, actionParam);
924
925 Net_SendPacket(0, GPT_ACTION_REQUEST, Writer_Data(msg), Writer_Size(msg));
926 }
927
NetCl_LocalMobjState(reader_s * msg)928 void NetCl_LocalMobjState(reader_s *msg)
929 {
930 thid_t mobjId = Reader_ReadUInt16(msg);
931 thid_t targetId = Reader_ReadUInt16(msg);
932 int newState = 0;
933 int special1 = 0;
934 mobj_t* mo = 0;
935 ddstring_t* stateName = Str_New();
936
937 Str_Read(stateName, msg);
938 newState = Defs().getStateNum(Str_Text(stateName));
939 Str_Delete(stateName);
940
941 special1 = Reader_ReadInt32(msg);
942
943 if(!(mo = ClMobj_Find(mobjId)))
944 {
945 App_Log(DE2_DEV_MAP_NOTE, "NetCl_LocalMobjState: ClMobj %i not found", mobjId);
946 return;
947 }
948
949 // Let it run the sequence locally.
950 ClMobj_EnableLocalActions(mo, true);
951
952 App_Log(DE2_DEV_MAP_VERBOSE, "ClMobj %i => state %i (target:%i, special1:%i)",
953 mobjId, newState, targetId, special1);
954
955 if(!targetId)
956 {
957 mo->target = NULL;
958 }
959 else
960 {
961 mo->target = ClMobj_Find(targetId);
962 }
963 #if !defined(__JDOOM__) && !defined(__JDOOM64__)
964 mo->special1 = special1;
965 #endif
966 P_MobjChangeState(mo, statenum_t(newState));
967 }
968
NetCl_DamageRequest(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damage)969 void NetCl_DamageRequest(mobj_t *target, mobj_t *inflictor, mobj_t *source, int damage)
970 {
971 if(!IS_CLIENT) return;
972 if(!target) return;
973
974 App_Log(DE2_DEV_NET_MSG,
975 "NetCl_DamageRequest: Damage %i on target=%i via inflictor=%i by source=%i",
976 damage, target->thinker.id, inflictor? inflictor->thinker.id : 0,
977 source? source->thinker.id : 0);
978
979 writer_s *msg = D_NetWrite();
980
981 // Amount of damage.
982 Writer_WriteInt32(msg, damage);
983
984 // Mobjs.
985 Writer_WriteUInt16(msg, target->thinker.id);
986 Writer_WriteUInt16(msg, inflictor? inflictor->thinker.id : 0);
987 Writer_WriteUInt16(msg, source? source->thinker.id : 0);
988
989 Net_SendPacket(0, GPT_DAMAGE_REQUEST, Writer_Data(msg), Writer_Size(msg));
990 }
991
NetCl_UpdateTotalCounts(reader_s * msg)992 void NetCl_UpdateTotalCounts(reader_s *msg)
993 {
994 #ifndef __JHEXEN__
995 totalKills = Reader_ReadInt32(msg);
996 totalItems = Reader_ReadInt32(msg);
997 totalSecret = Reader_ReadInt32(msg);
998
999 App_Log(DE2_DEV_NET_MSG, "NetCl_UpdateTotalCounts: kills=%i, items=%i, secrets=%i",
1000 totalKills, totalItems, totalSecret);
1001 #else
1002 DENG2_UNUSED(msg);
1003 #endif
1004 }
1005