Netplay: Use enet for GGPO packets

This commit is contained in:
Stenzek 2023-05-07 16:11:41 +10:00
parent 785b36ce5f
commit df5f87997e
20 changed files with 183 additions and 688 deletions

View File

@ -14,11 +14,9 @@
<ClInclude Include="src\game_input.h" />
<ClInclude Include="src\input_queue.h" />
<ClInclude Include="src\log.h" />
<ClInclude Include="src\network\udp.h" />
<ClInclude Include="src\network\udp_msg.h" />
<ClInclude Include="src\network\udp_proto.h" />
<ClInclude Include="src\platform_windows.h" />
<ClInclude Include="src\poll.h" />
<ClInclude Include="src\ring_buffer.h" />
<ClInclude Include="src\static_buffer.h" />
<ClInclude Include="src\sync.h" />
@ -34,10 +32,8 @@
<ClCompile Include="src\input_queue.cpp" />
<ClCompile Include="src\log.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\network\udp.cpp" />
<ClCompile Include="src\network\udp_proto.cpp" />
<ClCompile Include="src\platform_windows.cpp" />
<ClCompile Include="src\poll.cpp" />
<ClCompile Include="src\sync.cpp" />
<ClCompile Include="src\timesync.cpp" />
</ItemGroup>

View File

@ -6,9 +6,7 @@
<ClInclude Include="src\game_input.h" />
<ClInclude Include="src\input_queue.h" />
<ClInclude Include="src\log.h" />
<ClInclude Include="src\platform_linux.h" />
<ClInclude Include="src\platform_windows.h" />
<ClInclude Include="src\poll.h" />
<ClInclude Include="src\ring_buffer.h" />
<ClInclude Include="src\static_buffer.h" />
<ClInclude Include="src\sync.h" />
@ -32,9 +30,6 @@
<ClInclude Include="src\network\udp_proto.h">
<Filter>network</Filter>
</ClInclude>
<ClInclude Include="src\network\udp.h">
<Filter>network</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\bitvector.cpp" />
@ -42,9 +37,7 @@
<ClCompile Include="src\input_queue.cpp" />
<ClCompile Include="src\log.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\platform_linux.cpp" />
<ClCompile Include="src\platform_windows.cpp" />
<ClCompile Include="src\poll.cpp" />
<ClCompile Include="src\sync.cpp" />
<ClCompile Include="src\timesync.cpp" />
<ClCompile Include="src\backends\spectator.cpp">
@ -59,9 +52,6 @@
<ClCompile Include="src\network\udp_proto.cpp">
<Filter>network</Filter>
</ClCompile>
<ClCompile Include="src\network\udp.cpp">
<Filter>network</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="backends">

View File

@ -8,6 +8,8 @@
#ifndef _GGPONET_H_
#define _GGPONET_H_
#include "enet/enet.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -78,8 +80,7 @@ typedef struct GGPOPlayer {
struct {
} local;
struct {
char ip_address[32];
unsigned short port;
ENetPeer* peer;
} remote;
} u;
} GGPOPlayer;
@ -153,8 +154,7 @@ typedef enum {
GGPO_EVENTCODE_TIMESYNC = 1005,
GGPO_EVENTCODE_CONNECTION_INTERRUPTED = 1006,
GGPO_EVENTCODE_CONNECTION_RESUMED = 1007,
GGPO_EVENTCODE_CHAT = 1008,
GGPO_EVENTCODE_DESYNC = 1009
GGPO_EVENTCODE_DESYNC = 1008
} GGPOEventCode;
/*
@ -190,10 +190,6 @@ typedef struct {
struct {
GGPOPlayerHandle player;
} connection_resumed;
struct {
int senderID;
const char* msg;
} chat;
struct {
int nFrameOfDesync;
uint32_t ourCheckSum;
@ -480,25 +476,6 @@ GGPO_API GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *,
GGPOPlayerHandle player,
void *values,
int size);
/*
* ggpo_add_local_input --
*
* Used to notify GGPO.net of inputs that should be trasmitted to remote
* players. ggpo_add_local_input must be called once every frame for
* all player of type GGPO_PLAYERTYPE_LOCAL.
*
* player - The player handle returned for this player when you called
* ggpo_add_local_player.
*
* values - The controller inputs for this player.
*
* size - The size of the controller inputs. This must be exactly equal to the
* size passed into ggpo_start_session.
*/
GGPO_API GGPOErrorCode __cdecl ggpo_client_chat(GGPOSession *,
const char* message);
/*
* ggpo_synchronize_input --
*
@ -575,15 +552,6 @@ GGPO_API GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession *,
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *,
int timeout);
/*
* ggpo_enable_manual_network_polling --
*
* disables polling done by ggpo and it's expected that ggpo_poll_network will be used instead.
*
*/
GGPO_API GGPOErrorCode __cdecl ggpo_set_manual_network_polling(GGPOSession*,
bool value);
/*
* ggpo_set_disconnect_notify_start --
*
* The time to wait before the first GGPO_EVENTCODE_NETWORK_INTERRUPTED timeout
@ -594,14 +562,12 @@ GGPO_API GGPOErrorCode __cdecl ggpo_set_manual_network_polling(GGPOSession*,
*/
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *,
int timeout);
/*
* ggpo_poll_network --
*
* polls the network socket for any messages to be sent and recieved.
*
*/
GGPO_API GGPOErrorCode __cdecl ggpo_poll_network(GGPOSession*);
/*
* ENet packet processing
*/
GGPO_API GGPOErrorCode __cdecl ggpo_handle_packet(GGPOSession*,
ENetPeer* peer, const ENetPacket* pkt);
/*
* ggpo_log --

View File

@ -20,16 +20,14 @@ public:
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags) = 0;
virtual GGPOErrorCode IncrementFrame(uint16_t checksum) = 0;
virtual GGPOErrorCode CurrentFrame(int& current) =0;
virtual GGPOErrorCode Chat(const char* text) = 0;// { return GGPO_OK; }
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) = 0;// { return GGPO_OK; }
virtual GGPOErrorCode PollNetwork() = 0;
virtual GGPOErrorCode GetNetworkStats(GGPONetworkStats *stats, GGPOPlayerHandle handle) { return GGPO_OK; }
virtual GGPOErrorCode Logv(const char *fmt, va_list list) { ::Logv(fmt, list); return GGPO_OK; }
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetManualNetworkPolling(bool value) = 0;
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) = 0;
};

View File

@ -27,7 +27,6 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
_callbacks = *cb;
_synchronizing = true;
_next_recommended_sleep = 0;
_manual_network_polling = false;
/*
* Initialize the synchronziation layer
@ -39,11 +38,6 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
config.num_prediction_frames = nframes;
_sync.Init(config);
/*
* Initialize the UDP port
*/
_udp.Init(localport, &_poll, this);
_endpoints.resize(_num_players);
memset(_local_connect_status, 0, sizeof(_local_connect_status));
for (int i = 0; i < ARRAY_SIZE(_local_connect_status); i++) {
@ -61,23 +55,20 @@ Peer2PeerBackend::~Peer2PeerBackend()
}
void
Peer2PeerBackend::AddRemotePlayer(char *ip,
uint16 port,
int queue)
Peer2PeerBackend::AddRemotePlayer(ENetPeer* peer, int queue)
{
/*
* Start the state machine (xxx: no)
*/
_synchronizing = true;
_endpoints[queue].Init(&_udp, _poll, queue, ip, port, _local_connect_status);
_endpoints[queue].Init(peer, queue, _local_connect_status);
_endpoints[queue].SetDisconnectTimeout(_disconnect_timeout);
_endpoints[queue].SetDisconnectNotifyStart(_disconnect_notify_start);
_endpoints[queue].Synchronize();
}
GGPOErrorCode Peer2PeerBackend::AddSpectator(char *ip,
uint16 port)
GGPOErrorCode Peer2PeerBackend::AddSpectator(ENetPeer* peer)
{
if (_num_spectators == GGPO_MAX_SPECTATORS) {
return GGPO_ERRORCODE_TOO_MANY_SPECTATORS;
@ -90,7 +81,7 @@ GGPOErrorCode Peer2PeerBackend::AddSpectator(char *ip,
}
int queue = _num_spectators++;
_spectators[queue].Init(&_udp, _poll, queue + 1000, ip, port, _local_connect_status);
_spectators[queue].Init(peer, queue + 1000, _local_connect_status);
_spectators[queue].SetDisconnectTimeout(_disconnect_timeout);
_spectators[queue].SetDisconnectNotifyStart(_disconnect_notify_start);
_spectators[queue].Synchronize();
@ -155,20 +146,10 @@ void Peer2PeerBackend::CheckDesync()
GGPOErrorCode
Peer2PeerBackend::DoPoll()
{
// Pass on chat
for (int i = 0; i < _num_players; i++) {
_endpoints[i].ConsumeChat([&](const char* msg) {
GGPOEvent info;
info.u.chat.senderID = i;
info.code = GGPO_EVENTCODE_CHAT;
info.u.chat.msg = msg;
_callbacks.on_event(_callbacks.context, &info);
});
}
if (!_sync.InRollback()) {
if (!_manual_network_polling)
_poll.Pump(0);
if (!_sync.InRollback())
{
for (UdpProtocol& udp : _endpoints)
udp.OnLoopPoll();
PollUdpProtocolEvents();
CheckDesync();
@ -309,7 +290,7 @@ Peer2PeerBackend::AddPlayer(GGPOPlayer *player,
GGPOPlayerHandle *handle)
{
if (player->type == GGPO_PLAYERTYPE_SPECTATOR) {
return AddSpectator(player->u.remote.ip_address, player->u.remote.port);
return AddSpectator(player->u.remote.peer);
}
int queue = player->player_num - 1;
@ -319,7 +300,7 @@ Peer2PeerBackend::AddPlayer(GGPOPlayer *player,
*handle = QueueToPlayerHandle(queue);
if (player->type == GGPO_PLAYERTYPE_REMOTE) {
AddRemotePlayer(player->u.remote.ip_address, player->u.remote.port, queue);
AddRemotePlayer(player->u.remote.peer, queue);
}
return GGPO_OK;
}
@ -413,19 +394,6 @@ Peer2PeerBackend::CurrentFrame(int& current)
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::PollNetwork()
{
_poll.Pump(0);
return GGPO_OK;
}
GGPOErrorCode Peer2PeerBackend::SetManualNetworkPolling(bool value)
{
_manual_network_polling = value;
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::IncrementFrame(uint16_t checksum1)
{
@ -766,21 +734,6 @@ Peer2PeerBackend::SetDisconnectNotifyStart(int timeout)
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::Chat(const char* text)
{
if (strlen(text) >= MAX_CHAT_LENGTH)
return GGPO_CHAT_MESSAGE_TOO_LONG;
// Send the input to all the remote players.
for (int i = 0; i < _num_players; i++) {
if (_endpoints[i].IsInitialized()) {
_endpoints[i].SendChat(text);
}
}
return GGPO_OK;
}
GGPOErrorCode
Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
{
@ -793,21 +746,27 @@ Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
}
void
Peer2PeerBackend::OnMsg(sockaddr_in &from, UdpMsg *msg, int len)
GGPOErrorCode
Peer2PeerBackend::OnPacket(ENetPeer* peer, const ENetPacket* pkt)
{
// ugh why is const so hard for some people...
UdpMsg* msg = const_cast<UdpMsg*>(reinterpret_cast<const UdpMsg*>(pkt->data));
const int len = static_cast<int>(pkt->dataLength);
for (int i = 0; i < _num_players; i++) {
if (_endpoints[i].HandlesMsg(from, msg)) {
if (_endpoints[i].GetENetPeer() == peer) {
_endpoints[i].OnMsg(msg, len);
return;
return GGPO_OK;
}
}
for (int i = 0; i < _num_spectators; i++) {
if (_spectators[i].HandlesMsg(from, msg)) {
if (_spectators[i].GetENetPeer() == peer) {
_spectators[i].OnMsg(msg, len);
return;
return GGPO_OK;
}
}
return GGPO_ERRORCODE_INVALID_PLAYER_HANDLE;
}
void

View File

@ -9,13 +9,13 @@
#define _P2P_H
#include "types.h"
#include "poll.h"
#include "sync.h"
#include "backend.h"
#include "timesync.h"
#include "network/udp_proto.h"
#include <map>
class Peer2PeerBackend : public GGPOSession, Udp::Callbacks {
class Peer2PeerBackend final : public GGPOSession
{
public:
Peer2PeerBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, int nframes);
virtual ~Peer2PeerBackend();
@ -32,13 +32,8 @@ public:
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) override;
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) override;
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout) override;
virtual GGPOErrorCode Chat(const char* text) override;
virtual GGPOErrorCode CurrentFrame(int& current) override;
virtual GGPOErrorCode PollNetwork() override;
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override;
public:
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len);
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override;
protected:
GGPOErrorCode PlayerHandleToQueue(GGPOPlayerHandle player, int *queue);
@ -50,8 +45,8 @@ protected:
void CheckInitialSync(void);
int Poll2Players(int current_frame);
int PollNPlayers(int current_frame);
void AddRemotePlayer(char *remoteip, uint16 reportport, int queue);
GGPOErrorCode AddSpectator(char *remoteip, uint16 reportport);
void AddRemotePlayer(ENetPeer* peer, int queue);
GGPOErrorCode AddSpectator(ENetPeer* peer);
virtual void OnSyncEvent(Sync::Event &e) { }
virtual void OnUdpProtocolEvent(UdpProtocol::Event &e, GGPOPlayerHandle handle);
virtual void OnUdpProtocolPeerEvent(UdpProtocol::Event &e, int queue);
@ -59,9 +54,7 @@ protected:
protected:
GGPOSessionCallbacks _callbacks;
Poll _poll;
Sync _sync;
Udp _udp;
std::vector<UdpProtocol> _endpoints;
UdpProtocol _spectators[GGPO_MAX_SPECTATORS];
int _num_spectators;
@ -75,8 +68,6 @@ protected:
int _disconnect_timeout;
int _disconnect_notify_start;
bool _manual_network_polling;
UdpMsg::connect_status _local_connect_status[UDP_MSG_MAX_PLAYERS];
struct ChecksumEntry {
int nFrame;

View File

@ -20,7 +20,6 @@ SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
{
_callbacks = *cb;
_synchronizing = true;
_manual_network_polling = false;
for (int i = 0; i < ARRAY_SIZE(_inputs); i++) {
_inputs[i].frame = -1;
@ -29,12 +28,14 @@ SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
/*
* Initialize the UDP port
*/
_udp.Init(localport, &_poll, this);
// FIXME
abort();
//_udp.Init(localport, &_poll, this);
/*
* Init the host endpoint
*/
_host.Init(&_udp, _poll, 0, hostip, hostport, NULL);
//_host.Init(&_udp, _poll, 0, hostip, hostport, NULL);
_host.Synchronize();
/*
@ -50,9 +51,6 @@ SpectatorBackend::~SpectatorBackend()
GGPOErrorCode
SpectatorBackend::DoPoll()
{
if (!_manual_network_polling)
_poll.Pump(0);
PollUdpProtocolEvents();
return GGPO_OK;
}
@ -95,20 +93,6 @@ SpectatorBackend::CurrentFrame(int& current)
return GGPO_OK;
}
GGPOErrorCode
SpectatorBackend::PollNetwork()
{
_poll.Pump(0);
return GGPO_OK;
}
GGPOErrorCode
SpectatorBackend::SetManualNetworkPolling(bool value)
{
_manual_network_polling = value;
return GGPO_OK;
}
GGPOErrorCode
SpectatorBackend::IncrementFrame(uint16_t checksum)
{
@ -188,11 +172,14 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
}
}
void
SpectatorBackend::OnMsg(sockaddr_in &from, UdpMsg *msg, int len)
GGPOErrorCode SpectatorBackend::OnPacket(ENetPeer* peer, const ENetPacket* pkt)
{
if (_host.HandlesMsg(from, msg)) {
_host.OnMsg(msg, len);
}
if (_host.GetENetPeer() != peer)
return GGPO_ERRORCODE_INVALID_PLAYER_HANDLE;
UdpMsg* msg = const_cast<UdpMsg*>(reinterpret_cast<const UdpMsg*>(pkt->data));
const int len = static_cast<int>(pkt->dataLength);
_host.OnMsg(msg, len);
return GGPO_OK;
}

View File

@ -9,7 +9,6 @@
#define _SPECTATOR_H
#include "types.h"
#include "poll.h"
#include "sync.h"
#include "backend.h"
#include "timesync.h"
@ -17,7 +16,7 @@
#define SPECTATOR_FRAME_BUFFER_SIZE 64
class SpectatorBackend : public GGPOSession, Udp::Callbacks {
class SpectatorBackend final : public GGPOSession {
public:
SpectatorBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, char *hostip, u_short hostport);
virtual ~SpectatorBackend();
@ -34,13 +33,8 @@ public:
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode Chat(const char* text) override { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode CurrentFrame(int& current) override;
virtual GGPOErrorCode PollNetwork() override;
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override;
public:
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len);
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override;
protected:
void PollUdpProtocolEvents(void);
@ -50,15 +44,12 @@ protected:
protected:
GGPOSessionCallbacks _callbacks;
Poll _poll;
Udp _udp;
UdpProtocol _host;
bool _synchronizing;
int _input_size;
int _num_players;
int _next_input_to_send;
GameInput _inputs[SPECTATOR_FRAME_BUFFER_SIZE];
bool _manual_network_polling;
};
#endif

View File

@ -13,7 +13,7 @@
#include "sync.h"
#include "ring_buffer.h"
class SyncTestBackend : public GGPOSession {
class SyncTestBackend final : public GGPOSession {
public:
SyncTestBackend(GGPOSessionCallbacks *cb, char *gamename, int frames, int num_players);
virtual ~SyncTestBackend();
@ -25,10 +25,8 @@ public:
virtual GGPOErrorCode IncrementFrame(uint16_t checksum);
virtual GGPOErrorCode Logv(char *fmt, va_list list);
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) { return GGPO_OK; }
virtual GGPOErrorCode Chat(const char* text) override { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode CurrentFrame(int& current) override;
virtual GGPOErrorCode PollNetwork() override { return GGPO_OK; };
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override { return GGPO_OK; };
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override { return GGPO_ERRORCODE_UNSUPPORTED; }
protected:
struct SavedInfo {

View File

@ -151,15 +151,6 @@ ggpo_get_current_frame(GGPOSession *ggpo, int& nFrame)
return ggpo->CurrentFrame(nFrame);
}
GGPOErrorCode
ggpo_client_chat(GGPOSession *ggpo, const char *text)
{
if (!ggpo) {
return GGPO_ERRORCODE_INVALID_SESSION;
}
return ggpo->Chat(text);
}
GGPOErrorCode
ggpo_get_network_stats(GGPOSession *ggpo,
GGPOPlayerHandle player,
@ -172,6 +163,13 @@ ggpo_get_network_stats(GGPOSession *ggpo,
}
GGPOErrorCode ggpo_handle_packet(GGPOSession* ggpo, ENetPeer* peer, const ENetPacket* pkt)
{
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
return ggpo->OnPacket(peer, pkt);
}
GGPOErrorCode
ggpo_close_session(GGPOSession *ggpo)
{
@ -191,16 +189,6 @@ ggpo_set_disconnect_timeout(GGPOSession *ggpo, int timeout)
return ggpo->SetDisconnectTimeout(timeout);
}
GGPOErrorCode
ggpo_set_manual_network_polling(GGPOSession *ggpo, bool value)
{
if (!ggpo)
{
return GGPO_ERRORCODE_INVALID_SESSION;
}
return ggpo->SetManualNetworkPolling(value);
}
GGPOErrorCode
ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
{
@ -210,16 +198,6 @@ ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
return ggpo->SetDisconnectNotifyStart(timeout);
}
GGPOErrorCode
ggpo_poll_network(GGPOSession* ggpo)
{
if (!ggpo)
{
return GGPO_ERRORCODE_INVALID_SESSION;
}
return ggpo->PollNetwork();
}
GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
GGPOSessionCallbacks *cb,
const char *game,
@ -238,4 +216,3 @@ GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
host_port);
return GGPO_OK;
}

View File

@ -1,125 +0,0 @@
/* -----------------------------------------------------------------------
* GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC.
*
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#include "types.h"
#include "udp.h"
SOCKET
CreateSocket(uint16 bind_port, int retries)
{
SOCKET s;
sockaddr_in sin;
uint16 port;
int optval = 1;
s = socket(AF_INET, SOCK_DGRAM, 0);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof optval);
setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (const char *)&optval, sizeof optval);
// non-blocking...
u_long iMode = 1;
ioctlsocket(s, FIONBIO, &iMode);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
for (port = bind_port; port <= bind_port + retries; port++) {
sin.sin_port = htons(port);
if (bind(s, (sockaddr *)&sin, sizeof sin) != SOCKET_ERROR) {
Log("Udp bound to port: %d.\n", port);
return s;
}
}
closesocket(s);
return INVALID_SOCKET;
}
Udp::Udp() :
_socket(INVALID_SOCKET),
_callbacks(NULL)
{
}
Udp::~Udp(void)
{
if (_socket != INVALID_SOCKET) {
closesocket(_socket);
_socket = INVALID_SOCKET;
}
}
void
Udp::Init(uint16 port, Poll *poll, Callbacks *callbacks)
{
_callbacks = callbacks;
_poll = poll;
_poll->RegisterLoop(this);
Log("binding udp socket to port %d.\n", port);
_socket = CreateSocket(port, 0);
}
void
Udp::SendTo(char *buffer, int len, int flags, struct sockaddr *dst, int destlen)
{
struct sockaddr_in *to = (struct sockaddr_in *)dst;
int res = sendto(_socket, buffer, len, flags, dst, destlen);
if (res == SOCKET_ERROR) {
DWORD err = WSAGetLastError();
Log("unknown error in sendto (erro: %d wsaerr: %d).\n", res, err);
ASSERT(FALSE && "Unknown error in sendto");
}
char dst_ip[1024];
Log("sent packet length %d to %s:%d (ret:%d).\n", len, inet_ntop(AF_INET, (void *)&to->sin_addr, dst_ip, ARRAY_SIZE(dst_ip)), ntohs(to->sin_port), res);
}
bool
Udp::OnLoopPoll(void *cookie)
{
uint8 recv_buf[MAX_UDP_PACKET_SIZE];
sockaddr_in recv_addr;
int recv_addr_len;
for (;;) {
recv_addr_len = sizeof(recv_addr);
int len = recvfrom(_socket, (char *)recv_buf, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&recv_addr, &recv_addr_len);
// TODO: handle len == 0... indicates a disconnect.
if (len == -1) {
int error = WSAGetLastError();
if (error != WSAEWOULDBLOCK) {
Log("recvfrom WSAGetLastError returned %d (%x).\n", error, error);
}
break;
} else if (len > 0) {
char src_ip[1024];
Log("recvfrom returned (len:%d from:%s:%d).\n", len, inet_ntop(AF_INET, (void*)&recv_addr.sin_addr, src_ip, ARRAY_SIZE(src_ip)), ntohs(recv_addr.sin_port) );
UdpMsg *msg = (UdpMsg *)recv_buf;
_callbacks->OnMsg(recv_addr, msg, len);
}
}
return true;
}
void
Udp::Log(const char *fmt, ...)
{
char buf[1024];
size_t offset;
va_list args;
strcpy_s(buf, "udp | ");
offset = strlen(buf);
va_start(args, fmt);
vsnprintf(buf + offset, ARRAY_SIZE(buf) - offset - 1, fmt, args);
buf[ARRAY_SIZE(buf)-1] = '\0';
::Log(buf);
va_end(args);
}

View File

@ -1,61 +0,0 @@
/* -----------------------------------------------------------------------
* GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC.
*
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#ifndef _UDP_H
#define _UDP_H
#include "poll.h"
#include "udp_msg.h"
#include "ggponet.h"
#include "ring_buffer.h"
#define MAX_UDP_ENDPOINTS 16
static const int MAX_UDP_PACKET_SIZE = 4096;
class Udp : public IPollSink
{
public:
struct Stats {
int bytes_sent;
int packets_sent;
float kbps_sent;
};
struct Callbacks {
virtual ~Callbacks() { }
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len) = 0;
};
protected:
void Log(const char *fmt, ...);
public:
Udp();
void Init(uint16 port, Poll *p, Callbacks *callbacks);
void SendTo(char *buffer, int len, int flags, struct sockaddr *dst, int destlen);
bool OnLoopPoll(void *cookie) override;
public:
~Udp(void);
protected:
// Network transmission information
SOCKET _socket;
// state management
Callbacks *_callbacks;
Poll *_poll;
};
#endif

View File

@ -10,7 +10,6 @@
#define MAX_COMPRESSED_BITS 4096
#define UDP_MSG_MAX_PLAYERS 4
#define MAX_CHAT_LENGTH 120
#pragma pack(push, 1)
struct UdpMsg
@ -24,7 +23,6 @@ struct UdpMsg
QualityReply = 5,
KeepAlive = 6,
InputAck = 7,
Chat = 8,
};
struct connect_status {
@ -75,9 +73,6 @@ struct UdpMsg
struct {
int ack_frame:31;
} input_ack;
struct {
char msg[MAX_CHAT_LENGTH];
} chat;
} u;
public:
@ -94,7 +89,6 @@ public:
case QualityReport: return sizeof(u.quality_report);
case QualityReply: return sizeof(u.quality_reply);
case InputAck: return sizeof(u.input_ack);
case Chat: return MAX_CHAT_LENGTH;
case KeepAlive: return 0;
case Input:
size = (int)((char *)&u.input.bits - (char *)&u.input);

View File

@ -21,6 +21,8 @@ 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;
UdpProtocol::UdpProtocol() :
_local_frame_advantage(0),
_remote_frame_advantage(0),
@ -39,7 +41,7 @@ UdpProtocol::UdpProtocol() :
_connected(false),
_next_send_seq(0),
_next_recv_seq(0),
_udp(NULL)
_peer(nullptr)
{
_last_sent_input.init(-1, NULL, 1);
_last_received_input.init(-1, NULL, 1);
@ -50,17 +52,12 @@ UdpProtocol::UdpProtocol() :
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
_peer_connect_status[i].last_frame = -1;
}
memset(&_peer_addr, 0, sizeof _peer_addr);
_oo_packet.msg = NULL;
_send_latency = Platform::GetConfigInt("ggpo.network.delay");
_oop_percent = Platform::GetConfigInt("ggpo.oop.percent");
}
UdpProtocol::~UdpProtocol()
{
ClearSendQueue();
}
void UdpProtocol::SetFrameDelay(int delay)
{
_timesync.SetFrameDelay(delay);
@ -70,32 +67,22 @@ int UdpProtocol::RemoteFrameDelay()const
{
return _timesync._remoteFrameDelay;
}
void
UdpProtocol::Init(Udp *udp,
Poll &poll,
int queue,
char *ip,
u_short port,
UdpMsg::connect_status *status)
void UdpProtocol::Init(ENetPeer* peer, int queue, UdpMsg::connect_status *status)
{
_udp = udp;
_peer = peer;
_queue = queue;
_local_connect_status = status;
_peer_addr.sin_family = AF_INET;
_peer_addr.sin_port = htons(port);
inet_pton(AF_INET, ip, &_peer_addr.sin_addr.s_addr);
do {
_magic_number = (uint16)rand();
} while (_magic_number == 0);
poll.RegisterLoop(this);
}
void
UdpProtocol::SendInput(GameInput &input)
{
if (_udp) {
if (_peer) {
if (_current_state == Running) {
/*
* Check to see if this is a good time to adjust for the rift...
@ -205,24 +192,17 @@ void UdpProtocol::EndPollLoop()
if (_remoteCheckSumsThisFrame.size())
_remoteCheckSums.emplace(*_remoteCheckSumsThisFrame.rbegin());
}
void UdpProtocol::SendChat(const char* message)
{
UdpMsg* msg = new UdpMsg(UdpMsg::Chat);
strcpy_s<MAX_CHAT_LENGTH>(msg->u.chat.msg, message);
SendMsg(msg);
}
bool
UdpProtocol::OnLoopPoll(void *cookie)
UdpProtocol::OnLoopPoll()
{
if (!_udp) {
if (!_peer) {
return true;
}
unsigned int now = Platform::GetCurrentTimeMS();
unsigned int next_interval;
PumpSendQueue();
switch (_current_state) {
case Syncing:
next_interval = (_state.sync.roundtrips_remaining == NUM_SYNC_PACKETS) ? SYNC_FIRST_RETRY_INTERVAL : SYNC_RETRY_INTERVAL;
@ -255,11 +235,13 @@ UdpProtocol::OnLoopPoll(void *cookie)
_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);
@ -281,7 +263,8 @@ UdpProtocol::OnLoopPoll(void *cookie)
case Disconnected:
if (_shutdown_timeout < now) {
Log("Shutting down udp connection.\n");
_udp = NULL;
abort();
_peer = NULL;
_shutdown_timeout = 0;
}
@ -311,28 +294,21 @@ UdpProtocol::SendSyncRequest()
void
UdpProtocol::SendMsg(UdpMsg *msg)
{
const int size = msg->PacketSize();
LogMsg("send", msg);
_packets_sent++;
_last_send_time = Platform::GetCurrentTimeMS();
_bytes_sent += msg->PacketSize();
_bytes_sent += size;
msg->hdr.magic = _magic_number;
msg->hdr.sequence_number = _next_send_seq++;
_send_queue.push(QueueEntry(Platform::GetCurrentTimeMS(), _peer_addr, msg));
PumpSendQueue();
}
bool
UdpProtocol::HandlesMsg(sockaddr_in &from,
UdpMsg *msg)
{
if (!_udp) {
return false;
}
return _peer_addr.sin_addr.S_un.S_addr == from.sin_addr.S_un.S_addr &&
_peer_addr.sin_port == from.sin_port;
ENetPacket* pkt = enet_packet_create(msg, size, 0);
enet_peer_send(_peer, ENET_CHANNEL_ID, pkt);
// TODO: flush packets?
// TODO: get rid of the extra heap allocation here...
delete msg;
}
void
@ -349,7 +325,6 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
&UdpProtocol::OnQualityReply, /* QualityReply */
&UdpProtocol::OnKeepAlive, /* KeepAlive */
&UdpProtocol::OnInputAck, /* InputAck */
&UdpProtocol::OnChat, /* InputAck */
};
// filter out messages that don't match what we expect
@ -422,7 +397,7 @@ UdpProtocol::QueueEvent(const UdpProtocol::Event &evt)
void
UdpProtocol::Synchronize()
{
if (_udp) {
if (_peer) {
_current_state = Syncing;
_state.sync.roundtrips_remaining = NUM_SYNC_PACKETS;
SendSyncRequest();
@ -479,9 +454,6 @@ UdpProtocol::LogMsg(const char *prefix, UdpMsg *msg)
case UdpMsg::InputAck:
Log("%s input ack.\n", prefix);
break;
case UdpMsg::Chat:
Log("%s chat.\n", prefix);
break;
default:
Log("Unknown UdpMsg type.");
}
@ -706,17 +678,6 @@ UdpProtocol::OnKeepAlive(UdpMsg *msg, int len)
{
return true;
}
void UdpProtocol::ConsumeChat(std::function<void(const char*)> onChat)
{
for (const auto& msg : _chatMessages)
onChat(msg.c_str());
_chatMessages.clear();
}
bool UdpProtocol::OnChat(UdpMsg* msg, int len)
{
_chatMessages.push_back(msg->u.chat.msg);
return true;
}
void
UdpProtocol::GetNetworkStats(struct GGPONetworkStats *s)
@ -768,52 +729,3 @@ UdpProtocol::SetDisconnectNotifyStart(int timeout)
{
_disconnect_notify_start = timeout;
}
void
UdpProtocol::PumpSendQueue()
{
while (!_send_queue.empty()) {
QueueEntry &entry = _send_queue.front();
if (_send_latency) {
// should really come up with a gaussian distributation based on the configured
// value, but this will do for now.
int jitter = (_send_latency * 2 / 3) + ((rand() % _send_latency) / 3);
if (Platform::GetCurrentTimeMS() < _send_queue.front().queue_time + jitter) {
break;
}
}
if (_oop_percent && !_oo_packet.msg && ((rand() % 100) < _oop_percent)) {
int delay = rand() % (_send_latency * 10 + 1000);
Log("creating rogue oop (seq: %d delay: %d)\n", entry.msg->hdr.sequence_number, delay);
_oo_packet.send_time = Platform::GetCurrentTimeMS() + delay;
_oo_packet.msg = entry.msg;
_oo_packet.dest_addr = entry.dest_addr;
} else {
ASSERT(entry.dest_addr.sin_addr.s_addr);
_udp->SendTo((char *)entry.msg, entry.msg->PacketSize(), 0,
(struct sockaddr *)&entry.dest_addr, sizeof entry.dest_addr);
delete entry.msg;
}
_send_queue.pop();
}
if (_oo_packet.msg && _oo_packet.send_time < Platform::GetCurrentTimeMS()) {
Log("sending rogue oop!");
_udp->SendTo((char *)_oo_packet.msg, _oo_packet.msg->PacketSize(), 0,
(struct sockaddr *)&_oo_packet.dest_addr, sizeof _oo_packet.dest_addr);
delete _oo_packet.msg;
_oo_packet.msg = NULL;
}
}
void
UdpProtocol::ClearSendQueue()
{
while (!_send_queue.empty()) {
delete _send_queue.front().msg;
_send_queue.pop();
}
}

View File

@ -8,8 +8,7 @@
#ifndef _UDP_PROTO_H_
#define _UDP_PROTO_H_
#include "poll.h"
#include "udp.h"
#include "enet/enet.h"
#include "udp_msg.h"
#include "game_input.h"
#include "timesync.h"
@ -19,7 +18,7 @@
#include <vector>
#include <string>
#include <map>
class UdpProtocol : public IPollSink
class UdpProtocol
{
public:
struct Stats {
@ -29,7 +28,6 @@ public:
float av_remote_frame_advantage;
float av_local_frame_advantage;
int send_queue_len;
Udp::Stats udp;
};
struct Event {
@ -62,23 +60,23 @@ public:
};
public:
virtual bool OnLoopPoll(void *cookie);
bool OnLoopPoll();
public:
UdpProtocol();
virtual ~UdpProtocol();
~UdpProtocol();
void Init(Udp *udp, Poll &p, int queue, char *ip, u_short port, UdpMsg::connect_status *status);
ENetPeer* GetENetPeer() const { return _peer; }
void Init(ENetPeer* peer, int queue, UdpMsg::connect_status *status);
void Synchronize();
bool GetPeerConnectStatus(int id, int *frame);
bool IsInitialized() { return _udp != NULL; }
bool IsInitialized() { return _peer != nullptr; }
bool IsSynchronized() { return _current_state == Running; }
bool IsRunning() { return _current_state == Running; }
void SendInput(GameInput &input);
void SendChat(const char* message);
void SendInputAck();
bool HandlesMsg(sockaddr_in &from, UdpMsg *msg);
void OnMsg(UdpMsg *msg, int len);
void Disconnect();
@ -90,7 +88,6 @@ public:
void SetDisconnectTimeout(int timeout);
void SetDisconnectNotifyStart(int timeout);
void SetFrameDelay(int delay);
void ConsumeChat(std::function<void(const char*)> onChat);
void ApplyToEvents(std::function<void(UdpProtocol::Event&)> cb);
void StartPollLoop();
void EndPollLoop();
@ -114,13 +111,11 @@ protected:
void UpdateNetworkStats(void);
void QueueEvent(const UdpProtocol::Event &evt);
void ClearSendQueue(void);
void Log(const char *fmt, ...);
void LogMsg(const char *prefix, UdpMsg *msg);
void LogEvent(const char *prefix, const UdpProtocol::Event &evt);
void SendSyncRequest();
void SendMsg(UdpMsg *msg);
void PumpSendQueue();
void SendPendingOutput();
bool OnInvalid(UdpMsg *msg, int len);
bool OnSyncRequest(UdpMsg *msg, int len);
@ -130,26 +125,16 @@ protected:
bool OnQualityReport(UdpMsg *msg, int len);
bool OnQualityReply(UdpMsg *msg, int len);
bool OnKeepAlive(UdpMsg *msg, int len);
bool OnChat(UdpMsg *msg, int len);
protected:
/*
* Network transmission information
*/
Udp *_udp;
sockaddr_in _peer_addr;
ENetPeer *_peer;
uint16 _magic_number;
int _queue;
uint16 _remote_magic_number;
bool _connected;
int _send_latency;
int _oop_percent;
struct {
int send_time;
sockaddr_in dest_addr;
UdpMsg* msg;
} _oo_packet;
RingBuffer<QueueEntry, 64> _send_queue;
/*
* Stats
@ -212,7 +197,6 @@ protected:
* Event queue
*/
RingBuffer<UdpProtocol::Event, 64> _event_queue;
std::vector<std::string> _chatMessages;
};
#endif

View File

@ -1,72 +0,0 @@
/* -----------------------------------------------------------------------
* GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC.
*
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#include "types.h"
#include "poll.h"
Poll::Poll(void) :
_start_time(0)
{
}
//
//void
//Poll::RegisterMsgLoop(IPollSink *sink, void *cookie)
//{
// _msg_sinks.push_back(PollSinkCb(sink, cookie));
//}
void
Poll::RegisterLoop(IPollSink *sink, void *cookie)
{
_loop_sinks.push_back(PollSinkCb(sink, cookie));
}
//void
//Poll::RegisterPeriodic(IPollSink *sink, int interval, void *cookie)
//{
// _periodic_sinks.push_back(PollPeriodicSinkCb(sink, cookie, interval));
//}
void
Poll::Run()
{
while (Pump(100)) {
continue;
}
}
bool
Poll::Pump(int timeout)
{
int i;
bool finished = false;
for (i = 0; i < _loop_sinks.size(); i++) {
PollSinkCb &cb = _loop_sinks[i];
finished = !cb.sink->OnLoopPoll(cb.cookie) || finished;
}
return finished;
}
//
//int
//Poll::ComputeWaitTime(int elapsed)
//{
// int waitTime = INFINITE;
// size_t count = _periodic_sinks.size();
//
// if (count > 0) {
// for (int i = 0; i < count; i++) {
// PollPeriodicSinkCb &cb = _periodic_sinks[i];
// int timeout = (cb.interval + cb.last_fired) - elapsed;
// if (waitTime == INFINITE || (timeout < waitTime)) {
// waitTime = MAX(timeout, 0);
// }
// }
// }
// return waitTime;
//}

View File

@ -1,56 +0,0 @@
/* -----------------------------------------------------------------------
* GGPO.net (http://ggpo.net) - Copyright 2009 GroundStorm Studios, LLC.
*
* Use of this software is governed by the MIT license that can be found
* in the LICENSE file.
*/
#ifndef _POLL_H
#define _POLL_H
#include "static_buffer.h"
#define MAX_POLLABLE_HANDLES 64
class IPollSink {
public:
virtual ~IPollSink() { }
//virtual bool OnMsgPoll(void*) = 0;//{ return true; }
// virtual bool OnPeriodicPoll(void*, int) = 0;// { return true; }
virtual bool OnLoopPoll(void*) = 0;// { return true; }
};
class Poll {
public:
Poll(void);
void RegisterLoop(IPollSink *sink, void *cookie = NULL);
void Run();
bool Pump(int timeout);
protected:
//int ComputeWaitTime(int elapsed);
struct PollSinkCb {
IPollSink *sink;
void *cookie;
PollSinkCb() : sink(NULL), cookie(NULL) { }
PollSinkCb(IPollSink *s, void *c) : sink(s), cookie(c) { }
};
struct PollPeriodicSinkCb : public PollSinkCb {
int interval;
int last_fired;
PollPeriodicSinkCb() : PollSinkCb(NULL, NULL), interval(0), last_fired(0) { }
PollPeriodicSinkCb(IPollSink *s, void *c, int i) :
PollSinkCb(s, c), interval(i), last_fired(0) { }
};
int _start_time;
// StaticBuffer<PollSinkCb, 16> _msg_sinks;
StaticBuffer<PollSinkCb, 16> _loop_sinks;
// StaticBuffer<PollPeriodicSinkCb, 16> _periodic_sinks;
};
#endif

View File

@ -41,6 +41,7 @@ static bool NpBeginGameCb(void* ctx, const char* game_name);
static void NpFreeBuffCb(void* ctx, void* buffer, int frame);
static bool NpOnEventCb(void* ctx, GGPOEvent* ev);
static GGPOPlayerHandle PlayerIdToGGPOHandle(s32 player_id);
static Input ReadLocalInput();
static GGPOErrorCode AddLocalInput(Netplay::Input input);
static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags);
@ -58,6 +59,7 @@ static bool ConnectToLowerPeers(gsl::span<const ENetAddress> peer_addresses);
static bool WaitForPeerConnections();
static void HandleEnetEvent(const ENetEvent* event);
static void PollEnet(Common::Timer::Value until_time);
static void HandleControlPacket(s32 player_id, const ENetPacket* pkt);
// l = local, r = remote
static s32 Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport, s32 ldelay, u32 pred, std::string game_path);
@ -154,6 +156,11 @@ void Netplay::ShutdownEnetHost()
s_enet_host = nullptr;
}
GGPOPlayerHandle PlayerIdToGGPOHandle(s32 player_id)
{
return player_id + 1;
}
s32 Netplay::GetPlayerIdForPeer(const ENetPeer* peer)
{
for (s32 i = 0; i < MAX_PLAYERS; i++)
@ -213,6 +220,33 @@ void Netplay::HandleEnetEvent(const ENetEvent* event)
}
break;
case ENET_EVENT_TYPE_RECEIVE:
{
const s32 player_id = GetPlayerIdForPeer(event->peer);
if (player_id < 0)
{
Log_WarningPrintf("Received packet from unknown player");
return;
}
if (event->channelID == ENET_CHANNEL_CONTROL)
{
HandleControlPacket(player_id, event->packet);
}
else if (event->channelID == ENET_CHANNEL_GGPO)
{
Log_TracePrintf("Received %zu ggpo bytes from player %d", event->packet->dataLength, player_id);
const int rc = ggpo_handle_packet(s_ggpo, event->peer, event->packet);
if (rc != GGPO_OK)
Log_ErrorPrintf("Failed to process GGPO packet!");
}
else
{
Log_ErrorPrintf("Unexpected packet channel %u", event->channelID);
}
}
break;
default:
{
Log_WarningPrintf("Unhandled enet event %d", event->type);
@ -307,6 +341,12 @@ bool Netplay::WaitForPeerConnections()
return true;
}
void Netplay::HandleControlPacket(s32 player_id, const ENetPacket* pkt)
{
// TODO
Log_ErrorPrintf("Unhandled control packet from player %d of size %zu", player_id, pkt->dataLength);
}
s32 Netplay::Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport, s32 ldelay, u32 pred, std::string game_path)
{
SetSettings();
@ -358,7 +398,7 @@ s32 Netplay::Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport,
!ConnectToLowerPeers(gsl::span<const ENetAddress>(peer_addresses).subspan(0, num_peer_addresses))) ||
!WaitForPeerConnections())
{
ShutdownEnetHost();
// System shutdown cleans up enet.
return -1;
}
@ -377,37 +417,51 @@ s32 Netplay::Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport,
GGPOErrorCode result;
result =
ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", 2, sizeof(Netplay::Input), lport, MAX_ROLLBACK_FRAMES);
// result = ggpo_start_synctest(&s_ggpo, &cb, (char*)"asdf", 2, sizeof(Netplay::Input), 1);
result = ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", MAX_PLAYERS, sizeof(Netplay::Input), lport, MAX_ROLLBACK_FRAMES);
if (!GGPO_SUCCEEDED(result))
{
Log_ErrorPrintf("ggpo_start_session() failed: %d", result);
return -1;
}
ggpo_set_disconnect_timeout(s_ggpo, 2000);
ggpo_set_disconnect_notify_start(s_ggpo, 1000);
for (int i = 1; i <= 2; i++)
for (s32 i = 0; i < MAX_PLAYERS; i++)
{
GGPOPlayer player = {};
GGPOPlayerHandle handle = 0;
if (!s_enet_peers[i] && i != s_player_id)
continue;
player.size = sizeof(GGPOPlayer);
player.player_num = i;
// This is *awful*. Need to merge the player IDs, enough of this indices-start-at-1 rubbish.
const GGPOPlayerHandle expected_handle = PlayerIdToGGPOHandle(i);
if (lhandle == i)
GGPOPlayer player = { sizeof(GGPOPlayer) };
GGPOPlayerHandle got_handle;
player.player_num = expected_handle;
if (i == s_player_id)
{
player.type = GGPOPlayerType::GGPO_PLAYERTYPE_LOCAL;
result = ggpo_add_player(s_ggpo, &player, &handle);
s_local_handle = handle;
player.type = GGPO_PLAYERTYPE_LOCAL;
result = ggpo_add_player(s_ggpo, &player, &got_handle);
if (GGPO_SUCCEEDED(result))
s_local_handle = got_handle;
}
else
{
player.type = GGPOPlayerType::GGPO_PLAYERTYPE_REMOTE;
StringUtil::Strlcpy(player.u.remote.ip_address, raddr.c_str(), std::size(player.u.remote.ip_address));
player.u.remote.port = rport;
result = ggpo_add_player(s_ggpo, &player, &handle);
player.type = GGPO_PLAYERTYPE_REMOTE;
player.u.remote.peer = s_enet_peers[i];
result = ggpo_add_player(s_ggpo, &player, &got_handle);
}
if (!GGPO_SUCCEEDED(result))
{
Log_ErrorPrintf("Failed to add player %d", i);
return -1;
}
Assert(expected_handle == got_handle);
}
ggpo_set_frame_delay(s_ggpo, s_local_handle, ldelay);
ggpo_set_manual_network_polling(s_ggpo, true);
return result;
}
@ -421,6 +475,8 @@ void Netplay::CloseSession()
s_save_buffer_pool.clear();
s_local_handle = GGPO_INVALID_HANDLE;
ShutdownEnetHost();
// Restore original settings.
Host::Internal::SetNetplaySettingsLayer(nullptr);
System::ApplySettings(false);
@ -527,9 +583,6 @@ void Netplay::Throttle()
const Common::Timer::Value sleep_period = Common::Timer::ConvertMillisecondsToValue(1);
for (;;)
{
// Poll network.
ggpo_poll_network(s_ggpo);
// TODO: make better, we can tell this function to stall until the next frame
PollEnet(0);
@ -630,6 +683,11 @@ void Netplay::CollectInput(u32 slot, u32 bind, float value)
s_net_input[slot][bind] = value;
}
GGPOPlayerHandle Netplay::PlayerIdToGGPOHandle(s32 player_id)
{
return player_id + 1;
}
Netplay::Input Netplay::ReadLocalInput()
{
// get controller data of the first controller (0 internally)
@ -644,7 +702,6 @@ Netplay::Input Netplay::ReadLocalInput()
void Netplay::SendMsg(const char* msg)
{
ggpo_client_chat(s_ggpo, msg);
}
GGPOErrorCode Netplay::SyncInput(Netplay::Input inputs[2], int* disconnect_flags)
@ -830,9 +887,6 @@ bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED:
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Resumed", ev->u.connection_resumed.player));
break;
case GGPOEventCode::GGPO_EVENTCODE_CHAT:
Host::OnNetplayMessage(ev->u.chat.msg);
break;
case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC:
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
break;

View File

@ -11,8 +11,14 @@ enum : s32
MAX_PLAYERS = 2,
// Maximum netplay prediction frames
MAX_ROLLBACK_FRAMES = 8,
};
NUM_ENET_CHANNELS = 1,
enum : u8
{
ENET_CHANNEL_CONTROL = 0,
ENET_CHANNEL_GGPO = 1,
NUM_ENET_CHANNELS,
};
void StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, s32 input_delay,

View File

@ -1089,6 +1089,9 @@ void EmuThread::startNetplaySession(int local_handle, quint16 local_port, const
auto remAddr = remote_addr.trimmed().toStdString();
auto gamePath = game_path.trimmed().toStdString();
Netplay::StartNetplaySession(local_handle, local_port, remAddr, remote_port, input_delay, gamePath);
// TODO: Fix this junk.. for some reason, it stays sleeping...
g_emu_thread->wakeThread();
}
void EmuThread::sendNetplayMessage(const QString& message)
@ -2243,6 +2246,9 @@ int main(int argc, char* argv[])
std::string remote = "127.0.0.1";
std::string game = "D:\\PSX\\chd\\padtest.chd";
Netplay::StartNetplaySession(h, port_base + h, remote, port_base + nh, 1, game);
// TODO: Fix this junk.. for some reason, it stays sleeping...
g_emu_thread->wakeThread();
});
}