95 lines
3.1 KiB
C++
95 lines
3.1 KiB
C++
|
|
#pragma once
|
||
|
|
|
||
|
|
#include "MessageRegistry.hpp"
|
||
|
|
|
||
|
|
#include "message_protocol/MessageConnection.hpp"
|
||
|
|
#include "message_protocol/MessageEndpoint.hpp"
|
||
|
|
|
||
|
|
#include <spdlog/spdlog.h>
|
||
|
|
|
||
|
|
#include <functional>
|
||
|
|
#include <span>
|
||
|
|
#include <utility>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
namespace tw {
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sends and receives protobuf messages over an endpoint.
|
||
|
|
*
|
||
|
|
* A view rather than an owner: several of these may share one endpoint, and
|
||
|
|
* messages encoded some other way can travel over it at the same time.
|
||
|
|
*/
|
||
|
|
class ProtobufMessages {
|
||
|
|
msg::MessageEndpoint* m_endpoint;
|
||
|
|
|
||
|
|
// Reused between sends so a steady stream of messages does not allocate.
|
||
|
|
std::vector<std::byte> m_buffer;
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
bool serialize(const T& message) {
|
||
|
|
m_buffer.resize(message.ByteSizeLong());
|
||
|
|
return message.SerializeToArray(m_buffer.data(), static_cast<int>(m_buffer.size()));
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
explicit ProtobufMessages(msg::MessageEndpoint* endpoint) :
|
||
|
|
m_endpoint(endpoint) {
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Calls `handler` for every T that arrives from any peer. */
|
||
|
|
template<typename T>
|
||
|
|
void set_handler(std::function<void(msg::PeerId, const T&)> handler) {
|
||
|
|
m_endpoint->set_handler(
|
||
|
|
Message<T>::value,
|
||
|
|
[handler = std::move(handler), message = T{}](msg::PeerId peer,
|
||
|
|
std::span<const std::byte> body) mutable {
|
||
|
|
if(!message.ParseFromArray(body.data(), static_cast<int>(body.size()))) {
|
||
|
|
spdlog::warn("Failed to parse a {} of {} bytes", Message<T>::value, body.size());
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
handler(peer, message);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
tl::expected<void, msg::MessageError> send(msg::MessageConnection* peer,
|
||
|
|
const T& message,
|
||
|
|
bool reliable = true) {
|
||
|
|
if(!serialize(message)) {
|
||
|
|
return tl::make_unexpected(
|
||
|
|
msg::MessageError(msg::MessageErrorType::SendFailed, "failed to serialize the message"));
|
||
|
|
}
|
||
|
|
|
||
|
|
return peer->send(Message<T>::value, m_buffer, reliable);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
tl::expected<void, msg::MessageError> send_to(msg::PeerId id, const T& message, bool reliable = true) {
|
||
|
|
auto* peer = m_endpoint->peer(id);
|
||
|
|
if(peer == nullptr) {
|
||
|
|
return tl::make_unexpected(msg::MessageError(msg::MessageErrorType::NotConnected));
|
||
|
|
}
|
||
|
|
|
||
|
|
return send(peer, message, reliable);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
void broadcast(const T& message, bool reliable = false) {
|
||
|
|
if(!serialize(message)) {
|
||
|
|
spdlog::error("Failed to serialize a {} for broadcast", Message<T>::value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
for(auto* peer : m_endpoint->peers()) {
|
||
|
|
auto send_r = peer->send(Message<T>::value, m_buffer, reliable);
|
||
|
|
if(!send_r) {
|
||
|
|
spdlog::error("Failed to send to peer {}: {}", peer->peer_id(), send_r.error().message());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
}
|