590 lines
21 KiB
C++
590 lines
21 KiB
C++
/*
|
|
PolyGun
|
|
|
|
Copyright (c) 2023 mrkubax10 <mrkubax10@onet.pl>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#include "server/server.hpp"
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <csignal>
|
|
#include <algorithm>
|
|
#include <config.hpp>
|
|
#if defined(__unix__)
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
#if defined(__FreeBSD__)
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include "common/math/rect3d.hpp"
|
|
#include "common/logger.hpp"
|
|
#include "common/world/chunk.hpp"
|
|
#include "server/as/ast.hpp"
|
|
#include "server/as/vm.hpp"
|
|
|
|
using namespace polygun::server;
|
|
|
|
static const float DAY_TIME_INTERVAL = 10.0f*60.0f/65536.0f;
|
|
|
|
static Server* g_current_server = nullptr;
|
|
|
|
as::Value test_native_function(as::VM& vm, const std::vector<as::Value>& args) {
|
|
LOG_INFO("%d", args[0].i);
|
|
return as::Value{};
|
|
}
|
|
|
|
Server::Server(std::atomic<bool>* running_atomic, std::optional<unsigned short> port_override) :
|
|
m_config(),
|
|
m_running(running_atomic?running_atomic:new std::atomic<bool>),
|
|
m_running_atomic_created(!running_atomic),
|
|
m_clients(),
|
|
m_clients_mutex(),
|
|
m_latest_client(),
|
|
m_server_socket(new polygun::network::UDPSocket),
|
|
m_command_thread(),
|
|
m_update_thread(),
|
|
m_day_time(15000),
|
|
m_mod_manager(*this),
|
|
m_chunk_manager(*this),
|
|
m_command_parser(*this),
|
|
m_player_storage(*this)
|
|
{
|
|
LOG_INFO("Starting server");
|
|
m_server_socket->bind(port_override.value_or(m_config.m_port));
|
|
if(m_running_atomic_created) {
|
|
m_running->store(true);
|
|
g_current_server = this;
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
#if defined(__unix__)
|
|
signal(SIGQUIT, signal_handler);
|
|
signal(SIGSTOP, signal_handler);
|
|
#endif
|
|
m_command_thread.reset(new std::thread(&Server::command_thread_func, this));
|
|
}
|
|
m_player_storage.load();
|
|
m_chunk_manager.load_map_from_file(m_config.m_default_map);
|
|
m_mod_manager.enumerate_modules();
|
|
|
|
as::Value val1;
|
|
val1.i = 10;
|
|
as::Value val2;
|
|
val2.i = 5;
|
|
as::VM vm(1024);
|
|
vm.register_function("test_native_function", as::VarType::VAR_TYPE_VOID, std::vector<as::VarType>{as::VarType::VAR_TYPE_INT32}, test_native_function);
|
|
std::unique_ptr<as::StatementBlock> block = std::make_unique<as::StatementBlock>();
|
|
std::vector<std::unique_ptr<as::Expression>> arguments;
|
|
arguments.push_back(std::make_unique<as::BinaryExpression>(as::BinaryExpression::Operation::OPERATION_MUL, std::make_unique<as::NumericExpression>(val1, as::ValueType::VALUE_TYPE_INT), std::make_unique<as::NumericExpression>(val2, as::ValueType::VALUE_TYPE_INT)));
|
|
block->add_expression(std::make_unique<as::FunctionCall>(vm, as::FunctionType::FUNCTION_NATIVE, 0, std::move(arguments)));
|
|
block->eval(vm);
|
|
}
|
|
|
|
Server::~Server() {
|
|
LOG_INFO("Shutting down server");
|
|
LOG_VERBOSE("Waiting for update thread to finish");
|
|
if(m_update_thread->joinable())
|
|
m_update_thread->join();
|
|
if(m_running_atomic_created) {
|
|
LOG_VERBOSE("Waiting for command thread to finish");
|
|
if(m_command_thread->joinable())
|
|
m_command_thread->join();
|
|
delete m_running;
|
|
}
|
|
m_player_storage.save();
|
|
}
|
|
|
|
void Server::run() {
|
|
m_mod_manager.run_modules();
|
|
m_running->store(true);
|
|
m_update_thread.reset(new std::thread(&Server::update_thread_func, this));
|
|
polygun::network::NetworkPacket packet;
|
|
while(m_running->load()) {
|
|
bool handled = false;
|
|
polygun::network::NetworkEndpoint sender;
|
|
memset((void*)&sender, 0, sizeof(sender));
|
|
m_clients_mutex.lock();
|
|
for(size_t i = 0; i<m_clients.size(); i++) {
|
|
m_clients[i]->get_connection().update();
|
|
if(time(0)-m_clients[i]->get_last_activity()>=10) {
|
|
handle_player_timeout(m_clients[i].get());
|
|
m_clients.erase(m_clients.begin()+i);
|
|
i--;
|
|
}
|
|
}
|
|
m_clients_mutex.unlock();
|
|
if(!m_server_socket->recv(packet, &sender))
|
|
continue;
|
|
|
|
m_clients_mutex.lock();
|
|
for(size_t i = 0; i<m_clients.size(); i++) {
|
|
Client& client = *m_clients[i];
|
|
if(memcmp(&sender, &client.get_connection().get_endpoint(), sizeof(polygun::network::NetworkEndpoint))==0) {
|
|
if(client.get_join_completed() && packet.get_id()==polygun::network::NetworkPacketID::PACKET_HANDSHAKE) {
|
|
LOG_WARNING("Player %s(%d) has resent handshake packet!!", client.get_nick().c_str(), client.get_uuid());
|
|
handled = true;
|
|
}
|
|
else if((packet.get_flags() & polygun::network::NetworkPacketFlag::FLAG_RELIABLE) || packet.get_id()==polygun::network::NetworkPacketID::PACKET_ACK)
|
|
client.get_connection().push_packet(packet);
|
|
else if(!handle_packet(packet, &client)) {
|
|
handle_player_disconnected(&client);
|
|
m_clients.erase(m_clients.begin()+i);
|
|
i--;
|
|
}
|
|
handled = true;
|
|
}
|
|
|
|
if(client.get_connection().recv_packet(packet)) {
|
|
if(!handle_packet(packet, &client)) {
|
|
m_clients.erase(m_clients.begin()+i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
m_clients_mutex.unlock();
|
|
|
|
if(!handled && packet.get_id()==polygun::network::NetworkPacketID::PACKET_HANDSHAKE) {
|
|
std::unique_ptr<polygun::network::NetworkManager> network_manager = std::make_unique<polygun::network::NetworkManager>(m_server_socket, sender);
|
|
network_manager->increment_incoming_packet_counter();
|
|
m_latest_client.reset(new Client(std::move(network_manager)));
|
|
if(handle_new_player(packet)) {
|
|
LOG_INFO("Player %s joined the game (uuid: %d)", m_latest_client->get_nick().c_str(), m_latest_client->get_uuid());
|
|
m_clients_mutex.lock();
|
|
m_clients.push_back(std::move(m_latest_client));
|
|
m_clients_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
m_chunk_manager.update();
|
|
}
|
|
|
|
for(std::unique_ptr<Client>& client : m_clients) {
|
|
network::NetworkPacket packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_NONE, network::NetworkPacketID::PACKET_TO_CLIENT_SERVER_EXIT);
|
|
client->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
void Server::register_node(const world::NodeDef& def) {
|
|
m_node_defs[def.m_string_id] = def;
|
|
m_node_defs[def.m_string_id].m_id = m_mod_manager.get_next_node_id();
|
|
}
|
|
|
|
void Server::notify_map_invalidation() {
|
|
for(std::unique_ptr<Client>& client : m_clients) {
|
|
network::NetworkPacket packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_INVALIDATE_MAP);
|
|
client->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
Client* Server::get_client_by_nick(const std::string& nick) {
|
|
for(std::unique_ptr<Client>& client : m_clients) {
|
|
if(client->get_nick()==nick)
|
|
return client.get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Server::command_thread_func() {
|
|
std::string line;
|
|
bool has_input_data = false;
|
|
bool show_input_prompt = true;
|
|
while(m_running->load()) {
|
|
if(show_input_prompt) {
|
|
std::cout<<"> ";
|
|
std::cout.flush();
|
|
show_input_prompt = false;
|
|
}
|
|
#if defined(__unix__)
|
|
int byte_count = 0;
|
|
ioctl(0, FIONREAD, &byte_count);
|
|
if(byte_count>0) {
|
|
has_input_data = true;
|
|
getline(std::cin, line);
|
|
}
|
|
#elif defined(_WIN32)
|
|
// FIXME: Support Unicode characters there
|
|
// Note: this code is very hacky but it works
|
|
HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
INPUT_RECORD records[4];
|
|
DWORD record_count;
|
|
ReadConsoleInput(stdin_handle, records, sizeof(records)/sizeof(INPUT_RECORD), &record_count);
|
|
for(DWORD i = 0; i<record_count; i++) {
|
|
if(records[i].EventType==KEY_EVENT && records[i].Event.KeyEvent.bKeyDown) {
|
|
CHAR ch = records[i].Event.KeyEvent.uChar.AsciiChar;
|
|
if(isprint(ch)) {
|
|
line+=ch;
|
|
std::cout<<ch;
|
|
std::cout.flush();
|
|
}
|
|
else if(ch==8 && !line.empty()) {
|
|
line.back()=' ';
|
|
std::cout<<"\r> "<<line;
|
|
line.pop_back();
|
|
std::cout<<"\r> "<<line;
|
|
}
|
|
else if(ch==13) {
|
|
std::cout<<std::endl;
|
|
has_input_data = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if(has_input_data) {
|
|
m_command_parser.parse(line);
|
|
has_input_data = false;
|
|
show_input_prompt = true;
|
|
#if defined(_WIN32)
|
|
line.clear();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::update_thread_func() {
|
|
std::chrono::time_point<std::chrono::steady_clock> prev_time = std::chrono::steady_clock::now();
|
|
float since_day_time_update = 0.0f;
|
|
|
|
while(m_running->load()) {
|
|
const std::chrono::time_point<std::chrono::steady_clock> tm = std::chrono::steady_clock::now();
|
|
const std::chrono::duration<float> interval = tm-prev_time;
|
|
prev_time = tm;
|
|
|
|
since_day_time_update+=interval.count();
|
|
const uint16_t to_add = since_day_time_update/DAY_TIME_INTERVAL;
|
|
if(to_add==0)
|
|
continue;
|
|
m_day_time+=to_add;
|
|
since_day_time_update-=to_add*DAY_TIME_INTERVAL;
|
|
}
|
|
}
|
|
|
|
bool Server::handle_packet(polygun::network::NetworkPacket& packet, Client* client) {
|
|
switch(packet.get_id()) {
|
|
case polygun::network::NetworkPacketID::PACKET_HANDSHAKE:
|
|
return handle_new_player(packet);
|
|
case polygun::network::NetworkPacketID::PACKET_POSITION:
|
|
handle_position(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_ROTATE:
|
|
handle_player_rotate(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_MESSAGE:
|
|
handle_player_message(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_CHUNK:
|
|
handle_chunk_request(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_NODE_CHANGE:
|
|
handle_node_change(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_NODE_FILL:
|
|
handle_node_fill(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_SYNC:
|
|
handle_sync(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_NODE_DEFINITION:
|
|
handle_node_definition(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_RESOURCE:
|
|
handle_resource_request(packet, client);
|
|
return true;
|
|
case polygun::network::NetworkPacketID::PACKET_EXIT:
|
|
handle_player_disconnected(client);
|
|
break;
|
|
case polygun::network::NetworkPacketID::PACKET_ACTIVITY:
|
|
handle_player_activity(client);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Server::handle_new_player(polygun::network::NetworkPacket& packet) {
|
|
std::string nick;
|
|
packet.read(nick);
|
|
m_latest_client->set_nick(nick);
|
|
// Generate UUID
|
|
bool unique = true;
|
|
uint32_t uuid;
|
|
// FIXME: there is possible hang here
|
|
do{
|
|
uuid = rand();
|
|
for(const std::unique_ptr<Client>& client : m_clients)
|
|
unique = uuid != client->get_uuid();
|
|
}
|
|
while(!unique);
|
|
|
|
m_latest_client->get_connection().send_ack(packet.get_num());
|
|
polygun::network::NetworkPacket out_packet = m_latest_client->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_RELIABLE, polygun::network::NetworkPacketID::PACKET_HANDSHAKE);
|
|
out_packet.write(uuid);
|
|
out_packet.write(m_mod_manager.get_current_node_id());
|
|
out_packet.write(static_cast<uint8_t>(m_config.m_default_player_mode));
|
|
out_packet.write(m_day_time);
|
|
m_latest_client->get_connection().send_packet(out_packet);
|
|
m_latest_client->set_uuid(uuid);
|
|
m_latest_client->set_mode(m_config.m_default_player_mode);
|
|
m_latest_client->set_join_completed_flag();
|
|
|
|
if(!m_clients.empty()) {
|
|
out_packet = m_latest_client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_TO_CLIENT_PLAYER_INFO);
|
|
out_packet.write(static_cast<uint8_t>(m_clients.size()));
|
|
for(const std::unique_ptr<Client>& client : m_clients)
|
|
out_packet.write(client.get());
|
|
m_latest_client->get_connection().send_packet(out_packet);
|
|
}
|
|
|
|
for(std::unique_ptr<Client>& client : m_clients) {
|
|
out_packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_TO_CLIENT_PLAYER_JOIN);
|
|
out_packet.write(uuid);
|
|
out_packet.write(nick);
|
|
client->get_connection().send_packet(out_packet);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Server::handle_position(polygun::network::NetworkPacket& packet, Client* client) {
|
|
math::Vector3f pos;
|
|
if(!packet.read(pos)) {
|
|
LOG_ERROR("Player %s(%d) sent invalid position packet", client->get_nick().c_str(), client->get_uuid());
|
|
return;
|
|
}
|
|
client->set_position(pos);
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
if(c->get_uuid()==client->get_uuid())
|
|
continue;
|
|
network::NetworkPacket out_packet = c->get_connection().create_packet(network::NetworkPacketFlag::FLAG_NONE, packet.get_id());
|
|
out_packet.write(client->get_uuid());
|
|
out_packet.write(pos);
|
|
c->get_connection().send_packet(out_packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_player_rotate(polygun::network::NetworkPacket& packet, Client* client) {
|
|
float rot;
|
|
if(!packet.read(rot)) {
|
|
LOG_ERROR("Player %s(%d) sent invalid rotation packet", client->get_nick().c_str(), client->get_uuid());
|
|
return;
|
|
}
|
|
client->set_rotation(rot);
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
if(c->get_uuid()==client->get_uuid())
|
|
continue;
|
|
network::NetworkPacket out_packet = c->get_connection().create_packet(network::NetworkPacketFlag::FLAG_NONE, packet.get_id());
|
|
out_packet.write(client->get_uuid());
|
|
out_packet.write(rot);
|
|
c->get_connection().send_packet(out_packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_player_message(polygun::network::NetworkPacket& packet, Client* client) {
|
|
std::string message;
|
|
if(!packet.read(message)) {
|
|
LOG_ERROR("Player %s(%d) sent invalid message packet", client->get_nick().c_str(), client->get_uuid());
|
|
return;
|
|
}
|
|
if(message.empty())
|
|
return;
|
|
LOG_INFO("Message: <%s> %s", client->get_nick().c_str(), message.c_str());
|
|
if(message[0]=='/') {
|
|
m_command_parser.parse(message.substr(1, message.length()-1), client);
|
|
return;
|
|
}
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
network::NetworkPacket out_packet = c->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, packet.get_id());
|
|
out_packet.write(static_cast<uint8_t>(0)); // regular player message
|
|
out_packet.write(client->get_nick());
|
|
out_packet.write(message);
|
|
c->get_connection().send_packet(out_packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_chunk_request(polygun::network::NetworkPacket& packet, Client* client) {
|
|
math::Vector3i pos;
|
|
packet.read(pos);
|
|
|
|
network::NetworkPacket out_packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_CHUNK);
|
|
out_packet.write(pos);
|
|
|
|
const math::Rect3D chunk_rect(pos.convert<float>(), math::Vector3f(world::Chunk::CHUNK_SIZE));
|
|
const math::Rect3D player_view_rect = math::Rect3D::with_center(client->get_position()/math::Vector3f(world::Chunk::CHUNK_SIZE), math::Vector3f(5));
|
|
if(player_view_rect.overlaps(chunk_rect)) {
|
|
const world::Chunk* const chunk = m_chunk_manager.get_chunk(pos);
|
|
out_packet.write(chunk);
|
|
}
|
|
else {
|
|
// Note: Maybe warn player for possible cheating/ddosing server with chunk requests outside of viewing range
|
|
world::Chunk dummy_chunk;
|
|
dummy_chunk.fill(1);
|
|
out_packet.write(&dummy_chunk);
|
|
}
|
|
|
|
client->get_connection().send_packet(out_packet);
|
|
}
|
|
|
|
void Server::handle_node_change(polygun::network::NetworkPacket& packet, Client* client) {
|
|
if(client->get_mode()!=world::PlayerMode::PLAYER_MODE_EDITMODE)
|
|
return;
|
|
math::Vector3i pos;
|
|
uint16_t node;
|
|
packet.read(pos);
|
|
packet.read(node);
|
|
m_chunk_manager.add_node(node, pos);
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
polygun::network::NetworkPacket packet = c->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_RELIABLE, polygun::network::NetworkPacketID::PACKET_NODE_CHANGE);
|
|
packet.write(pos);
|
|
packet.write(node);
|
|
c->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_node_fill(polygun::network::NetworkPacket& packet, Client* client) {
|
|
if(client->get_mode()!=world::PlayerMode::PLAYER_MODE_EDITMODE)
|
|
return;
|
|
math::Vector3i from;
|
|
math::Vector3i to;
|
|
uint16_t node;
|
|
packet.read(from);
|
|
packet.read(to);
|
|
packet.read(node);
|
|
m_chunk_manager.fill_node(node, from, to);
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
polygun::network::NetworkPacket packet = c->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_RELIABLE, polygun::network::NetworkPacketID::PACKET_NODE_FILL);
|
|
packet.write(from);
|
|
packet.write(to);
|
|
packet.write(node);
|
|
c->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_sync(polygun::network::NetworkPacket& packet, Client* client) {
|
|
network::NetworkPacket out_packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_SYNC);
|
|
out_packet.write(m_day_time);
|
|
client->get_connection().send_packet(out_packet);
|
|
}
|
|
|
|
void Server::handle_node_definition(polygun::network::NetworkPacket& packet, Client* client) {
|
|
uint16_t node_id;
|
|
if(!packet.read(node_id)) {
|
|
LOG_ERROR("Player %s(%d) sent invalid node definition request", client->get_nick().c_str(), client->get_uuid());
|
|
return;
|
|
}
|
|
|
|
network::NetworkPacket out_packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_NODE_DEFINITION);
|
|
|
|
std::map<std::string, world::NodeDef>::iterator it = std::find_if(m_node_defs.begin(), m_node_defs.end(), [node_id](const std::pair<std::string, world::NodeDef>& element) {
|
|
return element.second.m_id==node_id;
|
|
});
|
|
out_packet.write(node_id);
|
|
if(it==m_node_defs.end()) {
|
|
world::NodeDef unknown_node;
|
|
unknown_node.m_string_id = "builtin:unknown";
|
|
out_packet.write(&unknown_node);
|
|
}
|
|
else
|
|
out_packet.write(&it->second);
|
|
|
|
client->get_connection().send_packet(out_packet);
|
|
}
|
|
|
|
void Server::handle_resource_request(polygun::network::NetworkPacket& packet, Client* client) {
|
|
std::string resource_id;
|
|
if(!packet.read(resource_id)) {
|
|
LOG_ERROR("Player %s(%d) sent invalid resource request", client->get_nick().c_str(), client->get_uuid());
|
|
return;
|
|
}
|
|
|
|
std::vector<uint8_t> resource_data;
|
|
if(!m_mod_manager.load_mod_resource(resource_id, resource_data)) {
|
|
send_empty_resource(client, resource_id);
|
|
return;
|
|
}
|
|
|
|
network::NetworkPacket p = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_RESOURCE);
|
|
p.write(resource_id);
|
|
p.write(resource_data);
|
|
client->get_connection().send_packet(p);
|
|
}
|
|
|
|
void Server::handle_player_disconnected(const Client* client) {
|
|
LOG_INFO("Player %s(%d) left the game", client->get_nick().c_str(), client->get_uuid());
|
|
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
if(c->get_uuid()==client->get_uuid())
|
|
continue;
|
|
polygun::network::NetworkPacket packet = c->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_RELIABLE, polygun::network::NetworkPacketID::PACKET_EXIT);
|
|
packet.write(client->get_uuid()); // client UUID
|
|
packet.write((uint8_t)0); // reason ID
|
|
c->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_player_timeout(const Client* client) {
|
|
LOG_INFO("Player %s(%d) has left the game: timeout", client->get_nick().c_str(), client->get_uuid());
|
|
|
|
for(std::unique_ptr<Client>& c : m_clients) {
|
|
if(c->get_uuid()==client->get_uuid())
|
|
continue;
|
|
polygun::network::NetworkPacket packet = c->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_RELIABLE, polygun::network::NetworkPacketID::PACKET_EXIT);
|
|
packet.write(client->get_uuid()); // client UUID
|
|
packet.write((uint8_t)1); // reason ID
|
|
c->get_connection().send_packet(packet);
|
|
}
|
|
}
|
|
|
|
void Server::handle_player_activity(Client* client) {
|
|
client->update_activity();
|
|
network::NetworkPacket packet = client->get_connection().create_packet(polygun::network::NetworkPacketFlag::FLAG_NONE, polygun::network::NetworkPacketID::PACKET_ACTIVITY);
|
|
client->get_connection().send_packet(packet);
|
|
}
|
|
|
|
void Server::send_empty_resource(Client* client, const std::string& resource_id) {
|
|
network::NetworkPacket packet = client->get_connection().create_packet(network::NetworkPacketFlag::FLAG_RELIABLE, network::NetworkPacketID::PACKET_RESOURCE);
|
|
packet.write(resource_id);
|
|
packet.write(static_cast<uint16_t>(0));
|
|
client->get_connection().send_packet(packet);
|
|
}
|
|
|
|
#if !defined(HAVE_STRSIGNAL)
|
|
static const char* strsignal(int sig) {
|
|
switch(sig){
|
|
case SIGINT:
|
|
return "Interrupt";
|
|
case SIGTERM:
|
|
return "Terminated";
|
|
}
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
void Server::signal_handler(int sig) {
|
|
if(!g_current_server)
|
|
return;
|
|
|
|
const char* signal_name = strsignal(sig);
|
|
if(!signal_name)
|
|
signal_name = "Unknown";
|
|
|
|
LOG_INFO("Received %s signal", signal_name);
|
|
g_current_server->m_running->store(false);
|
|
}
|