182 lines
5.7 KiB
C++
182 lines
5.7 KiB
C++
#include "EventManager.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <netdb.h>
|
|
#include <ostream>
|
|
#include <thread>
|
|
#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
|
|
// TODO: should be merged with the two others addressHints
|
|
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)));
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
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) {
|
|
// 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 << "Unsupported status." << std::endl;
|
|
continue;
|
|
}
|
|
|
|
// ask the task class to handle the task
|
|
task->handle();
|
|
|
|
// wait a second
|
|
// TODO(Faraphel): might be moved to the tasks directly ? what if they want a lower cooldown ?
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
|
|
// 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(
|
|
nullptr, // 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,
|
|
reinterpret_cast<sockaddr*>(&fromAddress),
|
|
fromAddressLength
|
|
);
|
|
}
|
|
}
|