You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

142 lines
3.3 KiB

#include "dvlnet/tcp_client.h"
#include "options.h"
#include "utils/language.h"
#include <SDL.h>
#include <exception>
#include <functional>
#include <memory>
#include <sodium.h>
#include <sstream>
#include <stdexcept>
#include <system_error>
#include <asio/connect.hpp>
namespace devilution {
namespace net {
int tcp_client::create(std::string addrstr, std::string passwd)
{
try {
auto port = sgOptions.Network.nPort;
local_server = std::make_unique<tcp_server>(ioc, addrstr, port, passwd);
return join(local_server->localhost_self(), passwd);
} catch (std::system_error &e) {
SDL_SetError("%s", e.what());
return -1;
}
}
int tcp_client::join(std::string addrstr, std::string passwd)
{
constexpr int MsSleep = 10;
constexpr int NoSleep = 250;
setup_password(passwd);
try {
std::stringstream port;
port << sgOptions.Network.nPort;
asio::connect(sock, resolver.resolve(addrstr, port.str()));
asio::ip::tcp::no_delay option(true);
sock.set_option(option);
} catch (std::exception &e) {
SDL_SetError("%s", e.what());
return -1;
}
start_recv();
{
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie_self),
sizeof(cookie_t));
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST,
PLR_MASTER, cookie_self,
game_init_info);
send(*pkt);
for (auto i = 0; i < NoSleep; ++i) {
try {
poll();
} catch (const std::runtime_error &e) {
SDL_SetError("%s", e.what());
return -1;
}
if (plr_self != PLR_BROADCAST)
break; // join successful
SDL_Delay(MsSleep);
}
}
if (plr_self == PLR_BROADCAST) {
SDL_SetError("%s", _("Unable to connect"));
return -1;
}
return plr_self;
}
void tcp_client::poll()
{
ioc.poll();
}
void tcp_client::handle_recv(const asio::error_code &error, size_t bytesRead)
{
if (error) {
// error in recv from server
// returning and doing nothing should be the same
// as if all connections to other clients were lost
return;
}
if (bytesRead == 0) {
throw std::runtime_error(_("error: read 0 bytes from server"));
}
recv_buffer.resize(bytesRead);
recv_queue.write(std::move(recv_buffer));
recv_buffer.resize(frame_queue::max_frame_size);
while (recv_queue.packet_ready()) {
auto pkt = pktfty->make_packet(recv_queue.read_packet());
recv_local(*pkt);
}
start_recv();
}
void tcp_client::start_recv()
{
sock.async_receive(asio::buffer(recv_buffer),
std::bind(&tcp_client::handle_recv, this,
std::placeholders::_1, std::placeholders::_2));
}
void tcp_client::handle_send(const asio::error_code &error, size_t bytesSent)
{
// empty for now
}
void tcp_client::send(packet &pkt)
{
const auto *frame = new buffer_t(frame_queue::make_frame(pkt.data()));
auto buf = asio::buffer(*frame);
asio::async_write(sock, buf, [this, frame](const asio::error_code &error, size_t bytesSent) {
handle_send(error, bytesSent);
delete frame;
});
}
bool tcp_client::SNetLeaveGame(int type)
{
auto ret = base::SNetLeaveGame(type);
poll();
if (local_server != nullptr)
local_server->close();
return ret;
}
std::string tcp_client::make_default_gamename()
{
return std::string(sgOptions.Network.szBindAddress);
}
tcp_client::~tcp_client()
{
}
} // namespace net
} // namespace devilution