tibia-rme/source/live_client.cpp

448 lines
12 KiB
C++
Raw Permalink Normal View History

//////////////////////////////////////////////////////////////////////
// This file is part of Remere's Map Editor
//////////////////////////////////////////////////////////////////////
2020-07-30 11:42:28 -03:00
// 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 <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////
#include "main.h"
#include "live_client.h"
#include "live_tab.h"
#include "live_action.h"
#include "editor.h"
2023-10-09 19:12:16 -07:00
LiveClient::LiveClient() :
LiveSocket(),
readMessage(), queryNodeList(), currentOperation(),
2023-10-09 19:12:16 -07:00
resolver(nullptr), socket(nullptr), editor(nullptr), stopped(false) {
//
}
2023-10-09 19:12:16 -07:00
LiveClient::~LiveClient() {
//
}
2023-10-09 19:12:16 -07:00
bool LiveClient::connect(const std::string &address, uint16_t port) {
NetworkConnection &connection = NetworkConnection::getInstance();
if (!connection.start()) {
2016-11-05 15:28:14 +01:00
setLastError("The previous connection has not been terminated yet.");
return false;
}
2023-10-09 19:12:16 -07:00
auto &service = connection.get_service();
if (!resolver) {
resolver = std::make_shared<asio::ip::tcp::resolver>(service);
}
2023-10-09 19:12:16 -07:00
if (!socket) {
socket = std::make_shared<asio::ip::tcp::socket>(service);
}
asio::ip::tcp::resolver::query query(address, std::to_string(port));
2023-10-09 19:12:16 -07:00
resolver->async_resolve(query, [this](const std::error_code &error, asio::ip::tcp::resolver::iterator endpoint_iterator) -> void {
if (error) {
2016-11-05 15:28:14 +01:00
logMessage("Error: " + error.message());
} else {
tryConnect(endpoint_iterator);
}
});
/*
2015-12-06 11:42:37 -03:00
if(!client->WaitOnConnect(5, 0)) {
if(log)
log->Disconnect();
2016-11-05 15:28:14 +01:00
last_err = "Connection timed out.";
client->Destroy();
client = nullptr;
delete connection;
return false;
}
2016-09-29 19:24:45 -03:00
2015-12-06 11:42:37 -03:00
if(!client->IsConnected()) {
if(log)
log->Disconnect();
2016-11-05 15:28:14 +01:00
last_err = "Connection refused by peer.";
client->Destroy();
client = nullptr;
delete connection;
return false;
}
2016-09-29 19:24:45 -03:00
if(log)
2016-11-05 15:28:14 +01:00
log->Message("Connection established!");
*/
return true;
}
2023-10-09 19:12:16 -07:00
void LiveClient::tryConnect(asio::ip::tcp::resolver::iterator endpoint_iterator) {
if (stopped) {
return;
}
2023-10-09 19:12:16 -07:00
if (endpoint_iterator == asio::ip::tcp::resolver::iterator()) {
return;
}
2016-11-05 15:28:14 +01:00
logMessage("Joining server " + endpoint_iterator->host_name() + ":" + endpoint_iterator->service_name() + "...");
2023-10-09 19:12:16 -07:00
asio::async_connect(*socket, endpoint_iterator, [this](std::error_code error, asio::ip::tcp::resolver::iterator endpoint_iterator) -> void {
if (!socket->is_open()) {
tryConnect(++endpoint_iterator);
2023-10-09 19:12:16 -07:00
} else if (error) {
if (handleError(error)) {
tryConnect(++endpoint_iterator);
} else {
wxTheApp->CallAfter([this]() {
close();
g_gui.CloseLiveEditors(this);
});
}
} else {
socket->set_option(asio::ip::tcp::no_delay(true), error);
2023-10-09 19:12:16 -07:00
if (error) {
wxTheApp->CallAfter([this]() {
close();
});
return;
}
sendHello();
receiveHeader();
}
});
}
2023-10-09 19:12:16 -07:00
void LiveClient::close() {
if (resolver) {
resolver->cancel();
}
2023-10-09 19:12:16 -07:00
if (socket) {
socket->close();
}
2023-10-09 19:12:16 -07:00
if (log) {
2016-11-05 15:28:14 +01:00
log->Message("Disconnected from server.");
log->Disconnect();
log = nullptr;
}
stopped = true;
}
2023-10-09 19:12:16 -07:00
bool LiveClient::handleError(const std::error_code &error) {
if (error == asio::error::eof || error == asio::error::connection_reset) {
wxTheApp->CallAfter([this]() {
2016-11-05 15:28:14 +01:00
log->Message(wxString() + getHostName() + ": disconnected.");
close();
});
return true;
2023-10-09 19:12:16 -07:00
} else if (error == asio::error::connection_aborted) {
2016-11-05 15:28:14 +01:00
logMessage("You have left the server.");
return true;
}
return false;
}
2023-10-09 19:12:16 -07:00
std::string LiveClient::getHostName() const {
if (!socket) {
return "not connected";
}
return socket->remote_endpoint().address().to_string();
}
2023-10-09 19:12:16 -07:00
void LiveClient::receiveHeader() {
readMessage.position = 0;
2023-10-09 19:12:16 -07:00
asio::async_read(*socket, asio::buffer(readMessage.buffer, 4), [this](const std::error_code &error, size_t bytesReceived) -> void {
if (error) {
if (!handleError(error)) {
logMessage(wxString() + getHostName() + ": " + error.message());
}
2023-10-09 19:12:16 -07:00
} else if (bytesReceived < 4) {
logMessage(wxString() + getHostName() + ": Could not receive header[size: " + std::to_string(bytesReceived) + "], disconnecting client.");
} else {
receive(readMessage.read<uint32_t>());
}
2023-10-09 19:12:16 -07:00
});
}
2023-10-09 19:12:16 -07:00
void LiveClient::receive(uint32_t packetSize) {
readMessage.buffer.resize(readMessage.position + packetSize);
2023-10-09 19:12:16 -07:00
asio::async_read(*socket, asio::buffer(&readMessage.buffer[readMessage.position], packetSize), [this](const std::error_code &error, size_t bytesReceived) -> void {
if (error) {
if (!handleError(error)) {
logMessage(wxString() + getHostName() + ": " + error.message());
}
2023-10-09 19:12:16 -07:00
} else if (bytesReceived < readMessage.buffer.size() - 4) {
logMessage(wxString() + getHostName() + ": Could not receive packet[size: " + std::to_string(bytesReceived) + "], disconnecting client.");
} else {
wxTheApp->CallAfter([this]() {
parsePacket(std::move(readMessage));
receiveHeader();
});
}
2023-10-09 19:12:16 -07:00
});
}
2023-10-09 19:12:16 -07:00
void LiveClient::send(NetworkMessage &message) {
memcpy(&message.buffer[0], &message.size, 4);
2023-10-09 19:12:16 -07:00
asio::async_write(*socket, asio::buffer(message.buffer, message.size + 4), [this](const std::error_code &error, size_t bytesTransferred) -> void {
if (error) {
logMessage(wxString() + getHostName() + ": " + error.message());
}
2023-10-09 19:12:16 -07:00
});
}
2023-10-09 19:12:16 -07:00
void LiveClient::updateCursor(const Position &position) {
LiveCursor cursor;
cursor.id = 77; // Unimportant, server fixes it for us
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)
);
NetworkMessage message;
message.write<uint8_t>(PACKET_CLIENT_UPDATE_CURSOR);
writeCursor(message, cursor);
send(message);
}
2023-10-09 19:12:16 -07:00
LiveLogTab* LiveClient::createLogWindow(wxWindow* parent) {
MapTabbook* mtb = dynamic_cast<MapTabbook*>(parent);
ASSERT(mtb);
log = newd LiveLogTab(mtb, this);
2016-11-05 15:28:14 +01:00
log->Message("New Live mapping session started.");
return log;
}
2023-10-09 19:12:16 -07:00
MapTab* LiveClient::createEditorWindow() {
MapTabbook* mtb = dynamic_cast<MapTabbook*>(g_gui.tabbook);
ASSERT(mtb);
MapTab* edit = newd MapTab(mtb, editor);
edit->OnSwitchEditorMode(g_gui.IsSelectionMode() ? SELECTION_MODE : DRAWING_MODE);
return edit;
}
2023-10-09 19:12:16 -07:00
void LiveClient::sendHello() {
NetworkMessage message;
message.write<uint8_t>(PACKET_HELLO_FROM_CLIENT);
message.write<uint32_t>(__RME_VERSION_ID__);
message.write<uint32_t>(__LIVE_NET_VERSION__);
message.write<uint32_t>(g_gui.GetCurrentVersionID());
message.write<std::string>(nstr(name));
message.write<std::string>(nstr(password));
send(message);
}
2023-10-09 19:12:16 -07:00
void LiveClient::sendNodeRequests() {
if (queryNodeList.empty()) {
return;
}
NetworkMessage message;
message.write<uint8_t>(PACKET_REQUEST_NODES);
message.write<uint32_t>(queryNodeList.size());
2023-10-09 19:12:16 -07:00
for (uint32_t node : queryNodeList) {
message.write<uint32_t>(node);
}
send(message);
queryNodeList.clear();
}
2023-10-09 19:12:16 -07:00
void LiveClient::sendChanges(DirtyList &dirtyList) {
ChangeList &changeList = dirtyList.GetChanges();
if (changeList.empty()) {
return;
}
2016-09-29 19:24:45 -03:00
mapWriter.reset();
2023-10-09 19:12:16 -07:00
for (Change* change : changeList) {
switch (change->getType()) {
case CHANGE_TILE: {
2023-10-09 19:12:16 -07:00
const Position &position = static_cast<Tile*>(change->getData())->getPosition();
feat: synchronizing commits with the official rme repository (#36) * Show indicators for pickupable and moveable items * Fix: drawing always refreshing ui * Fix crash on invalid friend for wallbrush * Option to remove empty spawns * Draws position indicator, some code cleanup * Teleport copy/paste improvements * Fix flood fill * Add function to get minimap/8bit color * Small code cleanup * Cleanup Position * Code cleanup and small optimizations * Code cleanup * Code cleanup and small optimizations (#406) * Cleanup and cast functions. * Avoid adding a new Unique ID if it already exists * Code cleanup and small optimizations * More changes and cleanup * Changes and cleanup * Some changes * Fix copy position. * Only show uid/aid alert if it really changed * Add actions history panel * Does not draw tooltips on minimap mode * Use constexpr * Replace items fix (#408) * Draw grid small optimization * Small change in selection box and Fix #409 * Fix some xpm * Ingame box improvements, add lights support. * Fix go to previous position (#410) * Fix depot crash (#411) * Fix XPMs * Export minimap as .otmm (otclient format) or .png (#413) * Fix glitch after drawing secondary map * fix * Update about_window.cpp * sonar * fix Minimap and progress bar * fix erro load items.xml * fix * fix doodad brush * fix: slightly more accurate house size estimation * feat: update items.otb and items.xml * fix: bad merge * Revert "feat: update items.otb and items.xml" This reverts commit 40f1edc70f17c423282d546a41541a8fe9b92dcc. --------- Co-authored-by: Nailson <Mignari@users.noreply.github.com> Co-authored-by: wtver <51377408+maattch@users.noreply.github.com> Co-authored-by: Majesty <32709570+majestyotbr@users.noreply.github.com> Co-authored-by: Luan Santos <github@luan.sh>
2023-10-09 23:02:37 -03:00
sendTile(mapWriter, editor->getMap().getTile(position), &position);
break;
}
default:
break;
}
}
mapWriter.endNode();
NetworkMessage message;
message.write<uint8_t>(PACKET_CHANGE_LIST);
std::string data(reinterpret_cast<const char*>(mapWriter.getMemory()), mapWriter.getSize());
message.write<std::string>(data);
send(message);
}
2023-10-09 19:12:16 -07:00
void LiveClient::sendChat(const wxString &chatMessage) {
NetworkMessage message;
message.write<uint8_t>(PACKET_CLIENT_TALK);
message.write<std::string>(nstr(chatMessage));
send(message);
}
2023-10-09 19:12:16 -07:00
void LiveClient::sendReady() {
NetworkMessage message;
message.write<uint8_t>(PACKET_READY_CLIENT);
send(message);
}
2023-10-09 19:12:16 -07:00
void LiveClient::queryNode(int32_t ndx, int32_t ndy, bool underground) {
uint32_t nd = 0;
nd |= ((ndx >> 2) << 18);
nd |= ((ndy >> 2) << 4);
nd |= (underground ? 1 : 0);
queryNodeList.insert(nd);
}
2023-10-09 19:12:16 -07:00
void LiveClient::parsePacket(NetworkMessage message) {
uint8_t packetType;
2023-10-09 19:12:16 -07:00
while (message.position < message.buffer.size()) {
packetType = message.read<uint8_t>();
switch (packetType) {
case PACKET_HELLO_FROM_SERVER:
parseHello(message);
break;
case PACKET_KICK:
parseKick(message);
break;
case PACKET_ACCEPTED_CLIENT:
parseClientAccepted(message);
break;
case PACKET_CHANGE_CLIENT_VERSION:
parseChangeClientVersion(message);
break;
case PACKET_SERVER_TALK:
parseServerTalk(message);
break;
case PACKET_NODE:
parseNode(message);
break;
case PACKET_CURSOR_UPDATE:
parseCursorUpdate(message);
break;
case PACKET_START_OPERATION:
parseStartOperation(message);
break;
case PACKET_UPDATE_OPERATION:
parseUpdateOperation(message);
break;
default: {
2016-11-05 15:28:14 +01:00
log->Message("Unknown packet receieved!");
close();
break;
}
}
}
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseHello(NetworkMessage &message) {
ASSERT(editor == nullptr);
editor = newd Editor(g_gui.copybuffer, this);
2023-10-09 19:12:16 -07:00
Map &map = editor->getMap();
map.setName("Live Map - " + message.read<std::string>());
map.setWidth(message.read<uint16_t>());
map.setHeight(message.read<uint16_t>());
createEditorWindow();
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseKick(NetworkMessage &message) {
const std::string &kickMessage = message.read<std::string>();
close();
2016-11-05 15:28:14 +01:00
g_gui.PopupDialog("Disconnected", wxstr(kickMessage), wxOK);
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseClientAccepted(NetworkMessage &message) {
sendReady();
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseChangeClientVersion(NetworkMessage &message) {
ClientVersionID clientVersion = static_cast<ClientVersionID>(message.read<uint32_t>());
2023-10-09 19:12:16 -07:00
if (!g_gui.CloseAllEditors()) {
close();
return;
}
wxString error;
wxArrayString warnings;
g_gui.LoadVersion(clientVersion, error, warnings);
sendReady();
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseServerTalk(NetworkMessage &message) {
const std::string &speaker = message.read<std::string>();
const std::string &chatMessage = message.read<std::string>();
log->Chat(
wxstr(speaker),
wxstr(chatMessage)
);
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseNode(NetworkMessage &message) {
uint32_t ind = message.read<uint32_t>();
// Extract node position
int32_t ndx = ind >> 18;
int32_t ndy = (ind >> 4) & 0x3FFF;
bool underground = ind & 1;
feat: synchronizing commits with the official rme repository (#36) * Show indicators for pickupable and moveable items * Fix: drawing always refreshing ui * Fix crash on invalid friend for wallbrush * Option to remove empty spawns * Draws position indicator, some code cleanup * Teleport copy/paste improvements * Fix flood fill * Add function to get minimap/8bit color * Small code cleanup * Cleanup Position * Code cleanup and small optimizations * Code cleanup * Code cleanup and small optimizations (#406) * Cleanup and cast functions. * Avoid adding a new Unique ID if it already exists * Code cleanup and small optimizations * More changes and cleanup * Changes and cleanup * Some changes * Fix copy position. * Only show uid/aid alert if it really changed * Add actions history panel * Does not draw tooltips on minimap mode * Use constexpr * Replace items fix (#408) * Draw grid small optimization * Small change in selection box and Fix #409 * Fix some xpm * Ingame box improvements, add lights support. * Fix go to previous position (#410) * Fix depot crash (#411) * Fix XPMs * Export minimap as .otmm (otclient format) or .png (#413) * Fix glitch after drawing secondary map * fix * Update about_window.cpp * sonar * fix Minimap and progress bar * fix erro load items.xml * fix * fix doodad brush * fix: slightly more accurate house size estimation * feat: update items.otb and items.xml * fix: bad merge * Revert "feat: update items.otb and items.xml" This reverts commit 40f1edc70f17c423282d546a41541a8fe9b92dcc. --------- Co-authored-by: Nailson <Mignari@users.noreply.github.com> Co-authored-by: wtver <51377408+maattch@users.noreply.github.com> Co-authored-by: Majesty <32709570+majestyotbr@users.noreply.github.com> Co-authored-by: Luan Santos <github@luan.sh>
2023-10-09 23:02:37 -03:00
Action* action = editor->createAction(ACTION_REMOTE);
receiveNode(message, *editor, action, ndx, ndy, underground);
feat: synchronizing commits with the official rme repository (#36) * Show indicators for pickupable and moveable items * Fix: drawing always refreshing ui * Fix crash on invalid friend for wallbrush * Option to remove empty spawns * Draws position indicator, some code cleanup * Teleport copy/paste improvements * Fix flood fill * Add function to get minimap/8bit color * Small code cleanup * Cleanup Position * Code cleanup and small optimizations * Code cleanup * Code cleanup and small optimizations (#406) * Cleanup and cast functions. * Avoid adding a new Unique ID if it already exists * Code cleanup and small optimizations * More changes and cleanup * Changes and cleanup * Some changes * Fix copy position. * Only show uid/aid alert if it really changed * Add actions history panel * Does not draw tooltips on minimap mode * Use constexpr * Replace items fix (#408) * Draw grid small optimization * Small change in selection box and Fix #409 * Fix some xpm * Ingame box improvements, add lights support. * Fix go to previous position (#410) * Fix depot crash (#411) * Fix XPMs * Export minimap as .otmm (otclient format) or .png (#413) * Fix glitch after drawing secondary map * fix * Update about_window.cpp * sonar * fix Minimap and progress bar * fix erro load items.xml * fix * fix doodad brush * fix: slightly more accurate house size estimation * feat: update items.otb and items.xml * fix: bad merge * Revert "feat: update items.otb and items.xml" This reverts commit 40f1edc70f17c423282d546a41541a8fe9b92dcc. --------- Co-authored-by: Nailson <Mignari@users.noreply.github.com> Co-authored-by: wtver <51377408+maattch@users.noreply.github.com> Co-authored-by: Majesty <32709570+majestyotbr@users.noreply.github.com> Co-authored-by: Luan Santos <github@luan.sh>
2023-10-09 23:02:37 -03:00
editor->addAction(action);
g_gui.RefreshView();
g_gui.UpdateMinimap();
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseCursorUpdate(NetworkMessage &message) {
LiveCursor cursor = readCursor(message);
cursors[cursor.id] = cursor;
2016-09-29 19:24:45 -03:00
g_gui.RefreshView();
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseStartOperation(NetworkMessage &message) {
const std::string &operation = message.read<std::string>();
2016-09-29 19:24:45 -03:00
currentOperation = wxstr(operation);
2016-11-05 15:28:14 +01:00
g_gui.SetStatusText("Server Operation in Progress: " + currentOperation + "... (0%)");
}
2023-10-09 19:12:16 -07:00
void LiveClient::parseUpdateOperation(NetworkMessage &message) {
int32_t percent = message.read<uint32_t>();
2023-10-09 19:12:16 -07:00
if (percent >= 100) {
2016-11-05 15:28:14 +01:00
g_gui.SetStatusText("Server Operation Finished.");
} else {
2016-11-05 15:28:14 +01:00
g_gui.SetStatusText("Server Operation in Progress: " + currentOperation + "... (" + std::to_string(percent) + "%)");
}
}