Netplay: Use enet for GGPO packets
This commit is contained in:
parent
785b36ce5f
commit
df5f87997e
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 --
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
//}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue