dxx-rebirth/common/main/multi.h

945 lines
28 KiB
C
Raw Normal View History

2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Defines and exported variables for multi.c
*
*/
#pragma once
2006-03-20 17:12:09 +00:00
#include <type_traits>
#include "dxxsconf.h"
#include "fwd-partial_range.h"
#include "fwd-player.h"
#include "player-callsign.h"
2015-10-30 02:52:55 +00:00
#include "player-flags.h"
#include "fwd-weapon.h"
2013-12-26 04:18:28 +00:00
#include "mission.h"
#include "powerup.h"
#include "fwd-object.h"
#include "fwd-robot.h"
#include "fwd-segment.h"
#include "fwd-wall.h"
#include "window.h"
#include "game.h"
#include "gameplayopt.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/time.h>
#endif
#include <stdexcept>
#include "digi.h"
2014-06-26 02:27:16 +00:00
#include "pack.h"
2014-12-22 04:35:48 +00:00
#include "ntstring.h"
2015-06-05 02:34:40 +00:00
#include "compiler-static_assert.h"
#include <array>
2015-12-13 18:00:49 +00:00
namespace dcx {
struct _sockaddr
{
union {
sockaddr sa;
sockaddr_in sin;
#if DXX_USE_IPv6
sockaddr_in6 sin6;
#define DXX_IPv6(v4,v6) v6
#else
#define DXX_IPv6(v4,v6) v4
#endif
};
using presentation_buffer = std::array<char, DXX_IPv6(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)>;
static constexpr std::integral_constant<int, DXX_IPv6(AF_INET, AF_INET6)> address_family{};
#undef DXX_IPv6
};
2006-03-20 17:12:09 +00:00
// PROTOCOL VARIABLES AND DEFINES
extern int multi_protocol; // set and determinate used protocol
2021-11-01 03:37:19 +00:00
enum class network_game_type : uint8_t
{
anarchy,
team_anarchy,
robot_anarchy,
cooperative,
capture_flag,
hoard,
team_hoard,
bounty,
};
enum class multiplayer_data_priority : uint8_t
{
_0,
_1,
_2,
};
}
#define MULTI_PROTO_UDP 1 // UDP protocol
// 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
2022-08-21 00:32:38 +00:00
#define MULTI_PROTO_VERSION static_cast<uint16_t>(16)
// PROTOCOL VARIABLES AND DEFINES - END
2006-03-20 17:12:09 +00:00
2013-12-09 13:25:32 +00:00
// limits for Packets (i.e. positional updates) per sec
#define DEFAULT_PPS 30
2013-12-09 13:25:32 +00:00
#define MIN_PPS 5
#define MAX_PPS 40
2006-03-20 17:12:09 +00:00
#define MAX_MESSAGE_LEN 35
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
#define MAX_NET_CREATE_OBJECTS 20
#define MAX_MULTI_MESSAGE_LEN 90 //didn't change it, just moved it up
#elif defined(DXX_BUILD_DESCENT_II)
#define MAX_NET_CREATE_OBJECTS 40
#define MAX_MULTI_MESSAGE_LEN 120
#endif
2006-03-20 17:12:09 +00:00
// reasons for a packet with type PID_DUMP
#define DUMP_CLOSED 0 // no new players allowed after game started
#define DUMP_FULL 1 // player cound maxed out
#define DUMP_ENDLEVEL 2
#define DUMP_DORK 3
#define DUMP_ABORTED 4
#define DUMP_CONNECTED 5 // never used
#define DUMP_LEVEL 6
#define DUMP_KICKED 7
#define DUMP_PKTTIMEOUT 8
#if defined(DXX_BUILD_DESCENT_I) || defined(DXX_BUILD_DESCENT_II)
2015-03-28 17:18:02 +00:00
#define NETFLAG_LABEL_QUAD "Quad Lasers"
#define NETFLAG_LABEL_VULCAN "Vulcan cannon"
#define NETFLAG_LABEL_SPREAD "Spreadfire cannon"
#define NETFLAG_LABEL_PLASMA "Plasma cannon"
#define NETFLAG_LABEL_FUSION "Fusion cannon"
#define for_each_netflag_value(VALUE) \
2013-06-22 02:26:08 +00:00
VALUE(NETFLAG_DOLASER, "Laser upgrade") \
2015-03-28 17:18:02 +00:00
VALUE(NETFLAG_DOQUAD, NETFLAG_LABEL_QUAD) \
VALUE(NETFLAG_DOVULCAN, NETFLAG_LABEL_VULCAN) \
VALUE(NETFLAG_DOSPREAD, NETFLAG_LABEL_SPREAD) \
VALUE(NETFLAG_DOPLASMA, NETFLAG_LABEL_PLASMA) \
VALUE(NETFLAG_DOFUSION, NETFLAG_LABEL_FUSION) \
2013-06-22 02:26:08 +00:00
VALUE(NETFLAG_DOHOMING, "Homing Missiles") \
VALUE(NETFLAG_DOPROXIM, "Proximity Bombs") \
VALUE(NETFLAG_DOSMART, "Smart Missiles") \
VALUE(NETFLAG_DOMEGA, "Mega Missiles") \
VALUE(NETFLAG_DOCLOAK, "Cloaking") \
VALUE(NETFLAG_DOINVUL, "Invulnerability") \
D2X_MP_NETFLAGS(VALUE) \
2015-03-28 17:18:02 +00:00
#define for_each_netgrant_value(VALUE) \
VALUE(NETGRANT_QUAD, NETFLAG_LABEL_QUAD) \
VALUE(NETGRANT_VULCAN, NETFLAG_LABEL_VULCAN) \
VALUE(NETGRANT_SPREAD, NETFLAG_LABEL_SPREAD) \
VALUE(NETGRANT_PLASMA, NETFLAG_LABEL_PLASMA) \
VALUE(NETGRANT_FUSION, NETFLAG_LABEL_FUSION) \
D2X_MP_NETGRANT(VALUE)
#define MULTI_GAME_TYPE_COUNT 8
namespace dsx {
#if defined(DXX_BUILD_DESCENT_I)
constexpr std::integral_constant<unsigned, 13> MULTI_GAME_NAME_LENGTH{};
constexpr std::integral_constant<unsigned, 18> MULTI_ALLOW_POWERUP_TEXT_LENGTH{};
#define MULTI_ALLOW_POWERUP_MAX 12
#define D2X_MP_NETFLAGS(VALUE)
2015-03-28 17:18:02 +00:00
#define DXX_GRANT_LASER_LEVEL_BITS 2
#define D2X_MP_NETGRANT(VALUE)
#elif defined(DXX_BUILD_DESCENT_II)
constexpr std::integral_constant<unsigned, 17> MULTI_GAME_NAME_LENGTH{};
constexpr std::integral_constant<unsigned, 21> MULTI_ALLOW_POWERUP_TEXT_LENGTH{};
#define MULTI_ALLOW_POWERUP_MAX 26
2015-03-28 17:18:02 +00:00
#define NETFLAG_LABEL_GAUSS "Gauss cannon"
#define NETFLAG_LABEL_HELIX "Helix cannon"
#define NETFLAG_LABEL_PHOENIX "Phoenix cannon"
#define NETFLAG_LABEL_OMEGA "Omega cannon"
#define NETFLAG_LABEL_AFTERBURNER "Afterburners"
#define NETFLAG_LABEL_AMMORACK "Ammo rack"
#define NETFLAG_LABEL_CONVERTER "Energy Converter"
#define NETFLAG_LABEL_HEADLIGHT "Headlight"
#define D2X_MP_NETFLAGS(VALUE) \
2013-06-22 02:26:08 +00:00
VALUE(NETFLAG_DOSUPERLASER, "Super lasers") \
2015-03-28 17:18:02 +00:00
VALUE(NETFLAG_DOGAUSS, NETFLAG_LABEL_GAUSS) \
VALUE(NETFLAG_DOHELIX, NETFLAG_LABEL_HELIX) \
VALUE(NETFLAG_DOPHOENIX, NETFLAG_LABEL_PHOENIX) \
VALUE(NETFLAG_DOOMEGA, NETFLAG_LABEL_OMEGA) \
2013-06-22 02:26:08 +00:00
VALUE(NETFLAG_DOFLASH, "Flash Missiles") \
VALUE(NETFLAG_DOGUIDED, "Guided Missiles") \
VALUE(NETFLAG_DOSMARTMINE, "Smart Mines") \
VALUE(NETFLAG_DOMERCURY, "Mercury Missiles") \
2015-03-28 17:18:02 +00:00
VALUE(NETFLAG_DOSHAKER, "Earthshaker Missiles") \
VALUE(NETFLAG_DOAFTERBURNER, NETFLAG_LABEL_AFTERBURNER) \
VALUE(NETFLAG_DOAMMORACK, NETFLAG_LABEL_AMMORACK) \
VALUE(NETFLAG_DOCONVERTER, NETFLAG_LABEL_CONVERTER) \
2015-04-19 04:18:53 +00:00
VALUE(NETFLAG_DOHEADLIGHT, NETFLAG_LABEL_HEADLIGHT)
2015-03-28 17:18:02 +00:00
#define DXX_GRANT_LASER_LEVEL_BITS 3
#define D2X_MP_NETGRANT(VALUE) \
VALUE(NETGRANT_GAUSS, NETFLAG_LABEL_GAUSS) \
VALUE(NETGRANT_HELIX, NETFLAG_LABEL_HELIX) \
VALUE(NETGRANT_PHOENIX, NETFLAG_LABEL_PHOENIX) \
VALUE(NETGRANT_OMEGA, NETFLAG_LABEL_OMEGA) \
VALUE(NETGRANT_AFTERBURNER, NETFLAG_LABEL_AFTERBURNER) \
VALUE(NETGRANT_AMMORACK, NETFLAG_LABEL_AMMORACK) \
VALUE(NETGRANT_CONVERTER, NETFLAG_LABEL_CONVERTER) \
VALUE(NETGRANT_HEADLIGHT, NETFLAG_LABEL_HEADLIGHT)
2013-06-22 02:26:08 +00:00
#endif
namespace multi {
struct dispatch_table
{
constexpr const dispatch_table *operator->() const
{
return this;
}
virtual void send_data(const uint8_t *data, unsigned data_len, multiplayer_data_priority) const = 0;
virtual void send_data_direct(const uint8_t *data, unsigned data_len, playernum_t pnum, int needack) const = 0;
virtual int objnum_is_past(objnum_t objnum) const = 0;
virtual void do_protocol_frame(int force, int listen) const = 0;
virtual window_event_result level_sync() const = 0;
virtual void send_endlevel_packet() const = 0;
virtual void kick_player(const _sockaddr &dump_addr, int why) const = 0;
virtual void disconnect_player(int playernum) const = 0;
virtual int end_current_level(
#if defined(DXX_BUILD_DESCENT_I)
next_level_request_secret_flag *secret
#endif
) const = 0;
virtual void leave_game() const = 0;
};
}
2013-06-22 02:26:08 +00:00
#define define_netflag_bit_enum(NAME,STR) BIT_##NAME,
#define define_netflag_bit_mask(NAME,STR) NAME = (1 << BIT_##NAME),
2013-06-22 02:26:08 +00:00
#define define_netflag_powerup_mask(NAME,STR) | (NAME)
enum netflag_bit : uint8_t
{
for_each_netflag_value(define_netflag_bit_enum)
};
// Bitmask for netgame_info->AllowedItems to set allowed items in Netgame
enum netflag_flag :
#if defined(DXX_BUILD_DESCENT_I)
uint16_t
#elif defined(DXX_BUILD_DESCENT_II)
uint32_t
#endif
{
for_each_netflag_value(define_netflag_bit_mask)
};
enum netgrant_bit : uint8_t
{
2015-03-28 17:18:02 +00:00
BIT_NETGRANT_LASER = DXX_GRANT_LASER_LEVEL_BITS - 1,
for_each_netgrant_value(define_netflag_bit_enum)
2015-04-19 04:18:53 +00:00
BIT_NETGRANT_MAXIMUM
2015-03-28 17:18:02 +00:00
};
enum netgrant_flag :
#if defined(DXX_BUILD_DESCENT_I)
uint8_t
#elif defined(DXX_BUILD_DESCENT_II)
uint16_t
#endif
{
for_each_netgrant_value(define_netflag_bit_mask)
};
2015-03-28 17:18:02 +00:00
#undef define_netflag_bit_enum
#undef define_netflag_bit_mask
2015-04-19 04:18:53 +00:00
struct packed_spawn_granted_items
{
#if defined(DXX_BUILD_DESCENT_I)
typedef uint8_t mask_type;
#elif defined(DXX_BUILD_DESCENT_II)
typedef uint16_t mask_type;
#endif
mask_type mask;
static_assert(BIT_NETGRANT_MAXIMUM <= sizeof(mask) << 3, "mask too small");
packed_spawn_granted_items() = default;
constexpr packed_spawn_granted_items(mask_type m) :
mask(m)
{
}
2015-06-05 02:34:40 +00:00
template <unsigned U>
constexpr packed_spawn_granted_items(std::integral_constant<unsigned, U>) :
2015-06-05 02:34:40 +00:00
mask(U)
{
assert_equal(U, static_cast<mask_type>(U), "truncation error");
}
2015-04-19 04:18:53 +00:00
explicit operator bool() const { return mask; }
bool has_quad_laser() const { return mask & NETGRANT_QUAD; }
#if defined(DXX_BUILD_DESCENT_II)
bool has_afterburner() const { return mask & NETGRANT_AFTERBURNER; }
#endif
};
class packed_netduplicate_items
{
public:
enum
{
primary_shift = 0,
primary_width = 3,
secondary_shift = primary_shift + primary_width,
secondary_width = 3,
#if defined(DXX_BUILD_DESCENT_II)
accessory_shift = secondary_shift + secondary_width,
accessory_width = 3,
#endif
};
private:
#if defined(DXX_BUILD_DESCENT_I)
typedef uint8_t count_type;
#elif defined(DXX_BUILD_DESCENT_II)
typedef uint16_t count_type;
#endif
count_type count;
template <uint_fast32_t shift, uint_fast32_t width>
uint_fast32_t get_sub_field() const
{
static_assert(shift + width <= sizeof(count) << 3, "shift+width too big");
constexpr auto low_mask = (1 << width) - 1;
return (count >> shift) & low_mask;
}
public:
template <uint_fast32_t shift, uint_fast32_t width>
void set_sub_field(uint_fast32_t value)
{
constexpr auto low_mask = (1 << width) - 1;
constexpr auto shifted_mask = low_mask << shift;
count = (count & ~shifted_mask) | (value << shift);
}
#define DEFINE_ACCESSOR(N) \
uint_fast32_t get_##N##_count() const \
{ \
return get_sub_field<N##_shift, N##_width>(); \
} \
void set_##N##_count(uint_fast32_t value) \
{ \
set_sub_field<N##_shift, N##_width>(value); \
}
DEFINE_ACCESSOR(primary);
DEFINE_ACCESSOR(secondary);
#if defined(DXX_BUILD_DESCENT_II)
DEFINE_ACCESSOR(accessory);
#endif
count_type get_packed_field() const
{
return count;
}
void set_packed_field(count_type c)
{
count = c;
}
};
static inline laser_level map_granted_flags_to_laser_level(const packed_spawn_granted_items &grant)
2015-03-28 17:18:02 +00:00
{
/* Laser level in lowest bits */
return laser_level{static_cast<uint8_t>(grant.mask & ((1 << DXX_GRANT_LASER_LEVEL_BITS) - 1))};
2015-03-28 17:18:02 +00:00
}
2015-10-30 02:52:55 +00:00
player_flags map_granted_flags_to_player_flags(packed_spawn_granted_items grant);
2015-04-19 04:18:53 +00:00
uint_fast32_t map_granted_flags_to_primary_weapon_flags(packed_spawn_granted_items grant);
uint16_t map_granted_flags_to_vulcan_ammo(packed_spawn_granted_items grant);
void multi_digi_link_sound_to_pos(int soundnum, vcsegptridx_t segnum, sidenum_t sidenum, const vms_vector &pos, int forever, fix max_volume);
void multi_object_to_object_rw(const object &obj, object_rw *obj_rw);
void multi_object_rw_to_object(const object_rw *obj_rw, object &obj);
2020-05-02 21:18:42 +00:00
using GMNames_array = std::array<char[MULTI_GAME_NAME_LENGTH], MULTI_GAME_TYPE_COUNT>;
2016-07-09 17:58:35 +00:00
extern const GMNames_array GMNames;
2020-05-02 21:18:42 +00:00
using multi_allow_powerup_text_array = std::array<char[MULTI_ALLOW_POWERUP_TEXT_LENGTH], MULTI_ALLOW_POWERUP_MAX>;
extern const multi_allow_powerup_text_array multi_allow_powerup_text;
2020-05-02 21:18:42 +00:00
extern const std::array<char[8], MULTI_GAME_TYPE_COUNT> GMNamesShrt;
}
2015-12-13 18:00:49 +00:00
namespace dcx {
2020-05-02 21:18:42 +00:00
extern std::array<objnum_t, MAX_NET_CREATE_OBJECTS> Net_create_objnums;
2015-02-14 22:48:27 +00:00
extern unsigned Net_create_loc;
2020-12-26 21:17:29 +00:00
int multi_maybe_disable_friendly_fire(const object_base *attacker);
}
2016-10-02 19:35:34 +00:00
namespace dsx {
void multi_send_fire(const vms_matrix &orient, int laser_gun, laser_level, int laser_flags, objnum_t laser_track, imobjptridx_t is_bomb_objnum);
void multi_send_destroy_controlcen(objnum_t objnum, playernum_t player);
void multi_send_position(object &objnum);
void multi_send_kill(vmobjptridx_t objnum);
void multi_send_remobj(vmobjidx_t objnum);
void multi_send_door_open(vcsegidx_t segnum, sidenum_t side, wall_flags flag);
void multi_send_drop_weapon(vmobjptridx_t objnum,int seed);
void multi_reset_player_object(object &objp);
}
#endif
2021-11-01 03:37:19 +00:00
enum class msgsend_state : uint8_t {
none,
typing,
automap,
};
enum deres_type_t {
deres_explode,
deres_drop,
};
2006-03-20 17:12:09 +00:00
// Exported functions
2014-09-13 23:45:13 +00:00
struct owned_remote_objnum
{
int8_t owner;
2015-06-13 22:42:15 +00:00
uint16_t objnum;
2014-09-13 23:45:13 +00:00
};
2021-09-04 12:17:14 +00:00
enum class trgnum_t : uint8_t;
2014-09-13 23:45:13 +00:00
objnum_t objnum_remote_to_local(uint16_t remote_obj, int8_t owner);
2014-09-13 23:45:13 +00:00
owned_remote_objnum objnum_local_to_remote(objnum_t local);
void map_objnum_local_to_remote(objnum_t local, int remote, int owner);
void map_objnum_local_to_local(objnum_t objnum);
2006-03-20 17:12:09 +00:00
void reset_network_objects();
void multi_do_ping_frame();
2006-03-20 17:12:09 +00:00
void multi_init_objects(void);
window_event_result multi_do_frame();
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:15 +00:00
#ifdef dsx
2015-12-13 18:00:49 +00:00
namespace dsx {
enum class multi_endlevel_type : bool
{
normal,
#if defined(DXX_BUILD_DESCENT_I)
secret,
#endif
};
#if defined(DXX_BUILD_DESCENT_I)
void multi_send_endlevel_start(multi_endlevel_type);
#elif defined(DXX_BUILD_DESCENT_II)
void multi_send_endlevel_start();
static inline void multi_send_endlevel_start(multi_endlevel_type)
{
multi_send_endlevel_start();
}
#endif
void multi_send_player_deres(deres_type_t type);
void multi_send_create_powerup(powerup_type_t powerup_type, vcsegidx_t segnum, vcobjidx_t objnum, const vms_vector &pos);
}
void multi_send_play_sound(int sound_num, fix volume, sound_stack once);
#endif
2006-03-20 17:12:09 +00:00
void multi_send_reappear();
2014-09-21 22:10:12 +00:00
void multi_send_create_explosion(playernum_t);
void multi_send_controlcen_fire(const vms_vector &to_target, int gun_num, objnum_t objnum);
2015-12-13 18:00:49 +00:00
namespace dcx {
2006-03-20 17:12:09 +00:00
void multi_send_cloak(void);
void multi_send_decloak(void);
}
2015-08-05 02:59:02 +00:00
void multi_digi_play_sample(int sndnum, fix max_volume);
void multi_digi_play_sample_once(int soundnum, fix max_volume);
2006-03-20 17:12:09 +00:00
void multi_send_score(void);
2021-09-04 12:17:14 +00:00
void multi_send_trigger(trgnum_t trigger);
#if defined(DXX_BUILD_DESCENT_II)
2015-12-13 18:00:49 +00:00
namespace dsx {
2012-11-11 00:14:30 +00:00
extern char Multi_is_guided;
2014-09-21 22:10:12 +00:00
void multi_send_flags(playernum_t);
2013-12-28 18:56:34 +00:00
struct marker_message_text_t;
2020-07-22 03:11:18 +00:00
void multi_send_drop_marker(unsigned player, const vms_vector &position, player_marker_index messagenum, const marker_message_text_t &text);
void multi_send_markers();
void multi_send_guided_info (const object_base &miss, char);
2017-03-18 18:07:36 +00:00
void multi_send_orb_bonus(playernum_t pnum, uint8_t);
2014-09-21 22:10:12 +00:00
void multi_send_got_orb(playernum_t pnum);
void multi_send_effect_blowup(vcsegidx_t segnum, sidenum_t side, const vms_vector &pnt);
#ifndef RELEASE
2015-11-27 03:56:13 +00:00
void multi_add_lifetime_kills(int count);
#endif
2020-12-20 20:39:07 +00:00
}
#endif
void multi_send_bounty( void );
2006-03-20 17:12:09 +00:00
void multi_consistency_error(int reset);
window_event_result multi_level_sync();
2016-01-09 16:38:15 +00:00
#ifdef dsx
2015-12-13 18:00:49 +00:00
namespace dsx {
Improve vulcan/gauss pickup rules - Change D1X to use D2X rules regarding Vulcan cannon pickup. The D1X rules were confusing at best, and seem outright wrong in some ways. - When a Vulcan cannon was picked up, it was treated as containing not less than VULCAN_WEAPON_AMMO_AMOUNT rounds, regardless of what it actually contained. D2X respected the actual contained count, even when running in D1X emulation mode. - In D1X single player, if the Vulcan cannon was not picked up, then it could be treated as Vulcan ammo. If at least 1 unit of ammunition was added to the player, the entire powerup would be consumed, regardless of how much ammunition remained. - In D2X single player, if the Vulcan cannon was not picked up, then ammunition would be taken from it, but the powerup would only be consumed if all its ammunition was taken. - In D1X multiplayer, a player who already had a Vulcan cannon could not get anything from touching a new cannon, and the cannon would not be changed. - In D2X multiplayer, a player who already had a Vulcan cannon could take ammunition from the cannon, but the cannon could not be drained below VULCAN_AMMO_AMOUNT. If the cannon had VULCAN_AMMO_AMOUNT or less, then the player could not take ammunition. If the cannon had more, the player could drain it to that level. - Replace all that with a simplified version of the D2X rules: - If the player does not have the cannon, the cannon is picked up and removed from the mine. The player takes as much of its ammunition as possible. If the cannon was well stocked, and the player was nearly full, some ammunition will be lost. This is unfortunate, but the game has always had this rule, and changing it would require dropping one or more Vulcan Ammo packs to represent the untaken ammunition. - If the player already had that cannon, then the player takes as much ammunition as the cannon has, while not exceeding the ammunition cap. Other players, if any, are updated about how much ammunition remains in the cannon. The cannon remains in the mine. - Backport to D1X the network message for updating the contained ammunition in a vulcan cannon. zico added the basic feature in 7684ce92, but only applied it to D2X. With the change to let D1X multiplayer take ammunition from the cannon, D1X now needs the same feature. - Remove the special case to delete an empty cannon. Instead, let the cannon remain in the mine without ammunition. This allows a player in single player mode to leave behind a backup cannon, which could be useful if the player is killed and wishes to rearm before returning to the death site. Similarly, under the new rule that players in multiplayer can drain the cannon down to 0 ammunition, this removal allows the cannon to remain behind for someone else to take, rather than allowing it to be deleted by a player who already had an instance of it.
2022-02-05 13:30:56 +00:00
void multi_send_vulcan_weapon_ammo_adjust(const vmobjptridx_t objnum);
void multi_send_hostage_door_status(vcwallptridx_t wallnum);
void multi_prep_level_objects(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip);
void multi_prep_level_player();
2006-03-20 17:12:09 +00:00
void multi_leave_game(void);
void multi_process_bigdata(const d_level_shared_robot_info_state &LevelSharedRobotInfoState, playernum_t pnum, const uint8_t *buf, uint_fast32_t len);
2014-09-21 22:10:12 +00:00
void multi_make_ghost_player(playernum_t);
void multi_make_player_ghost(playernum_t);
}
#endif
2006-03-20 17:12:09 +00:00
void multi_define_macro(int key);
void multi_send_macro(int key);
2014-09-21 22:10:12 +00:00
int multi_get_kill_list(playernum_array_t &sorted_kills);
2006-03-20 17:12:09 +00:00
void multi_new_game(void);
2016-01-09 16:38:15 +00:00
#ifdef dsx
2015-12-13 18:00:49 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
void multi_sort_kill_list(void);
}
#endif
2006-03-20 17:12:09 +00:00
void multi_reset_stuff(void);
2014-09-21 22:10:12 +00:00
int get_team(playernum_t pnum);
void multi_disconnect_player(playernum_t);
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:15 +00:00
#ifdef dsx
2015-12-13 18:00:49 +00:00
namespace dsx {
void multi_initiate_save_game();
void multi_initiate_restore_game();
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);
#if defined(DXX_BUILD_DESCENT_I)
static inline void multi_send_got_flag (playernum_t) {}
#elif defined(DXX_BUILD_DESCENT_II)
2014-09-21 22:10:12 +00:00
void multi_send_got_flag (playernum_t);
#endif
}
#endif
2006-03-20 17:12:09 +00:00
// Exported variables
2006-03-20 17:12:09 +00:00
2015-12-13 18:00:49 +00:00
namespace dcx {
2022-07-09 13:39:29 +00:00
/* These values are sent over the network. If they are changed, the
* multiplayer protocol version must be updated.
*/
enum class network_state : uint8_t
{
menu,
playing,
browsing,
waiting,
starting,
endlevel,
};
std::optional<network_state> build_network_state_from_untrusted(uint8_t untrusted);
extern network_state Network_status;
// IMPORTANT: These variables needed for player rejoining done by protocol-specific code
extern int Network_send_objects;
extern int Network_send_object_mode;
extern int Network_send_objnum;
extern int Network_rejoined;
extern int Network_sending_extras;
extern int VerifyPlayerJoined;
extern int Player_joining_extras;
extern int Network_player_added;
2006-03-20 17:12:09 +00:00
2020-05-02 21:18:42 +00:00
extern std::array<std::array<uint16_t, MAX_PLAYERS>, MAX_PLAYERS> kill_matrix;
extern std::array<int16_t, 2> team_kills;
2006-03-20 17:12:09 +00:00
extern ushort my_segments_checksum;
2006-03-20 17:12:09 +00:00
//do we draw the kill list on the HUD?
2021-11-01 03:37:19 +00:00
enum class show_kill_list_mode : int8_t
{
None,
_1,
efficiency = 2,
team_kills = 3,
};
extern show_kill_list_mode Show_kill_list;
2006-03-20 17:12:09 +00:00
extern int Show_reticle_name;
extern fix Show_kill_list_timer;
// Used to send network messages
extern ntstring<MAX_MESSAGE_LEN - 1> Network_message;
2006-03-20 17:12:09 +00:00
extern int Network_message_reciever;
// Which player 'owns' each local object for network purposes
2020-05-02 21:18:42 +00:00
extern std::array<sbyte, MAX_OBJECTS> object_owner;
2006-03-20 17:12:09 +00:00
extern int multi_quit_game;
2021-11-01 03:37:19 +00:00
extern std::array<msgsend_state, MAX_PLAYERS> multi_sending_message;
2006-03-20 17:12:09 +00:00
extern int multi_defining_message;
vms_vector multi_get_vector(const uint8_t *buf);
void multi_put_vector(uint8_t *buf, const vms_vector &v);
}
2006-03-20 17:12:09 +00:00
extern void multi_send_message_start();
2021-11-01 03:37:19 +00:00
void multi_send_msgsend_state(msgsend_state state);
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_II)
2015-12-13 18:00:49 +00:00
namespace dsx {
2020-05-02 21:18:42 +00:00
extern std::array<grs_main_bitmap, 2> Orb_icons;
2017-08-13 20:38:32 +00:00
struct hoard_highest_record
{
unsigned points;
unsigned player = UINT_MAX;
};
extern hoard_highest_record hoard_highest_record_stats;
}
#endif
2015-12-13 18:00:49 +00:00
namespace dcx {
2017-08-13 20:38:32 +00:00
extern playernum_t Bounty_target;
2020-05-02 21:18:42 +00:00
extern std::array<std::array<bitmap_index, N_PLAYER_SHIP_TEXTURES>, MAX_PLAYERS> multi_player_textures;
2006-03-20 17:12:09 +00:00
#define GetRankStringWithSpace(I) (PlayerCfg.NoRankings ? std::pair<const char *, const char *>("", "") : std::pair<const char *, const char *>(RankStrings[I], " "))
// Globals for protocol-bound Refuse-functions
extern char RefuseThisPlayer,WaitForRefuseAnswer,RefuseTeam,RefusePlayerName[12];
extern fix64 RefuseTimeLimit;
#define REFUSE_INTERVAL (F1_0*8)
}
2016-01-09 16:38:15 +00:00
#ifdef dsx
2015-12-13 18:00:49 +00:00
namespace dsx {
struct bit_game_flags {
unsigned closed : 1;
unsigned : 1;
unsigned show_on_map : 1;
/*
* These #define are written to .NGP files and to the network.
* Changing them breaks ABI compatibility.
* The bit flags need not match in value, and are converted below in
* pack_game_flags / unpack_game_flags.
*/
2006-03-20 17:12:09 +00:00
#define NETGAME_FLAG_CLOSED 1
#define NETGAME_FLAG_SHOW_MAP 4
#if defined(DXX_BUILD_DESCENT_II)
unsigned hoard : 1;
unsigned team_hoard : 1;
unsigned endlevel : 1;
unsigned forming : 1;
2006-03-20 17:12:09 +00:00
#define NETGAME_FLAG_HOARD 8
#define NETGAME_FLAG_TEAM_HOARD 16
#define NETGAME_FLAG_REALLY_ENDLEVEL 32
#define NETGAME_FLAG_REALLY_FORMING 64
#endif
} __pack__;
}
2015-12-13 18:00:49 +00:00
namespace dcx {
struct packed_game_flags
{
unsigned char value;
};
#if DXX_USE_TRACKER
enum TrackerNATHolePunchWarn : uint8_t
{
Unset,
UserEnabledHP,
UserRejectedHP,
};
#endif
}
2015-12-13 18:00:49 +00:00
namespace dsx {
static inline bit_game_flags unpack_game_flags(const packed_game_flags *p)
{
bit_game_flags flags;
flags.closed = !!(p->value & NETGAME_FLAG_CLOSED);
flags.show_on_map = !!(p->value & NETGAME_FLAG_SHOW_MAP);
#if defined(DXX_BUILD_DESCENT_II)
flags.hoard = !!(p->value & NETGAME_FLAG_HOARD);
flags.team_hoard = !!(p->value & NETGAME_FLAG_TEAM_HOARD);
flags.endlevel = !!(p->value & NETGAME_FLAG_REALLY_ENDLEVEL);
flags.forming = !!(p->value & NETGAME_FLAG_REALLY_FORMING);
#endif
return flags;
}
static inline packed_game_flags pack_game_flags(const bit_game_flags *flags)
{
packed_game_flags p;
p.value =
(flags->closed ? NETGAME_FLAG_CLOSED : 0) |
(flags->show_on_map ? NETGAME_FLAG_SHOW_MAP : 0) |
#if defined(DXX_BUILD_DESCENT_II)
(flags->hoard ? NETGAME_FLAG_HOARD : 0) |
(flags->team_hoard ? NETGAME_FLAG_TEAM_HOARD : 0) |
(flags->endlevel ? NETGAME_FLAG_REALLY_ENDLEVEL : 0) |
(flags->forming ? NETGAME_FLAG_REALLY_FORMING : 0) |
#endif
0;
return p;
}
2006-03-20 17:12:09 +00:00
#define NETGAME_NAME_LEN 25
2006-03-20 17:12:09 +00:00
extern struct netgame_info Netgame;
}
#endif
2006-03-20 17:12:09 +00:00
2015-07-25 23:10:46 +00:00
#define multi_i_am_master() (Player_num == 0)
void change_playernum_to(playernum_t new_pnum);
2006-03-20 17:12:09 +00:00
// Multiplayer powerup capping
void MultiLevelInv_InitializeCount();
void MultiLevelInv_Recount();
#ifdef dsx
namespace dsx {
extern bool MultiLevelInv_AllowSpawn(powerup_type_t powerup_type);
uint_fast32_t multi_powerup_is_allowed(const unsigned id, const unsigned AllowedItems);
uint_fast32_t multi_powerup_is_allowed(const unsigned id, const unsigned AllowedItems, const unsigned SpawnGrantedItems);
void show_netgame_info(const netgame_info &netgame);
void multi_send_player_inventory(multiplayer_data_priority priority);
const char *multi_common_deny_save_game(const fvcobjptr &vcobjptr, partial_range_t<const player *> player_range);
const char *multi_interactive_deny_save_game(const fvcobjptr &vcobjptr, partial_range_t<const player *> player_range, const d_level_unique_control_center_state &);
void multi_check_for_killgoal_winner(const d_robot_info_array &Robot_info);
}
#endif
extern void multi_send_kill_goal_counts();
#if defined(DXX_BUILD_DESCENT_II)
2015-12-13 18:00:49 +00:00
namespace dsx {
extern void multi_send_stolen_items();
2021-09-04 12:17:14 +00:00
void multi_send_trigger_specific(playernum_t pnum, trgnum_t trig);
void multi_send_door_open_specific(playernum_t pnum, vcsegidx_t segnum, sidenum_t side, wall_flags flag);
2021-11-01 03:37:19 +00:00
void multi_send_wall_status_specific(playernum_t pnum, wallnum_t wallnum, uint8_t type, wall_flags flags, wall_state state);
void multi_send_light_specific (playernum_t pnum, vcsegptridx_t segnum, sidemask_t val);
2014-09-21 22:10:12 +00:00
void multi_send_capture_bonus (playernum_t pnum);
int multi_all_players_alive(const fvcobjptr &, partial_range_t<const player *>);
2015-02-14 22:48:29 +00:00
void multi_send_seismic(fix);
2014-09-21 22:10:12 +00:00
void multi_send_drop_blobs(playernum_t);
2012-11-11 00:14:30 +00:00
void multi_send_sound_function (char,char);
void DropFlag();
void multi_send_finish_game ();
2018-10-21 00:24:07 +00:00
void init_hoard_data(d_vclip_array &Vclip);
2012-11-11 00:14:30 +00:00
void multi_apply_goal_textures();
void multi_send_escort_goal(const d_unique_buddy_state &);
int HoardEquipped();
#if DXX_USE_EDITOR
void save_hoard_data(void);
#endif
}
#endif
//how to encode missiles & flares in weapon packets
#define MISSILE_ADJUST 100
#define FLARE_ADJUST 127
/*
* The Network Players structure
* Contains protocol-specific data with designated prefixes and general player-related data.
* Note that not all of these infos will be sent to other users - some are used and/or set locally, only.
*/
2014-06-26 02:27:16 +00:00
struct netplayer_info : prohibit_void_ptr<netplayer_info>
{
2021-11-01 03:37:19 +00:00
enum class player_rank : uint8_t
{
None,
Cadet,
Ensign,
Lieutenant,
LtCommander,
Commander,
Captain,
ViceAdmiral,
Admiral,
Demigod
};
#if DXX_USE_UDP
union
{
#if DXX_USE_UDP
struct
{
struct _sockaddr addr; // IP address of this peer
} udp;
#endif
} protocol;
#endif
2014-07-05 16:48:12 +00:00
callsign_t callsign;
player_connection_status connected;
2021-11-01 03:37:19 +00:00
player_rank rank;
fix ping;
fix64 LastPacketTime;
2014-06-26 02:27:16 +00:00
};
2016-01-09 16:38:15 +00:00
#ifdef dsx
Allow players to remove thief at level start Commit f4b21088a039 ("Track vulcan ammo explicitly") fixed an original retail bug that prevented the thief from stealing energy weapons, because the thief could only steal weapons for which the player had ammo and energy weapons never have ammo. This went unremarked for several years, until a recent report of the new semantics as a game-breaking regression because the thief is now "ridiculously potent". Address this report, as well as an intermittently raised issue from various users over time, by adding two new knobs to both the single player "Gameplay" menu and the multiplayer setup screen: "Remove Thief at level start" and "Prevent Thief Stealing Energy Weapons". "Remove Thief" deletes the thief object during level load. It has no impact on save games, and changing it after entering a level has no effect on any thief already in the level. "Prevent Thief Stealing" is checked at the moment of theft and, when enabled, prevents stealing primary weapons other than Vulcan/Gauss. This can be changed at will in single player and is immediately effective. In multiplayer, this option can only be changed by the game host in the pre-game setup. For both knobs, there is one pair of checkboxes to control this as a player preference, which applies in single player games. There is a second pair of checkboxes in the multiplayer setup, which applies only to multiplayer games. Therefore, in multiplayer, the host chooses thief settings and all clients use the host's choice. The host may configure the thief differently in multiplayer from how the host plays in single player. For users who wanted to remove the thief, no specific tally has been kept for who requested it or when. Now that the code is being updated, this is thrown in as an easy addition. Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
#if defined(DXX_BUILD_DESCENT_II)
struct ThiefModifier
{
enum Flags : uint8_t {
Absent = 1,
NoEnergyWeapons,
};
};
#endif
2015-12-13 18:00:49 +00:00
namespace dsx {
/*
* The Network Game structure
* Contains protocol-specific data with designated prefixes and general game-related data.
* Note that not all of these infos will be sent to clients - some are used and/or set locally, only.
*/
2020-10-22 02:26:16 +00:00
struct netgame_info : prohibit_void_ptr<netgame_info>
{
static constexpr std::integral_constant<unsigned, (0 for_each_netflag_value(define_netflag_powerup_mask))> MaskAllKnownAllowedItems{};
#undef define_netflag_powerup_mask
using play_time_allowed_abi_ratio = std::ratio<5 * 60>;
#if DXX_USE_UDP
union
{
#if DXX_USE_UDP
struct
{
struct _sockaddr addr; // IP address of this netgame's host
2020-05-02 21:18:42 +00:00
std::array<short, 4> program_iver; // IVER of program for version checking
sbyte valid; // Status of Netgame info: -1 = Failed, Wrong version; 0 = No info, yet; 1 = Success
uint8_t your_index; // Tell player his designated (re)join position in players[]
fix GameID;
} udp;
#endif
} protocol;
#endif
2014-12-22 04:35:48 +00:00
ntstring<NETGAME_NAME_LEN> game_name;
2014-12-22 04:35:48 +00:00
ntstring<MISSION_NAME_LEN> mission_title;
2015-01-03 23:44:32 +00:00
ntstring<8> mission_name;
int levelnum;
2018-05-12 18:24:19 +00:00
Difficulty_level_type difficulty;
2021-11-01 03:37:19 +00:00
network_game_type gamemode;
ubyte RefusePlayers;
2022-07-09 13:39:29 +00:00
network_state game_status;
ubyte numplayers;
ubyte max_numplayers;
ubyte numconnected;
bit_game_flags game_flag;
ubyte team_vector;
2015-07-04 21:01:18 +00:00
uint8_t SecludedSpawns;
2017-03-25 19:34:02 +00:00
uint8_t MouselookFlags;
2022-08-21 00:32:38 +00:00
uint8_t PitchLockFlags;
uint32_t AllowedItems;
2015-04-19 04:18:53 +00:00
packed_spawn_granted_items SpawnGrantedItems;
packed_netduplicate_items DuplicatePowerups;
unsigned ShufflePowerupSeed;
d_mp_gameplay_options MPGameplayOptions;
#if defined(DXX_BUILD_DESCENT_II)
uint8_t Allow_marker_view;
uint8_t AlwaysLighting;
Allow players to remove thief at level start Commit f4b21088a039 ("Track vulcan ammo explicitly") fixed an original retail bug that prevented the thief from stealing energy weapons, because the thief could only steal weapons for which the player had ammo and energy weapons never have ammo. This went unremarked for several years, until a recent report of the new semantics as a game-breaking regression because the thief is now "ridiculously potent". Address this report, as well as an intermittently raised issue from various users over time, by adding two new knobs to both the single player "Gameplay" menu and the multiplayer setup screen: "Remove Thief at level start" and "Prevent Thief Stealing Energy Weapons". "Remove Thief" deletes the thief object during level load. It has no impact on save games, and changing it after entering a level has no effect on any thief already in the level. "Prevent Thief Stealing" is checked at the moment of theft and, when enabled, prevents stealing primary weapons other than Vulcan/Gauss. This can be changed at will in single player and is immediately effective. In multiplayer, this option can only be changed by the game host in the pre-game setup. For both knobs, there is one pair of checkboxes to control this as a player preference, which applies in single player games. There is a second pair of checkboxes in the multiplayer setup, which applies only to multiplayer games. Therefore, in multiplayer, the host chooses thief settings and all clients use the host's choice. The host may configure the thief differently in multiplayer from how the host plays in single player. For users who wanted to remove the thief, no specific tally has been kept for who requested it or when. Now that the code is being updated, this is thrown in as an easy addition. Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
uint8_t ThiefModifierFlags;
uint8_t AllowGuidebot;
#endif
uint8_t ShowEnemyNames;
uint8_t BrightPlayers;
uint8_t InvulAppear;
ushort segments_checksum;
int KillGoal;
/* The UI enforces that this steps in units of 5 minutes, but for
* efficiency, it is stored as ticks (1 second = F1_0). The UI
* imposes a maximum value that is small enough that overflow is
* impossible.
*/
d_time_fix PlayTimeAllowed;
fix level_time;
int control_invul_time;
int monitor_vector;
short PacketsPerSec;
ubyte PacketLossPrevention;
ubyte NoFriendlyFire;
2020-05-02 21:18:42 +00:00
std::array<callsign_t, 2> team_name;
std::array<uint32_t, MAX_PLAYERS> locations;
std::array<std::array<uint16_t, MAX_PLAYERS>, MAX_PLAYERS> kills;
std::array<int16_t, 2> team_kills;
std::array<uint16_t, MAX_PLAYERS> killed;
std::array<uint16_t, MAX_PLAYERS> player_kills;
std::array<uint32_t, MAX_PLAYERS> player_score;
std::array<player_flags, MAX_PLAYERS> net_player_flags;
std::array<netplayer_info, MAX_PLAYERS> players;
#if DXX_USE_TRACKER
ubyte Tracker;
TrackerNATHolePunchWarn TrackerNATWarned;
#endif
2014-06-26 02:27:16 +00:00
};
/*
* Structure holding all powerup types we want to keep track of. Used to cap or respawn powerups and keep the level inventory steady.
* 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.
* I'm sorry if this isn't very optimized but I found this easier to handle than a single variable per powerup.
*/
struct multi_level_inv
{
2020-05-02 21:18:42 +00:00
std::array<uint32_t, MAX_POWERUP_TYPES> Initial; // initial (level start) count of this powerup type
std::array<uint32_t, MAX_POWERUP_TYPES> Current; // current count of this powerup type
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!
};
namespace multi
{
struct level_checksum_mismatch : std::runtime_error
{
level_checksum_mismatch() :
runtime_error("level checksum mismatch")
{
}
};
struct local_player_not_playing : std::runtime_error
{
local_player_not_playing() :
runtime_error("local player not playing")
{
}
};
}
}
#endif
2021-11-01 03:37:19 +00:00
netplayer_info::player_rank GetMyNetRanking();
namespace dcx {
extern const enumerated_array<char[16], 10, netplayer_info::player_rank> RankStrings;
netplayer_info::player_rank build_rank_from_untrusted(uint8_t untrusted);
}
/* Stub for mods that remap player colors */
static inline unsigned get_player_color(unsigned pnum)
{
return pnum;
}
static inline unsigned get_team_color(unsigned tnum)
{
return tnum;
}
static inline unsigned get_player_or_team_color(unsigned pnum)
{
return Game_mode & GM_TEAM
? get_team_color(get_team(pnum))
: get_player_color(pnum);
}
#define PUT_INTEL_SEGNUM(D,S) ( DXX_BEGIN_COMPOUND_STATEMENT { \
const segnum_t PUT_INTEL_SEGNUM = S; \
PUT_INTEL_SHORT(D, static_cast<uint16_t>(PUT_INTEL_SEGNUM)); \
} DXX_END_COMPOUND_STATEMENT )