//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
// Remere's Map Editor is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Remere's Map Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//////////////////////////////////////////////////////////////////////
#include "main.h"
#include "live_server.h"
#include "live_peer.h"
#include "live_tab.h"
#include "live_action.h"
#include "editor.h"
LiveServer::LiveServer(Editor& editor) :
LiveSocket(),
clients(), acceptor(nullptr), socket(nullptr), editor(&editor),
clientIds(0), port(0), stopped(false) {
//
}
LiveServer::~LiveServer() {
//
}
bool LiveServer::bind() {
NetworkConnection& connection = NetworkConnection::getInstance();
if (!connection.start()) {
setLastError("The previous connection has not been terminated yet.");
return false;
}
auto& service = connection.get_service();
acceptor = std::make_shared(service);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
acceptor->open(endpoint.protocol());
boost::system::error_code error;
acceptor->set_option(boost::asio::ip::tcp::no_delay(true), error);
if (error) {
setLastError("Error: " + error.message());
return false;
}
acceptor->bind(endpoint);
acceptor->listen();
acceptClient();
return true;
}
void LiveServer::close() {
for (auto& clientEntry : clients) {
delete clientEntry.second;
}
clients.clear();
if (log) {
log->Message("Server was shutdown.");
log->Disconnect();
log = nullptr;
}
stopped = true;
if (acceptor) {
acceptor->close();
}
if (socket) {
socket->close();
}
}
void LiveServer::acceptClient() {
static uint32_t id = 0;
if (stopped) {
return;
}
if (!socket) {
socket = std::make_shared(
NetworkConnection::getInstance().get_service()
);
}
acceptor->async_accept(*socket, [this](const boost::system::error_code& error) -> void {
if (error) {
//
} else {
LivePeer* peer = new LivePeer(this, std::move(*socket));
peer->log = log;
peer->receiveHeader();
clients.insert(std::make_pair(id++, peer));
}
acceptClient();
});
}
void LiveServer::removeClient(uint32_t id) {
auto it = clients.find(id);
if (it == clients.end()) {
return;
}
const uint32_t clientId = it->second->getClientId();
if (clientId != 0) {
clientIds &= ~clientId;
editor->map.clearVisible(clientIds);
}
clients.erase(it);
updateClientList();
}
void LiveServer::updateCursor(const Position& position) {
LiveCursor cursor;
cursor.id = 0;
cursor.pos = position;
cursor.color = wxColor(
g_settings.getInteger(Config::CURSOR_RED),
g_settings.getInteger(Config::CURSOR_GREEN),
g_settings.getInteger(Config::CURSOR_BLUE),
g_settings.getInteger(Config::CURSOR_ALPHA)
);
broadcastCursor(cursor);
}
void LiveServer::updateClientList() const {
log->UpdateClientList(clients);
}
uint16_t LiveServer::getPort() const {
return port;
}
bool LiveServer::setPort(int32_t newPort) {
if (newPort < 1 || newPort > 65535) {
setLastError("Port must be a number in the range 1-65535.");
return false;
}
port = newPort;
return true;
}
uint32_t LiveServer::getFreeClientId() {
for (int32_t bit = 1; bit < (1 << 16); bit <<= 1) {
if (!testFlags(clientIds, bit)) {
clientIds |= bit;
return bit;
}
}
return 0;
}
std::string LiveServer::getHostName() const {
if (acceptor) {
auto endpoint = acceptor->local_endpoint();
return endpoint.address().to_string() + ":" + std::to_string(endpoint.port());
}
return "localhost";
}
void LiveServer::broadcastNodes(DirtyList& dirtyList) {
if (dirtyList.Empty()) {
return;
}
for (const auto& ind : dirtyList.GetPosList()) {
int32_t ndx = ind.pos >> 18;
int32_t ndy = (ind.pos >> 4) & 0x3FFF;
uint32_t floors = ind.floors;
QTreeNode* node = editor->map.getLeaf(ndx * 4, ndy * 4);
if (!node) {
continue;
}
for (auto& clientEntry : clients) {
LivePeer* peer = clientEntry.second;
const uint32_t clientId = peer->getClientId();
if (dirtyList.owner != 0 && dirtyList.owner == clientId) {
continue;
}
if (node->isVisible(clientId, true)) {
peer->sendNode(clientId, node, ndx, ndy, floors & 0xFF00);
}
if (node->isVisible(clientId, false)) {
peer->sendNode(clientId, node, ndx, ndy, floors & 0x00FF);
}
}
}
}
void LiveServer::broadcastCursor(const LiveCursor& cursor) {
if (clients.empty()) {
return;
}
if (cursor.id != 0) {
cursors[cursor.id] = cursor;
}
NetworkMessage message;
message.write(PACKET_CURSOR_UPDATE);
writeCursor(message, cursor);
for (auto& clientEntry : clients) {
LivePeer* peer = clientEntry.second;
if (peer->getClientId() != cursor.id) {
peer->send(message);
}
}
}
void LiveServer::broadcastChat(const wxString& speaker, const wxString& chatMessage) {
if (clients.empty()) {
return;
}
NetworkMessage message;
message.write(PACKET_SERVER_TALK);
message.write(nstr(speaker));
message.write(nstr(chatMessage));
for (auto& clientEntry : clients) {
clientEntry.second->send(message);
}
log->Chat(name, chatMessage);
}
void LiveServer::startOperation(const wxString& operationMessage) {
if (clients.empty()) {
return;
}
NetworkMessage message;
message.write(PACKET_START_OPERATION);
message.write(nstr(operationMessage));
for (auto& clientEntry : clients) {
clientEntry.second->send(message);
}
}
void LiveServer::updateOperation(int32_t percent) {
if (clients.empty()) {
return;
}
NetworkMessage message;
message.write(PACKET_UPDATE_OPERATION);
message.write(percent);
for (auto& clientEntry : clients) {
clientEntry.second->send(message);
}
}
LiveLogTab* LiveServer::createLogWindow(wxWindow* parent) {
MapTabbook* mapTabBook = dynamic_cast(parent);
ASSERT(mapTabBook);
log = newd LiveLogTab(mapTabBook, this);
log->Message("New Live mapping session started.");
log->Message("Hosted on server " + getHostName() + ".");
updateClientList();
return log;
}