|
|
|
|
@ -10,8 +10,7 @@
|
|
|
|
|
#include "player.h" |
|
|
|
|
#include "utils/log.hpp" |
|
|
|
|
|
|
|
|
|
namespace devilution { |
|
|
|
|
namespace net { |
|
|
|
|
namespace devilution::net { |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
class base_protocol : public base { |
|
|
|
|
@ -56,18 +55,18 @@ private:
|
|
|
|
|
bool isGameHost_; |
|
|
|
|
|
|
|
|
|
plr_t get_master(); |
|
|
|
|
void InitiateHandshake(plr_t player); |
|
|
|
|
tl::expected<void, PacketError> InitiateHandshake(plr_t player); |
|
|
|
|
void SendTo(plr_t player, packet &pkt); |
|
|
|
|
void DrainSendQueue(plr_t player); |
|
|
|
|
void recv(); |
|
|
|
|
void handle_join_request(packet &pkt, endpoint_t sender); |
|
|
|
|
void recv_decrypted(packet &pkt, endpoint_t sender); |
|
|
|
|
void recv_ingame(packet &pkt, endpoint_t sender); |
|
|
|
|
tl::expected<void, PacketError> handle_join_request(packet &pkt, endpoint_t sender); |
|
|
|
|
tl::expected<void, PacketError> recv_decrypted(packet &pkt, endpoint_t sender); |
|
|
|
|
tl::expected<void, PacketError> recv_ingame(packet &pkt, endpoint_t sender); |
|
|
|
|
bool is_recognized(endpoint_t sender); |
|
|
|
|
|
|
|
|
|
bool wait_network(); |
|
|
|
|
bool wait_firstpeer(); |
|
|
|
|
void wait_join(); |
|
|
|
|
tl::expected<void, PacketError> wait_join(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
@ -121,24 +120,33 @@ bool base_protocol<P>::send_info_request()
|
|
|
|
|
{ |
|
|
|
|
if (!proto.network_online()) |
|
|
|
|
return false; |
|
|
|
|
auto pkt = pktfty->make_packet<PT_INFO_REQUEST>(PLR_BROADCAST, PLR_MASTER); |
|
|
|
|
proto.send_oob_mc(pkt->Data()); |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> pkt |
|
|
|
|
= pktfty->make_packet<PT_INFO_REQUEST>(PLR_BROADCAST, PLR_MASTER); |
|
|
|
|
if (!pkt.has_value()) { |
|
|
|
|
LogError("make_packet: {}", pkt.error().what()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
proto.send_oob_mc((*pkt)->Data()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
void base_protocol<P>::wait_join() |
|
|
|
|
tl::expected<void, PacketError> base_protocol<P>::wait_join() |
|
|
|
|
{ |
|
|
|
|
cookie_self = packet_out::GenerateCookie(); |
|
|
|
|
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST, |
|
|
|
|
PLR_MASTER, cookie_self, game_init_info); |
|
|
|
|
proto.send(firstpeer, pkt->Data()); |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> pkt |
|
|
|
|
= pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST, PLR_MASTER, cookie_self, game_init_info); |
|
|
|
|
if (!pkt.has_value()) { |
|
|
|
|
return tl::unexpected(pkt.error()); |
|
|
|
|
} |
|
|
|
|
proto.send(firstpeer, (*pkt)->Data()); |
|
|
|
|
for (auto i = 0; i < 500; ++i) { |
|
|
|
|
recv(); |
|
|
|
|
if (plr_self != PLR_BROADCAST) |
|
|
|
|
break; // join successful
|
|
|
|
|
SDL_Delay(10); |
|
|
|
|
} |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
@ -149,7 +157,11 @@ int base_protocol<P>::create(std::string addrstr)
|
|
|
|
|
|
|
|
|
|
if (wait_network()) { |
|
|
|
|
plr_self = 0; |
|
|
|
|
Connect(plr_self); |
|
|
|
|
if (tl::expected<void, PacketError> result = Connect(plr_self); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
LogError("Connect: {}", result.error().what()); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return (plr_self == PLR_BROADCAST ? -1 : plr_self); |
|
|
|
|
} |
|
|
|
|
@ -161,8 +173,12 @@ int base_protocol<P>::join(std::string addrstr)
|
|
|
|
|
isGameHost_ = false; |
|
|
|
|
|
|
|
|
|
if (wait_network()) { |
|
|
|
|
if (wait_firstpeer()) |
|
|
|
|
wait_join(); |
|
|
|
|
if (wait_firstpeer()) { |
|
|
|
|
if (tl::expected<void, PacketError> result = wait_join(); !result.has_value()) { |
|
|
|
|
LogError("wait_join: {}", result.error().what()); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return (plr_self == PLR_BROADCAST ? -1 : plr_self); |
|
|
|
|
} |
|
|
|
|
@ -180,7 +196,7 @@ void base_protocol<P>::poll()
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
void base_protocol<P>::InitiateHandshake(plr_t player) |
|
|
|
|
tl::expected<void, PacketError> base_protocol<P>::InitiateHandshake(plr_t player) |
|
|
|
|
{ |
|
|
|
|
Peer &peer = peers[player]; |
|
|
|
|
|
|
|
|
|
@ -189,7 +205,9 @@ void base_protocol<P>::InitiateHandshake(plr_t player)
|
|
|
|
|
// If the connection is already open, it should be safe to initiate from either end.
|
|
|
|
|
// If not, only the player with the smaller player number should initiate the handshake.
|
|
|
|
|
if (plr_self < player || proto.is_peer_connected(peer.endpoint)) |
|
|
|
|
SendEchoRequest(player); |
|
|
|
|
return SendEchoRequest(player); |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
@ -232,13 +250,15 @@ void base_protocol<P>::recv()
|
|
|
|
|
buffer_t pkt_buf; |
|
|
|
|
endpoint_t sender; |
|
|
|
|
while (proto.recv(sender, pkt_buf)) { // read until kernel buffer is empty?
|
|
|
|
|
try { |
|
|
|
|
auto pkt = pktfty->make_packet(pkt_buf); |
|
|
|
|
recv_decrypted(*pkt, sender); |
|
|
|
|
} catch (packet_exception &e) { |
|
|
|
|
tl::expected<void, PacketError> result |
|
|
|
|
= pktfty->make_packet(pkt_buf) |
|
|
|
|
.and_then([&](std::unique_ptr<packet> &&pkt) { |
|
|
|
|
return recv_decrypted(*pkt, sender); |
|
|
|
|
}); |
|
|
|
|
if (!result.has_value()) { |
|
|
|
|
// drop packet
|
|
|
|
|
proto.disconnect(sender); |
|
|
|
|
Log("{}", e.what()); |
|
|
|
|
Log("{}", result.error().what()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
while (proto.get_disconnected(sender)) { |
|
|
|
|
@ -256,7 +276,7 @@ void base_protocol<P>::recv()
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
void base_protocol<P>::handle_join_request(packet &pkt, endpoint_t sender) |
|
|
|
|
tl::expected<void, PacketError> base_protocol<P>::handle_join_request(packet &inPkt, endpoint_t sender) |
|
|
|
|
{ |
|
|
|
|
plr_t i; |
|
|
|
|
for (i = 0; i < Players.size(); ++i) { |
|
|
|
|
@ -264,70 +284,94 @@ void base_protocol<P>::handle_join_request(packet &pkt, endpoint_t sender)
|
|
|
|
|
if (i != plr_self && !peer.endpoint) { |
|
|
|
|
peer.endpoint = sender; |
|
|
|
|
peer.sendQueue = std::make_unique<std::deque<packet>>(); |
|
|
|
|
Connect(i); |
|
|
|
|
if (tl::expected<void, PacketError> result = Connect(i); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (i >= MAX_PLRS) { |
|
|
|
|
// already full
|
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto senderinfo = sender.serialize(); |
|
|
|
|
for (plr_t j = 0; j < Players.size(); ++j) { |
|
|
|
|
endpoint_t peer = peers[j].endpoint; |
|
|
|
|
if ((j != plr_self) && (j != i) && peer) { |
|
|
|
|
auto peerpkt = pktfty->make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST, i, senderinfo); |
|
|
|
|
proto.send(peer, peerpkt->Data()); |
|
|
|
|
|
|
|
|
|
auto infopkt = pktfty->make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST, j, peer.serialize()); |
|
|
|
|
proto.send(sender, infopkt->Data()); |
|
|
|
|
{ |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> pkt |
|
|
|
|
= pktfty->make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST, i, senderinfo); |
|
|
|
|
if (!pkt.has_value()) |
|
|
|
|
return tl::make_unexpected(pkt.error()); |
|
|
|
|
proto.send(peer, (*pkt)->Data()); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> pkt |
|
|
|
|
= pktfty->make_packet<PT_CONNECT>(PLR_MASTER, PLR_BROADCAST, j, peer.serialize()); |
|
|
|
|
if (!pkt.has_value()) |
|
|
|
|
return tl::make_unexpected(pkt.error()); |
|
|
|
|
proto.send(sender, (*pkt)->Data()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// PT_JOIN_ACCEPT must be sent after all PT_CONNECT packets so the new player does
|
|
|
|
|
// not resume game logic until after having been notified of all existing players
|
|
|
|
|
auto reply = pktfty->make_packet<PT_JOIN_ACCEPT>(plr_self, PLR_BROADCAST, |
|
|
|
|
pkt.Cookie(), i, |
|
|
|
|
game_init_info); |
|
|
|
|
proto.send(sender, reply->Data()); |
|
|
|
|
tl::expected<cookie_t, PacketError> cookie = inPkt.Cookie(); |
|
|
|
|
if (!cookie.has_value()) |
|
|
|
|
return tl::make_unexpected(cookie.error()); |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> pkt |
|
|
|
|
= pktfty->make_packet<PT_JOIN_ACCEPT>(plr_self, PLR_BROADCAST, *cookie, i, game_init_info); |
|
|
|
|
if (!pkt.has_value()) |
|
|
|
|
return tl::make_unexpected(pkt.error()); |
|
|
|
|
proto.send(sender, (*pkt)->Data()); |
|
|
|
|
DrainSendQueue(i); |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
void base_protocol<P>::recv_decrypted(packet &pkt, endpoint_t sender) |
|
|
|
|
tl::expected<void, PacketError> base_protocol<P>::recv_decrypted(packet &pkt, endpoint_t sender) |
|
|
|
|
{ |
|
|
|
|
if (pkt.Source() == PLR_BROADCAST && pkt.Destination() == PLR_MASTER && pkt.Type() == PT_INFO_REPLY) { |
|
|
|
|
size_t neededSize = sizeof(GameData) + (PlayerNameLength * MAX_PLRS); |
|
|
|
|
if (pkt.Info().size() < neededSize) |
|
|
|
|
return; |
|
|
|
|
const GameData *gameData = (const GameData *)pkt.Info().data(); |
|
|
|
|
const tl::expected<const buffer_t *, PacketError> pktInfo = pkt.Info(); |
|
|
|
|
if (!pktInfo.has_value()) |
|
|
|
|
return tl::make_unexpected(pktInfo.error()); |
|
|
|
|
const buffer_t &infoBuffer = **pktInfo; |
|
|
|
|
if (infoBuffer.size() < neededSize) |
|
|
|
|
return {}; |
|
|
|
|
const GameData *gameData = reinterpret_cast<const GameData *>(infoBuffer.data()); |
|
|
|
|
if (gameData->size != sizeof(GameData)) |
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
std::vector<std::string> playerNames; |
|
|
|
|
for (size_t i = 0; i < Players.size(); i++) { |
|
|
|
|
std::string playerName; |
|
|
|
|
const char *playerNamePointer = (const char *)(pkt.Info().data() + sizeof(GameData) + (i * PlayerNameLength)); |
|
|
|
|
const char *playerNamePointer = reinterpret_cast<const char *>(infoBuffer.data() + sizeof(GameData) + (i * PlayerNameLength)); |
|
|
|
|
playerName.append(playerNamePointer, strnlen(playerNamePointer, PlayerNameLength)); |
|
|
|
|
if (!playerName.empty()) |
|
|
|
|
playerNames.push_back(playerName); |
|
|
|
|
} |
|
|
|
|
std::string gameName; |
|
|
|
|
size_t gameNameSize = pkt.Info().size() - neededSize; |
|
|
|
|
size_t gameNameSize = infoBuffer.size() - neededSize; |
|
|
|
|
gameName.resize(gameNameSize); |
|
|
|
|
std::memcpy(&gameName[0], pkt.Info().data() + neededSize, gameNameSize); |
|
|
|
|
std::memcpy(&gameName[0], infoBuffer.data() + neededSize, gameNameSize); |
|
|
|
|
game_list[gameName] = GameListValue { *gameData, std::move(playerNames), sender }; |
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
recv_ingame(pkt, sender); |
|
|
|
|
return recv_ingame(pkt, sender); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
void base_protocol<P>::recv_ingame(packet &pkt, endpoint_t sender) |
|
|
|
|
tl::expected<void, PacketError> base_protocol<P>::recv_ingame(packet &pkt, endpoint_t sender) |
|
|
|
|
{ |
|
|
|
|
if (pkt.Source() == PLR_BROADCAST && pkt.Destination() == PLR_MASTER) { |
|
|
|
|
if (pkt.Type() == PT_JOIN_REQUEST) { |
|
|
|
|
handle_join_request(pkt, sender); |
|
|
|
|
if (tl::expected<void, PacketError> result = handle_join_request(pkt, sender); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} else if (pkt.Type() == PT_INFO_REQUEST) { |
|
|
|
|
if ((plr_self != PLR_BROADCAST) && (get_master() == plr_self)) { |
|
|
|
|
buffer_t buf; |
|
|
|
|
@ -341,58 +385,81 @@ void base_protocol<P>::recv_ingame(packet &pkt, endpoint_t sender)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
std::memcpy(buf.data() + game_init_info.size() + (PlayerNameLength * MAX_PLRS), &gamename[0], gamename.size()); |
|
|
|
|
auto reply = pktfty->make_packet<PT_INFO_REPLY>(PLR_BROADCAST, |
|
|
|
|
PLR_MASTER, |
|
|
|
|
buf); |
|
|
|
|
proto.send_oob(sender, reply->Data()); |
|
|
|
|
tl::expected<std::unique_ptr<packet>, PacketError> reply |
|
|
|
|
= pktfty->make_packet<PT_INFO_REPLY>(PLR_BROADCAST, PLR_MASTER, buf); |
|
|
|
|
if (!reply.has_value()) { |
|
|
|
|
return tl::make_unexpected(reply.error()); |
|
|
|
|
} |
|
|
|
|
proto.send_oob(sender, (*reply)->Data()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return; |
|
|
|
|
} else if (pkt.Source() == PLR_MASTER && pkt.Type() == PT_CONNECT) { |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
if (pkt.Source() == PLR_MASTER && pkt.Type() == PT_CONNECT) { |
|
|
|
|
if (!is_recognized(sender)) { |
|
|
|
|
LogDebug("Invalid packet: PT_CONNECT received from unrecognized endpoint"); |
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// addrinfo packets
|
|
|
|
|
plr_t newPlayer = pkt.NewPlayer(); |
|
|
|
|
Peer &peer = peers[newPlayer]; |
|
|
|
|
peer.endpoint.unserialize(pkt.Info()); |
|
|
|
|
tl::expected<plr_t, PacketError> newPlayer = pkt.NewPlayer(); |
|
|
|
|
if (!newPlayer.has_value()) |
|
|
|
|
return tl::make_unexpected(newPlayer.error()); |
|
|
|
|
Peer &peer = peers[*newPlayer]; |
|
|
|
|
tl::expected<const buffer_t *, PacketError> pktInfo = pkt.Info(); |
|
|
|
|
if (!pktInfo.has_value()) |
|
|
|
|
return tl::make_unexpected(pktInfo.error()); |
|
|
|
|
peer.endpoint.unserialize(**pktInfo); |
|
|
|
|
peer.sendQueue = std::make_unique<std::deque<packet>>(); |
|
|
|
|
Connect(newPlayer); |
|
|
|
|
if (tl::expected<void, PacketError> result = Connect(*newPlayer); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (plr_self != PLR_BROADCAST) |
|
|
|
|
InitiateHandshake(newPlayer); |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
} else if (pkt.Source() >= MAX_PLRS) { |
|
|
|
|
return InitiateHandshake(*newPlayer); |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
if (pkt.Source() >= MAX_PLRS) { |
|
|
|
|
// normal packets
|
|
|
|
|
LogDebug("Invalid packet: packet source ({}) >= MAX_PLRS", pkt.Source()); |
|
|
|
|
return; |
|
|
|
|
} else if (sender == firstpeer && pkt.Type() == PT_JOIN_ACCEPT) { |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
if (sender == firstpeer && pkt.Type() == PT_JOIN_ACCEPT) { |
|
|
|
|
plr_t src = pkt.Source(); |
|
|
|
|
peers[src].endpoint = sender; |
|
|
|
|
Connect(src); |
|
|
|
|
if (tl::expected<void, PacketError> result = Connect(src); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
firstpeer = {}; |
|
|
|
|
} else if (sender != peers[pkt.Source()].endpoint) { |
|
|
|
|
LogDebug("Invalid packet: packet source ({}) received from unrecognized endpoint", pkt.Source()); |
|
|
|
|
return; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
if (pkt.Destination() != plr_self && pkt.Destination() != PLR_BROADCAST) |
|
|
|
|
return; // packet not for us, drop
|
|
|
|
|
return {}; // packet not for us, drop
|
|
|
|
|
|
|
|
|
|
bool wasBroadcast = plr_self == PLR_BROADCAST; |
|
|
|
|
RecvLocal(pkt); |
|
|
|
|
if (tl::expected<void, PacketError> result = RecvLocal(pkt); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (plr_self != PLR_BROADCAST) { |
|
|
|
|
if (wasBroadcast) { |
|
|
|
|
// Send a handshake to everyone just after PT_JOIN_ACCEPT
|
|
|
|
|
for (plr_t player = 0; player < Players.size(); player++) |
|
|
|
|
InitiateHandshake(player); |
|
|
|
|
for (plr_t player = 0; player < Players.size(); player++) { |
|
|
|
|
if (tl::expected<void, PacketError> result = InitiateHandshake(player); |
|
|
|
|
!result.has_value()) { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DrainSendQueue(pkt.Source()); |
|
|
|
|
} |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <class P> |
|
|
|
|
@ -462,5 +529,4 @@ std::string base_protocol<P>::make_default_gamename()
|
|
|
|
|
return proto.make_default_gamename(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace net
|
|
|
|
|
} // namespace devilution
|
|
|
|
|
} // namespace devilution::net
|
|
|
|
|
|