#pragma once #include #include #include #include #include "dvlnet/base.h" #include "dvlnet/packet.h" #include "player.h" #include "utils/log.hpp" namespace devilution { namespace net { template class base_protocol : public base { public: virtual int create(std::string addrstr, std::string passwd); virtual int join(std::string addrstr, std::string passwd); virtual void poll(); virtual void send(packet &pkt); virtual void disconnect_net(plr_t plr); virtual bool SNetLeaveGame(int type); virtual std::string make_default_gamename(); virtual void send_info_request(); virtual std::vector get_gamelist(); virtual ~base_protocol() = default; private: P proto; typedef typename P::endpoint endpoint; endpoint firstpeer; std::string gamename; std::map game_list; std::array peers; plr_t get_master(); void recv(); void handle_join_request(packet &pkt, endpoint sender); void recv_decrypted(packet &pkt, endpoint sender); void recv_ingame(packet &pkt, endpoint sender); bool wait_network(); bool wait_firstpeer(); void wait_join(); }; template plr_t base_protocol

::get_master() { plr_t ret = plr_self; for (plr_t i = 0; i < MAX_PLRS; ++i) if (peers[i]) ret = std::min(ret, i); return ret; } template bool base_protocol

::wait_network() { // wait for ZeroTier for 5 seconds for (auto i = 0; i < 500; ++i) { if (proto.network_online()) break; SDL_Delay(10); } return proto.network_online(); } template void base_protocol

::disconnect_net(plr_t plr) { proto.disconnect(peers[plr]); peers[plr] = endpoint(); } template bool base_protocol

::wait_firstpeer() { // wait for peer for 5 seconds for (auto i = 0; i < 500; ++i) { if (game_list.count(gamename)) { firstpeer = game_list[gamename]; break; } send_info_request(); recv(); SDL_Delay(10); } return (bool)firstpeer; } template void base_protocol

::send_info_request() { auto pkt = pktfty->make_packet(PLR_BROADCAST, PLR_MASTER); proto.send_oob_mc(pkt->data()); } template void base_protocol

::wait_join() { randombytes_buf(reinterpret_cast(&cookie_self), sizeof(cookie_t)); auto pkt = pktfty->make_packet(PLR_BROADCAST, PLR_MASTER, cookie_self, game_init_info); proto.send(firstpeer, pkt->data()); for (auto i = 0; i < 500; ++i) { recv(); if (plr_self != PLR_BROADCAST) break; // join successful SDL_Delay(10); } } template int base_protocol

::create(std::string addrstr, std::string passwd) { setup_password(passwd); gamename = addrstr; if (wait_network()) { plr_self = 0; connected_table[plr_self] = true; } return (plr_self == PLR_BROADCAST ? MAX_PLRS : plr_self); } template int base_protocol

::join(std::string addrstr, std::string passwd) { //addrstr = "fd80:56c2:e21c:0:199:931d:b14:c4d2"; setup_password(passwd); gamename = addrstr; if (wait_network()) if (wait_firstpeer()) wait_join(); return (plr_self == PLR_BROADCAST ? MAX_PLRS : plr_self); } template void base_protocol

::poll() { recv(); } template void base_protocol

::send(packet &pkt) { if (pkt.dest() < MAX_PLRS) { if (pkt.dest() == myplr) return; if (peers[pkt.dest()]) proto.send(peers[pkt.dest()], pkt.data()); } else if (pkt.dest() == PLR_BROADCAST) { for (auto &peer : peers) if (peer) proto.send(peer, pkt.data()); } else if (pkt.dest() == PLR_MASTER) { throw dvlnet_exception(); } else { throw dvlnet_exception(); } } template void base_protocol

::recv() { try { buffer_t pkt_buf; endpoint 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) { // drop packet proto.disconnect(sender); Log("{}", e.what()); } } while (proto.get_disconnected(sender)) { for (plr_t i = 0; i < MAX_PLRS; ++i) { if (peers[i] == sender) { disconnect_net(i); break; } } } } catch (std::exception &e) { Log("{}", e.what()); return; } } template void base_protocol

::handle_join_request(packet &pkt, endpoint sender) { plr_t i; for (i = 0; i < MAX_PLRS; ++i) { if (i != plr_self && !peers[i]) { peers[i] = sender; break; } } if (i >= MAX_PLRS) { //already full return; } for (plr_t j = 0; j < MAX_PLRS; ++j) { if ((j != plr_self) && (j != i) && peers[j]) { auto infopkt = pktfty->make_packet(PLR_MASTER, PLR_BROADCAST, j, peers[j].serialize()); proto.send(sender, infopkt->data()); } } auto reply = pktfty->make_packet(plr_self, PLR_BROADCAST, pkt.cookie(), i, game_init_info); proto.send(sender, reply->data()); } template void base_protocol

::recv_decrypted(packet &pkt, endpoint sender) { if (pkt.src() == PLR_BROADCAST && pkt.dest() == PLR_MASTER && pkt.type() == PT_INFO_REPLY) { std::string pname; pname.resize(pkt.info().size()); std::memcpy(&pname[0], pkt.info().data(), pkt.info().size()); game_list[pname] = sender; return; } recv_ingame(pkt, sender); } template void base_protocol

::recv_ingame(packet &pkt, endpoint sender) { if (pkt.src() == PLR_BROADCAST && pkt.dest() == PLR_MASTER) { if (pkt.type() == PT_JOIN_REQUEST) { handle_join_request(pkt, sender); } else if (pkt.type() == PT_INFO_REQUEST) { if ((plr_self != PLR_BROADCAST) && (get_master() == plr_self)) { buffer_t buf; buf.resize(gamename.size()); std::memcpy(buf.data(), &gamename[0], gamename.size()); auto reply = pktfty->make_packet(PLR_BROADCAST, PLR_MASTER, buf); proto.send_oob(sender, reply->data()); } } return; } else if (pkt.src() == PLR_MASTER && pkt.type() == PT_CONNECT) { // addrinfo packets connected_table[pkt.newplr()] = true; peers[pkt.newplr()].unserialize(pkt.info()); return; } else if (pkt.src() >= MAX_PLRS) { // normal packets ABORT(); } connected_table[pkt.src()] = true; peers[pkt.src()] = sender; if (pkt.dest() != plr_self && pkt.dest() != PLR_BROADCAST) return; //packet not for us, drop recv_local(pkt); } template std::vector base_protocol

::get_gamelist() { recv(); std::vector ret; for (auto &s : game_list) { ret.push_back(s.first); } return ret; } template bool base_protocol

::SNetLeaveGame(int type) { auto ret = base::SNetLeaveGame(type); recv(); return ret; } template std::string base_protocol

::make_default_gamename() { return proto.make_default_gamename(); } } // namespace net } // namespace devilution