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.
 
 
 
 
 
 

145 lines
3.4 KiB

#include "dvlnet/tcp_client.h"
#include "options.h"
#include "utils/language.h"
#include <SDL.h>
#include <exception>
#include <functional>
#include <memory>
#include <stdexcept>
#include <system_error>
#include <asio/connect.hpp>
namespace devilution {
namespace net {
int tcp_client::create(std::string addrstr)
{
try {
auto port = *sgOptions.Network.port;
local_server = std::make_unique<tcp_server>(ioc, addrstr, port, *pktfty);
return join(local_server->LocalhostSelf());
} catch (std::system_error &e) {
SDL_SetError("%s", e.what());
return -1;
}
}
int tcp_client::join(std::string addrstr)
{
constexpr int MsSleep = 10;
constexpr int NoSleep = 250;
try {
std::stringstream port;
port << *sgOptions.Network.port;
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;
}
StartReceive();
{
cookie_self = packet_out::GenerateCookie();
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 dvlnet_exception &e) {
SDL_SetError("Network error: %s", e.what());
return -1;
} 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").data());
return -1;
}
return plr_self;
}
bool tcp_client::IsGameHost()
{
return local_server != nullptr;
}
void tcp_client::poll()
{
ioc.poll();
}
void tcp_client::HandleReceive(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").data());
}
recv_buffer.resize(bytesRead);
recv_queue.Write(std::move(recv_buffer));
recv_buffer.resize(frame_queue::max_frame_size);
while (recv_queue.PacketReady()) {
auto pkt = pktfty->make_packet(recv_queue.ReadPacket());
RecvLocal(*pkt);
}
StartReceive();
}
void tcp_client::StartReceive()
{
sock.async_receive(
asio::buffer(recv_buffer),
std::bind(&tcp_client::HandleReceive, this, std::placeholders::_1, std::placeholders::_2));
}
void tcp_client::HandleSend(const asio::error_code &error, size_t bytesSent)
{
// empty for now
}
void tcp_client::send(packet &pkt)
{
auto frame = std::make_unique<buffer_t>(frame_queue::MakeFrame(pkt.Data()));
auto buf = asio::buffer(*frame);
asio::async_write(sock, buf, [this, frame = std::move(frame)](const asio::error_code &error, size_t bytesSent) {
HandleSend(error, bytesSent);
});
}
bool tcp_client::SNetLeaveGame(int type)
{
auto ret = base::SNetLeaveGame(type);
poll();
if (local_server != nullptr)
local_server->Close();
sock.close();
return ret;
}
std::string tcp_client::make_default_gamename()
{
return std::string(sgOptions.Network.szBindAddress);
}
tcp_client::~tcp_client()
= default;
} // namespace net
} // namespace devilution