Skip to content

Commit 05ce58d

Browse files
committed
Add high-level multiplayer packet management
Add PacketType tests
1 parent 0fdf0ea commit 05ce58d

File tree

13 files changed

+831
-0
lines changed

13 files changed

+831
-0
lines changed

src/openvic-simulation/GameManager.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#include "GameManager.hpp"
22

33
#include <chrono>
4+
#include <string>
5+
6+
#include "openvic-simulation/multiplayer/ClientManager.hpp"
7+
#include "openvic-simulation/multiplayer/HostManager.hpp"
48

59
using namespace OpenVic;
610

@@ -112,6 +116,17 @@ bool GameManager::update_clock() {
112116
return instance_manager->update_clock();
113117
}
114118

119+
void GameManager::create_client() {
120+
client_manager = std::make_unique<ClientManager>(this);
121+
}
122+
123+
void GameManager::create_host(std::string session_name) {
124+
host_manager = std::make_unique<HostManager>(this);
125+
if (!session_name.empty()) {
126+
host_manager->get_host_session().set_game_name(session_name);
127+
}
128+
}
129+
115130
uint64_t GameManager::get_elapsed_microseconds() {
116131
return get_elapsed_usec_time_callback();
117132
}

src/openvic-simulation/GameManager.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
#pragma once
22

3+
#include <memory>
34
#include <optional>
5+
#include <string>
46

57
#include "openvic-simulation/DefinitionManager.hpp"
68
#include "openvic-simulation/InstanceManager.hpp"
79
#include "openvic-simulation/dataloader/Dataloader.hpp"
810
#include "openvic-simulation/misc/GameRulesManager.hpp"
911
#include "openvic-simulation/gen/commit_info.gen.hpp"
12+
#include "openvic-simulation/multiplayer/ClientManager.hpp"
13+
#include "openvic-simulation/multiplayer/HostManager.hpp"
1014

1115
#include <function2/function2.hpp>
1216

1317
namespace OpenVic {
18+
struct HostManager;
19+
struct ClientManager;
20+
1421
struct GameManager {
1522
using elapsed_time_getter_func_t = fu2::function_base<true, true, fu2::capacity_none, false, false, uint64_t() const>;
1623

@@ -26,6 +33,9 @@ namespace OpenVic {
2633
InstanceManager::gamestate_updated_func_t gamestate_updated_callback;
2734
bool PROPERTY_CUSTOM_PREFIX(definitions_loaded, are);
2835

36+
std::unique_ptr<HostManager> host_manager;
37+
std::unique_ptr<ClientManager> client_manager;
38+
2939
public:
3040
GameManager(
3141
InstanceManager::gamestate_updated_func_t new_gamestate_updated_callback,
@@ -41,6 +51,22 @@ namespace OpenVic {
4151
return instance_manager ? &*instance_manager : nullptr;
4252
}
4353

54+
inline HostManager* get_host_manager() {
55+
return host_manager.get();
56+
}
57+
58+
inline HostManager const* get_host_manager() const {
59+
return host_manager.get();
60+
}
61+
62+
inline ClientManager* get_client_manager() {
63+
return client_manager.get();
64+
}
65+
66+
inline ClientManager const* get_client_manager() const {
67+
return client_manager.get();
68+
}
69+
4470
bool set_roots(Dataloader::path_vector_t const& roots);
4571

4672
bool load_definitions(Dataloader::localisation_callback_t localisation_callback);
@@ -51,6 +77,9 @@ namespace OpenVic {
5177

5278
bool update_clock();
5379

80+
void create_client();
81+
void create_host(std::string session_name = "");
82+
5483
static constexpr std::string_view get_commit_hash() {
5584
return SIM_COMMIT_HASH;
5685
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
#include "BaseMultiplayerManager.hpp"
3+
4+
#include <any>
5+
6+
#include "openvic-simulation/multiplayer/PacketType.hpp"
7+
#include "openvic-simulation/utility/ErrorMacros.hpp"
8+
9+
using namespace OpenVic;
10+
11+
BaseMultiplayerManager::BaseMultiplayerManager(GameManager* game_manager) : game_manager(game_manager) {
12+
packet_cache.reserve_power(15);
13+
}
14+
15+
bool BaseMultiplayerManager::broadcast_packet(PacketType const& type, std::any const& argument) {
16+
OV_ERR_FAIL_COND_V(!PacketType::is_valid_type(type), false);
17+
return true;
18+
}
19+
20+
bool BaseMultiplayerManager::send_packet(client_id_type client_id, PacketType const& type, std::any const& argument) {
21+
OV_ERR_FAIL_COND_V(!PacketType::is_valid_type(type), false);
22+
return true;
23+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#pragma once
2+
3+
#include <any>
4+
#include <cstdint>
5+
6+
#include "openvic-simulation/multiplayer/HostSession.hpp"
7+
#include "openvic-simulation/multiplayer/PacketType.hpp"
8+
#include "openvic-simulation/multiplayer/lowlevel/ReliableUdpClient.hpp"
9+
#include "openvic-simulation/types/RingBuffer.hpp"
10+
#include "openvic-simulation/utility/Getters.hpp"
11+
12+
namespace OpenVic {
13+
struct GameManager;
14+
15+
struct BaseMultiplayerManager {
16+
BaseMultiplayerManager(GameManager* game_manager = nullptr);
17+
18+
using client_id_type = uint64_t;
19+
using sequence_type = ReliableUdpClient::sequence_type;
20+
21+
virtual bool broadcast_packet(PacketType const& type, std::any const& argument);
22+
virtual bool send_packet(client_id_type client_id, PacketType const& type, std::any const& argument);
23+
virtual bool poll() = 0;
24+
25+
enum class Type : uint8_t { HOST, CLIENT };
26+
27+
virtual Type get_type() const = 0;
28+
29+
template<typename T>
30+
T* cast_as() {
31+
if (get_type() == T::type_tag) {
32+
return static_cast<T*>(this);
33+
}
34+
return nullptr;
35+
}
36+
37+
PacketSpan get_last_raw_packet() {
38+
return last_raw_packet;
39+
}
40+
41+
protected:
42+
bool PROPERTY_ACCESS(in_lobby, protected, false);
43+
GameManager* PROPERTY_PTR_ACCESS(game_manager, protected);
44+
HostSession PROPERTY_ACCESS(host_session, protected);
45+
46+
RingBuffer<uint8_t> packet_cache;
47+
48+
struct PacketCacheIndex {
49+
decltype(packet_cache)::const_iterator begin;
50+
decltype(packet_cache)::const_iterator end;
51+
constexpr bool is_valid() const {
52+
return begin != end;
53+
}
54+
};
55+
56+
std::vector<uint8_t> last_raw_packet;
57+
58+
friend bool PacketTypes::send_raw_packet_process_callback( //
59+
BaseMultiplayerManager* multiplayer_manager, PacketSpan packet
60+
);
61+
};
62+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
2+
#include "ClientManager.hpp"
3+
4+
#include "openvic-simulation/multiplayer/PacketType.hpp"
5+
#include "openvic-simulation/multiplayer/lowlevel/HostnameAddress.hpp"
6+
#include "openvic-simulation/multiplayer/lowlevel/NetworkError.hpp"
7+
#include "openvic-simulation/multiplayer/lowlevel/NetworkSocket.hpp"
8+
#include "openvic-simulation/multiplayer/lowlevel/PacketBuilder.hpp"
9+
#include "openvic-simulation/multiplayer/lowlevel/ReliableUdpClient.hpp"
10+
#include "openvic-simulation/utility/ErrorMacros.hpp"
11+
12+
using namespace OpenVic;
13+
14+
bool ClientManager::connect_to(HostnameAddress const& address, NetworkSocket::port_type port) {
15+
NetworkError err = client.connect_to(address.resolved_address(), port);
16+
OV_ERR_FAIL_COND_V(err != NetworkError::OK, false);
17+
PacketBuilder builder;
18+
builder.put_back<uint8_t>(1);
19+
return client.set_packet(builder) == NetworkError::OK;
20+
}
21+
22+
template<PacketType const& type>
23+
bool ClientManager::_send_packet_to_host(auto const& argument) {
24+
PacketBuilder builder;
25+
builder.put_back(type.packet_id);
26+
if (!type.create_packet(this, argument, builder)) {
27+
return false;
28+
}
29+
30+
if (!add_packet_to_cache(builder)) {
31+
return false;
32+
}
33+
34+
client.set_packet(builder);
35+
return true;
36+
}
37+
38+
bool ClientManager::broadcast_packet(PacketType const& type, std::any const& argument) {
39+
if (!BaseMultiplayerManager::broadcast_packet(type, argument)) {
40+
return false;
41+
}
42+
43+
OV_ERR_FAIL_COND_V(!type.can_client_send, false);
44+
return _send_packet_to_host<PacketTypes::broadcast_packet>(BroadcastData { type.packet_id, argument });
45+
}
46+
47+
bool ClientManager::send_packet(client_id_type client_id, PacketType const& type, std::any const& argument) {
48+
if (!BaseMultiplayerManager::send_packet(client_id, type, argument)) {
49+
return false;
50+
}
51+
52+
OV_ERR_FAIL_COND_V(!type.can_client_send, false);
53+
return _send_packet_to_host<PacketTypes::retransmit_packet>(RetransmitData { client_id, type.packet_id, argument });
54+
}
55+
56+
bool ClientManager::poll() {
57+
if (client.available_packets() < 1) {
58+
return false;
59+
}
60+
61+
PacketSpan span = client.packet_span();
62+
if (client_id == INVALID_CLIENT_ID) {
63+
OV_ERR_FAIL_COND_V(client.get_current_sequence_value() != 0, false);
64+
client_id = span.read<decltype(client_id)>();
65+
return poll();
66+
}
67+
68+
decltype(PacketType::packet_id) packet_id = span.read<decltype(packet_id)>();
69+
OV_ERR_FAIL_COND_V(!PacketType::is_valid_type(packet_id), false);
70+
71+
PacketType const& packet_type = PacketType::get_type_by_id(packet_id);
72+
if (!packet_type.process_packet(this, span)) {
73+
// TODO: packet processing failed
74+
return false;
75+
}
76+
// TODO: packet was processed
77+
78+
return true;
79+
}
80+
81+
bool ClientManager::add_packet_to_cache(std::span<uint8_t> bytes) {
82+
OV_ERR_FAIL_COND_V(bytes.size_bytes() > ReliableUdpClient::MAX_PACKET_SIZE, false);
83+
decltype(packet_cache)::iterator begin = packet_cache.append(bytes.data(), bytes.size_bytes());
84+
decltype(packet_cache)::iterator end = packet_cache.end();
85+
OV_ERR_FAIL_COND_V(begin == end, false);
86+
return sequence_to_index.try_emplace(client.get_next_sequence_value(), PacketCacheIndex { begin, end }).second;
87+
}
88+
89+
std::vector<uint8_t> ClientManager::get_packet_cache(sequence_type sequence_value) {
90+
decltype(sequence_to_index)::iterator it = sequence_to_index.find(sequence_value);
91+
OV_ERR_FAIL_COND_V(it == sequence_to_index.end(), {});
92+
return { it.value().begin, it.value().end };
93+
}
94+
95+
void ClientManager::remove_from_cache(sequence_type sequence_value) {
96+
decltype(sequence_to_index)::iterator it = sequence_to_index.find(sequence_value);
97+
OV_ERR_FAIL_COND(it == sequence_to_index.end());
98+
sequence_to_index.unordered_erase(it);
99+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#pragma once
2+
3+
#include <limits>
4+
5+
#include "openvic-simulation/multiplayer/BaseMultiplayerManager.hpp"
6+
#include "openvic-simulation/multiplayer/lowlevel/HostnameAddress.hpp"
7+
#include "openvic-simulation/multiplayer/lowlevel/NetworkSocket.hpp"
8+
#include "openvic-simulation/multiplayer/lowlevel/ReliableUdpClient.hpp"
9+
#include "openvic-simulation/multiplayer/lowlevel/TcpPacketStream.hpp"
10+
11+
namespace OpenVic {
12+
struct GameManager;
13+
14+
struct ClientManager final : BaseMultiplayerManager {
15+
using BaseMultiplayerManager::BaseMultiplayerManager;
16+
17+
bool connect_to(HostnameAddress const& address, NetworkSocket::port_type port);
18+
19+
bool broadcast_packet(PacketType const& type, std::any const& argument) override;
20+
bool send_packet(client_id_type client_id, PacketType const& type, std::any const& argument) override;
21+
bool poll() override;
22+
23+
static constexpr Type type_tag = Type::CLIENT;
24+
inline constexpr Type get_type() const override {
25+
return type_tag;
26+
}
27+
28+
static constexpr client_id_type INVALID_CLIENT_ID = std::numeric_limits<client_id_type>::max();
29+
30+
private:
31+
ReliableUdpClient PROPERTY_REF(client);
32+
TcpPacketStream file_client;
33+
client_id_type PROPERTY(client_id, INVALID_CLIENT_ID);
34+
35+
friend bool PacketTypes::update_host_session_process_callback(BaseMultiplayerManager* game_manager, PacketSpan packet);
36+
37+
ordered_map<sequence_type, PacketCacheIndex> sequence_to_index;
38+
39+
bool add_packet_to_cache(std::span<uint8_t> bytes);
40+
std::vector<uint8_t> get_packet_cache(sequence_type sequence_value);
41+
void remove_from_cache(sequence_type sequence_value);
42+
43+
template<PacketType const& type>
44+
bool _send_packet_to_host(auto const& argument);
45+
};
46+
}

0 commit comments

Comments
 (0)