0ad/source/network/NetHost.h
2026-08-10 11:18:39 +02:00

187 lines
4.3 KiB
C++

/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* Various declarations shared by networking code.
*/
#ifndef NETHOST_H
#define NETHOST_H
#include "lib/external_libraries/enet.h"
#include "lib/types.h"
#include "ps/CStr.h"
#include <deque>
#include <map>
#include <ngtcp2/ngtcp2.h>
#include <optional>
#include <span>
#include <vector>
#include <gnutls/gnutls.h>
class CNetMessage;
typedef struct _ENetPeer ENetPeer;
typedef struct _ENetPacket ENetPacket;
typedef struct _ENetHost ENetHost;
constexpr std::size_t MAX_UDP_PAYLOAD_SIZE{64 * KiB};
struct CredentialsDeleter
{
void operator()(const gnutls_certificate_credentials_t cred) const
{
gnutls_certificate_free_credentials(cred);
}
};
struct SessionDeleter
{
void operator()(const gnutls_session_t p) const
{
gnutls_deinit(p);
}
};
struct ConnectionDeleter
{
void operator()(ngtcp2_conn* p) const
{
ngtcp2_conn_del(p);
}
};
struct AddressStorage
{
ngtcp2_sockaddr_union address;
ngtcp2_socklen length{sizeof(address)};
};
struct PlayerAssignment
{
/**
* Whether the player is currently connected and active.
* (We retain information on disconnected players to support rejoining,
* but don't transmit these to other clients.)
*/
bool m_Enabled;
/// Player name
CStrW m_Name;
/// The player that the given host controls, or -1 if none (observer)
i32 m_PlayerID;
/// Status - Ready or not: 0 for not ready, 1 for ready, 2 to stay ready
u8 m_Status;
};
typedef std::map<CStr, PlayerAssignment> PlayerAssignmentMap; // map from GUID -> assignment
/**
* Reasons sent by server to clients in disconnection messages.
* Must be kept in sync with binaries/data/mods/public/gui/common/network.js
* To avoid ambiguity, use a distinct reason for each callstack leading to a disconnect.
* NDR_UNKNOWN should remain reserved to the case where it is actually not a known disconnect by the server.
*/
enum NetDisconnectReason
{
NDR_UNKNOWN = 0,
NDR_CONNECTION_REQUEST_TIMED_OUT,
NDR_SERVER_SHUTDOWN,
NDR_INCORRECT_PROTOCOL_VERSION,
NDR_SERVER_LOADING,
NDR_SERVER_ALREADY_IN_GAME,
NDR_KICKED,
NDR_BANNED,
NDR_PLAYERNAME_IN_USE,
NDR_SERVER_FULL,
NDR_LOBBY_AUTH_FAILED,
NDR_GUID_FAILED,
NDR_INCORRECT_READY_TURN_COMMANDS,
NDR_INCORRECT_READY_TURN_SIMULATED,
NDR_SERVER_REFUSED,
NDR_STUN_PORT_FAILED,
NDR_STUN_ENDPOINT_FAILED,
NDR_INCORRECT_SOFTWARE_VERSION
};
struct DestroyHost
{
void operator()(ENetHost* host) const noexcept
{
enet_host_destroy(host);
}
};
struct DestroyPeer
{
void operator()(ENetPeer* peer) const noexcept
{
// Disconnect immediately (we can't wait for acks)
enet_peer_disconnect_now(peer, NDR_SERVER_SHUTDOWN);
}
};
class CNetHost
{
public:
static const int DEFAULT_CHANNEL = 0;
/**
* Construct an ENet packet by serialising the given message.
* @return NULL on failure
*/
static std::vector<std::uint8_t> CreatePacket(const CNetMessage* message);
/**
* Initialize ENet.
* This must be called before any other networking code.
*/
static void Initialize();
/**
* Deinitialize ENet.
*/
static void Deinitialize();
};
class Stream
{
public:
Stream(const std::int64_t streamId);
void PushData(std::vector<std::uint8_t> data);
void PushMessage(const CNetMessage* message);
std::optional<std::span<const std::uint8_t>> PeekData();
void MarkSent(const std::size_t offset);
void MarkAcknowledged(const std::size_t offset);
std::optional<std::vector<std::uint8_t>> Receive(const std::span<const std::uint8_t> data);
std::int64_t m_Id;
private:
std::deque<std::vector<std::uint8_t>> m_SendBuffer;
std::deque<std::vector<std::uint8_t>> m_ReceiveBuffer;
/* invariant: m_SentOffset >= m_AckedOffset */
std::size_t m_SentOffset{0};
std::size_t m_AckedOffset{0};
};
#endif // NETHOST_H