1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * Defines and exported variables for multi.c
23  *
24  */
25 
26 #pragma once
27 
28 #include <type_traits>
29 #include "dxxsconf.h"
30 #include "fwd-partial_range.h"
31 #include "fwd-player.h"
32 #include "player-callsign.h"
33 #include "player-flags.h"
34 #include "fwd-weapon.h"
35 #include "mission.h"
36 #include "powerup.h"
37 #include "fwd-object.h"
38 #include "fwd-wall.h"
39 #include "window.h"
40 #include "game.h"
41 #include "gameplayopt.h"
42 
43 #ifdef _WIN32
44 #include <winsock2.h>
45 #include <ws2tcpip.h>
46 #include <io.h>
47 #else
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <netdb.h>
51 #include <arpa/inet.h>
52 #include <unistd.h>
53 #include <stdint.h>
54 #include <sys/time.h>
55 #endif
56 
57 #include <stdexcept>
58 #include "digi.h"
59 #include "pack.h"
60 #include "ntstring.h"
61 #include "compiler-static_assert.h"
62 #include <array>
63 
64 namespace dcx {
65 
66 struct _sockaddr
67 {
68 	union {
69 		sockaddr sa;
70 		sockaddr_in sin;
71 #if DXX_USE_IPv6
72 		sockaddr_in6 sin6;
73 #define DXX_IPv6(v4,v6) v6
74 #else
75 #define DXX_IPv6(v4,v6) v4
76 #endif
77 	};
78 	enum {
79 		presentation_buffer_size = DXX_IPv6(INET_ADDRSTRLEN, INET6_ADDRSTRLEN),
80 	};
address_family_sockaddr81 	static int address_family()
82 	{
83 		return DXX_IPv6(AF_INET, AF_INET6);
84 	}
protocol_family_sockaddr85 	static int protocol_family()
86 	{
87 		return DXX_IPv6(PF_INET, PF_INET6);
88 	}
89 #undef DXX_IPv6
90 };
91 
92 // PROTOCOL VARIABLES AND DEFINES
93 extern int multi_protocol; // set and determinate used protocol
94 
95 enum class network_game_type : uint8_t
96 {
97 	anarchy,
98 	team_anarchy,
99 	robot_anarchy,
100 	cooperative,
101 	capture_flag,
102 	hoard,
103 	team_hoard,
104 	bounty,
105 };
106 
107 }
108 #define MULTI_PROTO_UDP 1 // UDP protocol
109 
110 // What version of the multiplayer protocol is this? Increment each time something drastic changes in Multiplayer without the version number changes. Reset to 0 each time the version of the game changes
111 #define MULTI_PROTO_VERSION	static_cast<uint16_t>(12)
112 // PROTOCOL VARIABLES AND DEFINES - END
113 
114 // limits for Packets (i.e. positional updates) per sec
115 #define DEFAULT_PPS 30
116 #define MIN_PPS 5
117 #define MAX_PPS 40
118 
119 #define MAX_MESSAGE_LEN 35
120 
121 #if defined(DXX_BUILD_DESCENT_I)
122 #define MAX_NET_CREATE_OBJECTS 20
123 #define MAX_MULTI_MESSAGE_LEN  90 //didn't change it, just moved it up
124 #elif defined(DXX_BUILD_DESCENT_II)
125 #define MAX_NET_CREATE_OBJECTS  40
126 #define MAX_MULTI_MESSAGE_LEN   120
127 
128 #endif
129 
130 #define NETSTAT_MENU                0
131 #define NETSTAT_PLAYING             1
132 #define NETSTAT_BROWSING            2
133 #define NETSTAT_WAITING             3
134 #define NETSTAT_STARTING            4
135 #define NETSTAT_ENDLEVEL            5
136 
137 #define CONNECT_DISCONNECTED        0
138 #define CONNECT_PLAYING             1
139 #define CONNECT_WAITING             2
140 #define CONNECT_DIED_IN_MINE        3
141 #define CONNECT_FOUND_SECRET        4
142 #define CONNECT_ESCAPE_TUNNEL       5
143 #define CONNECT_END_MENU            6
144 #if defined(DXX_BUILD_DESCENT_II)
145 #define CONNECT_KMATRIX_WAITING     7 // Like CONNECT_WAITING but used especially in kmatrix.c to seperate "escaped" and "waiting"
146 #endif
147 
148 // reasons for a packet with type PID_DUMP
149 #define DUMP_CLOSED     0 // no new players allowed after game started
150 #define DUMP_FULL       1 // player cound maxed out
151 #define DUMP_ENDLEVEL   2
152 #define DUMP_DORK       3
153 #define DUMP_ABORTED    4
154 #define DUMP_CONNECTED  5 // never used
155 #define DUMP_LEVEL      6
156 #define DUMP_KICKED     7
157 #define DUMP_PKTTIMEOUT 8
158 
159 #if defined(DXX_BUILD_DESCENT_I) || defined(DXX_BUILD_DESCENT_II)
160 #define NETFLAG_LABEL_QUAD	 "Quad Lasers"
161 #define NETFLAG_LABEL_VULCAN	 "Vulcan cannon"
162 #define NETFLAG_LABEL_SPREAD	 "Spreadfire cannon"
163 #define NETFLAG_LABEL_PLASMA	 "Plasma cannon"
164 #define NETFLAG_LABEL_FUSION	 "Fusion cannon"
165 #define for_each_netflag_value(VALUE)	\
166 	VALUE(NETFLAG_DOLASER, "Laser upgrade")	\
167 	VALUE(NETFLAG_DOQUAD, NETFLAG_LABEL_QUAD)	\
168 	VALUE(NETFLAG_DOVULCAN, NETFLAG_LABEL_VULCAN)	\
169 	VALUE(NETFLAG_DOSPREAD, NETFLAG_LABEL_SPREAD)	\
170 	VALUE(NETFLAG_DOPLASMA, NETFLAG_LABEL_PLASMA)	\
171 	VALUE(NETFLAG_DOFUSION, NETFLAG_LABEL_FUSION)	\
172 	VALUE(NETFLAG_DOHOMING, "Homing Missiles")	\
173 	VALUE(NETFLAG_DOPROXIM, "Proximity Bombs")	\
174 	VALUE(NETFLAG_DOSMART, "Smart Missiles")	\
175 	VALUE(NETFLAG_DOMEGA, "Mega Missiles")	\
176 	VALUE(NETFLAG_DOCLOAK, "Cloaking")	\
177 	VALUE(NETFLAG_DOINVUL, "Invulnerability")	\
178 	D2X_MP_NETFLAGS(VALUE)	\
179 
180 #define for_each_netgrant_value(VALUE)	\
181 	VALUE(NETGRANT_QUAD, NETFLAG_LABEL_QUAD)	\
182 	VALUE(NETGRANT_VULCAN, NETFLAG_LABEL_VULCAN)	\
183 	VALUE(NETGRANT_SPREAD, NETFLAG_LABEL_SPREAD)	\
184 	VALUE(NETGRANT_PLASMA, NETFLAG_LABEL_PLASMA)	\
185 	VALUE(NETGRANT_FUSION, NETFLAG_LABEL_FUSION)	\
186 	D2X_MP_NETGRANT(VALUE)
187 
188 #define MULTI_GAME_TYPE_COUNT	8
189 namespace dsx {
190 #if defined(DXX_BUILD_DESCENT_I)
191 constexpr std::integral_constant<unsigned, 13> MULTI_GAME_NAME_LENGTH{};
192 constexpr std::integral_constant<unsigned, 18> MULTI_ALLOW_POWERUP_TEXT_LENGTH{};
193 #define MULTI_ALLOW_POWERUP_MAX 12
194 #define D2X_MP_NETFLAGS(VALUE)
195 #define DXX_GRANT_LASER_LEVEL_BITS	2
196 #define D2X_MP_NETGRANT(VALUE)
197 #elif defined(DXX_BUILD_DESCENT_II)
198 constexpr std::integral_constant<unsigned, 17> MULTI_GAME_NAME_LENGTH{};
199 constexpr std::integral_constant<unsigned, 21> MULTI_ALLOW_POWERUP_TEXT_LENGTH{};
200 #define MULTI_ALLOW_POWERUP_MAX 26
201 #define NETFLAG_LABEL_GAUSS	"Gauss cannon"
202 #define NETFLAG_LABEL_HELIX	"Helix cannon"
203 #define NETFLAG_LABEL_PHOENIX	"Phoenix cannon"
204 #define NETFLAG_LABEL_OMEGA	"Omega cannon"
205 #define NETFLAG_LABEL_AFTERBURNER	"Afterburners"
206 #define NETFLAG_LABEL_AMMORACK	"Ammo rack"
207 #define NETFLAG_LABEL_CONVERTER	"Energy Converter"
208 #define NETFLAG_LABEL_HEADLIGHT	"Headlight"
209 #define D2X_MP_NETFLAGS(VALUE)	\
210 	VALUE(NETFLAG_DOSUPERLASER, "Super lasers")	\
211 	VALUE(NETFLAG_DOGAUSS, NETFLAG_LABEL_GAUSS)	\
212 	VALUE(NETFLAG_DOHELIX, NETFLAG_LABEL_HELIX)	\
213 	VALUE(NETFLAG_DOPHOENIX, NETFLAG_LABEL_PHOENIX)	\
214 	VALUE(NETFLAG_DOOMEGA, NETFLAG_LABEL_OMEGA)	\
215 	VALUE(NETFLAG_DOFLASH, "Flash Missiles")	\
216 	VALUE(NETFLAG_DOGUIDED, "Guided Missiles")	\
217 	VALUE(NETFLAG_DOSMARTMINE, "Smart Mines")	\
218 	VALUE(NETFLAG_DOMERCURY, "Mercury Missiles")	\
219 	VALUE(NETFLAG_DOSHAKER, "Earthshaker Missiles")	\
220 	VALUE(NETFLAG_DOAFTERBURNER, NETFLAG_LABEL_AFTERBURNER)	\
221 	VALUE(NETFLAG_DOAMMORACK, NETFLAG_LABEL_AMMORACK)	\
222 	VALUE(NETFLAG_DOCONVERTER, NETFLAG_LABEL_CONVERTER)	\
223 	VALUE(NETFLAG_DOHEADLIGHT, NETFLAG_LABEL_HEADLIGHT)
224 
225 #define DXX_GRANT_LASER_LEVEL_BITS	3
226 #define D2X_MP_NETGRANT(VALUE)	\
227 	VALUE(NETGRANT_GAUSS, NETFLAG_LABEL_GAUSS)	\
228 	VALUE(NETGRANT_HELIX, NETFLAG_LABEL_HELIX)	\
229 	VALUE(NETGRANT_PHOENIX, NETFLAG_LABEL_PHOENIX)	\
230 	VALUE(NETGRANT_OMEGA, NETFLAG_LABEL_OMEGA)	\
231 	VALUE(NETGRANT_AFTERBURNER, NETFLAG_LABEL_AFTERBURNER)	\
232 	VALUE(NETGRANT_AMMORACK, NETFLAG_LABEL_AMMORACK)	\
233 	VALUE(NETGRANT_CONVERTER, NETFLAG_LABEL_CONVERTER)	\
234 	VALUE(NETGRANT_HEADLIGHT, NETFLAG_LABEL_HEADLIGHT)
235 
236 #endif
237 
238 namespace multi {
239 struct dispatch_table
240 {
241 	constexpr const dispatch_table *operator->() const
242 	{
243 		return this;
244 	}
245 	virtual int objnum_is_past(objnum_t objnum) const = 0;
246 	virtual void do_protocol_frame(int force, int listen) const = 0;
247 	virtual window_event_result level_sync() const = 0;
248 	virtual void send_endlevel_packet() const = 0;
249 	virtual void kick_player(const _sockaddr &dump_addr, int why) const = 0;
250 	virtual void disconnect_player(int playernum) const = 0;
251 	virtual int end_current_level(int *secret) const = 0;
252 	virtual void leave_game() const = 0;
253 };
254 }
255 }
256 
257 #define define_netflag_bit_enum(NAME,STR)	BIT_##NAME,
258 #define define_netflag_bit_mask(NAME,STR)	NAME = (1 << BIT_##NAME),
259 #define define_netflag_powerup_mask(NAME,STR)	| (NAME)
260 enum { for_each_netflag_value(define_netflag_bit_enum) };
261 // Bitmask for netgame_info->AllowedItems to set allowed items in Netgame
262 enum netflag_flag :
263 #if defined(DXX_BUILD_DESCENT_I)
264 	uint16_t
265 #elif defined(DXX_BUILD_DESCENT_II)
266 	uint32_t
267 #endif
268 {
269 	for_each_netflag_value(define_netflag_bit_mask)
270 };
271 enum {
272 	BIT_NETGRANT_LASER = DXX_GRANT_LASER_LEVEL_BITS - 1,
273 	for_each_netgrant_value(define_netflag_bit_enum)
274 	BIT_NETGRANT_MAXIMUM
275 };
276 enum netgrant_flag :
277 #if defined(DXX_BUILD_DESCENT_I)
278 	uint8_t
279 #elif defined(DXX_BUILD_DESCENT_II)
280 	uint16_t
281 #endif
282 {
283 	for_each_netgrant_value(define_netflag_bit_mask)
284 };
285 #undef define_netflag_bit_enum
286 #undef define_netflag_bit_mask
287 
288 namespace dsx {
289 
290 struct packed_spawn_granted_items
291 {
292 #if defined(DXX_BUILD_DESCENT_I)
293 	typedef uint8_t mask_type;
294 #elif defined(DXX_BUILD_DESCENT_II)
295 	typedef uint16_t mask_type;
296 #endif
297 	mask_type mask;
298 	static_assert(BIT_NETGRANT_MAXIMUM <= sizeof(mask) << 3, "mask too small");
299 	packed_spawn_granted_items() = default;
packed_spawn_granted_itemspacked_spawn_granted_items300 	constexpr packed_spawn_granted_items(mask_type m) :
301 		mask(m)
302 	{
303 	}
304 	template <unsigned U>
packed_spawn_granted_itemspacked_spawn_granted_items305 		constexpr packed_spawn_granted_items(std::integral_constant<unsigned, U>) :
306 			mask(U)
307 	{
308 		assert_equal(U, static_cast<mask_type>(U), "truncation error");
309 	}
310 	explicit operator bool() const { return mask; }
has_quad_laserpacked_spawn_granted_items311 	bool has_quad_laser() const { return mask & NETGRANT_QUAD; }
312 #if defined(DXX_BUILD_DESCENT_II)
has_afterburnerpacked_spawn_granted_items313 	bool has_afterburner() const { return mask & NETGRANT_AFTERBURNER; }
314 #endif
315 };
316 
317 class packed_netduplicate_items
318 {
319 public:
320 	enum
321 	{
322 		primary_shift = 0,
323 		primary_width = 3,
324 		secondary_shift = primary_shift + primary_width,
325 		secondary_width = 3,
326 #if defined(DXX_BUILD_DESCENT_II)
327 		accessory_shift = secondary_shift + secondary_width,
328 		accessory_width = 3,
329 #endif
330 	};
331 private:
332 #if defined(DXX_BUILD_DESCENT_I)
333 	typedef uint8_t count_type;
334 #elif defined(DXX_BUILD_DESCENT_II)
335 	typedef uint16_t count_type;
336 #endif
337 	count_type count;
338 	template <uint_fast32_t shift, uint_fast32_t width>
get_sub_field()339 	uint_fast32_t get_sub_field() const
340 	{
341 		static_assert(shift + width <= sizeof(count) << 3, "shift+width too big");
342 		constexpr auto low_mask = (1 << width) - 1;
343 		return (count >> shift) & low_mask;
344 	}
345 public:
346 	template <uint_fast32_t shift, uint_fast32_t width>
set_sub_field(uint_fast32_t value)347 	void set_sub_field(uint_fast32_t value)
348 	{
349 		constexpr auto low_mask = (1 << width) - 1;
350 		constexpr auto shifted_mask = low_mask << shift;
351 		count = (count & ~shifted_mask) | (value << shift);
352 	}
353 #define DEFINE_ACCESSOR(N)	\
354 	uint_fast32_t get_##N##_count() const	\
355 	{	\
356 		return get_sub_field<N##_shift, N##_width>();	\
357 	}	\
358 	void set_##N##_count(uint_fast32_t value)	\
359 	{	\
360 		set_sub_field<N##_shift, N##_width>(value);	\
361 	}
362 	DEFINE_ACCESSOR(primary);
363 	DEFINE_ACCESSOR(secondary);
364 #if defined(DXX_BUILD_DESCENT_II)
365 	DEFINE_ACCESSOR(accessory);
366 #endif
get_packed_field()367 	count_type get_packed_field() const
368 	{
369 		return count;
370 	}
set_packed_field(count_type c)371 	void set_packed_field(count_type c)
372 	{
373 		count = c;
374 	}
375 };
376 
map_granted_flags_to_laser_level(const packed_spawn_granted_items & grant)377 static inline laser_level map_granted_flags_to_laser_level(const packed_spawn_granted_items &grant)
378 {
379 	/* Laser level in lowest bits */
380 	return laser_level{static_cast<uint8_t>(grant.mask & ((1 << DXX_GRANT_LASER_LEVEL_BITS) - 1))};
381 }
382 player_flags map_granted_flags_to_player_flags(packed_spawn_granted_items grant);
383 uint_fast32_t map_granted_flags_to_primary_weapon_flags(packed_spawn_granted_items grant);
384 uint16_t map_granted_flags_to_vulcan_ammo(packed_spawn_granted_items grant);
385 void multi_digi_link_sound_to_pos(int soundnum, vcsegptridx_t segnum, unsigned sidenum, const vms_vector &pos, int forever, fix max_volume);
386 void multi_object_to_object_rw(object &obj, object_rw *obj_rw);
387 void multi_object_rw_to_object(object_rw *obj_rw, object &obj);
388 
389 using GMNames_array = std::array<char[MULTI_GAME_NAME_LENGTH], MULTI_GAME_TYPE_COUNT>;
390 extern const GMNames_array GMNames;
391 using multi_allow_powerup_text_array = std::array<char[MULTI_ALLOW_POWERUP_TEXT_LENGTH], MULTI_ALLOW_POWERUP_MAX>;
392 extern const multi_allow_powerup_text_array multi_allow_powerup_text;
393 extern const std::array<char[8], MULTI_GAME_TYPE_COUNT> GMNamesShrt;
394 }
395 
396 namespace dcx {
397 extern std::array<objnum_t, MAX_NET_CREATE_OBJECTS> Net_create_objnums;
398 extern unsigned Net_create_loc;
399 int multi_maybe_disable_friendly_fire(const object_base *attacker);
400 }
401 
402 namespace dsx {
403 
404 void multi_send_fire(int laser_gun, laser_level, int laser_flags, int laser_fired, objnum_t laser_track, imobjptridx_t is_bomb_objnum);
405 void multi_send_destroy_controlcen(objnum_t objnum, playernum_t player);
406 void multi_send_position(object &objnum);
407 void multi_send_kill(vmobjptridx_t objnum);
408 void multi_send_remobj(vmobjidx_t objnum);
409 void multi_send_door_open(vcsegidx_t segnum, unsigned side, wall_flags flag);
410 void multi_send_drop_weapon(vmobjptridx_t objnum,int seed);
411 void multi_reset_player_object(object &objp);
412 }
413 #endif
414 
415 enum class msgsend_state : uint8_t {
416 	none,
417 	typing,
418 	automap,
419 };
420 
421 enum deres_type_t {
422 	deres_explode,
423 	deres_drop,
424 };
425 
426 // Exported functions
427 
428 struct owned_remote_objnum
429 {
430 	int8_t owner;
431 	uint16_t objnum;
432 };
433 enum class trgnum_t : uint8_t;
434 
435 objnum_t objnum_remote_to_local(uint16_t remote_obj, int8_t owner);
436 owned_remote_objnum objnum_local_to_remote(objnum_t local);
437 void map_objnum_local_to_remote(int local, int remote, int owner);
438 void map_objnum_local_to_local(objnum_t objnum);
439 void reset_network_objects();
440 void multi_do_ping_frame();
441 
442 void multi_init_objects(void);
443 window_event_result multi_do_frame();
444 
445 #ifdef dsx
446 namespace dsx {
447 
448 enum class multi_endlevel_type : bool
449 {
450 	normal,
451 #if defined(DXX_BUILD_DESCENT_I)
452 	secret,
453 #endif
454 };
455 #if defined(DXX_BUILD_DESCENT_I)
456 void multi_send_endlevel_start(multi_endlevel_type);
457 #elif defined(DXX_BUILD_DESCENT_II)
458 void multi_send_endlevel_start();
multi_send_endlevel_start(multi_endlevel_type)459 static inline void multi_send_endlevel_start(multi_endlevel_type)
460 {
461 	multi_send_endlevel_start();
462 }
463 #endif
464 void multi_send_player_deres(deres_type_t type);
465 void multi_send_create_powerup(powerup_type_t powerup_type, vcsegidx_t segnum, vcobjidx_t objnum, const vms_vector &pos);
466 }
467 void multi_send_play_sound(int sound_num, fix volume, sound_stack once);
468 #endif
469 void multi_send_reappear();
470 void multi_send_create_explosion(playernum_t);
471 void multi_send_controlcen_fire(const vms_vector &to_target, int gun_num, objnum_t objnum);
472 namespace dcx {
473 void multi_send_cloak(void);
474 void multi_send_decloak(void);
475 }
476 void multi_digi_play_sample(int sndnum, fix max_volume);
477 void multi_digi_play_sample_once(int soundnum, fix max_volume);
478 void multi_send_score(void);
479 void multi_send_trigger(trgnum_t trigger);
480 #if defined(DXX_BUILD_DESCENT_II)
481 namespace dsx {
482 extern char Multi_is_guided;
483 void multi_send_flags(playernum_t);
484 struct marker_message_text_t;
485 void multi_send_drop_marker(unsigned player, const vms_vector &position, player_marker_index messagenum, const marker_message_text_t &text);
486 void multi_send_markers();
487 void multi_send_guided_info (const object_base &miss, char);
488 void multi_send_orb_bonus(playernum_t pnum, uint8_t);
489 void multi_send_got_orb(playernum_t pnum);
490 void multi_send_effect_blowup(vcsegidx_t segnum, unsigned side, const vms_vector &pnt);
491 void multi_send_vulcan_weapon_ammo_adjust(const vmobjptridx_t objnum);
492 #ifndef RELEASE
493 void multi_add_lifetime_kills(int count);
494 #endif
495 }
496 #endif
497 void multi_send_bounty( void );
498 
499 void multi_consistency_error(int reset);
500 window_event_result multi_level_sync();
501 #ifdef dsx
502 namespace dsx {
503 void multi_send_hostage_door_status(vcwallptridx_t wallnum);
504 void multi_prep_level_objects(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip);
505 void multi_prep_level_player();
506 void multi_leave_game(void);
507 void multi_process_bigdata(playernum_t pnum, const uint8_t *buf, uint_fast32_t len);
508 void multi_make_ghost_player(playernum_t);
509 void multi_make_player_ghost(playernum_t);
510 }
511 #endif
512 void multi_define_macro(int key);
513 void multi_send_macro(int key);
514 int multi_get_kill_list(playernum_array_t &sorted_kills);
515 void multi_new_game(void);
516 #ifdef dsx
517 namespace dsx {
518 void multi_sort_kill_list(void);
519 }
520 #endif
521 void multi_reset_stuff(void);
522 int get_team(playernum_t pnum);
523 void multi_disconnect_player(playernum_t);
524 
525 #ifdef dsx
526 namespace dsx {
527 void multi_initiate_save_game();
528 void multi_initiate_restore_game();
529 void multi_execute_save_game(d_game_unique_state::save_slot slot, const d_game_unique_state::savegame_description &desc, partial_range_t<const player *> player_range);
530 #if defined(DXX_BUILD_DESCENT_I)
multi_send_got_flag(playernum_t)531 static inline void multi_send_got_flag (playernum_t) {}
532 #elif defined(DXX_BUILD_DESCENT_II)
533 void multi_send_got_flag (playernum_t);
534 #endif
535 }
536 #endif
537 
538 // Exported variables
539 
540 namespace dcx {
541 extern int Network_status;
542 
543 // IMPORTANT: These variables needed for player rejoining done by protocol-specific code
544 extern int Network_send_objects;
545 extern int Network_send_object_mode;
546 extern int Network_send_objnum;
547 extern int Network_rejoined;
548 extern int Network_sending_extras;
549 extern int VerifyPlayerJoined;
550 extern int Player_joining_extras;
551 extern int Network_player_added;
552 
553 extern std::array<std::array<uint16_t, MAX_PLAYERS>, MAX_PLAYERS> kill_matrix;
554 extern std::array<int16_t, 2> team_kills;
555 
556 extern ushort my_segments_checksum;
557 
558 //do we draw the kill list on the HUD?
559 enum class show_kill_list_mode : int8_t
560 {
561 	None,
562 	_1,
563 	efficiency = 2,
564 	team_kills = 3,
565 };
566 extern show_kill_list_mode Show_kill_list;
567 extern int Show_reticle_name;
568 extern fix Show_kill_list_timer;
569 
570 // Used to send network messages
571 
572 extern ntstring<MAX_MESSAGE_LEN - 1> Network_message;
573 extern int Network_message_reciever;
574 
575 // Which player 'owns' each local object for network purposes
576 extern std::array<sbyte, MAX_OBJECTS> object_owner;
577 
578 extern int multi_quit_game;
579 
580 extern std::array<msgsend_state, MAX_PLAYERS> multi_sending_message;
581 extern int multi_defining_message;
582 }
583 extern void multi_send_message_start();
584 void multi_send_msgsend_state(msgsend_state state);
585 
586 #if defined(DXX_BUILD_DESCENT_II)
587 namespace dsx {
588 extern std::array<grs_main_bitmap, 2> Orb_icons;
589 struct hoard_highest_record
590 {
591 	unsigned points;
592 	unsigned player = UINT_MAX;
593 };
594 
595 extern hoard_highest_record hoard_highest_record_stats;
596 }
597 #endif
598 namespace dcx {
599 extern playernum_t Bounty_target;
600 
601 extern std::array<std::array<bitmap_index, N_PLAYER_SHIP_TEXTURES>, MAX_PLAYERS> multi_player_textures;
602 
603 #define GetRankStringWithSpace(I)	(PlayerCfg.NoRankings ? std::pair<const char *, const char *>("", "") : std::pair<const char *, const char *>(RankStrings[I], " "))
604 
605 // Globals for protocol-bound Refuse-functions
606 extern char RefuseThisPlayer,WaitForRefuseAnswer,RefuseTeam,RefusePlayerName[12];
607 extern fix64 RefuseTimeLimit;
608 #define REFUSE_INTERVAL (F1_0*8)
609 }
610 
611 #ifdef dsx
612 namespace dsx {
613 window_event_result multi_message_input_sub(int key, control_info &Controls);
614 struct bit_game_flags {
615 	unsigned closed : 1;
616 	unsigned : 1;
617 	unsigned show_on_map : 1;
618 	/*
619 	 * These #define are written to .NGP files and to the network.
620 	 * Changing them breaks ABI compatibility.
621 	 * The bit flags need not match in value, and are converted below in
622 	 * pack_game_flags / unpack_game_flags.
623 	 */
624 #define NETGAME_FLAG_CLOSED             1
625 #define NETGAME_FLAG_SHOW_MAP           4
626 #if defined(DXX_BUILD_DESCENT_II)
627 	unsigned hoard : 1;
628 	unsigned team_hoard : 1;
629 	unsigned endlevel : 1;
630 	unsigned forming : 1;
631 #define NETGAME_FLAG_HOARD              8
632 #define NETGAME_FLAG_TEAM_HOARD         16
633 #define NETGAME_FLAG_REALLY_ENDLEVEL    32
634 #define NETGAME_FLAG_REALLY_FORMING     64
635 #endif
636 } __pack__;
637 }
638 
639 namespace dcx {
640 struct packed_game_flags
641 {
642 	unsigned char value;
643 };
644 
645 #if DXX_USE_TRACKER
646 enum TrackerNATHolePunchWarn : uint8_t
647 {
648 	Unset,
649 	UserEnabledHP,
650 	UserRejectedHP,
651 };
652 #endif
653 }
654 
655 namespace dsx {
656 
unpack_game_flags(const packed_game_flags * p)657 static inline bit_game_flags unpack_game_flags(const packed_game_flags *p)
658 {
659 	bit_game_flags flags;
660 	flags.closed = !!(p->value & NETGAME_FLAG_CLOSED);
661 	flags.show_on_map = !!(p->value & NETGAME_FLAG_SHOW_MAP);
662 #if defined(DXX_BUILD_DESCENT_II)
663 	flags.hoard = !!(p->value & NETGAME_FLAG_HOARD);
664 	flags.team_hoard = !!(p->value & NETGAME_FLAG_TEAM_HOARD);
665 	flags.endlevel = !!(p->value & NETGAME_FLAG_REALLY_ENDLEVEL);
666 	flags.forming = !!(p->value & NETGAME_FLAG_REALLY_FORMING);
667 #endif
668 	return flags;
669 }
670 
pack_game_flags(const bit_game_flags * flags)671 static inline packed_game_flags pack_game_flags(const bit_game_flags *flags)
672 {
673 	packed_game_flags p;
674 	p.value =
675 		(flags->closed ? NETGAME_FLAG_CLOSED : 0) |
676 		(flags->show_on_map ? NETGAME_FLAG_SHOW_MAP : 0) |
677 #if defined(DXX_BUILD_DESCENT_II)
678 		(flags->hoard ? NETGAME_FLAG_HOARD : 0) |
679 		(flags->team_hoard ? NETGAME_FLAG_TEAM_HOARD : 0) |
680 		(flags->endlevel ? NETGAME_FLAG_REALLY_ENDLEVEL : 0) |
681 		(flags->forming ? NETGAME_FLAG_REALLY_FORMING : 0) |
682 #endif
683 		0;
684 	return p;
685 }
686 
687 #define NETGAME_NAME_LEN	25
688 
689 extern struct netgame_info Netgame;
690 }
691 #endif
692 
693 #define multi_i_am_master()	(Player_num == 0)
694 void change_playernum_to(playernum_t new_pnum);
695 
696 // Multiplayer powerup capping
697 void MultiLevelInv_InitializeCount();
698 void MultiLevelInv_Recount();
699 #ifdef dsx
700 namespace dsx {
701 extern bool MultiLevelInv_AllowSpawn(powerup_type_t powerup_type);
702 }
703 #endif
704 extern void MultiLevelInv_Repopulate(fix frequency);
705 #ifdef dsx
706 namespace dsx {
707 uint_fast32_t multi_powerup_is_allowed(const unsigned id, const unsigned AllowedItems);
708 uint_fast32_t multi_powerup_is_allowed(const unsigned id, const unsigned AllowedItems, const unsigned SpawnGrantedItems);
709 void show_netgame_info(const netgame_info &netgame);
710 extern void multi_send_player_inventory(int priority);
711 const char *multi_common_deny_save_game(const fvcobjptr &vcobjptr, partial_range_t<const player *> player_range);
712 const char *multi_interactive_deny_save_game(const fvcobjptr &vcobjptr, partial_range_t<const player *> player_range, const d_level_unique_control_center_state &);
713 }
714 #endif
715 extern void multi_send_kill_goal_counts();
716 void multi_check_for_killgoal_winner();
717 #if defined(DXX_BUILD_DESCENT_II)
718 namespace dsx {
719 extern void multi_send_stolen_items();
720 void multi_send_trigger_specific(playernum_t pnum, trgnum_t trig);
721 void multi_send_door_open_specific(playernum_t pnum, vcsegidx_t segnum, unsigned side, wall_flags flag);
722 void multi_send_wall_status_specific(playernum_t pnum, wallnum_t wallnum, uint8_t type, wall_flags flags, wall_state state);
723 void multi_send_light_specific (playernum_t pnum, vcsegptridx_t segnum, uint8_t val);
724 void multi_send_capture_bonus (playernum_t pnum);
725 int multi_all_players_alive(const fvcobjptr &, partial_range_t<const player *>);
726 void multi_send_seismic(fix);
727 void multi_send_drop_blobs(playernum_t);
728 void multi_send_sound_function (char,char);
729 void DropFlag();
730 void multi_send_finish_game ();
731 void init_hoard_data(d_vclip_array &Vclip);
732 void multi_apply_goal_textures();
733 void multi_send_escort_goal(const d_unique_buddy_state &);
734 void multi_recv_escort_goal(d_unique_buddy_state &, const uint8_t *);
735 
736 int HoardEquipped();
737 #if DXX_USE_EDITOR
738 void save_hoard_data(void);
739 #endif
740 }
741 #endif
742 
743 //how to encode missiles & flares in weapon packets
744 #define MISSILE_ADJUST  100
745 #define FLARE_ADJUST    127
746 
747 /*
748  * The Network Players structure
749  * Contains protocol-specific data with designated prefixes and general player-related data.
750  * Note that not all of these infos will be sent to other users - some are used and/or set locally, only.
751  */
752 struct netplayer_info : prohibit_void_ptr<netplayer_info>
753 {
754 	enum class player_rank : uint8_t
755 	{
756 		None,
757 		Cadet,
758 		Ensign,
759 		Lieutenant,
760 		LtCommander,
761 		Commander,
762 		Captain,
763 		ViceAdmiral,
764 		Admiral,
765 		Demigod
766 	};
767 #if DXX_USE_UDP
768 	union
769 	{
770 #if DXX_USE_UDP
771 		struct
772 		{
773 			struct _sockaddr	addr; // IP address of this peer
774 		} udp;
775 #endif
776 	} protocol;
777 #endif
778 	callsign_t					callsign;
779 	sbyte						connected;
780 	player_rank					rank;
781 	fix							ping;
782 	fix64							LastPacketTime;
783 };
784 
785 #ifdef dsx
786 #if defined(DXX_BUILD_DESCENT_II)
787 struct ThiefModifier
788 {
789 	enum Flags : uint8_t {
790 		Absent = 1,
791 		NoEnergyWeapons,
792 	};
793 };
794 #endif
795 
796 namespace dsx {
797 /*
798  * The Network Game structure
799  * Contains protocol-specific data with designated prefixes and general game-related data.
800  * Note that not all of these infos will be sent to clients - some are used and/or set locally, only.
801  */
802 struct netgame_info : prohibit_void_ptr<netgame_info>
803 {
804 	static constexpr std::integral_constant<unsigned, (0 for_each_netflag_value(define_netflag_powerup_mask))> MaskAllKnownAllowedItems{};
805 #undef define_netflag_powerup_mask
806 	using play_time_allowed_abi_ratio = std::ratio<5 * 60>;
807 #if DXX_USE_UDP
808 	union
809 	{
810 #if DXX_USE_UDP
811 		struct
812 		{
813 			struct _sockaddr		addr; // IP address of this netgame's host
814 			std::array<short, 4>			program_iver; // IVER of program for version checking
815 			sbyte				valid; // Status of Netgame info: -1 = Failed, Wrong version; 0 = No info, yet; 1 = Success
816 			uint8_t				your_index; // Tell player his designated (re)join position in players[]
817 			fix				GameID;
818 		} udp;
819 #endif
820 	} protocol;
821 #endif
822 	ntstring<NETGAME_NAME_LEN> game_name;
823 	ntstring<MISSION_NAME_LEN> mission_title;
824 	ntstring<8> mission_name;
825 	int     					levelnum;
826 	Difficulty_level_type difficulty;
827 	network_game_type   		gamemode;
828 	ubyte   					RefusePlayers;
829 	ubyte   					game_status;
830 	ubyte   					numplayers;
831 	ubyte   					max_numplayers;
832 	ubyte   					numconnected;
833 	bit_game_flags game_flag;
834 	ubyte   					team_vector;
835 	uint8_t						SecludedSpawns;
836 	uint8_t MouselookFlags;
837 	uint32_t					AllowedItems;
838 	packed_spawn_granted_items SpawnGrantedItems;
839 	packed_netduplicate_items DuplicatePowerups;
840 	unsigned ShufflePowerupSeed;
841 	d_mp_gameplay_options MPGameplayOptions;
842 #if defined(DXX_BUILD_DESCENT_II)
843 	uint8_t Allow_marker_view;
844 	uint8_t AlwaysLighting;
845 	uint8_t ThiefModifierFlags;
846 	uint8_t AllowGuidebot;
847 #endif
848 	uint8_t ShowEnemyNames;
849 	uint8_t BrightPlayers;
850 	uint8_t InvulAppear;
851 	ushort						segments_checksum;
852 	int						KillGoal;
853 	/* The UI enforces that this steps in units of 5 minutes, but for
854 	 * efficiency, it is stored as ticks (1 second = F1_0).  The UI
855 	 * imposes a maximum value that is small enough that overflow is
856 	 * impossible.
857 	 */
858 	d_time_fix PlayTimeAllowed;
859 	fix						level_time;
860 	int						control_invul_time;
861 	int						monitor_vector;
862 	short						PacketsPerSec;
863 	ubyte						PacketLossPrevention;
864 	ubyte						NoFriendlyFire;
865 	std::array<callsign_t, 2>					team_name;
866 	std::array<uint32_t, MAX_PLAYERS>						locations;
867 	std::array<std::array<uint16_t, MAX_PLAYERS>, MAX_PLAYERS>						kills;
868 	std::array<int16_t, 2>						team_kills;
869 	std::array<uint16_t, MAX_PLAYERS>						killed;
870 	std::array<uint16_t, MAX_PLAYERS>						player_kills;
871 	std::array<uint32_t, MAX_PLAYERS>						player_score;
872 	std::array<player_flags, MAX_PLAYERS>					net_player_flags;
873 	std::array<netplayer_info, MAX_PLAYERS> 				players;
874 #if DXX_USE_TRACKER
875 	ubyte						Tracker;
876 	TrackerNATHolePunchWarn TrackerNATWarned;
877 #endif
878 };
879 
880 
881 /*
882  * Structure holding all powerup types we want to keep track of. Used to cap or respawn powerups and keep the level inventory steady.
883  * Using uint32_t because we don't count powerup units but what the powerup contains (1 or 4 missiles, vulcam amount, etc) so we can keep track of overhead.
884  * I'm sorry if this isn't very optimized but I found this easier to handle than a single variable per powerup.
885  */
886 struct multi_level_inv
887 {
888         std::array<uint32_t, MAX_POWERUP_TYPES> Initial; // initial (level start) count of this powerup type
889         std::array<uint32_t, MAX_POWERUP_TYPES> Current; // current count of this powerup type
890         std::array<fix, MAX_POWERUP_TYPES> RespawnTimer; // incremented by FrameTime if initial-current > 0 and triggers respawn after 2 seconds. Since we deal with a certain delay from clients, their inventory updates may happen a while after they remove the powerup object and we do not want to respawn it on accident during that time window!
891 };
892 
893 namespace multi
894 {
895 	struct level_checksum_mismatch : std::runtime_error
896 	{
level_checksum_mismatchlevel_checksum_mismatch897 		level_checksum_mismatch() :
898 			runtime_error("level checksum mismatch")
899 		{
900 		}
901 	};
902 	struct local_player_not_playing : std::runtime_error
903 	{
local_player_not_playinglocal_player_not_playing904 		local_player_not_playing() :
905 			runtime_error("local player not playing")
906 		{
907 		}
908 	};
909 }
910 }
911 #endif
912 
913 netplayer_info::player_rank GetMyNetRanking();
914 
915 namespace dcx {
916 extern const enumerated_array<char[16], 10, netplayer_info::player_rank> RankStrings;
917 netplayer_info::player_rank build_rank_from_untrusted(uint8_t untrusted);
918 }
919 
920 /* Stub for mods that remap player colors */
get_player_color(unsigned pnum)921 static inline unsigned get_player_color(unsigned pnum)
922 {
923 	return pnum;
924 }
925 
get_team_color(unsigned tnum)926 static inline unsigned get_team_color(unsigned tnum)
927 {
928 	return tnum;
929 }
930 
get_player_or_team_color(unsigned pnum)931 static inline unsigned get_player_or_team_color(unsigned pnum)
932 {
933 	return Game_mode & GM_TEAM
934 		? get_team_color(get_team(pnum))
935 		: get_player_color(pnum);
936 }
937