ggpo: support application messages

This commit is contained in:
Flyinghead 2021-10-18 16:26:24 +02:00
parent 7fb85e5b25
commit 59566eb363
9 changed files with 116 additions and 6 deletions

View File

@ -259,6 +259,12 @@ typedef struct {
* structure above for more information.
*/
bool (__cdecl *on_event)(GGPOEvent *info);
/*
* on_message - Delivers an application message. Allows arbitrary data to be
* exchanged with peers. This callback is optional.
*/
void (__cdecl *on_message)(unsigned char *msg, int len);
} GGPOSessionCallbacks;
/*
@ -593,6 +599,18 @@ GGPO_API void __cdecl ggpo_logv(GGPOSession *,
const char *fmt,
va_list args);
/*
* ggpo_send_message --
*
* Sends application-specific data to other peers. The message is
* delivered by the on_user_message callback.
* If spectators is true, the message is also sent to spectators.
*/
GGPO_API GGPOErrorCode __cdecl ggpo_send_message(GGPOSession *,
const void *msg,
int len,
bool spectators);
#ifdef __cplusplus
};
#endif

View File

@ -26,6 +26,7 @@ struct GGPOSession {
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectTimeout(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout) { return GGPO_ERRORCODE_UNSUPPORTED; }
virtual GGPOErrorCode SendMessage(const void *msg, int len, bool spectators) { return GGPO_ERRORCODE_UNSUPPORTED; }
};
typedef struct GGPOSession Quark, IQuarkBackend; /* XXX: nuke this */

View File

@ -392,6 +392,16 @@ Peer2PeerBackend::OnUdpProtocolPeerEvent(UdpProtocol::Event &evt, int queue)
DisconnectPlayer(QueueToPlayerHandle(queue));
break;
case UdpProtocol::Event::AppData:
if (evt.u.app_data.spectators)
for (int i = 0; i < _num_spectators; i++)
if (_spectators[i].IsInitialized())
_spectators[i].SendAppData(evt.u.app_data.data, evt.u.app_data.size, true);
Log("calling on_message callback with %d bytes", evt.u.app_data.size);
if (_callbacks.on_message != nullptr)
_callbacks.on_message(evt.u.app_data.data, evt.u.app_data.size);
break;
default:
break;
}
@ -635,3 +645,20 @@ Peer2PeerBackend::CheckInitialSync()
_synchronizing = false;
}
}
GGPOErrorCode Peer2PeerBackend::SendMessage(const void *msg, int len, bool spectators)
{
if (_synchronizing)
return GGPO_ERRORCODE_NOT_SYNCHRONIZED;
if (len > MAX_APPDATA_SIZE)
return GGPO_ERRORCODE_INVALID_REQUEST;
for (int i = 0; i < _num_players; i++)
if (_endpoints[i].IsInitialized())
_endpoints[i].SendAppData(msg, len, spectators);
if (spectators)
for (int i = 0; i < _num_spectators; i++)
if (_spectators[i].IsInitialized())
_spectators[i].SendAppData(msg, len, spectators);
return GGPO_ERRORCODE_SUCCESS;
}

View File

@ -33,6 +33,7 @@ public:
virtual GGPOErrorCode SetFrameDelay(GGPOPlayerHandle player, int delay);
virtual GGPOErrorCode SetDisconnectTimeout(int timeout);
virtual GGPOErrorCode SetDisconnectNotifyStart(int timeout);
GGPOErrorCode SendMessage(const void *msg, int len, bool spectators) override;
public:
virtual void OnMsg(sockaddr_in &from, UdpMsg *msg, int len);

View File

@ -167,6 +167,13 @@ SpectatorBackend::OnUdpProtocolEvent(UdpProtocol::Event &evt)
}
break;
case UdpProtocol::Event::AppData:
Log("calling on_message callback with %d bytes", evt.u.app_data.size);
if (_callbacks.on_message != nullptr)
_callbacks.on_message(evt.u.app_data.data, evt.u.app_data.size);
break;
default:
break;
}

View File

@ -277,3 +277,13 @@ GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
}
}
GGPOErrorCode ggpo_send_message(GGPOSession *ggpo,
const void *msg,
int len,
bool spectators)
{
if (ggpo == nullptr)
return GGPO_ERRORCODE_INVALID_SESSION;
return ggpo->SendMessage(msg, len, spectators);
}

View File

@ -11,6 +11,7 @@
#define MAX_COMPRESSED_BITS 4096
#define UDP_MSG_MAX_PLAYERS 4
#define MAX_VERIFICATION_SIZE 256
#define MAX_APPDATA_SIZE 512
#pragma pack(push, 1)
@ -25,6 +26,7 @@ struct UdpMsg
QualityReply = 5,
KeepAlive = 6,
InputAck = 7,
AppData = 8
};
struct connect_status {
@ -76,6 +78,12 @@ struct UdpMsg
int ack_frame:31;
} input_ack;
struct {
uint16 size;
uint8 spectators;
uint8 data[MAX_APPDATA_SIZE];
} app_data;
} u;
int verification_size = 0;
@ -86,8 +94,6 @@ public:
}
int PayloadSize() {
int size;
switch (hdr.type) {
case SyncRequest: return (int)(&u.sync_request.verification[0] - (uint8 *)&u) + verification_size;
case SyncReply: return sizeof(u.sync_reply);
@ -96,9 +102,12 @@ public:
case InputAck: return sizeof(u.input_ack);
case KeepAlive: return 0;
case Input:
size = (int)((char *)&u.input.bits - (char *)&u.input);
size += (u.input.num_bits + 7) / 8;
return size;
{
int size = (int)((char *)&u.input.bits - (char *)&u.input);
size += (u.input.num_bits + 7) / 8;
return size;
}
case AppData: return sizeof(u.app_data) - sizeof(u.app_data.data) + u.app_data.size;
}
ASSERT(false);
return 0;

View File

@ -328,6 +328,7 @@ UdpProtocol::OnMsg(UdpMsg *msg, int len)
&UdpProtocol::OnQualityReply, /* QualityReply */
&UdpProtocol::OnKeepAlive, /* KeepAlive */
&UdpProtocol::OnInputAck, /* InputAck */
&UdpProtocol::OnAppData, /* AppData */
};
// filter out messages that don't match what we expect
@ -458,6 +459,9 @@ UdpProtocol::LogMsg(const char *prefix, UdpMsg *msg)
case UdpMsg::InputAck:
Log("%s input ack.\n", prefix);
break;
case UdpMsg::AppData:
Log("%s app data (%d bytes).\n", prefix, msg->u.app_data.size);
break;
default:
ASSERT(false && "Unknown UdpMsg type.");
}
@ -794,3 +798,28 @@ UdpProtocol::ClearSendQueue()
_send_queue.pop();
}
}
void UdpProtocol::SendAppData(const void *data, int len, bool spectators)
{
if (_udp == nullptr)
return;
if (_current_state != Synchronzied && _current_state != Running)
return;
UdpMsg *msg = new UdpMsg(UdpMsg::AppData);
msg->u.app_data.spectators = spectators;
msg->u.app_data.size = len;
memcpy(msg->u.app_data.data, data, len);
SendMsg(msg);
}
bool UdpProtocol::OnAppData(UdpMsg *msg, int len)
{
UdpProtocol::Event evt(UdpProtocol::Event::AppData);
evt.u.app_data.spectators = msg->u.app_data.spectators != 0;
evt.u.app_data.size = msg->u.app_data.size;
memcpy(evt.u.app_data.data, msg->u.app_data.data, msg->u.app_data.size);
QueueEvent(evt);
return true;
}

View File

@ -38,6 +38,7 @@ public:
Disconnected,
NetworkInterrupted,
NetworkResumed,
AppData,
};
Type type;
@ -52,6 +53,11 @@ public:
struct {
int disconnect_timeout;
} network_interrupted;
struct {
bool spectators;
int size;
uint8 data[MAX_APPDATA_SIZE];
} app_data;
} u;
Event(Type t = Unknown) : type(t) { }
@ -76,6 +82,7 @@ public:
bool HandlesMsg(sockaddr_in &from, UdpMsg *msg);
void OnMsg(UdpMsg *msg, int len);
void Disconnect();
void SendAppData(const void *data, int len, bool spectators);
void GetNetworkStats(struct GGPONetworkStats *stats);
bool GetEvent(UdpProtocol::Event &e);
@ -117,7 +124,7 @@ protected:
void SendSyncRequest();
void SendMsg(UdpMsg *msg);
void PumpSendQueue();
void DispatchMsg(uint8 *buffer, int len);
// void DispatchMsg(uint8 *buffer, int len);
void SendPendingOutput();
bool OnInvalid(UdpMsg *msg, int len);
bool OnSyncRequest(UdpMsg *msg, int len);
@ -127,6 +134,7 @@ protected:
bool OnQualityReport(UdpMsg *msg, int len);
bool OnQualityReply(UdpMsg *msg, int len);
bool OnKeepAlive(UdpMsg *msg, int len);
bool OnAppData(UdpMsg *msg, int len);
protected:
/*