Netplay: Use enet for disconnections

This commit is contained in:
Stenzek 2023-05-07 17:39:38 +10:00
parent df5f87997e
commit 1d338abe3b
8 changed files with 23 additions and 210 deletions

View File

@ -150,11 +150,8 @@ typedef enum {
GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER = 1001,
GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER = 1002,
GGPO_EVENTCODE_RUNNING = 1003,
GGPO_EVENTCODE_DISCONNECTED_FROM_PEER = 1004,
GGPO_EVENTCODE_TIMESYNC = 1005,
GGPO_EVENTCODE_CONNECTION_INTERRUPTED = 1006,
GGPO_EVENTCODE_CONNECTION_RESUMED = 1007,
GGPO_EVENTCODE_DESYNC = 1008
GGPO_EVENTCODE_TIMESYNC = 1004,
GGPO_EVENTCODE_DESYNC = 1006
} GGPOEventCode;
/*

View File

@ -8,8 +8,6 @@
#include "p2p.h"
static const int RECOMMENDATION_INTERVAL = 120;
static const int DEFAULT_DISCONNECT_TIMEOUT = 5000;
static const int DEFAULT_DISCONNECT_NOTIFY_START = 750;
Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
const char *gamename,
@ -19,8 +17,6 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
_num_players(num_players),
_input_size(input_size),
_sync(_local_connect_status, nframes),
_disconnect_timeout(DEFAULT_DISCONNECT_TIMEOUT),
_disconnect_notify_start(DEFAULT_DISCONNECT_NOTIFY_START),
_num_spectators(0),
_next_spectator_frame(0)
{
@ -63,8 +59,6 @@ Peer2PeerBackend::AddRemotePlayer(ENetPeer* peer, int queue)
_synchronizing = true;
_endpoints[queue].Init(peer, queue, _local_connect_status);
_endpoints[queue].SetDisconnectTimeout(_disconnect_timeout);
_endpoints[queue].SetDisconnectNotifyStart(_disconnect_notify_start);
_endpoints[queue].Synchronize();
}
@ -82,8 +76,6 @@ GGPOErrorCode Peer2PeerBackend::AddSpectator(ENetPeer* peer)
int queue = _num_spectators++;
_spectators[queue].Init(peer, queue + 1000, _local_connect_status);
_spectators[queue].SetDisconnectTimeout(_disconnect_timeout);
_spectators[queue].SetDisconnectNotifyStart(_disconnect_notify_start);
_spectators[queue].Synchronize();
return GGPO_OK;
@ -538,10 +530,6 @@ Peer2PeerBackend::OnUdpProtocolPeerEvent(UdpProtocol::Event &evt, int queue)
}
}
break;
case UdpProtocol::Event::Disconnected:
DisconnectPlayer(QueueToPlayerHandle(queue));
break;
}
}
@ -551,19 +539,6 @@ Peer2PeerBackend::OnUdpProtocolSpectatorEvent(UdpProtocol::Event &evt, int queue
{
GGPOPlayerHandle handle = QueueToSpectatorHandle(queue);
OnUdpProtocolEvent(evt, handle);
GGPOEvent info;
switch (evt.type) {
case UdpProtocol::Event::Disconnected:
_spectators[queue].Disconnect();
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
info.u.disconnected.player = handle;
_callbacks.on_event(_callbacks.context, &info);
break;
}
}
void
@ -591,19 +566,6 @@ Peer2PeerBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt, GGPOPlayerHandle h
CheckInitialSync();
break;
case UdpProtocol::Event::NetworkInterrupted:
info.code = GGPO_EVENTCODE_CONNECTION_INTERRUPTED;
info.u.connection_interrupted.player = handle;
info.u.connection_interrupted.disconnect_timeout = evt.u.network_interrupted.disconnect_timeout;
_callbacks.on_event(_callbacks.context, &info);
break;
case UdpProtocol::Event::NetworkResumed:
info.code = GGPO_EVENTCODE_CONNECTION_RESUMED;
info.u.connection_resumed.player = handle;
_callbacks.on_event(_callbacks.context, &info);
break;
}
}
@ -650,6 +612,7 @@ Peer2PeerBackend::DisconnectPlayerQueue(int queue, int syncto)
GGPOEvent info;
int framecount = _sync.GetFrameCount();
// TODO: Where does the endpoint actually get removed? I can't see it anywhere...
_endpoints[queue].Disconnect();
Log("Changing queue %d local connect status for last frame from %d to %d on disconnect request (current: %d).\n",
@ -664,10 +627,6 @@ Peer2PeerBackend::DisconnectPlayerQueue(int queue, int syncto)
Log("finished adjusting simulation.\n");
}*/
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
info.u.disconnected.player = QueueToPlayerHandle(queue);
_callbacks.on_event(_callbacks.context, &info);
CheckInitialSync();
}
@ -710,30 +669,6 @@ Peer2PeerBackend::SetFrameDelay(GGPOPlayerHandle player, int delay)
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::SetDisconnectTimeout(int timeout)
{
_disconnect_timeout = timeout;
for (int i = 0; i < _num_players; i++) {
if (_endpoints[i].IsInitialized()) {
_endpoints[i].SetDisconnectTimeout(_disconnect_timeout);
}
}
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::SetDisconnectNotifyStart(int timeout)
{
_disconnect_notify_start = timeout;
for (int i = 0; i < _num_players; i++) {
if (_endpoints[i].IsInitialized()) {
_endpoints[i].SetDisconnectNotifyStart(_disconnect_notify_start);
}
}
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
{

View File

@ -30,8 +30,6 @@ public:
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) override;
virtual GGPOErrorCode GetNetworkStats(GGPONetworkStats *stats, GGPOPlayerHandle handle) override;
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) override;
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) override;
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout) override;
virtual GGPOErrorCode CurrentFrame(int& current) override;
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override;
@ -65,8 +63,6 @@ protected:
int _next_recommended_sleep;
int _next_spectator_frame;
int _disconnect_timeout;
int _disconnect_notify_start;
UdpMsg::connect_status _local_connect_status[UDP_MSG_MAX_PLAYERS];
struct ChecksumEntry {

View File

@ -143,25 +143,6 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
}
break;
case UdpProtocol::Event::NetworkInterrupted:
info.code = GGPO_EVENTCODE_CONNECTION_INTERRUPTED;
info.u.connection_interrupted.player = 0;
info.u.connection_interrupted.disconnect_timeout = evt.u.network_interrupted.disconnect_timeout;
_callbacks.on_event(_callbacks.context, &info);
break;
case UdpProtocol::Event::NetworkResumed:
info.code = GGPO_EVENTCODE_CONNECTION_RESUMED;
info.u.connection_resumed.player = 0;
_callbacks.on_event(_callbacks.context, &info);
break;
case UdpProtocol::Event::Disconnected:
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
info.u.disconnected.player = 0;
_callbacks.on_event(_callbacks.context, &info);
break;
case UdpProtocol::Event::Input:
GameInput& input = evt.u.input.input;

View File

@ -21,8 +21,7 @@ struct UdpMsg
Input = 3,
QualityReport = 4,
QualityReply = 5,
KeepAlive = 6,
InputAck = 7,
InputAck = 6,
};
struct connect_status {
@ -61,8 +60,7 @@ struct UdpMsg
uint32 start_frame;
int disconnect_requested:1;
int ack_frame:31;
int ack_frame;
uint16 num_bits;
uint32 checksum32;
@ -71,7 +69,7 @@ struct UdpMsg
} input;
struct {
int ack_frame:31;
int ack_frame;
} input_ack;
} u;
@ -89,7 +87,6 @@ public:
case QualityReport: return sizeof(u.quality_report);
case QualityReply: return sizeof(u.quality_reply);
case InputAck: return sizeof(u.input_ack);
case KeepAlive: return 0;
case Input:
size = (int)((char *)&u.input.bits - (char *)&u.input);
size += (u.input.num_bits + 7) / 8;

View File

@ -15,10 +15,8 @@ static const int NUM_SYNC_PACKETS = 5;
static const int SYNC_RETRY_INTERVAL = 2000;
static const int SYNC_FIRST_RETRY_INTERVAL = 500;
static const int RUNNING_RETRY_INTERVAL = 200;
static const int KEEP_ALIVE_INTERVAL = 200;
static const int QUALITY_REPORT_INTERVAL = 333;
static const int NETWORK_STATS_INTERVAL = 500;
static const int UDP_SHUTDOWN_TIMER = 5000;
static const int MAX_SEQ_DISTANCE = (1 << 15);
static const uint8_t ENET_CHANNEL_ID = 1;
@ -33,11 +31,6 @@ UdpProtocol::UdpProtocol() :
_bytes_sent(0),
_stats_start_time(0),
_last_send_time(0),
_shutdown_timeout(0),
_disconnect_timeout(0),
_disconnect_notify_start(0),
_disconnect_notify_sent(false),
_disconnect_event_sent(false),
_connected(false),
_next_send_seq(0),
_next_recv_seq(0),
@ -143,7 +136,6 @@ UdpProtocol::SendPendingOutput()
msg->u.input.ack_frame = _last_received_input.frame;
msg->u.input.num_bits = (uint16)offset;
msg->u.input.disconnect_requested = _current_state == Disconnected;
if (_local_connect_status) {
memcpy(msg->u.input.peer_connect_status, _local_connect_status, sizeof(UdpMsg::connect_status) * UDP_MSG_MAX_PLAYERS);
} else {
@ -235,38 +227,10 @@ UdpProtocol::OnLoopPoll()
_state.running.last_network_stats_interval = now;
}
// TODO: needed with enet?
if (_last_send_time && _last_send_time + KEEP_ALIVE_INTERVAL < now) {
Log("Sending keep alive packet\n");
SendMsg(new UdpMsg(UdpMsg::KeepAlive));
}
// These can be dropped..
if (_disconnect_timeout && _disconnect_notify_start &&
!_disconnect_notify_sent && (_last_recv_time + _disconnect_notify_start < now)) {
Log("Endpoint has stopped receiving packets for %d ms. Sending notification.\n", _disconnect_notify_start);
Event e(Event::NetworkInterrupted);
e.u.network_interrupted.disconnect_timeout = _disconnect_timeout - _disconnect_notify_start;
QueueEvent(e);
_disconnect_notify_sent = true;
}
if (_disconnect_timeout && (_last_recv_time + _disconnect_timeout < now)) {
if (!_disconnect_event_sent) {
Log("Endpoint has stopped receiving packets for %d ms. Disconnecting.\n", _disconnect_timeout);
QueueEvent(Event(Event::Disconnected));
_disconnect_event_sent = true;
}
}
break;
case Disconnected:
if (_shutdown_timeout < now) {
Log("Shutting down udp connection.\n");
abort();
_peer = NULL;
_shutdown_timeout = 0;
}
break;
}
@ -278,7 +242,7 @@ void
UdpProtocol::Disconnect()
{
_current_state = Disconnected;
_shutdown_timeout = Platform::GetCurrentTimeMS() + UDP_SHUTDOWN_TIMER;
_peer = nullptr;
}
void
@ -323,7 +287,6 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
&UdpProtocol::OnInput, /* Input */
&UdpProtocol::OnQualityReport, /* QualityReport */
&UdpProtocol::OnQualityReply, /* QualityReply */
&UdpProtocol::OnKeepAlive, /* KeepAlive */
&UdpProtocol::OnInputAck, /* InputAck */
};
@ -354,10 +317,6 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
}
if (handled) {
_last_recv_time = Platform::GetCurrentTimeMS();
if (_disconnect_notify_sent && _current_state == Running) {
QueueEvent(Event(Event::NetworkResumed));
_disconnect_notify_sent = false;
}
}
}
@ -445,9 +404,6 @@ UdpProtocol::LogMsg(const char *prefix, UdpMsg *msg)
case UdpMsg::QualityReply:
Log("%s quality reply.\n", prefix);
break;
case UdpMsg::KeepAlive:
Log("%s keep alive.\n", prefix);
break;
case UdpMsg::Input:
Log("%s game-compressed-input %d (+ %d bits).\n", prefix, msg->u.input.start_frame, msg->u.input.num_bits);
break;
@ -531,28 +487,16 @@ UdpProtocol::OnSyncReply(UdpMsg *msg, int len)
bool
UdpProtocol::OnInput(UdpMsg *msg, int len)
{
/*
* If a disconnect is requested, go ahead and disconnect now.
*/
bool disconnect_requested = msg->u.input.disconnect_requested;
if (disconnect_requested) {
if (_current_state != Disconnected && !_disconnect_event_sent) {
Log("Disconnecting endpoint on remote request.\n");
QueueEvent(Event(Event::Disconnected));
_disconnect_event_sent = true;
}
} else {
/*
* Update the peer connection status if this peer is still considered to be part
* of the network.
*/
UdpMsg::connect_status* remote_status = msg->u.input.peer_connect_status;
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
ASSERT(remote_status[i].last_frame >= _peer_connect_status[i].last_frame);
_peer_connect_status[i].disconnected = _peer_connect_status[i].disconnected || remote_status[i].disconnected;
_peer_connect_status[i].last_frame = MAX(_peer_connect_status[i].last_frame, remote_status[i].last_frame);
}
}
/*
* Update the peer connection status if this peer is still considered to be part
* of the network.
*/
UdpMsg::connect_status* remote_status = msg->u.input.peer_connect_status;
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
ASSERT(remote_status[i].last_frame >= _peer_connect_status[i].last_frame);
_peer_connect_status[i].disconnected = _peer_connect_status[i].disconnected || remote_status[i].disconnected;
_peer_connect_status[i].last_frame = MAX(_peer_connect_status[i].last_frame, remote_status[i].last_frame);
}
/*
* Decompress the input.
@ -673,12 +617,6 @@ UdpProtocol::OnQualityReply(UdpMsg *msg, int len)
return true;
}
bool
UdpProtocol::OnKeepAlive(UdpMsg *msg, int len)
{
return true;
}
void
UdpProtocol::GetNetworkStats(struct GGPONetworkStats *s)
{
@ -716,16 +654,3 @@ UdpProtocol::RecommendFrameDelay()
// XXX: require idle input should be a configuration parameter
return _timesync.recommend_frame_wait_duration(false);
}
void
UdpProtocol::SetDisconnectTimeout(int timeout)
{
_disconnect_timeout = timeout;
}
void
UdpProtocol::SetDisconnectNotifyStart(int timeout)
{
_disconnect_notify_start = timeout;
}

View File

@ -37,9 +37,6 @@ public:
Synchronizing,
Synchronzied,
Input,
Disconnected,
NetworkInterrupted,
NetworkResumed,
};
Type type;
@ -85,8 +82,6 @@ public:
void SetLocalFrameNumber(int num);
float RecommendFrameDelay();
int RemoteFrameDelay()const;
void SetDisconnectTimeout(int timeout);
void SetDisconnectNotifyStart(int timeout);
void SetFrameDelay(int delay);
void ApplyToEvents(std::function<void(UdpProtocol::Event&)> cb);
void StartPollLoop();
@ -124,7 +119,6 @@ protected:
bool OnInputAck(UdpMsg *msg, int len);
bool OnQualityReport(UdpMsg *msg, int len);
bool OnQualityReply(UdpMsg *msg, int len);
bool OnKeepAlive(UdpMsg *msg, int len);
protected:
/*
@ -179,11 +173,6 @@ protected:
GameInput _last_acked_input;
unsigned int _last_send_time;
unsigned int _last_recv_time;
unsigned int _shutdown_timeout;
unsigned int _disconnect_event_sent;
unsigned int _disconnect_timeout;
unsigned int _disconnect_notify_start;
bool _disconnect_notify_sent;
uint16 _next_send_seq;
uint16 _next_recv_seq;

View File

@ -215,7 +215,10 @@ void Netplay::HandleEnetEvent(const ENetEvent* event)
if (player_id < 0)
return;
// TODO: This one's gonna get kinda tricky... who do we orphan when they disconnect?
Log_WarningPrintf("ENet player %d disconnected", player_id);
Host::OnNetplayMessage(fmt::format("*** DISCONNECTED PLAYER {} ***", player_id));
ggpo_disconnect_player(s_ggpo, PlayerIdToGGPOHandle(player_id));
s_enet_peers[player_id] = nullptr;
}
break;
@ -355,7 +358,7 @@ s32 Netplay::Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport,
ENetAddress host_address;
host_address.host = ENET_HOST_ANY;
host_address.port = lport - 10;
host_address.port = lport;
s_enet_host = enet_host_create(&host_address, MAX_PLAYERS - 1, NUM_ENET_CHANNELS, 0, 0);
if (!s_enet_host)
{
@ -381,7 +384,7 @@ s32 Netplay::Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport,
return -1;
}
peer_addresses[other_player_id].port = rport - 10;
peer_addresses[other_player_id].port = rport;
}
// Create system.
@ -874,19 +877,9 @@ bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER:
Host::OnNetplayMessage(fmt::format("Netplay Synchronized With Player: {}", ev->u.synchronized.player));
break;
case GGPOEventCode::GGPO_EVENTCODE_DISCONNECTED_FROM_PEER:
Host::OnNetplayMessage(fmt::format("Netplay Player: %d Disconnected", ev->u.disconnected.player));
break;
case GGPOEventCode::GGPO_EVENTCODE_RUNNING:
Host::OnNetplayMessage("Netplay Is Running");
break;
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_INTERRUPTED:
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Interupted, Timeout: {}", ev->u.connection_interrupted.player,
ev->u.connection_interrupted.disconnect_timeout));
break;
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED:
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Resumed", ev->u.connection_resumed.player));
break;
case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC:
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
break;