Netplay: Use enet for connections
This commit is contained in:
parent
d6512dc8bc
commit
68e7fe0209
|
@ -14,11 +14,9 @@
|
||||||
<ClInclude Include="src\game_input.h" />
|
<ClInclude Include="src\game_input.h" />
|
||||||
<ClInclude Include="src\input_queue.h" />
|
<ClInclude Include="src\input_queue.h" />
|
||||||
<ClInclude Include="src\log.h" />
|
<ClInclude Include="src\log.h" />
|
||||||
<ClInclude Include="src\network\udp.h" />
|
|
||||||
<ClInclude Include="src\network\udp_msg.h" />
|
<ClInclude Include="src\network\udp_msg.h" />
|
||||||
<ClInclude Include="src\network\udp_proto.h" />
|
<ClInclude Include="src\network\udp_proto.h" />
|
||||||
<ClInclude Include="src\platform_windows.h" />
|
<ClInclude Include="src\platform_windows.h" />
|
||||||
<ClInclude Include="src\poll.h" />
|
|
||||||
<ClInclude Include="src\ring_buffer.h" />
|
<ClInclude Include="src\ring_buffer.h" />
|
||||||
<ClInclude Include="src\static_buffer.h" />
|
<ClInclude Include="src\static_buffer.h" />
|
||||||
<ClInclude Include="src\sync.h" />
|
<ClInclude Include="src\sync.h" />
|
||||||
|
@ -34,10 +32,8 @@
|
||||||
<ClCompile Include="src\input_queue.cpp" />
|
<ClCompile Include="src\input_queue.cpp" />
|
||||||
<ClCompile Include="src\log.cpp" />
|
<ClCompile Include="src\log.cpp" />
|
||||||
<ClCompile Include="src\main.cpp" />
|
<ClCompile Include="src\main.cpp" />
|
||||||
<ClCompile Include="src\network\udp.cpp" />
|
|
||||||
<ClCompile Include="src\network\udp_proto.cpp" />
|
<ClCompile Include="src\network\udp_proto.cpp" />
|
||||||
<ClCompile Include="src\platform_windows.cpp" />
|
<ClCompile Include="src\platform_windows.cpp" />
|
||||||
<ClCompile Include="src\poll.cpp" />
|
|
||||||
<ClCompile Include="src\sync.cpp" />
|
<ClCompile Include="src\sync.cpp" />
|
||||||
<ClCompile Include="src\timesync.cpp" />
|
<ClCompile Include="src\timesync.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -46,7 +42,8 @@
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||||
<PreprocessorDefinitions>_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)src;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)src;$(ProjectDir)include</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\enet\include</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<Import Project="..\msvc\vsprops\Targets.props" />
|
<Import Project="..\msvc\vsprops\Targets.props" />
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
<ClInclude Include="src\game_input.h" />
|
<ClInclude Include="src\game_input.h" />
|
||||||
<ClInclude Include="src\input_queue.h" />
|
<ClInclude Include="src\input_queue.h" />
|
||||||
<ClInclude Include="src\log.h" />
|
<ClInclude Include="src\log.h" />
|
||||||
<ClInclude Include="src\platform_linux.h" />
|
|
||||||
<ClInclude Include="src\platform_windows.h" />
|
<ClInclude Include="src\platform_windows.h" />
|
||||||
<ClInclude Include="src\poll.h" />
|
|
||||||
<ClInclude Include="src\ring_buffer.h" />
|
<ClInclude Include="src\ring_buffer.h" />
|
||||||
<ClInclude Include="src\static_buffer.h" />
|
<ClInclude Include="src\static_buffer.h" />
|
||||||
<ClInclude Include="src\sync.h" />
|
<ClInclude Include="src\sync.h" />
|
||||||
|
@ -32,9 +30,6 @@
|
||||||
<ClInclude Include="src\network\udp_proto.h">
|
<ClInclude Include="src\network\udp_proto.h">
|
||||||
<Filter>network</Filter>
|
<Filter>network</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\network\udp.h">
|
|
||||||
<Filter>network</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\bitvector.cpp" />
|
<ClCompile Include="src\bitvector.cpp" />
|
||||||
|
@ -42,9 +37,7 @@
|
||||||
<ClCompile Include="src\input_queue.cpp" />
|
<ClCompile Include="src\input_queue.cpp" />
|
||||||
<ClCompile Include="src\log.cpp" />
|
<ClCompile Include="src\log.cpp" />
|
||||||
<ClCompile Include="src\main.cpp" />
|
<ClCompile Include="src\main.cpp" />
|
||||||
<ClCompile Include="src\platform_linux.cpp" />
|
|
||||||
<ClCompile Include="src\platform_windows.cpp" />
|
<ClCompile Include="src\platform_windows.cpp" />
|
||||||
<ClCompile Include="src\poll.cpp" />
|
|
||||||
<ClCompile Include="src\sync.cpp" />
|
<ClCompile Include="src\sync.cpp" />
|
||||||
<ClCompile Include="src\timesync.cpp" />
|
<ClCompile Include="src\timesync.cpp" />
|
||||||
<ClCompile Include="src\backends\spectator.cpp">
|
<ClCompile Include="src\backends\spectator.cpp">
|
||||||
|
@ -59,9 +52,6 @@
|
||||||
<ClCompile Include="src\network\udp_proto.cpp">
|
<ClCompile Include="src\network\udp_proto.cpp">
|
||||||
<Filter>network</Filter>
|
<Filter>network</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\network\udp.cpp">
|
|
||||||
<Filter>network</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="backends">
|
<Filter Include="backends">
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#ifndef _GGPONET_H_
|
#ifndef _GGPONET_H_
|
||||||
#define _GGPONET_H_
|
#define _GGPONET_H_
|
||||||
|
|
||||||
|
#include "enet/enet.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -78,8 +80,7 @@ typedef struct GGPOPlayer {
|
||||||
struct {
|
struct {
|
||||||
} local;
|
} local;
|
||||||
struct {
|
struct {
|
||||||
char ip_address[32];
|
ENetPeer* peer;
|
||||||
unsigned short port;
|
|
||||||
} remote;
|
} remote;
|
||||||
} u;
|
} u;
|
||||||
} GGPOPlayer;
|
} GGPOPlayer;
|
||||||
|
@ -149,12 +150,8 @@ typedef enum {
|
||||||
GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER = 1001,
|
GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER = 1001,
|
||||||
GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER = 1002,
|
GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER = 1002,
|
||||||
GGPO_EVENTCODE_RUNNING = 1003,
|
GGPO_EVENTCODE_RUNNING = 1003,
|
||||||
GGPO_EVENTCODE_DISCONNECTED_FROM_PEER = 1004,
|
GGPO_EVENTCODE_TIMESYNC = 1004,
|
||||||
GGPO_EVENTCODE_TIMESYNC = 1005,
|
GGPO_EVENTCODE_DESYNC = 1006
|
||||||
GGPO_EVENTCODE_CONNECTION_INTERRUPTED = 1006,
|
|
||||||
GGPO_EVENTCODE_CONNECTION_RESUMED = 1007,
|
|
||||||
GGPO_EVENTCODE_CHAT = 1008,
|
|
||||||
GGPO_EVENTCODE_DESYNC = 1009
|
|
||||||
} GGPOEventCode;
|
} GGPOEventCode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -190,10 +187,6 @@ typedef struct {
|
||||||
struct {
|
struct {
|
||||||
GGPOPlayerHandle player;
|
GGPOPlayerHandle player;
|
||||||
} connection_resumed;
|
} connection_resumed;
|
||||||
struct {
|
|
||||||
int senderID;
|
|
||||||
const char* msg;
|
|
||||||
} chat;
|
|
||||||
struct {
|
struct {
|
||||||
int nFrameOfDesync;
|
int nFrameOfDesync;
|
||||||
uint32_t ourCheckSum;
|
uint32_t ourCheckSum;
|
||||||
|
@ -460,6 +453,7 @@ GGPO_API GGPOErrorCode __cdecl ggpo_set_frame_delay(GGPOSession *,
|
||||||
* in ggpo_idle.
|
* in ggpo_idle.
|
||||||
*/
|
*/
|
||||||
GGPO_API GGPOErrorCode __cdecl ggpo_idle(GGPOSession *);
|
GGPO_API GGPOErrorCode __cdecl ggpo_idle(GGPOSession *);
|
||||||
|
GGPO_API GGPOErrorCode __cdecl ggpo_network_idle(GGPOSession *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ggpo_add_local_input --
|
* ggpo_add_local_input --
|
||||||
|
@ -480,25 +474,6 @@ GGPO_API GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *,
|
||||||
GGPOPlayerHandle player,
|
GGPOPlayerHandle player,
|
||||||
void *values,
|
void *values,
|
||||||
int size);
|
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 --
|
* ggpo_synchronize_input --
|
||||||
*
|
*
|
||||||
|
@ -575,15 +550,6 @@ GGPO_API GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession *,
|
||||||
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *,
|
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *,
|
||||||
int timeout);
|
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 --
|
* ggpo_set_disconnect_notify_start --
|
||||||
*
|
*
|
||||||
* The time to wait before the first GGPO_EVENTCODE_NETWORK_INTERRUPTED timeout
|
* The time to wait before the first GGPO_EVENTCODE_NETWORK_INTERRUPTED timeout
|
||||||
|
@ -594,14 +560,12 @@ GGPO_API GGPOErrorCode __cdecl ggpo_set_manual_network_polling(GGPOSession*,
|
||||||
*/
|
*/
|
||||||
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *,
|
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *,
|
||||||
int timeout);
|
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 --
|
* ggpo_log --
|
||||||
|
|
|
@ -15,21 +15,20 @@ class GGPOSession {
|
||||||
public:
|
public:
|
||||||
virtual ~GGPOSession() { }
|
virtual ~GGPOSession() { }
|
||||||
virtual GGPOErrorCode DoPoll() = 0;
|
virtual GGPOErrorCode DoPoll() = 0;
|
||||||
|
virtual GGPOErrorCode NetworkIdle() = 0;
|
||||||
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) = 0;
|
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) = 0;
|
||||||
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) = 0;
|
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) = 0;
|
||||||
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags) = 0;
|
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags) = 0;
|
||||||
virtual GGPOErrorCode IncrementFrame(uint16_t checksum) = 0;
|
virtual GGPOErrorCode IncrementFrame(uint16_t checksum) = 0;
|
||||||
virtual GGPOErrorCode CurrentFrame(int& current) =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 DisconnectPlayer(GGPOPlayerHandle handle) = 0;// { return GGPO_OK; }
|
||||||
virtual GGPOErrorCode PollNetwork() = 0;
|
|
||||||
virtual GGPOErrorCode GetNetworkStats(GGPONetworkStats *stats, GGPOPlayerHandle handle) { return GGPO_OK; }
|
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 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 SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode SetDisconnectNotifyStart(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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
#include "p2p.h"
|
#include "p2p.h"
|
||||||
|
|
||||||
static const int RECOMMENDATION_INTERVAL = 120;
|
static const int RECOMMENDATION_INTERVAL = 120;
|
||||||
static const int DEFAULT_DISCONNECT_TIMEOUT = 5000;
|
|
||||||
static const int DEFAULT_DISCONNECT_NOTIFY_START = 750;
|
|
||||||
|
|
||||||
Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
|
Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
|
||||||
const char *gamename,
|
const char *gamename,
|
||||||
|
@ -19,15 +17,12 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
|
||||||
_num_players(num_players),
|
_num_players(num_players),
|
||||||
_input_size(input_size),
|
_input_size(input_size),
|
||||||
_sync(_local_connect_status, nframes),
|
_sync(_local_connect_status, nframes),
|
||||||
_disconnect_timeout(DEFAULT_DISCONNECT_TIMEOUT),
|
|
||||||
_disconnect_notify_start(DEFAULT_DISCONNECT_NOTIFY_START),
|
|
||||||
_num_spectators(0),
|
_num_spectators(0),
|
||||||
_next_spectator_frame(0)
|
_next_spectator_frame(0)
|
||||||
{
|
{
|
||||||
_callbacks = *cb;
|
_callbacks = *cb;
|
||||||
_synchronizing = true;
|
_synchronizing = true;
|
||||||
_next_recommended_sleep = 0;
|
_next_recommended_sleep = 0;
|
||||||
_manual_network_polling = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the synchronziation layer
|
* Initialize the synchronziation layer
|
||||||
|
@ -39,11 +34,6 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
|
||||||
config.num_prediction_frames = nframes;
|
config.num_prediction_frames = nframes;
|
||||||
_sync.Init(config);
|
_sync.Init(config);
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the UDP port
|
|
||||||
*/
|
|
||||||
_udp.Init(localport, &_poll, this);
|
|
||||||
|
|
||||||
_endpoints.resize(_num_players);
|
_endpoints.resize(_num_players);
|
||||||
memset(_local_connect_status, 0, sizeof(_local_connect_status));
|
memset(_local_connect_status, 0, sizeof(_local_connect_status));
|
||||||
for (int i = 0; i < ARRAY_SIZE(_local_connect_status); i++) {
|
for (int i = 0; i < ARRAY_SIZE(_local_connect_status); i++) {
|
||||||
|
@ -61,23 +51,18 @@ Peer2PeerBackend::~Peer2PeerBackend()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Peer2PeerBackend::AddRemotePlayer(char *ip,
|
Peer2PeerBackend::AddRemotePlayer(ENetPeer* peer, int queue)
|
||||||
uint16 port,
|
|
||||||
int queue)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Start the state machine (xxx: no)
|
* Start the state machine (xxx: no)
|
||||||
*/
|
*/
|
||||||
_synchronizing = true;
|
_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();
|
_endpoints[queue].Synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
GGPOErrorCode Peer2PeerBackend::AddSpectator(char *ip,
|
GGPOErrorCode Peer2PeerBackend::AddSpectator(ENetPeer* peer)
|
||||||
uint16 port)
|
|
||||||
{
|
{
|
||||||
if (_num_spectators == GGPO_MAX_SPECTATORS) {
|
if (_num_spectators == GGPO_MAX_SPECTATORS) {
|
||||||
return GGPO_ERRORCODE_TOO_MANY_SPECTATORS;
|
return GGPO_ERRORCODE_TOO_MANY_SPECTATORS;
|
||||||
|
@ -90,9 +75,7 @@ GGPOErrorCode Peer2PeerBackend::AddSpectator(char *ip,
|
||||||
}
|
}
|
||||||
int queue = _num_spectators++;
|
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();
|
_spectators[queue].Synchronize();
|
||||||
|
|
||||||
return GGPO_OK;
|
return GGPO_OK;
|
||||||
|
@ -155,21 +138,8 @@ void Peer2PeerBackend::CheckDesync()
|
||||||
GGPOErrorCode
|
GGPOErrorCode
|
||||||
Peer2PeerBackend::DoPoll()
|
Peer2PeerBackend::DoPoll()
|
||||||
{
|
{
|
||||||
// Pass on chat
|
if (!_sync.InRollback())
|
||||||
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);
|
|
||||||
|
|
||||||
PollUdpProtocolEvents();
|
PollUdpProtocolEvents();
|
||||||
CheckDesync();
|
CheckDesync();
|
||||||
if (!_synchronizing) {
|
if (!_synchronizing) {
|
||||||
|
@ -232,6 +202,14 @@ Peer2PeerBackend::DoPoll()
|
||||||
return GGPO_OK;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GGPOErrorCode Peer2PeerBackend::NetworkIdle()
|
||||||
|
{
|
||||||
|
for (UdpProtocol& udp : _endpoints)
|
||||||
|
udp.NetworkIdle();
|
||||||
|
|
||||||
|
return GGPO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int Peer2PeerBackend::Poll2Players(int current_frame)
|
int Peer2PeerBackend::Poll2Players(int current_frame)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -309,7 +287,7 @@ Peer2PeerBackend::AddPlayer(GGPOPlayer *player,
|
||||||
GGPOPlayerHandle *handle)
|
GGPOPlayerHandle *handle)
|
||||||
{
|
{
|
||||||
if (player->type == GGPO_PLAYERTYPE_SPECTATOR) {
|
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;
|
int queue = player->player_num - 1;
|
||||||
|
@ -319,7 +297,7 @@ Peer2PeerBackend::AddPlayer(GGPOPlayer *player,
|
||||||
*handle = QueueToPlayerHandle(queue);
|
*handle = QueueToPlayerHandle(queue);
|
||||||
|
|
||||||
if (player->type == GGPO_PLAYERTYPE_REMOTE) {
|
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;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
@ -413,19 +391,6 @@ Peer2PeerBackend::CurrentFrame(int& current)
|
||||||
return GGPO_OK;
|
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
|
GGPOErrorCode
|
||||||
Peer2PeerBackend::IncrementFrame(uint16_t checksum1)
|
Peer2PeerBackend::IncrementFrame(uint16_t checksum1)
|
||||||
{
|
{
|
||||||
|
@ -570,10 +535,6 @@ Peer2PeerBackend::OnUdpProtocolPeerEvent(UdpProtocol::Event &evt, int queue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UdpProtocol::Event::Disconnected:
|
|
||||||
DisconnectPlayer(QueueToPlayerHandle(queue));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,19 +544,6 @@ Peer2PeerBackend::OnUdpProtocolSpectatorEvent(UdpProtocol::Event &evt, int queue
|
||||||
{
|
{
|
||||||
GGPOPlayerHandle handle = QueueToSpectatorHandle(queue);
|
GGPOPlayerHandle handle = QueueToSpectatorHandle(queue);
|
||||||
OnUdpProtocolEvent(evt, handle);
|
OnUdpProtocolEvent(evt, handle);
|
||||||
|
|
||||||
GGPOEvent info;
|
|
||||||
|
|
||||||
switch (evt.type) {
|
|
||||||
case UdpProtocol::Event::Disconnected:
|
|
||||||
_spectators[queue].Disconnect();
|
|
||||||
|
|
||||||
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
|
|
||||||
info.u.disconnected.player = handle;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -623,19 +571,6 @@ Peer2PeerBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt, GGPOPlayerHandle h
|
||||||
|
|
||||||
CheckInitialSync();
|
CheckInitialSync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UdpProtocol::Event::NetworkInterrupted:
|
|
||||||
info.code = GGPO_EVENTCODE_CONNECTION_INTERRUPTED;
|
|
||||||
info.u.connection_interrupted.player = handle;
|
|
||||||
info.u.connection_interrupted.disconnect_timeout = evt.u.network_interrupted.disconnect_timeout;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UdpProtocol::Event::NetworkResumed:
|
|
||||||
info.code = GGPO_EVENTCODE_CONNECTION_RESUMED;
|
|
||||||
info.u.connection_resumed.player = handle;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,6 +617,7 @@ Peer2PeerBackend::DisconnectPlayerQueue(int queue, int syncto)
|
||||||
GGPOEvent info;
|
GGPOEvent info;
|
||||||
int framecount = _sync.GetFrameCount();
|
int framecount = _sync.GetFrameCount();
|
||||||
|
|
||||||
|
// TODO: Where does the endpoint actually get removed? I can't see it anywhere...
|
||||||
_endpoints[queue].Disconnect();
|
_endpoints[queue].Disconnect();
|
||||||
|
|
||||||
Log("Changing queue %d local connect status for last frame from %d to %d on disconnect request (current: %d).\n",
|
Log("Changing queue %d local connect status for last frame from %d to %d on disconnect request (current: %d).\n",
|
||||||
|
@ -696,10 +632,6 @@ Peer2PeerBackend::DisconnectPlayerQueue(int queue, int syncto)
|
||||||
Log("finished adjusting simulation.\n");
|
Log("finished adjusting simulation.\n");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
|
|
||||||
info.u.disconnected.player = QueueToPlayerHandle(queue);
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
|
|
||||||
CheckInitialSync();
|
CheckInitialSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,45 +674,6 @@ Peer2PeerBackend::SetFrameDelay(GGPOPlayerHandle player, int delay)
|
||||||
return GGPO_OK;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
GGPOErrorCode
|
|
||||||
Peer2PeerBackend::SetDisconnectTimeout(int timeout)
|
|
||||||
{
|
|
||||||
_disconnect_timeout = timeout;
|
|
||||||
for (int i = 0; i < _num_players; i++) {
|
|
||||||
if (_endpoints[i].IsInitialized()) {
|
|
||||||
_endpoints[i].SetDisconnectTimeout(_disconnect_timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GGPO_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
GGPOErrorCode
|
|
||||||
Peer2PeerBackend::SetDisconnectNotifyStart(int timeout)
|
|
||||||
{
|
|
||||||
_disconnect_notify_start = timeout;
|
|
||||||
for (int i = 0; i < _num_players; i++) {
|
|
||||||
if (_endpoints[i].IsInitialized()) {
|
|
||||||
_endpoints[i].SetDisconnectNotifyStart(_disconnect_notify_start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GGPO_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
GGPOErrorCode
|
|
||||||
Peer2PeerBackend::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
|
GGPOErrorCode
|
||||||
Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
|
Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
|
||||||
{
|
{
|
||||||
|
@ -793,21 +686,27 @@ Peer2PeerBackend::PlayerHandleToQueue(GGPOPlayerHandle player, int *queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
GGPOErrorCode
|
||||||
Peer2PeerBackend::OnMsg(sockaddr_in &from, UdpMsg *msg, int len)
|
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++) {
|
for (int i = 0; i < _num_players; i++) {
|
||||||
if (_endpoints[i].HandlesMsg(from, msg)) {
|
if (_endpoints[i].GetENetPeer() == peer) {
|
||||||
_endpoints[i].OnMsg(msg, len);
|
_endpoints[i].OnMsg(msg, len);
|
||||||
return;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < _num_spectators; i++) {
|
for (int i = 0; i < _num_spectators; i++) {
|
||||||
if (_spectators[i].HandlesMsg(from, msg)) {
|
if (_spectators[i].GetENetPeer() == peer) {
|
||||||
_spectators[i].OnMsg(msg, len);
|
_spectators[i].OnMsg(msg, len);
|
||||||
return;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GGPO_ERRORCODE_INVALID_PLAYER_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -9,13 +9,13 @@
|
||||||
#define _P2P_H
|
#define _P2P_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "poll.h"
|
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "backend.h"
|
#include "backend.h"
|
||||||
#include "timesync.h"
|
#include "timesync.h"
|
||||||
#include "network/udp_proto.h"
|
#include "network/udp_proto.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
class Peer2PeerBackend : public GGPOSession, Udp::Callbacks {
|
class Peer2PeerBackend final : public GGPOSession
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Peer2PeerBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, int nframes);
|
Peer2PeerBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, int nframes);
|
||||||
virtual ~Peer2PeerBackend();
|
virtual ~Peer2PeerBackend();
|
||||||
|
@ -23,6 +23,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual GGPOErrorCode DoPoll() override;
|
virtual GGPOErrorCode DoPoll() override;
|
||||||
|
virtual GGPOErrorCode NetworkIdle() override;
|
||||||
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) override;
|
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) override;
|
||||||
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) override;
|
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) override;
|
||||||
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags) override;
|
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags) override;
|
||||||
|
@ -30,15 +31,8 @@ public:
|
||||||
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) override;
|
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) override;
|
||||||
virtual GGPOErrorCode GetNetworkStats(GGPONetworkStats *stats, GGPOPlayerHandle handle) override;
|
virtual GGPOErrorCode GetNetworkStats(GGPONetworkStats *stats, GGPOPlayerHandle handle) override;
|
||||||
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) override;
|
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 CurrentFrame(int& current) override;
|
||||||
virtual GGPOErrorCode PollNetwork() override;
|
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override;
|
||||||
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GGPOErrorCode PlayerHandleToQueue(GGPOPlayerHandle player, int *queue);
|
GGPOErrorCode PlayerHandleToQueue(GGPOPlayerHandle player, int *queue);
|
||||||
|
@ -50,8 +44,8 @@ protected:
|
||||||
void CheckInitialSync(void);
|
void CheckInitialSync(void);
|
||||||
int Poll2Players(int current_frame);
|
int Poll2Players(int current_frame);
|
||||||
int PollNPlayers(int current_frame);
|
int PollNPlayers(int current_frame);
|
||||||
void AddRemotePlayer(char *remoteip, uint16 reportport, int queue);
|
void AddRemotePlayer(ENetPeer* peer, int queue);
|
||||||
GGPOErrorCode AddSpectator(char *remoteip, uint16 reportport);
|
GGPOErrorCode AddSpectator(ENetPeer* peer);
|
||||||
virtual void OnSyncEvent(Sync::Event &e) { }
|
virtual void OnSyncEvent(Sync::Event &e) { }
|
||||||
virtual void OnUdpProtocolEvent(UdpProtocol::Event &e, GGPOPlayerHandle handle);
|
virtual void OnUdpProtocolEvent(UdpProtocol::Event &e, GGPOPlayerHandle handle);
|
||||||
virtual void OnUdpProtocolPeerEvent(UdpProtocol::Event &e, int queue);
|
virtual void OnUdpProtocolPeerEvent(UdpProtocol::Event &e, int queue);
|
||||||
|
@ -59,9 +53,7 @@ protected:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GGPOSessionCallbacks _callbacks;
|
GGPOSessionCallbacks _callbacks;
|
||||||
Poll _poll;
|
|
||||||
Sync _sync;
|
Sync _sync;
|
||||||
Udp _udp;
|
|
||||||
std::vector<UdpProtocol> _endpoints;
|
std::vector<UdpProtocol> _endpoints;
|
||||||
UdpProtocol _spectators[GGPO_MAX_SPECTATORS];
|
UdpProtocol _spectators[GGPO_MAX_SPECTATORS];
|
||||||
int _num_spectators;
|
int _num_spectators;
|
||||||
|
@ -72,10 +64,6 @@ protected:
|
||||||
int _next_recommended_sleep;
|
int _next_recommended_sleep;
|
||||||
|
|
||||||
int _next_spectator_frame;
|
int _next_spectator_frame;
|
||||||
int _disconnect_timeout;
|
|
||||||
int _disconnect_notify_start;
|
|
||||||
|
|
||||||
bool _manual_network_polling;
|
|
||||||
|
|
||||||
UdpMsg::connect_status _local_connect_status[UDP_MSG_MAX_PLAYERS];
|
UdpMsg::connect_status _local_connect_status[UDP_MSG_MAX_PLAYERS];
|
||||||
struct ChecksumEntry {
|
struct ChecksumEntry {
|
||||||
|
|
|
@ -20,7 +20,6 @@ SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
|
||||||
{
|
{
|
||||||
_callbacks = *cb;
|
_callbacks = *cb;
|
||||||
_synchronizing = true;
|
_synchronizing = true;
|
||||||
_manual_network_polling = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(_inputs); i++) {
|
for (int i = 0; i < ARRAY_SIZE(_inputs); i++) {
|
||||||
_inputs[i].frame = -1;
|
_inputs[i].frame = -1;
|
||||||
|
@ -29,12 +28,14 @@ SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
|
||||||
/*
|
/*
|
||||||
* Initialize the UDP port
|
* Initialize the UDP port
|
||||||
*/
|
*/
|
||||||
_udp.Init(localport, &_poll, this);
|
// FIXME
|
||||||
|
abort();
|
||||||
|
//_udp.Init(localport, &_poll, this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init the host endpoint
|
* Init the host endpoint
|
||||||
*/
|
*/
|
||||||
_host.Init(&_udp, _poll, 0, hostip, hostport, NULL);
|
//_host.Init(&_udp, _poll, 0, hostip, hostport, NULL);
|
||||||
_host.Synchronize();
|
_host.Synchronize();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -50,13 +51,16 @@ SpectatorBackend::~SpectatorBackend()
|
||||||
GGPOErrorCode
|
GGPOErrorCode
|
||||||
SpectatorBackend::DoPoll()
|
SpectatorBackend::DoPoll()
|
||||||
{
|
{
|
||||||
if (!_manual_network_polling)
|
|
||||||
_poll.Pump(0);
|
|
||||||
|
|
||||||
PollUdpProtocolEvents();
|
PollUdpProtocolEvents();
|
||||||
return GGPO_OK;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GGPOErrorCode SpectatorBackend::NetworkIdle()
|
||||||
|
{
|
||||||
|
_host.NetworkIdle();
|
||||||
|
return GGPO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
GGPOErrorCode
|
GGPOErrorCode
|
||||||
SpectatorBackend::SyncInput(void *values,
|
SpectatorBackend::SyncInput(void *values,
|
||||||
int size,
|
int size,
|
||||||
|
@ -95,20 +99,6 @@ SpectatorBackend::CurrentFrame(int& current)
|
||||||
return GGPO_OK;
|
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
|
GGPOErrorCode
|
||||||
SpectatorBackend::IncrementFrame(uint16_t checksum)
|
SpectatorBackend::IncrementFrame(uint16_t checksum)
|
||||||
{
|
{
|
||||||
|
@ -159,25 +149,6 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UdpProtocol::Event::NetworkInterrupted:
|
|
||||||
info.code = GGPO_EVENTCODE_CONNECTION_INTERRUPTED;
|
|
||||||
info.u.connection_interrupted.player = 0;
|
|
||||||
info.u.connection_interrupted.disconnect_timeout = evt.u.network_interrupted.disconnect_timeout;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UdpProtocol::Event::NetworkResumed:
|
|
||||||
info.code = GGPO_EVENTCODE_CONNECTION_RESUMED;
|
|
||||||
info.u.connection_resumed.player = 0;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UdpProtocol::Event::Disconnected:
|
|
||||||
info.code = GGPO_EVENTCODE_DISCONNECTED_FROM_PEER;
|
|
||||||
info.u.disconnected.player = 0;
|
|
||||||
_callbacks.on_event(_callbacks.context, &info);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UdpProtocol::Event::Input:
|
case UdpProtocol::Event::Input:
|
||||||
GameInput& input = evt.u.input.input;
|
GameInput& input = evt.u.input.input;
|
||||||
|
|
||||||
|
@ -188,11 +159,14 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
GGPOErrorCode SpectatorBackend::OnPacket(ENetPeer* peer, const ENetPacket* pkt)
|
||||||
SpectatorBackend::OnMsg(sockaddr_in &from, UdpMsg *msg, int len)
|
|
||||||
{
|
{
|
||||||
if (_host.HandlesMsg(from, msg)) {
|
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);
|
_host.OnMsg(msg, len);
|
||||||
}
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#define _SPECTATOR_H
|
#define _SPECTATOR_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "poll.h"
|
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "backend.h"
|
#include "backend.h"
|
||||||
#include "timesync.h"
|
#include "timesync.h"
|
||||||
|
@ -17,7 +16,7 @@
|
||||||
|
|
||||||
#define SPECTATOR_FRAME_BUFFER_SIZE 64
|
#define SPECTATOR_FRAME_BUFFER_SIZE 64
|
||||||
|
|
||||||
class SpectatorBackend : public GGPOSession, Udp::Callbacks {
|
class SpectatorBackend final : public GGPOSession {
|
||||||
public:
|
public:
|
||||||
SpectatorBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, char *hostip, u_short hostport);
|
SpectatorBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, char *hostip, u_short hostport);
|
||||||
virtual ~SpectatorBackend();
|
virtual ~SpectatorBackend();
|
||||||
|
@ -25,6 +24,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual GGPOErrorCode DoPoll();
|
virtual GGPOErrorCode DoPoll();
|
||||||
|
virtual GGPOErrorCode NetworkIdle();
|
||||||
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) { return GGPO_OK; }
|
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size) { return GGPO_OK; }
|
||||||
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags);
|
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags);
|
||||||
|
@ -34,13 +34,8 @@ public:
|
||||||
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode SetDisconnectNotifyStart(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 CurrentFrame(int& current) override;
|
||||||
virtual GGPOErrorCode PollNetwork() override;
|
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override;
|
||||||
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void PollUdpProtocolEvents(void);
|
void PollUdpProtocolEvents(void);
|
||||||
|
@ -50,15 +45,12 @@ protected:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GGPOSessionCallbacks _callbacks;
|
GGPOSessionCallbacks _callbacks;
|
||||||
Poll _poll;
|
|
||||||
Udp _udp;
|
|
||||||
UdpProtocol _host;
|
UdpProtocol _host;
|
||||||
bool _synchronizing;
|
bool _synchronizing;
|
||||||
int _input_size;
|
int _input_size;
|
||||||
int _num_players;
|
int _num_players;
|
||||||
int _next_input_to_send;
|
int _next_input_to_send;
|
||||||
GameInput _inputs[SPECTATOR_FRAME_BUFFER_SIZE];
|
GameInput _inputs[SPECTATOR_FRAME_BUFFER_SIZE];
|
||||||
bool _manual_network_polling;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -54,6 +54,12 @@ SyncTestBackend::DoPoll()
|
||||||
return GGPO_OK;
|
return GGPO_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GGPOErrorCode
|
||||||
|
SyncTestBackend::NetworkIdle()
|
||||||
|
{
|
||||||
|
return GGPO_OK;
|
||||||
|
}
|
||||||
|
|
||||||
GGPOErrorCode
|
GGPOErrorCode
|
||||||
SyncTestBackend::AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle)
|
SyncTestBackend::AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,22 +13,21 @@
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "ring_buffer.h"
|
#include "ring_buffer.h"
|
||||||
|
|
||||||
class SyncTestBackend : public GGPOSession {
|
class SyncTestBackend final : public GGPOSession {
|
||||||
public:
|
public:
|
||||||
SyncTestBackend(GGPOSessionCallbacks *cb, char *gamename, int frames, int num_players);
|
SyncTestBackend(GGPOSessionCallbacks *cb, char *gamename, int frames, int num_players);
|
||||||
virtual ~SyncTestBackend();
|
virtual ~SyncTestBackend();
|
||||||
|
|
||||||
virtual GGPOErrorCode DoPoll();
|
virtual GGPOErrorCode DoPoll();
|
||||||
|
virtual GGPOErrorCode NetworkIdle();
|
||||||
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle);
|
virtual GGPOErrorCode AddPlayer(GGPOPlayer *player, GGPOPlayerHandle *handle);
|
||||||
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size);
|
virtual GGPOErrorCode AddLocalInput(GGPOPlayerHandle player, void *values, int size);
|
||||||
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags);
|
virtual GGPOErrorCode SyncInput(void *values, int size, int *disconnect_flags);
|
||||||
virtual GGPOErrorCode IncrementFrame(uint16_t checksum);
|
virtual GGPOErrorCode IncrementFrame(uint16_t checksum);
|
||||||
virtual GGPOErrorCode Logv(char *fmt, va_list list);
|
virtual GGPOErrorCode Logv(char *fmt, va_list list);
|
||||||
virtual GGPOErrorCode DisconnectPlayer(GGPOPlayerHandle handle) { return GGPO_OK; }
|
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 CurrentFrame(int& current) override;
|
||||||
virtual GGPOErrorCode PollNetwork() override { return GGPO_OK; };
|
virtual GGPOErrorCode OnPacket(ENetPeer* peer, const ENetPacket* pkt) override { return GGPO_ERRORCODE_UNSUPPORTED; }
|
||||||
virtual GGPOErrorCode SetManualNetworkPolling(bool value) override { return GGPO_OK; };
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct SavedInfo {
|
struct SavedInfo {
|
||||||
|
|
|
@ -100,6 +100,15 @@ ggpo_idle(GGPOSession *ggpo)
|
||||||
return ggpo->DoPoll();
|
return ggpo->DoPoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GGPOErrorCode
|
||||||
|
ggpo_network_idle(GGPOSession* ggpo)
|
||||||
|
{
|
||||||
|
if (!ggpo) {
|
||||||
|
return GGPO_ERRORCODE_INVALID_SESSION;
|
||||||
|
}
|
||||||
|
return ggpo->NetworkIdle();
|
||||||
|
}
|
||||||
|
|
||||||
GGPOErrorCode
|
GGPOErrorCode
|
||||||
ggpo_add_local_input(GGPOSession *ggpo,
|
ggpo_add_local_input(GGPOSession *ggpo,
|
||||||
GGPOPlayerHandle player,
|
GGPOPlayerHandle player,
|
||||||
|
@ -151,15 +160,6 @@ ggpo_get_current_frame(GGPOSession *ggpo, int& nFrame)
|
||||||
return ggpo->CurrentFrame(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
|
GGPOErrorCode
|
||||||
ggpo_get_network_stats(GGPOSession *ggpo,
|
ggpo_get_network_stats(GGPOSession *ggpo,
|
||||||
GGPOPlayerHandle player,
|
GGPOPlayerHandle player,
|
||||||
|
@ -172,6 +172,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
|
GGPOErrorCode
|
||||||
ggpo_close_session(GGPOSession *ggpo)
|
ggpo_close_session(GGPOSession *ggpo)
|
||||||
{
|
{
|
||||||
|
@ -191,16 +198,6 @@ ggpo_set_disconnect_timeout(GGPOSession *ggpo, int timeout)
|
||||||
return ggpo->SetDisconnectTimeout(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
|
GGPOErrorCode
|
||||||
ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
|
ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
|
||||||
{
|
{
|
||||||
|
@ -210,16 +207,6 @@ ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
|
||||||
return ggpo->SetDisconnectNotifyStart(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,
|
GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
|
||||||
GGPOSessionCallbacks *cb,
|
GGPOSessionCallbacks *cb,
|
||||||
const char *game,
|
const char *game,
|
||||||
|
@ -238,4 +225,3 @@ GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
|
||||||
host_port);
|
host_port);
|
||||||
return GGPO_OK;
|
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 MAX_COMPRESSED_BITS 4096
|
||||||
#define UDP_MSG_MAX_PLAYERS 4
|
#define UDP_MSG_MAX_PLAYERS 4
|
||||||
#define MAX_CHAT_LENGTH 120
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
struct UdpMsg
|
struct UdpMsg
|
||||||
|
@ -22,9 +21,7 @@ struct UdpMsg
|
||||||
Input = 3,
|
Input = 3,
|
||||||
QualityReport = 4,
|
QualityReport = 4,
|
||||||
QualityReply = 5,
|
QualityReply = 5,
|
||||||
KeepAlive = 6,
|
InputAck = 6,
|
||||||
InputAck = 7,
|
|
||||||
Chat = 8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct connect_status {
|
struct connect_status {
|
||||||
|
@ -63,8 +60,7 @@ struct UdpMsg
|
||||||
|
|
||||||
uint32 start_frame;
|
uint32 start_frame;
|
||||||
|
|
||||||
int disconnect_requested:1;
|
int ack_frame;
|
||||||
int ack_frame:31;
|
|
||||||
|
|
||||||
uint16 num_bits;
|
uint16 num_bits;
|
||||||
uint32 checksum32;
|
uint32 checksum32;
|
||||||
|
@ -73,11 +69,8 @@ struct UdpMsg
|
||||||
} input;
|
} input;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int ack_frame:31;
|
int ack_frame;
|
||||||
} input_ack;
|
} input_ack;
|
||||||
struct {
|
|
||||||
char msg[MAX_CHAT_LENGTH];
|
|
||||||
} chat;
|
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -94,8 +87,6 @@ public:
|
||||||
case QualityReport: return sizeof(u.quality_report);
|
case QualityReport: return sizeof(u.quality_report);
|
||||||
case QualityReply: return sizeof(u.quality_reply);
|
case QualityReply: return sizeof(u.quality_reply);
|
||||||
case InputAck: return sizeof(u.input_ack);
|
case InputAck: return sizeof(u.input_ack);
|
||||||
case Chat: return MAX_CHAT_LENGTH;
|
|
||||||
case KeepAlive: return 0;
|
|
||||||
case Input:
|
case Input:
|
||||||
size = (int)((char *)&u.input.bits - (char *)&u.input);
|
size = (int)((char *)&u.input.bits - (char *)&u.input);
|
||||||
size += (u.input.num_bits + 7) / 8;
|
size += (u.input.num_bits + 7) / 8;
|
||||||
|
|
|
@ -15,12 +15,12 @@ static const int NUM_SYNC_PACKETS = 5;
|
||||||
static const int SYNC_RETRY_INTERVAL = 2000;
|
static const int SYNC_RETRY_INTERVAL = 2000;
|
||||||
static const int SYNC_FIRST_RETRY_INTERVAL = 500;
|
static const int SYNC_FIRST_RETRY_INTERVAL = 500;
|
||||||
static const int RUNNING_RETRY_INTERVAL = 200;
|
static const int RUNNING_RETRY_INTERVAL = 200;
|
||||||
static const int KEEP_ALIVE_INTERVAL = 200;
|
|
||||||
static const int QUALITY_REPORT_INTERVAL = 333;
|
static const int QUALITY_REPORT_INTERVAL = 333;
|
||||||
static const int NETWORK_STATS_INTERVAL = 500;
|
static const int NETWORK_STATS_INTERVAL = 500;
|
||||||
static const int UDP_SHUTDOWN_TIMER = 5000;
|
|
||||||
static const int MAX_SEQ_DISTANCE = (1 << 15);
|
static const int MAX_SEQ_DISTANCE = (1 << 15);
|
||||||
|
|
||||||
|
static const uint8_t ENET_CHANNEL_ID = 1;
|
||||||
|
|
||||||
UdpProtocol::UdpProtocol() :
|
UdpProtocol::UdpProtocol() :
|
||||||
_local_frame_advantage(0),
|
_local_frame_advantage(0),
|
||||||
_remote_frame_advantage(0),
|
_remote_frame_advantage(0),
|
||||||
|
@ -31,15 +31,10 @@ UdpProtocol::UdpProtocol() :
|
||||||
_bytes_sent(0),
|
_bytes_sent(0),
|
||||||
_stats_start_time(0),
|
_stats_start_time(0),
|
||||||
_last_send_time(0),
|
_last_send_time(0),
|
||||||
_shutdown_timeout(0),
|
|
||||||
_disconnect_timeout(0),
|
|
||||||
_disconnect_notify_start(0),
|
|
||||||
_disconnect_notify_sent(false),
|
|
||||||
_disconnect_event_sent(false),
|
|
||||||
_connected(false),
|
_connected(false),
|
||||||
_next_send_seq(0),
|
_next_send_seq(0),
|
||||||
_next_recv_seq(0),
|
_next_recv_seq(0),
|
||||||
_udp(NULL)
|
_peer(nullptr)
|
||||||
{
|
{
|
||||||
_last_sent_input.init(-1, NULL, 1);
|
_last_sent_input.init(-1, NULL, 1);
|
||||||
_last_received_input.init(-1, NULL, 1);
|
_last_received_input.init(-1, NULL, 1);
|
||||||
|
@ -50,17 +45,12 @@ UdpProtocol::UdpProtocol() :
|
||||||
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
|
for (int i = 0; i < ARRAY_SIZE(_peer_connect_status); i++) {
|
||||||
_peer_connect_status[i].last_frame = -1;
|
_peer_connect_status[i].last_frame = -1;
|
||||||
}
|
}
|
||||||
memset(&_peer_addr, 0, sizeof _peer_addr);
|
|
||||||
_oo_packet.msg = NULL;
|
|
||||||
|
|
||||||
_send_latency = Platform::GetConfigInt("ggpo.network.delay");
|
|
||||||
_oop_percent = Platform::GetConfigInt("ggpo.oop.percent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UdpProtocol::~UdpProtocol()
|
UdpProtocol::~UdpProtocol()
|
||||||
{
|
{
|
||||||
ClearSendQueue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UdpProtocol::SetFrameDelay(int delay)
|
void UdpProtocol::SetFrameDelay(int delay)
|
||||||
{
|
{
|
||||||
_timesync.SetFrameDelay(delay);
|
_timesync.SetFrameDelay(delay);
|
||||||
|
@ -70,32 +60,22 @@ int UdpProtocol::RemoteFrameDelay()const
|
||||||
{
|
{
|
||||||
return _timesync._remoteFrameDelay;
|
return _timesync._remoteFrameDelay;
|
||||||
}
|
}
|
||||||
void
|
|
||||||
UdpProtocol::Init(Udp *udp,
|
void UdpProtocol::Init(ENetPeer* peer, int queue, UdpMsg::connect_status *status)
|
||||||
Poll &poll,
|
|
||||||
int queue,
|
|
||||||
char *ip,
|
|
||||||
u_short port,
|
|
||||||
UdpMsg::connect_status *status)
|
|
||||||
{
|
{
|
||||||
_udp = udp;
|
_peer = peer;
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
_local_connect_status = status;
|
_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 {
|
do {
|
||||||
_magic_number = (uint16)rand();
|
_magic_number = (uint16)rand();
|
||||||
} while (_magic_number == 0);
|
} while (_magic_number == 0);
|
||||||
poll.RegisterLoop(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UdpProtocol::SendInput(GameInput &input)
|
UdpProtocol::SendInput(GameInput &input)
|
||||||
{
|
{
|
||||||
if (_udp) {
|
if (_peer) {
|
||||||
if (_current_state == Running) {
|
if (_current_state == Running) {
|
||||||
/*
|
/*
|
||||||
* Check to see if this is a good time to adjust for the rift...
|
* Check to see if this is a good time to adjust for the rift...
|
||||||
|
@ -156,7 +136,6 @@ UdpProtocol::SendPendingOutput()
|
||||||
msg->u.input.ack_frame = _last_received_input.frame;
|
msg->u.input.ack_frame = _last_received_input.frame;
|
||||||
msg->u.input.num_bits = (uint16)offset;
|
msg->u.input.num_bits = (uint16)offset;
|
||||||
|
|
||||||
msg->u.input.disconnect_requested = _current_state == Disconnected;
|
|
||||||
if (_local_connect_status) {
|
if (_local_connect_status) {
|
||||||
memcpy(msg->u.input.peer_connect_status, _local_connect_status, sizeof(UdpMsg::connect_status) * UDP_MSG_MAX_PLAYERS);
|
memcpy(msg->u.input.peer_connect_status, _local_connect_status, sizeof(UdpMsg::connect_status) * UDP_MSG_MAX_PLAYERS);
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,24 +184,17 @@ void UdpProtocol::EndPollLoop()
|
||||||
if (_remoteCheckSumsThisFrame.size())
|
if (_remoteCheckSumsThisFrame.size())
|
||||||
_remoteCheckSums.emplace(*_remoteCheckSumsThisFrame.rbegin());
|
_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
|
bool
|
||||||
UdpProtocol::OnLoopPoll(void *cookie)
|
UdpProtocol::NetworkIdle()
|
||||||
{
|
{
|
||||||
if (!_udp) {
|
if (!_peer) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int now = Platform::GetCurrentTimeMS();
|
unsigned int now = Platform::GetCurrentTimeMS();
|
||||||
unsigned int next_interval;
|
unsigned int next_interval;
|
||||||
|
|
||||||
PumpSendQueue();
|
|
||||||
switch (_current_state) {
|
switch (_current_state) {
|
||||||
case Syncing:
|
case Syncing:
|
||||||
next_interval = (_state.sync.roundtrips_remaining == NUM_SYNC_PACKETS) ? SYNC_FIRST_RETRY_INTERVAL : SYNC_RETRY_INTERVAL;
|
next_interval = (_state.sync.roundtrips_remaining == NUM_SYNC_PACKETS) ? SYNC_FIRST_RETRY_INTERVAL : SYNC_RETRY_INTERVAL;
|
||||||
|
@ -255,35 +227,10 @@ UdpProtocol::OnLoopPoll(void *cookie)
|
||||||
_state.running.last_network_stats_interval = now;
|
_state.running.last_network_stats_interval = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_last_send_time && _last_send_time + KEEP_ALIVE_INTERVAL < now) {
|
|
||||||
Log("Sending keep alive packet\n");
|
|
||||||
SendMsg(new UdpMsg(UdpMsg::KeepAlive));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_disconnect_timeout && _disconnect_notify_start &&
|
|
||||||
!_disconnect_notify_sent && (_last_recv_time + _disconnect_notify_start < now)) {
|
|
||||||
Log("Endpoint has stopped receiving packets for %d ms. Sending notification.\n", _disconnect_notify_start);
|
|
||||||
Event e(Event::NetworkInterrupted);
|
|
||||||
e.u.network_interrupted.disconnect_timeout = _disconnect_timeout - _disconnect_notify_start;
|
|
||||||
QueueEvent(e);
|
|
||||||
_disconnect_notify_sent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_disconnect_timeout && (_last_recv_time + _disconnect_timeout < now)) {
|
|
||||||
if (!_disconnect_event_sent) {
|
|
||||||
Log("Endpoint has stopped receiving packets for %d ms. Disconnecting.\n", _disconnect_timeout);
|
|
||||||
QueueEvent(Event(Event::Disconnected));
|
|
||||||
_disconnect_event_sent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Disconnected:
|
case Disconnected:
|
||||||
if (_shutdown_timeout < now) {
|
break;
|
||||||
Log("Shutting down udp connection.\n");
|
|
||||||
_udp = NULL;
|
|
||||||
_shutdown_timeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +242,7 @@ void
|
||||||
UdpProtocol::Disconnect()
|
UdpProtocol::Disconnect()
|
||||||
{
|
{
|
||||||
_current_state = Disconnected;
|
_current_state = Disconnected;
|
||||||
_shutdown_timeout = Platform::GetCurrentTimeMS() + UDP_SHUTDOWN_TIMER;
|
_peer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -311,28 +258,21 @@ UdpProtocol::SendSyncRequest()
|
||||||
void
|
void
|
||||||
UdpProtocol::SendMsg(UdpMsg *msg)
|
UdpProtocol::SendMsg(UdpMsg *msg)
|
||||||
{
|
{
|
||||||
|
const int size = msg->PacketSize();
|
||||||
LogMsg("send", msg);
|
LogMsg("send", msg);
|
||||||
|
|
||||||
_packets_sent++;
|
_packets_sent++;
|
||||||
_last_send_time = Platform::GetCurrentTimeMS();
|
_last_send_time = Platform::GetCurrentTimeMS();
|
||||||
_bytes_sent += msg->PacketSize();
|
_bytes_sent += size;
|
||||||
|
|
||||||
msg->hdr.magic = _magic_number;
|
msg->hdr.magic = _magic_number;
|
||||||
msg->hdr.sequence_number = _next_send_seq++;
|
msg->hdr.sequence_number = _next_send_seq++;
|
||||||
|
|
||||||
_send_queue.push(QueueEntry(Platform::GetCurrentTimeMS(), _peer_addr, msg));
|
ENetPacket* pkt = enet_packet_create(msg, size, 0);
|
||||||
PumpSendQueue();
|
enet_peer_send(_peer, ENET_CHANNEL_ID, pkt);
|
||||||
}
|
// TODO: flush packets?
|
||||||
|
// TODO: get rid of the extra heap allocation here...
|
||||||
bool
|
delete msg;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -347,9 +287,7 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
|
||||||
&UdpProtocol::OnInput, /* Input */
|
&UdpProtocol::OnInput, /* Input */
|
||||||
&UdpProtocol::OnQualityReport, /* QualityReport */
|
&UdpProtocol::OnQualityReport, /* QualityReport */
|
||||||
&UdpProtocol::OnQualityReply, /* QualityReply */
|
&UdpProtocol::OnQualityReply, /* QualityReply */
|
||||||
&UdpProtocol::OnKeepAlive, /* KeepAlive */
|
|
||||||
&UdpProtocol::OnInputAck, /* InputAck */
|
&UdpProtocol::OnInputAck, /* InputAck */
|
||||||
&UdpProtocol::OnChat, /* InputAck */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// filter out messages that don't match what we expect
|
// filter out messages that don't match what we expect
|
||||||
|
@ -379,10 +317,6 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
|
||||||
}
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
_last_recv_time = Platform::GetCurrentTimeMS();
|
_last_recv_time = Platform::GetCurrentTimeMS();
|
||||||
if (_disconnect_notify_sent && _current_state == Running) {
|
|
||||||
QueueEvent(Event(Event::NetworkResumed));
|
|
||||||
_disconnect_notify_sent = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +356,7 @@ UdpProtocol::QueueEvent(const UdpProtocol::Event &evt)
|
||||||
void
|
void
|
||||||
UdpProtocol::Synchronize()
|
UdpProtocol::Synchronize()
|
||||||
{
|
{
|
||||||
if (_udp) {
|
if (_peer) {
|
||||||
_current_state = Syncing;
|
_current_state = Syncing;
|
||||||
_state.sync.roundtrips_remaining = NUM_SYNC_PACKETS;
|
_state.sync.roundtrips_remaining = NUM_SYNC_PACKETS;
|
||||||
SendSyncRequest();
|
SendSyncRequest();
|
||||||
|
@ -470,18 +404,12 @@ UdpProtocol::LogMsg(const char *prefix, UdpMsg *msg)
|
||||||
case UdpMsg::QualityReply:
|
case UdpMsg::QualityReply:
|
||||||
Log("%s quality reply.\n", prefix);
|
Log("%s quality reply.\n", prefix);
|
||||||
break;
|
break;
|
||||||
case UdpMsg::KeepAlive:
|
|
||||||
Log("%s keep alive.\n", prefix);
|
|
||||||
break;
|
|
||||||
case UdpMsg::Input:
|
case UdpMsg::Input:
|
||||||
Log("%s game-compressed-input %d (+ %d bits).\n", prefix, msg->u.input.start_frame, msg->u.input.num_bits);
|
Log("%s game-compressed-input %d (+ %d bits).\n", prefix, msg->u.input.start_frame, msg->u.input.num_bits);
|
||||||
break;
|
break;
|
||||||
case UdpMsg::InputAck:
|
case UdpMsg::InputAck:
|
||||||
Log("%s input ack.\n", prefix);
|
Log("%s input ack.\n", prefix);
|
||||||
break;
|
break;
|
||||||
case UdpMsg::Chat:
|
|
||||||
Log("%s chat.\n", prefix);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
Log("Unknown UdpMsg type.");
|
Log("Unknown UdpMsg type.");
|
||||||
}
|
}
|
||||||
|
@ -559,17 +487,6 @@ UdpProtocol::OnSyncReply(UdpMsg *msg, int len)
|
||||||
bool
|
bool
|
||||||
UdpProtocol::OnInput(UdpMsg *msg, int len)
|
UdpProtocol::OnInput(UdpMsg *msg, int len)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* If a disconnect is requested, go ahead and disconnect now.
|
|
||||||
*/
|
|
||||||
bool disconnect_requested = msg->u.input.disconnect_requested;
|
|
||||||
if (disconnect_requested) {
|
|
||||||
if (_current_state != Disconnected && !_disconnect_event_sent) {
|
|
||||||
Log("Disconnecting endpoint on remote request.\n");
|
|
||||||
QueueEvent(Event(Event::Disconnected));
|
|
||||||
_disconnect_event_sent = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
/*
|
||||||
* Update the peer connection status if this peer is still considered to be part
|
* Update the peer connection status if this peer is still considered to be part
|
||||||
* of the network.
|
* of the network.
|
||||||
|
@ -580,7 +497,6 @@ UdpProtocol::OnInput(UdpMsg *msg, int len)
|
||||||
_peer_connect_status[i].disconnected = _peer_connect_status[i].disconnected || remote_status[i].disconnected;
|
_peer_connect_status[i].disconnected = _peer_connect_status[i].disconnected || remote_status[i].disconnected;
|
||||||
_peer_connect_status[i].last_frame = MAX(_peer_connect_status[i].last_frame, remote_status[i].last_frame);
|
_peer_connect_status[i].last_frame = MAX(_peer_connect_status[i].last_frame, remote_status[i].last_frame);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decompress the input.
|
* Decompress the input.
|
||||||
|
@ -701,23 +617,6 @@ UdpProtocol::OnQualityReply(UdpMsg *msg, int len)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
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
|
void
|
||||||
UdpProtocol::GetNetworkStats(struct GGPONetworkStats *s)
|
UdpProtocol::GetNetworkStats(struct GGPONetworkStats *s)
|
||||||
{
|
{
|
||||||
|
@ -755,65 +654,3 @@ UdpProtocol::RecommendFrameDelay()
|
||||||
// XXX: require idle input should be a configuration parameter
|
// XXX: require idle input should be a configuration parameter
|
||||||
return _timesync.recommend_frame_wait_duration(false);
|
return _timesync.recommend_frame_wait_duration(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
UdpProtocol::SetDisconnectTimeout(int timeout)
|
|
||||||
{
|
|
||||||
_disconnect_timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
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_
|
#ifndef _UDP_PROTO_H_
|
||||||
#define _UDP_PROTO_H_
|
#define _UDP_PROTO_H_
|
||||||
|
|
||||||
#include "poll.h"
|
#include "enet/enet.h"
|
||||||
#include "udp.h"
|
|
||||||
#include "udp_msg.h"
|
#include "udp_msg.h"
|
||||||
#include "game_input.h"
|
#include "game_input.h"
|
||||||
#include "timesync.h"
|
#include "timesync.h"
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
class UdpProtocol : public IPollSink
|
class UdpProtocol
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Stats {
|
struct Stats {
|
||||||
|
@ -29,7 +28,6 @@ public:
|
||||||
float av_remote_frame_advantage;
|
float av_remote_frame_advantage;
|
||||||
float av_local_frame_advantage;
|
float av_local_frame_advantage;
|
||||||
int send_queue_len;
|
int send_queue_len;
|
||||||
Udp::Stats udp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
|
@ -39,9 +37,6 @@ public:
|
||||||
Synchronizing,
|
Synchronizing,
|
||||||
Synchronzied,
|
Synchronzied,
|
||||||
Input,
|
Input,
|
||||||
Disconnected,
|
|
||||||
NetworkInterrupted,
|
|
||||||
NetworkResumed,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
|
@ -62,23 +57,23 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual bool OnLoopPoll(void *cookie);
|
bool NetworkIdle();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UdpProtocol();
|
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();
|
void Synchronize();
|
||||||
bool GetPeerConnectStatus(int id, int *frame);
|
bool GetPeerConnectStatus(int id, int *frame);
|
||||||
bool IsInitialized() { return _udp != NULL; }
|
bool IsInitialized() { return _peer != nullptr; }
|
||||||
bool IsSynchronized() { return _current_state == Running; }
|
bool IsSynchronized() { return _current_state == Running; }
|
||||||
bool IsRunning() { return _current_state == Running; }
|
bool IsRunning() { return _current_state == Running; }
|
||||||
void SendInput(GameInput &input);
|
void SendInput(GameInput &input);
|
||||||
void SendChat(const char* message);
|
|
||||||
void SendInputAck();
|
void SendInputAck();
|
||||||
bool HandlesMsg(sockaddr_in &from, UdpMsg *msg);
|
|
||||||
void OnMsg(UdpMsg *msg, int len);
|
void OnMsg(UdpMsg *msg, int len);
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
|
@ -87,10 +82,7 @@ public:
|
||||||
void SetLocalFrameNumber(int num);
|
void SetLocalFrameNumber(int num);
|
||||||
float RecommendFrameDelay();
|
float RecommendFrameDelay();
|
||||||
int RemoteFrameDelay()const;
|
int RemoteFrameDelay()const;
|
||||||
void SetDisconnectTimeout(int timeout);
|
|
||||||
void SetDisconnectNotifyStart(int timeout);
|
|
||||||
void SetFrameDelay(int delay);
|
void SetFrameDelay(int delay);
|
||||||
void ConsumeChat(std::function<void(const char*)> onChat);
|
|
||||||
void ApplyToEvents(std::function<void(UdpProtocol::Event&)> cb);
|
void ApplyToEvents(std::function<void(UdpProtocol::Event&)> cb);
|
||||||
void StartPollLoop();
|
void StartPollLoop();
|
||||||
void EndPollLoop();
|
void EndPollLoop();
|
||||||
|
@ -114,13 +106,11 @@ protected:
|
||||||
|
|
||||||
void UpdateNetworkStats(void);
|
void UpdateNetworkStats(void);
|
||||||
void QueueEvent(const UdpProtocol::Event &evt);
|
void QueueEvent(const UdpProtocol::Event &evt);
|
||||||
void ClearSendQueue(void);
|
|
||||||
void Log(const char *fmt, ...);
|
void Log(const char *fmt, ...);
|
||||||
void LogMsg(const char *prefix, UdpMsg *msg);
|
void LogMsg(const char *prefix, UdpMsg *msg);
|
||||||
void LogEvent(const char *prefix, const UdpProtocol::Event &evt);
|
void LogEvent(const char *prefix, const UdpProtocol::Event &evt);
|
||||||
void SendSyncRequest();
|
void SendSyncRequest();
|
||||||
void SendMsg(UdpMsg *msg);
|
void SendMsg(UdpMsg *msg);
|
||||||
void PumpSendQueue();
|
|
||||||
void SendPendingOutput();
|
void SendPendingOutput();
|
||||||
bool OnInvalid(UdpMsg *msg, int len);
|
bool OnInvalid(UdpMsg *msg, int len);
|
||||||
bool OnSyncRequest(UdpMsg *msg, int len);
|
bool OnSyncRequest(UdpMsg *msg, int len);
|
||||||
|
@ -129,27 +119,16 @@ protected:
|
||||||
bool OnInputAck(UdpMsg *msg, int len);
|
bool OnInputAck(UdpMsg *msg, int len);
|
||||||
bool OnQualityReport(UdpMsg *msg, int len);
|
bool OnQualityReport(UdpMsg *msg, int len);
|
||||||
bool OnQualityReply(UdpMsg *msg, int len);
|
bool OnQualityReply(UdpMsg *msg, int len);
|
||||||
bool OnKeepAlive(UdpMsg *msg, int len);
|
|
||||||
bool OnChat(UdpMsg *msg, int len);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*
|
/*
|
||||||
* Network transmission information
|
* Network transmission information
|
||||||
*/
|
*/
|
||||||
Udp *_udp;
|
ENetPeer *_peer;
|
||||||
sockaddr_in _peer_addr;
|
|
||||||
uint16 _magic_number;
|
uint16 _magic_number;
|
||||||
int _queue;
|
int _queue;
|
||||||
uint16 _remote_magic_number;
|
uint16 _remote_magic_number;
|
||||||
bool _connected;
|
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
|
* Stats
|
||||||
|
@ -194,11 +173,6 @@ protected:
|
||||||
GameInput _last_acked_input;
|
GameInput _last_acked_input;
|
||||||
unsigned int _last_send_time;
|
unsigned int _last_send_time;
|
||||||
unsigned int _last_recv_time;
|
unsigned int _last_recv_time;
|
||||||
unsigned int _shutdown_timeout;
|
|
||||||
unsigned int _disconnect_event_sent;
|
|
||||||
unsigned int _disconnect_timeout;
|
|
||||||
unsigned int _disconnect_notify_start;
|
|
||||||
bool _disconnect_notify_sent;
|
|
||||||
|
|
||||||
uint16 _next_send_seq;
|
uint16 _next_send_seq;
|
||||||
uint16 _next_recv_seq;
|
uint16 _next_recv_seq;
|
||||||
|
@ -212,7 +186,6 @@ protected:
|
||||||
* Event queue
|
* Event queue
|
||||||
*/
|
*/
|
||||||
RingBuffer<UdpProtocol::Event, 64> _event_queue;
|
RingBuffer<UdpProtocol::Event, 64> _event_queue;
|
||||||
std::vector<std::string> _chatMessages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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
|
|
|
@ -9,7 +9,7 @@
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\ggpo-x\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\enet\include;$(SolutionDir)dep\ggpo-x\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">$(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">$(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
|
||||||
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<Lib>
|
<Lib>
|
||||||
<AdditionalDependencies>$(RootBuildDir)ggpo-x\ggpo-x.lib;$(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>$(RootBuildDir)ggpo-x\ggpo-x.lib;$(RootBuildDir)enet\enet.lib;$(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalDependencies Condition="'$(Platform)'!='ARM64'">$(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies Condition="'$(Platform)'!='ARM64'">$(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Lib>
|
</Lib>
|
||||||
|
|
|
@ -7,12 +7,14 @@
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "digital_controller.h"
|
#include "digital_controller.h"
|
||||||
#include "ggponet.h"
|
#include "ggponet.h"
|
||||||
|
#include "enet/enet.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "host_settings.h"
|
#include "host_settings.h"
|
||||||
#include "pad.h"
|
#include "pad.h"
|
||||||
#include "spu.h"
|
#include "spu.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
#include <gsl/span>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
Log_SetChannel(Netplay);
|
Log_SetChannel(Netplay);
|
||||||
|
@ -30,6 +32,8 @@ struct Input
|
||||||
u32 button_data;
|
u32 button_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool InitializeEnet();
|
||||||
|
|
||||||
static bool NpAdvFrameCb(void* ctx, int flags);
|
static bool NpAdvFrameCb(void* ctx, int flags);
|
||||||
static bool NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame);
|
static bool NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame);
|
||||||
static bool NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_frames, int frame_to_load);
|
static bool NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_frames, int frame_to_load);
|
||||||
|
@ -37,6 +41,7 @@ static bool NpBeginGameCb(void* ctx, const char* game_name);
|
||||||
static void NpFreeBuffCb(void* ctx, void* buffer, int frame);
|
static void NpFreeBuffCb(void* ctx, void* buffer, int frame);
|
||||||
static bool NpOnEventCb(void* ctx, GGPOEvent* ev);
|
static bool NpOnEventCb(void* ctx, GGPOEvent* ev);
|
||||||
|
|
||||||
|
static GGPOPlayerHandle PlayerIdToGGPOHandle(s32 player_id);
|
||||||
static Input ReadLocalInput();
|
static Input ReadLocalInput();
|
||||||
static GGPOErrorCode AddLocalInput(Netplay::Input input);
|
static GGPOErrorCode AddLocalInput(Netplay::Input input);
|
||||||
static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags);
|
static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags);
|
||||||
|
@ -44,8 +49,20 @@ static void SetInputs(Input inputs[2]);
|
||||||
|
|
||||||
static void SetSettings();
|
static void SetSettings();
|
||||||
|
|
||||||
|
static bool CreateSystem(std::string game_path);
|
||||||
|
|
||||||
|
// ENet
|
||||||
|
static void ShutdownEnetHost();
|
||||||
|
static s32 GetFreePlayerId();
|
||||||
|
static s32 GetPlayerIdForPeer(const ENetPeer* peer);
|
||||||
|
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
|
// l = local, r = remote
|
||||||
static s32 Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred);
|
static s32 Start(s32 lhandle, u16 lport, const std::string& raddr, u16 rport, s32 ldelay, u32 pred, std::string game_path);
|
||||||
|
|
||||||
static void AdvanceFrame();
|
static void AdvanceFrame();
|
||||||
static void RunFrame();
|
static void RunFrame();
|
||||||
|
@ -62,13 +79,18 @@ static void Throttle();
|
||||||
// Desync Detection
|
// Desync Detection
|
||||||
static void GenerateChecksumForFrame(int* checksum, int frame, unsigned char* buffer, int buffer_size);
|
static void GenerateChecksumForFrame(int* checksum, int frame, unsigned char* buffer, int buffer_size);
|
||||||
static void GenerateDesyncReport(s32 desync_frame);
|
static void GenerateDesyncReport(s32 desync_frame);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Variables
|
// Variables
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static MemorySettingsInterface s_settings_overlay;
|
static MemorySettingsInterface s_settings_overlay;
|
||||||
|
|
||||||
static std::string s_game_path;
|
|
||||||
|
/// Enet
|
||||||
|
static ENetHost* s_enet_host;
|
||||||
|
static std::array<ENetPeer*, MAX_PLAYERS> s_enet_peers;
|
||||||
|
static s32 s_player_id;
|
||||||
|
|
||||||
static GGPOPlayerHandle s_local_handle = GGPO_INVALID_HANDLE;
|
static GGPOPlayerHandle s_local_handle = GGPO_INVALID_HANDLE;
|
||||||
static GGPONetworkStats s_last_net_stats{};
|
static GGPONetworkStats s_last_net_stats{};
|
||||||
|
@ -88,11 +110,301 @@ static s32 s_next_timesync_recovery_frame = -1;
|
||||||
|
|
||||||
// Netplay Impl
|
// Netplay Impl
|
||||||
|
|
||||||
s32 Netplay::Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred)
|
bool Netplay::CreateSystem(std::string game_path)
|
||||||
|
{
|
||||||
|
// close system if its already running
|
||||||
|
if (System::IsValid())
|
||||||
|
System::ShutdownSystem(false);
|
||||||
|
|
||||||
|
// fast boot the selected game and wait for the other player
|
||||||
|
auto param = SystemBootParameters(std::move(game_path));
|
||||||
|
param.override_fast_boot = true;
|
||||||
|
return System::BootSystem(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Netplay::InitializeEnet()
|
||||||
|
{
|
||||||
|
static bool enet_initialized = false;
|
||||||
|
int rc;
|
||||||
|
if (!enet_initialized && (rc = enet_initialize()) != 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("enet_initialize() returned %d", rc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atexit(enet_deinitialize);
|
||||||
|
enet_initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netplay::ShutdownEnetHost()
|
||||||
|
{
|
||||||
|
Log_DevPrint("Disconnecting all peers");
|
||||||
|
|
||||||
|
// forcefully disconnect all peers
|
||||||
|
// TODO: do we want to send disconnect requests and wait a bit?
|
||||||
|
for (u32 i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (s_enet_peers[i])
|
||||||
|
{
|
||||||
|
enet_peer_reset(s_enet_peers[i]);
|
||||||
|
s_enet_peers[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enet_host_destroy(s_enet_host);
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
if (s_enet_peers[i] == peer)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Netplay::GetFreePlayerId()
|
||||||
|
{
|
||||||
|
for (s32 i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (i != s_player_id && !s_enet_peers[i])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netplay::HandleEnetEvent(const ENetEvent* event)
|
||||||
|
{
|
||||||
|
switch (event->type)
|
||||||
|
{
|
||||||
|
case ENET_EVENT_TYPE_CONNECT:
|
||||||
|
{
|
||||||
|
// skip when it's one we set up ourselves, we're handling it in ConnectToLowerPeers().
|
||||||
|
if (GetPlayerIdForPeer(event->peer) >= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: the player ID should either come from the packet (for the non-first player),
|
||||||
|
// or be auto-assigned as below, for the connection to the first host
|
||||||
|
const s32 new_player_id = GetFreePlayerId();
|
||||||
|
Log_DevPrintf("Enet connect event: New client with id %d", new_player_id);
|
||||||
|
|
||||||
|
if (new_player_id < 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No free slots, disconnecting client");
|
||||||
|
enet_peer_disconnect(event->peer, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_enet_peers[new_player_id] = event->peer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENET_EVENT_TYPE_DISCONNECT:
|
||||||
|
{
|
||||||
|
const s32 player_id = GetPlayerIdForPeer(event->peer);
|
||||||
|
if (player_id < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: This one's gonna get kinda tricky... who do we orphan when they disconnect?
|
||||||
|
Log_WarningPrintf("ENet player %d disconnected", player_id);
|
||||||
|
Host::OnNetplayMessage(fmt::format("*** DISCONNECTED PLAYER {} ***", player_id));
|
||||||
|
ggpo_disconnect_player(s_ggpo, PlayerIdToGGPOHandle(player_id));
|
||||||
|
s_enet_peers[player_id] = nullptr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netplay::PollEnet(Common::Timer::Value until_time)
|
||||||
|
{
|
||||||
|
ENetEvent event;
|
||||||
|
|
||||||
|
u64 current_time = Common::Timer::GetCurrentValue();
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const u32 enet_timeout = (current_time >= until_time) ?
|
||||||
|
0 :
|
||||||
|
static_cast<u32>(Common::Timer::ConvertValueToMilliseconds(until_time - current_time));
|
||||||
|
const int res = enet_host_service(s_enet_host, &event, enet_timeout);
|
||||||
|
if (res > 0)
|
||||||
|
{
|
||||||
|
HandleEnetEvent(&event);
|
||||||
|
|
||||||
|
// make sure we get all events
|
||||||
|
current_time = Common::Timer::GetCurrentValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit once we're nonblocking
|
||||||
|
current_time = Common::Timer::GetCurrentValue();
|
||||||
|
if (enet_timeout == 0 || current_time >= until_time)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Netplay::ConnectToLowerPeers(gsl::span<const ENetAddress> peer_addresses)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < peer_addresses.size(); i++)
|
||||||
|
{
|
||||||
|
char ipstr[32];
|
||||||
|
if (enet_address_get_host_ip(&peer_addresses[i], ipstr, std::size(ipstr)) != 0)
|
||||||
|
ipstr[0] = 0;
|
||||||
|
Log_DevPrintf("Starting connection to peer %u at %s:%u", i, ipstr, peer_addresses[i].port);
|
||||||
|
|
||||||
|
DebugAssert(i != s_player_id);
|
||||||
|
s_enet_peers[i] = enet_host_connect(s_enet_host, &peer_addresses[i], NUM_ENET_CHANNELS, 0);
|
||||||
|
if (!s_enet_peers[i])
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("enet_host_connect() for peer %u failed", i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Netplay::WaitForPeerConnections()
|
||||||
|
{
|
||||||
|
static constexpr float MAX_CONNECT_TIME = 30.0f;
|
||||||
|
Common::Timer timeout;
|
||||||
|
|
||||||
|
const u32 clients_to_connect = MAX_PLAYERS - 1;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// TODO: Handle early shutdown/cancel request.
|
||||||
|
u32 num_connected_peers = 0;
|
||||||
|
for (s32 i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (i != s_player_id && s_enet_peers[i] && s_enet_peers[i]->state == ENET_PEER_STATE_CONNECTED)
|
||||||
|
num_connected_peers++;
|
||||||
|
}
|
||||||
|
if (num_connected_peers == clients_to_connect)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (timeout.GetTimeSeconds() >= MAX_CONNECT_TIME)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Peer connection timeout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Host::PumpMessagesOnCPUThread();
|
||||||
|
Host::DisplayLoadingScreen("Connected to netplay peers", 0, clients_to_connect, num_connected_peers);
|
||||||
|
|
||||||
|
const Common::Timer::Value poll_end_time =
|
||||||
|
Common::Timer::GetCurrentValue() + Common::Timer::ConvertMillisecondsToValue(16);
|
||||||
|
PollEnet(poll_end_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_InfoPrint("Peer connection complete.");
|
||||||
|
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();
|
SetSettings();
|
||||||
|
if (!InitializeEnet())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ENetAddress host_address;
|
||||||
|
host_address.host = ENET_HOST_ANY;
|
||||||
|
host_address.port = lport;
|
||||||
|
s_enet_host = enet_host_create(&host_address, MAX_PLAYERS - 1, NUM_ENET_CHANNELS, 0, 0);
|
||||||
|
if (!s_enet_host)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create enet host.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to any lower-ID'ed hosts.
|
||||||
|
// Eventually we'll assign these IDs as players connect, and everyone not starting it will get their ID sent back
|
||||||
|
s_player_id = lhandle - 1;
|
||||||
|
|
||||||
|
std::array<ENetAddress, MAX_PLAYERS> peer_addresses;
|
||||||
|
const u32 num_peer_addresses = s_player_id;
|
||||||
|
DebugAssert(num_peer_addresses == 0 || num_peer_addresses == 1);
|
||||||
|
if (num_peer_addresses == 1)
|
||||||
|
{
|
||||||
|
// TODO: rewrite this when we support more players
|
||||||
|
const u32 other_player_id = (lhandle == 1) ? 1 : 0;
|
||||||
|
if (enet_address_set_host_ip(&peer_addresses[other_player_id], raddr.c_str()) != 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to parse host: '%s'", raddr.c_str());
|
||||||
|
ShutdownEnetHost();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_addresses[other_player_id].port = rport;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create system.
|
||||||
|
if (!CreateSystem(std::move(game_path)))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create system.");
|
||||||
|
ShutdownEnetHost();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
InitializeFramePacing();
|
InitializeFramePacing();
|
||||||
|
|
||||||
|
// Connect to all peers.
|
||||||
|
if ((num_peer_addresses > 0 &&
|
||||||
|
!ConnectToLowerPeers(gsl::span<const ENetAddress>(peer_addresses).subspan(0, num_peer_addresses))) ||
|
||||||
|
!WaitForPeerConnections())
|
||||||
|
{
|
||||||
|
// System shutdown cleans up enet.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: since saving every frame during rollback loses us time to do actual gamestate iterations it might be better to
|
TODO: since saving every frame during rollback loses us time to do actual gamestate iterations it might be better to
|
||||||
hijack the update / save / load cycle to only save every confirmed frame only saving when actually needed.
|
hijack the update / save / load cycle to only save every confirmed frame only saving when actually needed.
|
||||||
|
@ -108,37 +420,51 @@ s32 Netplay::Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ld
|
||||||
|
|
||||||
GGPOErrorCode result;
|
GGPOErrorCode result;
|
||||||
|
|
||||||
result =
|
result = ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", MAX_PLAYERS, sizeof(Netplay::Input), lport, MAX_ROLLBACK_FRAMES);
|
||||||
ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", 2, sizeof(Netplay::Input), lport, MAX_ROLLBACK_FRAMES);
|
if (!GGPO_SUCCEEDED(result))
|
||||||
// result = ggpo_start_synctest(&s_ggpo, &cb, (char*)"asdf", 2, sizeof(Netplay::Input), 1);
|
{
|
||||||
|
Log_ErrorPrintf("ggpo_start_session() failed: %d", result);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ggpo_set_disconnect_timeout(s_ggpo, 2000);
|
ggpo_set_disconnect_timeout(s_ggpo, 2000);
|
||||||
ggpo_set_disconnect_notify_start(s_ggpo, 1000);
|
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 = {};
|
if (!s_enet_peers[i] && i != s_player_id)
|
||||||
GGPOPlayerHandle handle = 0;
|
continue;
|
||||||
|
|
||||||
player.size = sizeof(GGPOPlayer);
|
// This is *awful*. Need to merge the player IDs, enough of this indices-start-at-1 rubbish.
|
||||||
player.player_num = i;
|
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;
|
player.type = GGPO_PLAYERTYPE_LOCAL;
|
||||||
result = ggpo_add_player(s_ggpo, &player, &handle);
|
result = ggpo_add_player(s_ggpo, &player, &got_handle);
|
||||||
s_local_handle = handle;
|
if (GGPO_SUCCEEDED(result))
|
||||||
|
s_local_handle = got_handle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
player.type = GGPOPlayerType::GGPO_PLAYERTYPE_REMOTE;
|
player.type = GGPO_PLAYERTYPE_REMOTE;
|
||||||
StringUtil::Strlcpy(player.u.remote.ip_address, raddr.c_str(), std::size(player.u.remote.ip_address));
|
player.u.remote.peer = s_enet_peers[i];
|
||||||
player.u.remote.port = rport;
|
result = ggpo_add_player(s_ggpo, &player, &got_handle);
|
||||||
result = ggpo_add_player(s_ggpo, &player, &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_frame_delay(s_ggpo, s_local_handle, ldelay);
|
||||||
ggpo_set_manual_network_polling(s_ggpo, true);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +478,8 @@ void Netplay::CloseSession()
|
||||||
s_save_buffer_pool.clear();
|
s_save_buffer_pool.clear();
|
||||||
s_local_handle = GGPO_INVALID_HANDLE;
|
s_local_handle = GGPO_INVALID_HANDLE;
|
||||||
|
|
||||||
|
ShutdownEnetHost();
|
||||||
|
|
||||||
// Restore original settings.
|
// Restore original settings.
|
||||||
Host::Internal::SetNetplaySettingsLayer(nullptr);
|
Host::Internal::SetNetplaySettingsLayer(nullptr);
|
||||||
System::ApplySettings(false);
|
System::ApplySettings(false);
|
||||||
|
@ -258,8 +586,10 @@ void Netplay::Throttle()
|
||||||
const Common::Timer::Value sleep_period = Common::Timer::ConvertMillisecondsToValue(1);
|
const Common::Timer::Value sleep_period = Common::Timer::ConvertMillisecondsToValue(1);
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
// Poll network.
|
// TODO: make better, we can tell this function to stall until the next frame
|
||||||
ggpo_poll_network(s_ggpo);
|
PollEnet(0);
|
||||||
|
ggpo_network_idle(s_ggpo);
|
||||||
|
PollEnet(0);
|
||||||
|
|
||||||
current_time = Common::Timer::GetCurrentValue();
|
current_time = Common::Timer::GetCurrentValue();
|
||||||
if (current_time >= s_next_frame_time)
|
if (current_time >= s_next_frame_time)
|
||||||
|
@ -322,7 +652,12 @@ void Netplay::AdvanceFrame()
|
||||||
void Netplay::RunFrame()
|
void Netplay::RunFrame()
|
||||||
{
|
{
|
||||||
// housekeeping
|
// housekeeping
|
||||||
|
// TODO: get rid of double polling
|
||||||
|
PollEnet(0);
|
||||||
|
ggpo_network_idle(s_ggpo);
|
||||||
|
PollEnet(0);
|
||||||
ggpo_idle(s_ggpo);
|
ggpo_idle(s_ggpo);
|
||||||
|
|
||||||
// run game
|
// run game
|
||||||
auto result = GGPO_OK;
|
auto result = GGPO_OK;
|
||||||
int disconnect_flags = 0;
|
int disconnect_flags = 0;
|
||||||
|
@ -358,6 +693,11 @@ void Netplay::CollectInput(u32 slot, u32 bind, float value)
|
||||||
s_net_input[slot][bind] = value;
|
s_net_input[slot][bind] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GGPOPlayerHandle Netplay::PlayerIdToGGPOHandle(s32 player_id)
|
||||||
|
{
|
||||||
|
return player_id + 1;
|
||||||
|
}
|
||||||
|
|
||||||
Netplay::Input Netplay::ReadLocalInput()
|
Netplay::Input Netplay::ReadLocalInput()
|
||||||
{
|
{
|
||||||
// get controller data of the first controller (0 internally)
|
// get controller data of the first controller (0 internally)
|
||||||
|
@ -372,7 +712,6 @@ Netplay::Input Netplay::ReadLocalInput()
|
||||||
|
|
||||||
void Netplay::SendMsg(const char* msg)
|
void Netplay::SendMsg(const char* msg)
|
||||||
{
|
{
|
||||||
ggpo_client_chat(s_ggpo, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GGPOErrorCode Netplay::SyncInput(Netplay::Input inputs[2], int* disconnect_flags)
|
GGPOErrorCode Netplay::SyncInput(Netplay::Input inputs[2], int* disconnect_flags)
|
||||||
|
@ -414,13 +753,15 @@ void Netplay::StartNetplaySession(s32 local_handle, u16 local_port, std::string&
|
||||||
// dont want to start a session when theres already one going on.
|
// dont want to start a session when theres already one going on.
|
||||||
if (IsActive())
|
if (IsActive())
|
||||||
return;
|
return;
|
||||||
// set game path for later loading during the begin game callback
|
|
||||||
s_game_path = std::move(game_path);
|
|
||||||
// create session
|
// create session
|
||||||
int result = Netplay::Start(local_handle, local_port, remote_addr, remote_port, input_delay, MAX_ROLLBACK_FRAMES);
|
int result = Netplay::Start(local_handle, local_port, remote_addr, remote_port, input_delay, MAX_ROLLBACK_FRAMES, std::move(game_path));
|
||||||
// notify that the session failed
|
// notify that the session failed
|
||||||
if (result != GGPO_OK)
|
if (result != GGPO_OK)
|
||||||
|
{
|
||||||
Log_ErrorPrintf("Failed to Create Netplay Session! Error: %d", result);
|
Log_ErrorPrintf("Failed to Create Netplay Session! Error: %d", result);
|
||||||
|
System::ShutdownSystem(false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Load savestate if available
|
// Load savestate if available
|
||||||
|
@ -465,17 +806,6 @@ void Netplay::ExecuteNetplay()
|
||||||
|
|
||||||
bool Netplay::NpBeginGameCb(void* ctx, const char* game_name)
|
bool Netplay::NpBeginGameCb(void* ctx, const char* game_name)
|
||||||
{
|
{
|
||||||
// close system if its already running
|
|
||||||
if (System::IsValid())
|
|
||||||
System::ShutdownSystem(false);
|
|
||||||
// fast boot the selected game and wait for the other player
|
|
||||||
auto param = SystemBootParameters(s_game_path);
|
|
||||||
param.override_fast_boot = true;
|
|
||||||
if (!System::BootSystem(param))
|
|
||||||
{
|
|
||||||
StopNetplaySession();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SPU::SetAudioOutputMuted(true);
|
SPU::SetAudioOutputMuted(true);
|
||||||
// Fast Forward to Game Start if needed.
|
// Fast Forward to Game Start if needed.
|
||||||
while (System::GetInternalFrameNumber() < 2)
|
while (System::GetInternalFrameNumber() < 2)
|
||||||
|
@ -554,22 +884,9 @@ bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER:
|
case GGPOEventCode::GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER:
|
||||||
Host::OnNetplayMessage(fmt::format("Netplay Synchronized With Player: {}", ev->u.synchronized.player));
|
Host::OnNetplayMessage(fmt::format("Netplay Synchronized With Player: {}", ev->u.synchronized.player));
|
||||||
break;
|
break;
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_DISCONNECTED_FROM_PEER:
|
|
||||||
Host::OnNetplayMessage(fmt::format("Netplay Player: %d Disconnected", ev->u.disconnected.player));
|
|
||||||
break;
|
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_RUNNING:
|
case GGPOEventCode::GGPO_EVENTCODE_RUNNING:
|
||||||
Host::OnNetplayMessage("Netplay Is Running");
|
Host::OnNetplayMessage("Netplay Is Running");
|
||||||
break;
|
break;
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_INTERRUPTED:
|
|
||||||
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Interupted, Timeout: {}", ev->u.connection_interrupted.player,
|
|
||||||
ev->u.connection_interrupted.disconnect_timeout));
|
|
||||||
break;
|
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTION_RESUMED:
|
|
||||||
Host::OnNetplayMessage(fmt::format("Netplay Player: {} Connection Resumed", ev->u.connection_resumed.player));
|
|
||||||
break;
|
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_CHAT:
|
|
||||||
Host::OnNetplayMessage(ev->u.chat.msg);
|
|
||||||
break;
|
|
||||||
case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC:
|
case GGPOEventCode::GGPO_EVENTCODE_TIMESYNC:
|
||||||
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
|
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace Netplay {
|
namespace Netplay {
|
||||||
|
|
||||||
enum : u32
|
enum : s32
|
||||||
{
|
{
|
||||||
// Maximum number of emulated controllers.
|
// Maximum number of emulated controllers.
|
||||||
MAX_PLAYERS = 2,
|
MAX_PLAYERS = 2,
|
||||||
|
@ -13,6 +13,14 @@ enum : u32
|
||||||
MAX_ROLLBACK_FRAMES = 8,
|
MAX_ROLLBACK_FRAMES = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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,
|
void StartNetplaySession(s32 local_handle, u16 local_port, std::string& remote_addr, u16 remote_port, s32 input_delay,
|
||||||
std::string game_path);
|
std::string game_path);
|
||||||
void StopNetplaySession();
|
void StopNetplaySession();
|
||||||
|
|
|
@ -1089,6 +1089,9 @@ void EmuThread::startNetplaySession(int local_handle, quint16 local_port, const
|
||||||
auto remAddr = remote_addr.trimmed().toStdString();
|
auto remAddr = remote_addr.trimmed().toStdString();
|
||||||
auto gamePath = game_path.trimmed().toStdString();
|
auto gamePath = game_path.trimmed().toStdString();
|
||||||
Netplay::StartNetplaySession(local_handle, local_port, remAddr, remote_port, input_delay, gamePath);
|
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)
|
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 remote = "127.0.0.1";
|
||||||
std::string game = "D:\\PSX\\chd\\padtest.chd";
|
std::string game = "D:\\PSX\\chd\\padtest.chd";
|
||||||
Netplay::StartNetplaySession(h, port_base + h, remote, port_base + nh, 1, game);
|
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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -466,14 +466,14 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/,
|
||||||
ImGui::Text("%s: %d/%d", message, progress_value, progress_max);
|
ImGui::Text("%s: %d/%d", message, progress_value, progress_max);
|
||||||
ImGui::ProgressBar(static_cast<float>(progress_value) / static_cast<float>(progress_max - progress_min),
|
ImGui::ProgressBar(static_cast<float>(progress_value) / static_cast<float>(progress_max - progress_min),
|
||||||
ImVec2(-1.0f, 0.0f), "");
|
ImVec2(-1.0f, 0.0f), "");
|
||||||
Log_InfoPrintf("%s: %d/%d", message, progress_value, progress_max);
|
Log_DebugPrintf("%s: %d/%d", message, progress_value, progress_max);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const ImVec2 text_size(ImGui::CalcTextSize(message));
|
const ImVec2 text_size(ImGui::CalcTextSize(message));
|
||||||
ImGui::SetCursorPosX((width - text_size.x) / 2.0f);
|
ImGui::SetCursorPosX((width - text_size.x) / 2.0f);
|
||||||
ImGui::TextUnformatted(message);
|
ImGui::TextUnformatted(message);
|
||||||
Log_InfoPrintf("%s", message);
|
Log_DebugPrintf("%s", message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
Loading…
Reference in New Issue