Teaspeak-Server/server/src/server/VoiceServer.h

204 lines
8.2 KiB
C++

#pragma once
#include <netinet/in.h>
#include <deque>
#include <ThreadPool/Thread.h>
#include <ThreadPool/Mutex.h>
#include <event.h>
#include <condition_variable>
#include <misc/net.h>
#include <protocol/ringbuffer.h>
#include <misc/task_executor.h>
#include "./voice/DatagramPacket.h"
#include "Definitions.h"
#include <shared_mutex>
namespace ts {
namespace server {
class VirtualServer;
class VoiceServer;
class VoiceClient;
class POWHandler;
class VoiceServerSocket : public std::enable_shared_from_this<VoiceServerSocket> {
public:
/**
* Note: Only access event_read or event_write when the socket mutex is acquired or
* or within the event loop!
*/
struct NetworkEvents {
VoiceServerSocket* socket;
struct event* event_read{nullptr};
struct event* event_write{nullptr};
NetworkEvents(VoiceServerSocket* socket) : socket{socket} {};
NetworkEvents(const NetworkEvents&) = delete;
NetworkEvents(NetworkEvents&&) = delete;
~NetworkEvents();
};
explicit VoiceServerSocket(VoiceServer* server, sockaddr_storage address);
virtual ~VoiceServerSocket();
[[nodiscard]] inline auto is_active() const { return this->file_descriptor > 0; };
[[nodiscard]] inline const sockaddr_storage& address() const { return this->address_; };
/**
* Create a new UDP server socket on the target address.
*/
[[nodiscard]] bool activate(std::string& /* error */);
/**
* Deactivate the binding if activated.
* Note: This will block until all active network events have been processed.
*/
void deactivate();
inline void send_datagram(udp::DatagramPacket* datagram) {
assert(!datagram->next_packet);
datagram->next_packet = nullptr;
std::lock_guard lock{this->mutex};
if(!this->file_descriptor) {
udp::DatagramPacket::destroy(datagram);
return;
}
*this->write_datagram_tail = datagram;
this->write_datagram_tail = &datagram->next_packet;
this->enqueue_network_write();
}
inline void enqueue_client_write(std::weak_ptr<VoiceClient> client) {
std::lock_guard lock{this->mutex};
if(!this->file_descriptor) {
return;
}
this->write_client_queue.push_back(std::move(client));
this->enqueue_network_write();
}
private:
ServerId server_id;
VoiceServer* server;
sockaddr_storage address_;
std::mutex mutex{};
int file_descriptor{0};
std::vector<std::unique_ptr<NetworkEvents>> network_events{};
size_t network_write_index{0};
udp::DatagramPacket* write_datagram_head{nullptr};
udp::DatagramPacket** write_datagram_tail{&this->write_datagram_head};
std::deque<std::weak_ptr<VoiceClient>> write_client_queue;
inline udp::DatagramPacket* pop_dg_write_queue() {
std::lock_guard lock{this->mutex};
if(!this->write_datagram_head) {
return nullptr;
}
auto packet = std::exchange(this->write_datagram_head, this->write_datagram_head->next_packet);
if(!this->write_datagram_head) {
assert(this->write_datagram_tail == &packet->next_packet);
this->write_datagram_tail = &this->write_datagram_head;
}
return packet;
}
inline bool pop_voice_write_queue(std::shared_ptr<VoiceClient>& result) {
std::lock_guard lock{this->mutex};
auto it_begin = this->write_client_queue.begin();
auto it_end = this->write_client_queue.end();
auto it = it_begin;
while(it != it_end) {
result = it->lock();
if(result) {
this->write_client_queue.erase(it_begin, ++it);
return it != it_end;
}
it++;
}
if(it_begin != it_end) {
this->write_client_queue.erase(it_begin, it_end);
}
return false;
}
/**
* Enqueue a write event.
* Attention: The socket mutex must be locked!
*/
inline void enqueue_network_write() {
assert(!this->network_events.empty());
auto write_event = this->network_events[this->network_write_index++ % this->network_events.size()]->event_write;
event_add(write_event, nullptr);
}
static void network_event_read(int, short, void *);
static void network_event_write(int, short, void *);
};
class VoiceServer {
friend class VoiceServerSocket;
friend class POWHandler; /* TODO: Still needed? May use some kind of callback */
public:
explicit VoiceServer(const std::shared_ptr<VirtualServer>& server);
~VoiceServer();
bool start(const std::deque<sockaddr_storage>&, std::string&);
bool stop(const std::chrono::milliseconds& flushTimeout = std::chrono::milliseconds{1000});
[[nodiscard]] std::shared_ptr<VoiceClient> findClient(ClientId);
[[nodiscard]] std::shared_ptr<VoiceClient> findClient(sockaddr_in* addr, bool lock);
[[nodiscard]] std::shared_ptr<VoiceClient> findClient(sockaddr_in6* addr, bool lock);
[[nodiscard]] inline std::shared_ptr<VoiceClient> findClient(sockaddr_storage* address, bool lock = true) {
switch(address->ss_family) {
case AF_INET:
return this->findClient((sockaddr_in*) address, lock);
case AF_INET6:
return this->findClient((sockaddr_in6*) address, lock);
default:
return nullptr;
}
}
[[nodiscard]] inline auto getSockets() {
std::lock_guard lock{this->sockets_mutex};
return this->sockets;
}
[[nodiscard]] inline std::shared_ptr<VirtualServer> get_server() { return this->server; }
void tickHandshakingClients();
void execute_resend(const std::chrono::system_clock::time_point& /* now */, std::chrono::system_clock::time_point& /* next resend */);
bool unregisterConnection(std::shared_ptr<VoiceClient>);
private:
std::unique_ptr<POWHandler> pow_handler;
std::shared_ptr<VirtualServer> server{nullptr};
bool running{false};
std::shared_mutex sockets_mutex{};
std::deque<std::shared_ptr<VoiceServerSocket>> sockets{};
task_id handshake_tick_task{0};
std::recursive_mutex connectionLock;
std::deque<std::shared_ptr<VoiceClient>> activeConnections;
void handleClientAddressChange(
const std::shared_ptr<VoiceClient>& /* voice client */,
const sockaddr_storage& /* new address */,
const udp::pktinfo_storage& /* remote address info */
);
};
}
}