ggpo: report UDP bind error. Better error reporting for net init

fix ggpo lib warnings
This commit is contained in:
Flyinghead 2021-09-30 17:24:17 +02:00
parent 699dcdaf5b
commit a6248905a0
10 changed files with 85 additions and 55 deletions

View File

@ -113,7 +113,8 @@ typedef struct GGPOLocalEndpoint {
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PLAYER_DISCONNECTED, 9) \ GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PLAYER_DISCONNECTED, 9) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_TOO_MANY_SPECTATORS, 10) \ GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_TOO_MANY_SPECTATORS, 10) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_REQUEST, 11) \ GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_REQUEST, 11) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_SIZE_DIFF, 12) GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_SIZE_DIFF, 12) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_NETWORK_ERROR, 13)
#define GGPO_ERRORLIST_ENTRY(name, value) name = value, #define GGPO_ERRORLIST_ENTRY(name, value) name = value,
typedef enum { typedef enum {

View File

@ -18,13 +18,14 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
uint16 localport, uint16 localport,
int num_players, int num_players,
int input_size) : int input_size) :
_num_players(num_players),
_input_size(input_size),
_sync(_local_connect_status), _sync(_local_connect_status),
_disconnect_timeout(DEFAULT_DISCONNECT_TIMEOUT), _endpoints(nullptr),
_disconnect_notify_start(DEFAULT_DISCONNECT_NOTIFY_START),
_num_spectators(0), _num_spectators(0),
_next_spectator_frame(0) _input_size(input_size),
_num_players(num_players),
_next_spectator_frame(0),
_disconnect_timeout(DEFAULT_DISCONNECT_TIMEOUT),
_disconnect_notify_start(DEFAULT_DISCONNECT_NOTIFY_START)
{ {
_callbacks = *cb; _callbacks = *cb;
_synchronizing = true; _synchronizing = true;
@ -47,7 +48,7 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
_endpoints = new UdpProtocol[_num_players]; _endpoints = new UdpProtocol[_num_players];
memset(_local_connect_status, 0, sizeof(_local_connect_status)); memset(_local_connect_status, 0, sizeof(_local_connect_status));
for (int i = 0; i < ARRAY_SIZE(_local_connect_status); i++) { for (unsigned i = 0; i < ARRAY_SIZE(_local_connect_status); i++) {
_local_connect_status[i].last_frame = -1; _local_connect_status[i].last_frame = -1;
} }
@ -386,6 +387,9 @@ Peer2PeerBackend::OnUdpProtocolPeerEvent(UdpProtocol::Event &evt, int queue)
case UdpProtocol::Event::Disconnected: case UdpProtocol::Event::Disconnected:
DisconnectPlayer(QueueToPlayerHandle(queue)); DisconnectPlayer(QueueToPlayerHandle(queue));
break; break;
default:
break;
} }
} }
@ -396,17 +400,14 @@ Peer2PeerBackend::OnUdpProtocolSpectatorEvent(UdpProtocol::Event &evt, int queue
GGPOPlayerHandle handle = QueueToSpectatorHandle(queue); GGPOPlayerHandle handle = QueueToSpectatorHandle(queue);
OnUdpProtocolEvent(evt, handle); OnUdpProtocolEvent(evt, handle);
GGPOEvent info; if (evt.type == UdpProtocol::Event::Disconnected)
{
switch (evt.type) {
case UdpProtocol::Event::Disconnected:
_spectators[queue].Disconnect(); _spectators[queue].Disconnect();
GGPOEvent info;
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER; info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
info.u.disconnected.player = handle; info.u.disconnected.player = handle;
_callbacks.on_event(&info); _callbacks.on_event(&info);
break;
} }
} }
@ -448,6 +449,9 @@ Peer2PeerBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt, GGPOPlayerHandle h
info.u.connection_resumed.player = handle; info.u.connection_resumed.player = handle;
_callbacks.on_event(&info); _callbacks.on_event(&info);
break; break;
default:
break;
} }
} }

View File

@ -155,12 +155,17 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
break; break;
case UdpProtocol::Event::Input: case UdpProtocol::Event::Input:
GameInput& input = evt.u.input.input; {
GameInput& input = evt.u.input.input;
_host.SetLocalFrameNumber(input.frame); _host.SetLocalFrameNumber(input.frame);
_host.SendInputAck(); _host.SendInputAck();
_inputs[input.frame % SPECTATOR_FRAME_BUFFER_SIZE] = input; _inputs[input.frame % SPECTATOR_FRAME_BUFFER_SIZE] = input;
}
break; break;
default:
break;
} }
} }

View File

@ -60,12 +60,12 @@ void
Udp::Init(uint16 port, Poll *poll, Callbacks *callbacks) Udp::Init(uint16 port, Poll *poll, Callbacks *callbacks)
{ {
_callbacks = callbacks; _callbacks = callbacks;
poll->RegisterLoop(this);
_poll = poll;
_poll->RegisterLoop(this);
Log("binding udp socket to port %d.\n", port); Log("binding udp socket to port %d.\n", port);
_socket = CreateSocket(port, 0); _socket = CreateSocket(port, 0);
if (_socket == INVALID_SOCKET)
throw GGPOException("Socket creation or bind failed", GGPO_ERRORCODE_NETWORK_ERROR);
} }
void void

View File

@ -53,7 +53,6 @@ protected:
// state management // state management
Callbacks *_callbacks; Callbacks *_callbacks;
Poll *_poll;
}; };
#endif #endif

View File

@ -22,24 +22,24 @@ static const int UDP_SHUTDOWN_TIMER = 5000;
static const int MAX_SEQ_DISTANCE = (1 << 15); static const int MAX_SEQ_DISTANCE = (1 << 15);
UdpProtocol::UdpProtocol() : UdpProtocol::UdpProtocol() :
_local_frame_advantage(0), _udp(NULL),
_remote_frame_advantage(0),
_queue(-1),
_magic_number(0), _magic_number(0),
_queue(-1),
_remote_magic_number(0), _remote_magic_number(0),
_connected(false),
_packets_sent(0), _packets_sent(0),
_bytes_sent(0), _bytes_sent(0),
_stats_start_time(0), _stats_start_time(0),
_local_frame_advantage(0),
_remote_frame_advantage(0),
_last_send_time(0), _last_send_time(0),
_shutdown_timeout(0), _shutdown_timeout(0),
_disconnect_event_sent(false),
_disconnect_timeout(0), _disconnect_timeout(0),
_disconnect_notify_start(0), _disconnect_notify_start(0),
_disconnect_notify_sent(false), _disconnect_notify_sent(false),
_disconnect_event_sent(false),
_connected(false),
_next_send_seq(0), _next_send_seq(0),
_next_recv_seq(0), _next_recv_seq(0)
_udp(NULL)
{ {
_last_sent_input.init(-1, NULL, 1); _last_sent_input.init(-1, NULL, 1);
_last_received_input.init(-1, NULL, 1); _last_received_input.init(-1, NULL, 1);
@ -47,7 +47,7 @@ UdpProtocol::UdpProtocol() :
memset(&_state, 0, sizeof _state); memset(&_state, 0, sizeof _state);
memset(_peer_connect_status, 0, sizeof(_peer_connect_status)); memset(_peer_connect_status, 0, sizeof(_peer_connect_status));
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) { for (unsigned i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
_peer_connect_status[i].last_frame = -1; _peer_connect_status[i].last_frame = -1;
} }
memset(&_peer_addr, 0, sizeof _peer_addr); memset(&_peer_addr, 0, sizeof _peer_addr);
@ -123,7 +123,7 @@ UdpProtocol::SendPendingOutput()
msg->u.input.start_frame = _pending_output.front().frame; msg->u.input.start_frame = _pending_output.front().frame;
msg->u.input.input_size = (uint8)_pending_output.front().size; msg->u.input.input_size = (uint8)_pending_output.front().size;
ASSERT(last.frame == -1 || last.frame + 1 == msg->u.input.start_frame); ASSERT(last.frame == -1 || last.frame + 1 == (int)msg->u.input.start_frame);
for (j = 0; j < _pending_output.size(); j++) { for (j = 0; j < _pending_output.size(); j++) {
GameInput &current = _pending_output.item(j); GameInput &current = _pending_output.item(j);
if (memcmp(current.bits, last.bits, current.size) != 0) { if (memcmp(current.bits, last.bits, current.size) != 0) {
@ -249,7 +249,10 @@ UdpProtocol::OnLoopPoll(void *cookie)
_udp = NULL; _udp = NULL;
_shutdown_timeout = 0; _shutdown_timeout = 0;
} }
break;
default:
break;
} }
@ -455,11 +458,8 @@ UdpProtocol::LogMsg(const char *prefix, UdpMsg *msg)
void void
UdpProtocol::LogEvent(const char *prefix, const UdpProtocol::Event &evt) UdpProtocol::LogEvent(const char *prefix, const UdpProtocol::Event &evt)
{ {
switch (evt.type) { if (evt.type == UdpProtocol::Event::Synchronzied)
case UdpProtocol::Event::Synchronzied:
Log("%s (event: Synchronzied).\n", prefix); Log("%s (event: Synchronzied).\n", prefix);
break;
}
} }
bool bool
@ -545,7 +545,7 @@ UdpProtocol::OnInput(UdpMsg *msg, int len)
* of the network. * of the network.
*/ */
UdpMsg::connect_status* remote_status = msg->u.input.peer_connect_status; UdpMsg::connect_status* remote_status = msg->u.input.peer_connect_status;
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) { for (unsigned i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
ASSERT(remote_status[i].last_frame >= _peer_connect_status[i].last_frame); 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].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); _peer_connect_status[i].last_frame = MAX(_peer_connect_status[i].last_frame, remote_status[i].last_frame);
@ -734,7 +734,7 @@ UdpProtocol::PumpSendQueue()
// should really come up with a gaussian distributation based on the configured // should really come up with a gaussian distributation based on the configured
// value, but this will do for now. // value, but this will do for now.
int jitter = (_send_latency * 2 / 3) + ((rand() % _send_latency) / 3); int jitter = (_send_latency * 2 / 3) + ((rand() % _send_latency) / 3);
if (Platform::GetCurrentTimeMS() < _send_queue.front().queue_time + jitter) { if ((int)Platform::GetCurrentTimeMS() < _send_queue.front().queue_time + jitter) {
break; break;
} }
} }
@ -754,7 +754,7 @@ UdpProtocol::PumpSendQueue()
} }
_send_queue.pop(); _send_queue.pop();
} }
if (_oo_packet.msg && _oo_packet.send_time < Platform::GetCurrentTimeMS()) { if (_oo_packet.msg && _oo_packet.send_time < (int)Platform::GetCurrentTimeMS()) {
Log("sending rogue oop!"); Log("sending rogue oop!");
_udp->SendTo((char *)_oo_packet.msg, _oo_packet.msg->PacketSize(), 0, _udp->SendTo((char *)_oo_packet.msg, _oo_packet.msg->PacketSize(), 0,
(struct sockaddr *)&_oo_packet.dest_addr, sizeof _oo_packet.dest_addr); (struct sockaddr *)&_oo_packet.dest_addr, sizeof _oo_packet.dest_addr);

View File

@ -375,7 +375,7 @@ void startSession(int localPort, int localPlayerNum)
{ {
WARN_LOG(NETWORK, "GGPO start sync session failed: %d", result); WARN_LOG(NETWORK, "GGPO start sync session failed: %d", result);
ggpoSession = nullptr; ggpoSession = nullptr;
return; throw FlycastException("GGPO start sync session failed");
} }
ggpo_idle(ggpoSession, 0); ggpo_idle(ggpoSession, 0);
ggpo::localPlayerNum = localPlayerNum; ggpo::localPlayerNum = localPlayerNum;
@ -413,7 +413,7 @@ void startSession(int localPort, int localPlayerNum)
{ {
WARN_LOG(NETWORK, "GGPO start session failed: %d", result); WARN_LOG(NETWORK, "GGPO start session failed: %d", result);
ggpoSession = nullptr; ggpoSession = nullptr;
return; throw FlycastException("GGPO network initialization failed");
} }
// automatically disconnect clients after 3000 ms and start our count-down timer // automatically disconnect clients after 3000 ms and start our count-down timer
@ -429,7 +429,7 @@ void startSession(int localPort, int localPlayerNum)
{ {
WARN_LOG(NETWORK, "GGPO cannot add local player: %d", result); WARN_LOG(NETWORK, "GGPO cannot add local player: %d", result);
stopSession(); stopSession();
return; throw FlycastException("GGPO cannot add local player");
} }
ggpo_set_frame_delay(ggpoSession, localPlayer, config::GGPODelay.get()); ggpo_set_frame_delay(ggpoSession, localPlayer, config::GGPODelay.get());
@ -458,6 +458,7 @@ void startSession(int localPort, int localPlayerNum)
{ {
WARN_LOG(NETWORK, "GGPO cannot add remote player: %d", result); WARN_LOG(NETWORK, "GGPO cannot add remote player: %d", result);
stopSession(); stopSession();
throw FlycastException("GGPO cannot add remote player");
} }
DEBUG_LOG(NETWORK, "GGPO session started"); DEBUG_LOG(NETWORK, "GGPO session started");
#endif #endif
@ -616,11 +617,16 @@ std::future<bool> startNetwork()
miniupnp.Init(); miniupnp.Init();
miniupnp.AddPortMapping(SERVER_PORT, false); miniupnp.AddPortMapping(SERVER_PORT, false);
if (config::ActAsServer) try {
startSession(SERVER_PORT, 0); if (config::ActAsServer)
else startSession(SERVER_PORT, 0);
// Use SERVER_PORT-1 as local port if connecting to ourselves else
startSession(config::NetworkServer.get().empty() || config::NetworkServer.get() == "127.0.0.1" ? SERVER_PORT - 1 : SERVER_PORT, 1); // Use SERVER_PORT-1 as local port if connecting to ourselves
startSession(config::NetworkServer.get().empty() || config::NetworkServer.get() == "127.0.0.1" ? SERVER_PORT - 1 : SERVER_PORT, 1);
} catch (...) {
miniupnp.Term();
throw;
}
#endif #endif
} }
while (!synchronized && active()) while (!synchronized && active())

View File

@ -49,6 +49,7 @@ bool MiniUPnP::Init()
return false; return false;
} }
wanAddress[0] = 0; wanAddress[0] = 0;
initialized = true;
if (UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddress) != 0) if (UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddress) != 0)
WARN_LOG(NETWORK, "Cannot determine external IP address"); WARN_LOG(NETWORK, "Cannot determine external IP address");
DEBUG_LOG(NETWORK, "MiniUPnP: public IP is %s", wanAddress); DEBUG_LOG(NETWORK, "MiniUPnP: public IP is %s", wanAddress);
@ -57,12 +58,15 @@ bool MiniUPnP::Init()
void MiniUPnP::Term() void MiniUPnP::Term()
{ {
if (!initialized)
return;
DEBUG_LOG(NETWORK, "MiniUPnP::Term"); DEBUG_LOG(NETWORK, "MiniUPnP::Term");
for (const auto& port : mappedPorts) for (const auto& port : mappedPorts)
UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.first.c_str(), UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.first.c_str(),
port.second ? "TCP" : "UDP", nullptr); port.second ? "TCP" : "UDP", nullptr);
mappedPorts.clear(); mappedPorts.clear();
FreeUPNPUrls(&urls); FreeUPNPUrls(&urls);
initialized = false;
DEBUG_LOG(NETWORK, "MiniUPnP: terminated"); DEBUG_LOG(NETWORK, "MiniUPnP: terminated");
} }

View File

@ -43,6 +43,7 @@ private:
char lanAddress[32]; char lanAddress[32];
char wanAddress[32]; char wanAddress[32];
std::vector<std::pair<std::string, bool>> mappedPorts; std::vector<std::pair<std::string, bool>> mappedPorts;
bool initialized = false;
}; };
#else #else

View File

@ -2194,15 +2194,22 @@ static void gui_network_start()
if (networkStatus.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) if (networkStatus.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{ {
if (networkStatus.get()) try {
{ if (networkStatus.get())
gui_state = GuiState::Closed; {
ImGui::Text("STARTING..."); gui_state = GuiState::Closed;
} ImGui::Text("STARTING...");
else }
{ else
{
gui_state = GuiState::Main;
emu.unloadGame();
}
} catch (const FlycastException& e) {
NetworkHandshake::instance->stop();
gui_state = GuiState::Main; gui_state = GuiState::Main;
settings.content.path.clear(); emu.unloadGame();
error_msg = e.what();
} }
} }
else else
@ -2219,9 +2226,12 @@ static void gui_network_start()
if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f))) if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f)))
{ {
NetworkHandshake::instance->stop(); NetworkHandshake::instance->stop();
networkStatus.get(); try {
networkStatus.get();
} catch (const FlycastException& e) {
}
gui_state = GuiState::Main; gui_state = GuiState::Main;
settings.content.path.clear(); emu.unloadGame();
} }
ImGui::PopStyleVar(); ImGui::PopStyleVar();