1 /*
2  * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3  * It is copyright by its individual contributors, as recorded in the
4  * project's Git history.  See COPYING.txt at the top level for license
5  * terms and a link to the Git history.
6  */
7 #pragma once
8 
9 #include <type_traits>
10 #include "dxxerror.h"
11 #include "object.h"
12 #include "powerup.h"
13 #include "serial.h"
14 #include "fwd-player.h"
15 
16 #define _UNPACK_MULTIPLAYER_SERIAL_MESSAGE(A,...)	A, ## __VA_ARGS__
17 #define DEFINE_MULTIPLAYER_SERIAL_MESSAGE(C,T,V,A)	\
18 	DEFINE_SERIAL_UDT_TO_MESSAGE(T,V, (multiplayer_command<C>(), _UNPACK_MULTIPLAYER_SERIAL_MESSAGE A));	\
19 	ASSERT_SERIAL_UDT_MESSAGE_SIZE(T, command_length<C>::value)
20 
21 #define define_multiplayer_command(NAME,SIZE)	NAME,
22 
23 #if defined(DXX_BUILD_DESCENT_I) || defined(DXX_BUILD_DESCENT_II)
24 #define for_each_multiplayer_command(VALUE)	\
25 	VALUE(MULTI_POSITION              , 1 + quaternionpos::packed_size::value)	\
26 	VALUE(MULTI_REAPPEAR              , 4)	\
27 	VALUE(MULTI_FIRE                  , 18)	\
28 	VALUE(MULTI_FIRE_TRACK            , 21)	\
29 	VALUE(MULTI_FIRE_BOMB             , 20)	\
30 	VALUE(MULTI_REMOVE_OBJECT         , 4)	\
31 	VALUE(MULTI_MESSAGE               , 37)	/* (MAX_MESSAGE_LENGTH = 40) */	\
32 	VALUE(MULTI_QUIT                  , 2)	\
33 	VALUE(MULTI_PLAY_SOUND           , 8)	\
34 	VALUE(MULTI_CONTROLCEN           , 4)	\
35 	VALUE(MULTI_ROBOT_CLAIM          , 5)	\
36 	VALUE(MULTI_CLOAK                , 2)	\
37 	VALUE(MULTI_ENDLEVEL_START       , DXX_MP_SIZE_ENDLEVEL_START)	\
38 	VALUE(MULTI_CREATE_EXPLOSION     , 2)	\
39 	VALUE(MULTI_CONTROLCEN_FIRE      , 16)	\
40 	VALUE(MULTI_CREATE_POWERUP       , 19)	\
41 	VALUE(MULTI_DECLOAK              , 2)	\
42 	VALUE(MULTI_ROBOT_POSITION       , 5 + quaternionpos::packed_size::value)	\
43 	VALUE(MULTI_PLAYER_DERES         , DXX_MP_SIZE_PLAYER_RELATED)	\
44 	VALUE(MULTI_DOOR_OPEN            , DXX_MP_SIZE_DOOR_OPEN)	\
45 	VALUE(MULTI_ROBOT_EXPLODE        , 7)	\
46 	VALUE(MULTI_ROBOT_RELEASE        , 5)	\
47 	VALUE(MULTI_ROBOT_FIRE           , 18)	\
48 	VALUE(MULTI_SCORE                , 6)	\
49 	VALUE(MULTI_CREATE_ROBOT         , 6)	\
50 	VALUE(MULTI_TRIGGER              , 3)	\
51 	VALUE(MULTI_BOSS_TELEPORT        , 5)	\
52 	VALUE(MULTI_BOSS_CLOAK           , 3)	\
53 	VALUE(MULTI_BOSS_START_GATE      , 3)	\
54 	VALUE(MULTI_BOSS_STOP_GATE       , 3)	\
55 	VALUE(MULTI_BOSS_CREATE_ROBOT    , 8)	\
56 	VALUE(MULTI_CREATE_ROBOT_POWERUPS, 27)	\
57 	VALUE(MULTI_HOSTAGE_DOOR         , 7)	\
58 	VALUE(MULTI_SAVE_GAME            , 2+24)	/* (ubyte slot, uint id, char name[20]) */	\
59 	VALUE(MULTI_RESTORE_GAME         , 2+4)	/* (ubyte slot, uint id) */	\
60 	VALUE(MULTI_HEARTBEAT            , 5)	\
61 	VALUE(MULTI_KILLGOALS            , 1 + MAX_PLAYERS)	\
62 	VALUE(MULTI_DO_BOUNTY            , 2)	\
63 	VALUE(MULTI_TYPING_STATE         , 3)	\
64 	VALUE(MULTI_GMODE_UPDATE         , 3)	\
65 	VALUE(MULTI_KILL_HOST            , 7)	\
66 	VALUE(MULTI_KILL_CLIENT          , 5)	\
67 	VALUE(MULTI_RANK                 , 3)	\
68 	VALUE(MULTI_DROP_WEAPON          , 10)	\
69         VALUE(MULTI_PLAYER_INV           , DXX_MP_SIZE_PLAYER_INVENTORY)	\
70 	D2X_MP_COMMANDS(VALUE)	\
71 
72 #if defined(DXX_BUILD_DESCENT_I)
73 #define DXX_MP_SIZE_ENDLEVEL_START	3
74 #define DXX_MP_SIZE_PLAYER_RELATED	58
75 #define DXX_MP_SIZE_PLAYER_INVENTORY	15
76 #define DXX_MP_SIZE_BEGIN_SYNC	37
77 #define DXX_MP_SIZE_DOOR_OPEN	4
78 #define D2X_MP_COMMANDS(VALUE)
79 #elif defined(DXX_BUILD_DESCENT_II)
80 #define DXX_MP_SIZE_ENDLEVEL_START	2
81 #define DXX_MP_SIZE_PLAYER_RELATED	(98+10)
82 #define DXX_MP_SIZE_PLAYER_INVENTORY	21
83 #define DXX_MP_SIZE_BEGIN_SYNC	41
84 #define DXX_MP_SIZE_DOOR_OPEN	5
85 #define D2X_MP_COMMANDS(VALUE)	\
86 	VALUE(MULTI_MARKER               , 55)	\
87 	VALUE(MULTI_GUIDED               , 26)	\
88 	VALUE(MULTI_STOLEN_ITEMS         , 1 + std::tuple_size<decltype(d_thief_unique_state::Stolen_items)>::value)	\
89 	VALUE(MULTI_WALL_STATUS          , 6)	/* send to new players */	\
90 	VALUE(MULTI_SEISMIC              , 5)	\
91 	VALUE(MULTI_LIGHT                , 16)	\
92 	VALUE(MULTI_START_TRIGGER        , 2)	\
93 	VALUE(MULTI_FLAGS                , 6)	\
94 	VALUE(MULTI_DROP_BLOB            , 2)	\
95 	VALUE(MULTI_SOUND_FUNCTION       , 4)	\
96 	VALUE(MULTI_CAPTURE_BONUS        , 2)	\
97 	VALUE(MULTI_GOT_FLAG             , 2)	\
98 	VALUE(MULTI_DROP_FLAG            , 8)	\
99 	VALUE(MULTI_FINISH_GAME          , 2)	\
100 	VALUE(MULTI_ORB_BONUS            , 3)	\
101 	VALUE(MULTI_GOT_ORB              , 2)	\
102 	VALUE(MULTI_EFFECT_BLOWUP        , 17)	\
103 	VALUE(MULTI_VULWPN_AMMO_ADJ      , 6)	\
104 	VALUE(MULTI_UPDATE_BUDDY_STATE, 7)	\
105 
106 #endif
107 
108 #include "dxxsconf.h"
109 
110 enum multiplayer_command_t : uint8_t
111 {
112 	for_each_multiplayer_command(define_multiplayer_command)
113 };
114 
115 template <multiplayer_command_t>
116 struct command_length;
117 #define define_command_length(NAME,SIZE)	\
118 	template <>	\
119 	struct command_length<NAME> : public std::integral_constant<unsigned, SIZE> {};
120 for_each_multiplayer_command(define_command_length);
121 
122 template <multiplayer_command_t C>
123 struct multi_command : public std::array<uint8_t, command_length<C>::value>
124 {
125 };
126 
127 void _multi_send_data(const uint8_t *buf, unsigned len, int priority);
128 
129 template <multiplayer_command_t C>
multi_send_data(uint8_t * const buf,const unsigned len,const int priority)130 static inline void multi_send_data(uint8_t *const buf, const unsigned len, const int priority)
131 {
132 	buf[0] = C;
133 	constexpr unsigned expected = command_length<C>::value;
134 #ifdef DXX_CONSTANT_TRUE
135 	if (DXX_CONSTANT_TRUE(len != expected))
136 		DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_multi_send_data, "wrong packet size");
137 #endif
138 	if (len != expected)
139 	{
140 		Error("multi_send_data: Packet type %i length: %i, expected: %i\n", C, len, expected);
141 	}
142 	_multi_send_data(buf, len, priority);
143 }
144 
145 template <multiplayer_command_t C>
multi_send_data(multi_command<C> & buf,const int priority)146 static inline void multi_send_data(multi_command<C> &buf, const int priority)
147 {
148 	buf[0] = C;
149 	_multi_send_data(buf.data(), buf.size(), priority);
150 }
151 
152 template <typename T>
multi_serialize_read(const uint8_t * const buf,T & t)153 static inline void multi_serialize_read(const uint8_t *const buf, T &t)
154 {
155 	serial::reader::bytebuffer_t b(buf);
156 	serial::process_buffer(b, t);
157 }
158 
159 template <typename T>
multi_serialize_write(int priority,const T & t)160 static inline void multi_serialize_write(int priority, const T &t)
161 {
162 	constexpr size_t maximum_size = serial::message_type<T>::maximum_size;
163 	uint8_t buf[maximum_size];
164 	serial::writer::bytebuffer_t b(buf);
165 	serial::process_buffer(b, t);
166 	_multi_send_data(buf, maximum_size, priority);
167 }
168 
169 template <multiplayer_command_t C>
170 using multiplayer_command = serial::pad<1, static_cast<uint8_t>(C)>;
171 
172 #endif
173