M2-PT-DRP/source/EventManager.cpp

207 lines
6.6 KiB
C++

#include "EventManager.hpp"
#include <algorithm>
#include <stdexcept>
#include <cstring>
#include <iostream>
#include <map>
#include <netdb.h>
#include <ostream>
#include <thread>
#include <random>
#include <sys/socket.h>
#include "events/types.hpp"
#include "events/audio/AudioEvent.hpp"
#include "events/info/InfoEvent.hpp"
#include "events/pong/PongEvent.hpp"
#include "events/search/SearchEvent.hpp"
#include "packets/base/GenericPacket.hpp"
#include "tasks/client/ClientTask.hpp"
#include "tasks/server/ServerTask.hpp"
#include "tasks/undefined/UndefinedTask.hpp"
EventManager::EventManager() {
// register the different events type
this->eventRegistry = {
{drp::event::EventType::PONG, std::make_shared<drp::event::PongEvent>()},
{drp::event::EventType::SEARCH, std::make_shared<drp::event::SearchEvent>()},
{drp::event::EventType::INFO, std::make_shared<drp::event::InfoEvent>()},
{drp::event::EventType::AUDIO, std::make_shared<drp::event::AudioEvent>()},
};
// register the different tasks type
this->taskRegistry = {
{drp::task::TaskType::UNDEFINED, std::make_shared<drp::task::UndefinedTask>()},
{drp::task::TaskType::CLIENT, std::make_shared<drp::task::ClientTask>()},
{drp::task::TaskType::SERVER, std::make_shared<drp::task::ServerTask>()},
};
// hints for the communication
addrinfo addressHints {};
addressHints.ai_family = AF_INET; // TODO: AF_INET6
addressHints.ai_socktype = SOCK_DGRAM;
addressHints.ai_protocol = IPPROTO_UDP;
// create the client socket
this->context.socket = socket(
addressHints.ai_family,
addressHints.ai_socktype,
addressHints.ai_protocol
);
if (this->context.socket < 0)
throw std::runtime_error("[Receiver] Could not create the socket: " + std::string(strerror(errno)));
// get the information for the broadcast local-link address
// TODO(Faraphel): ip / port as argument ?
if(const int error = getaddrinfo(
"0.0.0.0", // TODO: ff02::1
"5650",
&addressHints,
&context.broadcastAddressInfo
) != 0)
throw std::runtime_error("[Sender] Could not get the address: " + std::string(gai_strerror(error)));
// generate a random identifier for ourselves
std::random_device randomDevice;
std::mt19937 randomGenerator(randomDevice());
std::uniform_int_distribution<std::uint32_t> distribution(
1,
std::numeric_limits<std::uint32_t>::max()
);
this->context.me.id = distribution(randomGenerator);
// TODO(Faraphel): should only be enabled in specific case.
this->context.me.serverEnabled = true;
// define the time of the latest discovery
this->context.latestPeerDiscovery = std::chrono::high_resolution_clock::now();
}
void EventManager::loop() {
// run an event receiver and sender
this->senderThread = std::thread(&EventManager::loopSender, this);
this->receiverThread = std::thread(&EventManager::loopReceiver, this);
this->senderThread.join();
this->receiverThread.join();
freeaddrinfo(this->context.broadcastAddressInfo);
}
void EventManager::loopSender() {
addrinfo* destinationInfo;
addrinfo addressHints {};
addressHints.ai_family = AF_INET; // TODO: AF_INET6
addressHints.ai_socktype = SOCK_DGRAM;
addressHints.ai_protocol = IPPROTO_UDP;
// get the information for the broadcast local-link address
// TODO(Faraphel): ip / port as argument ?
if(const int error = getaddrinfo(
"localhost", // TODO: ff02::1
"5650",
&addressHints,
&destinationInfo
) != 0)
throw std::runtime_error("[Sender] Could not get the address: " + std::string(gai_strerror(error)));
while (true) {
std::cout << "[Sender] Status: " + std::to_string(static_cast<int>(this->context.me.status)) << std::endl;
// get the corresponding task class
std::shared_ptr<drp::task::BaseTask> task;
try {
task = this->taskRegistry.at(this->context.me.status);
} catch (const std::out_of_range& exception) {
std::cerr << "[Sender] Unsupported status." << std::endl;
continue;
}
// ask the task class to handle the task
task->handle(this->context);
}
// free the address
freeaddrinfo(destinationInfo);
}
void EventManager::loopReceiver() {
// prepare space for the sender address
sockaddr_storage fromAddress {};
socklen_t fromAddressLength = sizeof(fromAddress);
drp::packet::GenericPacket packet {};
drp::packet::GenericPacketContent packetContent {};
addrinfo addressHints {};
addressHints.ai_family = AF_INET; // TODO: AF_INET6
addressHints.ai_socktype = SOCK_DGRAM;
addressHints.ai_protocol = IPPROTO_UDP;
// TODO(Faraphel): port as argument
addrinfo* senderInfo;
if(getaddrinfo(
"0.0.0.0", // hostname
"5650", // port
&addressHints,
&senderInfo
) != 0)
throw std::runtime_error("[Receiver] Could not get the address: " + std::string(gai_strerror(errno)));
// bind the socket to the address
if (bind(
this->context.socket,
senderInfo->ai_addr,
senderInfo->ai_addrlen
) < 0)
throw std::runtime_error("[Receiver] Could not bind to the address: " + std::string(strerror(errno)));
// free the sender address
freeaddrinfo(senderInfo);
// client loop
while (true) {
// receive new data
const ssize_t size = recvfrom(
this->context.socket,
&packet,
sizeof(packet),
0,
reinterpret_cast<sockaddr*>(&fromAddress),
&fromAddressLength
);
if (size == -1)
throw std::runtime_error("[Receiver] Could not receive the packet: " + std::string(strerror(errno)));
// if the packet channel is neither 0 (all) nor the current one, ignore it
if (packet.channel != 0 && packet.channel != this->context.me.channel)
continue;
// decrypt the packet
packetContent = packet.getContent();
// get the corresponding event class
std::shared_ptr<drp::event::BaseEvent> event;
try {
event = this->eventRegistry.at(static_cast<drp::event::EventType>(packetContent.eventType));
} catch (const std::out_of_range& exception) {
std::cerr << "Unsupported event type." << std::endl;
continue;
}
// ask the event class to handle the event
event->handle(
this->context,
packetContent,
fromAddress,
fromAddressLength
);
}
}