Browse Source

Apply sequence number to net turns (#4122)

pull/4137/head
Stephen C. Wills 4 years ago committed by GitHub
parent
commit
ef1821ce57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 225
      Source/dvlnet/base.cpp
  2. 26
      Source/dvlnet/base.h
  3. 17
      Source/dvlnet/base_protocol.h
  4. 10
      Source/dvlnet/packet.h
  5. 5
      Source/dvlnet/tcp_client.cpp
  6. 3
      Source/dvlnet/tcp_client.h

225
Source/dvlnet/base.cpp

@ -41,7 +41,7 @@ void base::HandleAccept(packet &pkt)
}
if (pkt.Cookie() == cookie_self) {
plr_self = pkt.NewPlayer();
connected_table[plr_self] = true;
Connect(plr_self);
}
if (game_init_info != pkt.Info()) {
if (pkt.Info().size() != sizeof(GameData)) {
@ -58,6 +58,48 @@ void base::HandleAccept(packet &pkt)
}
}
void base::HandleConnect(packet &pkt)
{
plr_t newPlayer = pkt.NewPlayer();
Connect(newPlayer);
}
void base::HandleTurn(packet &pkt)
{
plr_t src = pkt.Source();
PlayerState &playerState = playerStateTable_[src];
playerState.waitForTurns = true;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
const turn_t &turn = pkt.Turn();
turnQueue.push_back(turn);
MakeReady(turn.SequenceNumber);
}
void base::HandleDisconnect(packet &pkt)
{
plr_t newPlayer = pkt.NewPlayer();
if (newPlayer != plr_self) {
if (IsConnected(newPlayer)) {
auto leaveinfo = pkt.LeaveInfo();
_SNETEVENT ev;
ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME;
ev.playerid = pkt.NewPlayer();
ev.data = reinterpret_cast<unsigned char *>(&leaveinfo);
ev.databytes = sizeof(leaveinfo_t);
RunEventHandler(ev);
DisconnectNet(newPlayer);
ClearMsg(newPlayer);
PlayerState &playerState = playerStateTable_[newPlayer];
playerState.isConnected = false;
playerState.waitForTurns = false;
playerState.turnQueue.clear();
}
} else {
ABORT(); // we were dropped by the owner?!?
}
}
void base::ClearMsg(plr_t plr)
{
message_queue.erase(std::remove_if(message_queue.begin(),
@ -68,42 +110,38 @@ void base::ClearMsg(plr_t plr)
message_queue.end());
}
void base::Connect(plr_t player)
{
PlayerState &playerState = playerStateTable_[player];
playerState.isConnected = true;
}
bool base::IsConnected(plr_t player) const
{
const PlayerState &playerState = playerStateTable_[player];
return playerState.isConnected;
}
void base::RecvLocal(packet &pkt)
{
if (pkt.Source() < MAX_PLRS) {
connected_table[pkt.Source()] = true;
Connect(pkt.Source());
}
switch (pkt.Type()) {
case PT_MESSAGE:
message_queue.emplace_back(pkt.Source(), pkt.Message());
break;
case PT_TURN:
turn_queue[pkt.Source()].push_back(pkt.Turn());
HandleTurn(pkt);
break;
case PT_JOIN_ACCEPT:
HandleAccept(pkt);
break;
case PT_CONNECT:
connected_table[pkt.NewPlayer()] = true; // this can probably be removed
HandleConnect(pkt);
break;
case PT_DISCONNECT:
if (pkt.NewPlayer() != plr_self) {
if (connected_table[pkt.NewPlayer()]) {
auto leaveinfo = pkt.LeaveInfo();
_SNETEVENT ev;
ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME;
ev.playerid = pkt.NewPlayer();
ev.data = reinterpret_cast<unsigned char *>(&leaveinfo);
ev.databytes = sizeof(leaveinfo_t);
RunEventHandler(ev);
connected_table[pkt.NewPlayer()] = false;
DisconnectNet(pkt.NewPlayer());
ClearMsg(pkt.NewPlayer());
turn_queue[pkt.NewPlayer()].clear();
}
} else {
ABORT(); // we were dropped by the owner?!?
}
HandleDisconnect(pkt);
break;
default:
break;
@ -145,53 +183,136 @@ bool base::SNetSendMessage(int playerId, void *data, unsigned int size)
return true;
}
bool base::AllTurnsArrived()
{
for (auto i = 0; i < MAX_PLRS; ++i) {
PlayerState &playerState = playerStateTable_[i];
if (!playerState.waitForTurns)
continue;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
if (turnQueue.empty()) {
LogDebug("Turn missing from player {}", i);
return false;
}
}
return true;
}
bool base::SNetReceiveTurns(char **data, size_t *size, uint32_t *status)
{
poll();
bool allTurnsArrived = true;
for (auto i = 0; i < MAX_PLRS; ++i) {
status[i] = 0;
if (connected_table[i]) {
status[i] |= PS_CONNECTED;
if (turn_queue[i].empty())
allTurnsArrived = false;
PlayerState &playerState = playerStateTable_[i];
if (!playerState.waitForTurns)
continue;
status[i] |= PS_CONNECTED;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
while (!turnQueue.empty()) {
const turn_t &turn = turnQueue.front();
seq_t diff = turn.SequenceNumber - next_turn;
if (diff <= 0x7F)
break;
turnQueue.pop_front();
}
}
if (allTurnsArrived) {
if (AllTurnsArrived()) {
for (auto i = 0; i < MAX_PLRS; ++i) {
if (connected_table[i]) {
size[i] = sizeof(turn_t);
status[i] |= PS_ACTIVE;
status[i] |= PS_TURN_ARRIVED;
turn_last[i] = turn_queue[i].front();
turn_queue[i].pop_front();
data[i] = reinterpret_cast<char *>(&turn_last[i]);
}
PlayerState &playerState = playerStateTable_[i];
if (!playerState.waitForTurns)
continue;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
if (turnQueue.empty())
continue;
const turn_t &turn = turnQueue.front();
if (turn.SequenceNumber != next_turn)
continue;
playerState.lastTurnValue = turn.Value;
turnQueue.pop_front();
size[i] = sizeof(int32_t);
status[i] |= PS_ACTIVE;
status[i] |= PS_TURN_ARRIVED;
data[i] = reinterpret_cast<char *>(&playerState.lastTurnValue);
}
next_turn++;
return true;
}
for (auto i = 0; i < MAX_PLRS; ++i) {
if (connected_table[i]) {
if (!turn_queue[i].empty()) {
status[i] |= PS_ACTIVE;
}
}
PlayerState &playerState = playerStateTable_[i];
if (!playerState.waitForTurns)
continue;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
if (turnQueue.empty())
continue;
status[i] |= PS_ACTIVE;
}
return false;
}
bool base::SNetSendTurn(char *data, unsigned int size)
{
if (size != sizeof(turn_t))
if (size != sizeof(int32_t))
ABORT();
turn_t turn;
std::memcpy(&turn, data, sizeof(turn));
auto pkt = pktfty->make_packet<PT_TURN>(plr_self, PLR_BROADCAST, turn);
send(*pkt);
turn_queue[plr_self].push_back(pkt->Turn());
turn.SequenceNumber = next_turn;
std::memcpy(&turn.Value, data, size);
PlayerState &playerState = playerStateTable_[plr_self];
std::deque<turn_t> &turnQueue = playerState.turnQueue;
turnQueue.push_back(turn);
SendTurnIfReady(turn);
return true;
}
void base::SendTurnIfReady(turn_t turn)
{
PlayerState &playerState = playerStateTable_[plr_self];
bool &ready = playerState.waitForTurns;
if (!ready)
ready = IsGameHost();
if (ready) {
auto pkt = pktfty->make_packet<PT_TURN>(plr_self, PLR_BROADCAST, turn);
send(*pkt);
}
}
void base::MakeReady(seq_t sequenceNumber)
{
PlayerState &playerState = playerStateTable_[plr_self];
if (playerState.waitForTurns)
return;
next_turn = sequenceNumber;
playerState.waitForTurns = true;
std::deque<turn_t> &turnQueue = playerState.turnQueue;
if (!turnQueue.empty()) {
turn_t &turn = turnQueue.front();
turn.SequenceNumber = next_turn;
SendTurnIfReady(turn);
}
}
void base::SNetGetProviderCaps(struct _SNETCAPS *caps)
{
caps->size = 0; // engine writes only ?!?
@ -248,7 +369,7 @@ bool base::SNetDropPlayer(int playerid, uint32_t flags)
plr_t base::GetOwner()
{
for (auto i = 0; i < MAX_PLRS; ++i) {
if (connected_table[i]) {
if (IsConnected(i)) {
return i;
}
}
@ -257,13 +378,21 @@ plr_t base::GetOwner()
bool base::SNetGetOwnerTurnsWaiting(uint32_t *turns)
{
*turns = turn_queue[GetOwner()].size();
poll();
plr_t owner = GetOwner();
PlayerState &playerState = playerStateTable_[owner];
std::deque<turn_t> &turnQueue = playerState.turnQueue;
*turns = turnQueue.size();
return true;
}
bool base::SNetGetTurnsInTransit(uint32_t *turns)
{
*turns = turn_queue[plr_self].size();
PlayerState &playerState = playerStateTable_[plr_self];
std::deque<turn_t> &turnQueue = playerState.turnQueue;
*turns = turnQueue.size();
return true;
}

26
Source/dvlnet/base.h

@ -62,24 +62,42 @@ protected:
}
};
struct PlayerState {
bool isConnected = {};
bool waitForTurns = {};
std::deque<turn_t> turnQueue;
int32_t lastTurnValue = {};
};
seq_t next_turn;
message_t message_last;
std::deque<message_t> message_queue;
std::array<turn_t, MAX_PLRS> turn_last = {};
std::array<std::deque<turn_t>, MAX_PLRS> turn_queue;
std::array<bool, MAX_PLRS> connected_table = {};
plr_t plr_self = PLR_BROADCAST;
cookie_t cookie_self = 0;
std::unique_ptr<packet_factory> pktfty;
void HandleAccept(packet &pkt);
void Connect(plr_t player);
void RecvLocal(packet &pkt);
void RunEventHandler(_SNETEVENT &ev);
[[nodiscard]] bool IsConnected(plr_t player) const;
virtual bool IsGameHost() = 0;
private:
std::array<PlayerState, MAX_PLRS> playerStateTable_;
plr_t GetOwner();
bool AllTurnsArrived();
void MakeReady(seq_t sequenceNumber);
void SendTurnIfReady(turn_t turn);
void ClearMsg(plr_t plr);
void HandleAccept(packet &pkt);
void HandleConnect(packet &pkt);
void HandleTurn(packet &pkt);
void HandleDisconnect(packet &pkt);
};
} // namespace net

17
Source/dvlnet/base_protocol.h

@ -31,6 +31,9 @@ public:
virtual ~base_protocol() = default;
protected:
virtual bool IsGameHost();
private:
P proto;
typedef typename P::endpoint endpoint;
@ -129,7 +132,7 @@ int base_protocol<P>::create(std::string addrstr)
if (wait_network()) {
plr_self = 0;
connected_table[plr_self] = true;
Connect(plr_self);
}
return (plr_self == PLR_BROADCAST ? -1 : plr_self);
}
@ -145,6 +148,12 @@ int base_protocol<P>::join(std::string addrstr)
return (plr_self == PLR_BROADCAST ? -1 : plr_self);
}
template <class P>
bool base_protocol<P>::IsGameHost()
{
return firstpeer == endpoint();
}
template <class P>
void base_protocol<P>::poll()
{
@ -206,7 +215,7 @@ void base_protocol<P>::handle_join_request(packet &pkt, endpoint sender)
plr_t i;
for (i = 0; i < MAX_PLRS; ++i) {
if (i != plr_self && !peers[i]) {
connected_table[i] = true;
Connect(i);
peers[i] = sender;
break;
}
@ -295,7 +304,7 @@ void base_protocol<P>::recv_ingame(packet &pkt, endpoint sender)
}
// addrinfo packets
connected_table[pkt.NewPlayer()] = true;
Connect(pkt.NewPlayer());
peers[pkt.NewPlayer()].unserialize(pkt.Info());
return;
} else if (pkt.Source() >= MAX_PLRS) {
@ -303,7 +312,7 @@ void base_protocol<P>::recv_ingame(packet &pkt, endpoint sender)
LogDebug("Invalid packet: packet source ({}) >= MAX_PLRS", pkt.Source());
return;
} else if (sender == firstpeer && pkt.Type() == PT_JOIN_ACCEPT) {
connected_table[pkt.Source()] = true;
Connect(pkt.Source());
peers[pkt.Source()] = sender;
firstpeer = endpoint();
} else if (sender != peers[pkt.Source()]) {

10
Source/dvlnet/packet.h

@ -33,8 +33,8 @@ enum packet_type : uint8_t {
const char *packet_type_to_string(uint8_t packetType);
typedef uint8_t plr_t;
typedef uint8_t seq_t;
typedef uint32_t cookie_t;
typedef int turn_t; // change int to something else in devilution code later
typedef int leaveinfo_t; // also change later
#ifdef PACKET_ENCRYPTION
typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t;
@ -43,6 +43,11 @@ typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t;
using key_t = uint8_t;
#endif
struct turn_t {
seq_t SequenceNumber;
int32_t Value;
};
static constexpr plr_t PLR_MASTER = 0xFE;
static constexpr plr_t PLR_BROADCAST = 0xFF;
@ -149,7 +154,8 @@ void packet_proc<P>::process_data()
self.process_element(m_message);
break;
case PT_TURN:
self.process_element(m_turn);
self.process_element(m_turn.SequenceNumber);
self.process_element(m_turn.Value);
break;
case PT_JOIN_REQUEST:
self.process_element(m_cookie);

5
Source/dvlnet/tcp_client.cpp

@ -69,6 +69,11 @@ int tcp_client::join(std::string addrstr)
return plr_self;
}
bool tcp_client::IsGameHost()
{
return local_server != nullptr;
}
void tcp_client::poll()
{
ioc.poll();

3
Source/dvlnet/tcp_client.h

@ -30,6 +30,9 @@ public:
virtual std::string make_default_gamename();
protected:
virtual bool IsGameHost();
private:
frame_queue recv_queue;
buffer_t recv_buffer = buffer_t(frame_queue::max_frame_size);

Loading…
Cancel
Save