(Netplay) Support for gathering client info and kicking

Client info and kicking (by the host) is now implemented through netplay_driver_ctl and can be used by future features.
This commit is contained in:
Cthulhu-throwaway 2022-05-13 22:28:52 -03:00
parent e9914d6605
commit 6d96df0e49
3 changed files with 292 additions and 123 deletions

View File

@ -2,7 +2,7 @@
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2016-2017 - Gregor Richards
* Copyright (C) 2021-2021 - Roberto V. Rampim
* Copyright (C) 2021-2022 - Roberto V. Rampim
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
@ -58,6 +58,7 @@ enum rarch_netplay_ctl_state
RARCH_NETPLAY_CTL_ENABLE_SERVER,
RARCH_NETPLAY_CTL_ENABLE_CLIENT,
RARCH_NETPLAY_CTL_DISABLE,
RARCH_NETPLAY_CTL_REFRESH_CLIENT_INFO,
RARCH_NETPLAY_CTL_IS_ENABLED,
RARCH_NETPLAY_CTL_IS_REPLAYING,
RARCH_NETPLAY_CTL_IS_SERVER,
@ -73,7 +74,31 @@ enum rarch_netplay_ctl_state
RARCH_NETPLAY_CTL_DISCONNECT,
RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL,
RARCH_NETPLAY_CTL_DESYNC_PUSH,
RARCH_NETPLAY_CTL_DESYNC_POP
RARCH_NETPLAY_CTL_DESYNC_POP,
RARCH_NETPLAY_CTL_KICK_CLIENT
};
/* The current status of a connection */
enum rarch_netplay_connection_mode
{
NETPLAY_CONNECTION_NONE = 0,
NETPLAY_CONNECTION_DELAYED_DISCONNECT,
/* The connection is dead, but data
is still waiting to be forwarded */
/* Initialization: */
NETPLAY_CONNECTION_INIT, /* Waiting for header */
NETPLAY_CONNECTION_PRE_NICK, /* Waiting for nick */
NETPLAY_CONNECTION_PRE_PASSWORD, /* Waiting for password */
NETPLAY_CONNECTION_PRE_INFO, /* Waiting for core/content info */
NETPLAY_CONNECTION_PRE_SYNC, /* Waiting for sync */
/* Ready: */
NETPLAY_CONNECTION_CONNECTED, /* Modes above this are connected */
NETPLAY_CONNECTION_SPECTATING, /* Spectator mode */
NETPLAY_CONNECTION_SLAVE, /* Playing in slave mode */
NETPLAY_CONNECTION_PLAYING /* Normal ready state */
};
/* Preferences for sharing digital devices */
@ -115,6 +140,14 @@ enum rarch_netplay_discovery_ctl_state
typedef struct netplay netplay_t;
typedef struct netplay_client_info
{
int32_t ping;
int id;
enum rarch_netplay_connection_mode mode;
char name[NETPLAY_NICK_LEN];
} netplay_client_info_t;
struct ad_packet
{
uint32_t header;
@ -237,8 +270,10 @@ typedef struct
struct netplay_rooms *rooms_data;
/* Used while Netplay is running */
netplay_t *data;
netplay_client_info_t *client_info;
/* Chat messages */
struct netplay_chat *chat;
size_t client_info_count;
#ifdef HAVE_NETPLAYDISCOVERY
size_t discovered_hosts_allocated;
/* LAN discovery sockets */

View File

@ -6045,7 +6045,14 @@ static bool netplay_get_cmd(netplay_t *netplay,
break;
case NETPLAY_CMD_PING_REQUEST:
answer_ping(netplay, connection);
{
answer_ping(netplay, connection);
/* If we are the server,
we should request our own ping after answering. */
if (netplay->is_server)
request_ping(netplay, connection);
}
break;
case NETPLAY_CMD_PING_RESPONSE:
@ -8480,6 +8487,94 @@ failure:
return false;
}
static size_t retrieve_client_info(netplay_t *netplay, netplay_client_info_t *buf)
{
size_t i, j = 0;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *conn = &netplay->connections[i];
/* We only want info from already connected clients. */
if (conn->active && conn->mode >= NETPLAY_CONNECTION_CONNECTED)
{
netplay_client_info_t *info = &buf[j++];
info->id = (int)i;
strlcpy(info->name, conn->nick, sizeof(info->name));
info->mode = conn->mode;
info->ping = conn->ping;
}
}
return j;
}
static bool kick_client_by_id(netplay_t *netplay, int client_id)
{
struct netplay_connection *connection = NULL;
/* Make sure the id is valid. */
if ((size_t)client_id >= netplay->connections_size)
return false;
connection = &netplay->connections[client_id];
/* We can only kick connected clients. */
if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED)
return false;
netplay_hangup(netplay, connection);
return true;
}
static bool kick_client_by_name(netplay_t *netplay, const char *client_name)
{
size_t i;
/* Find the connection with the name we want. */
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
/* We can only kick connected clients. */
if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED)
continue;
/* Kick the first client with a matched name. */
if (string_is_equal(client_name, connection->nick))
{
netplay_hangup(netplay, connection);
return true;
}
}
return false;
}
static bool kick_client_by_id_and_name(netplay_t *netplay,
int client_id, const char *client_name)
{
struct netplay_connection *connection = NULL;
/* Make sure the id is valid. */
if ((size_t)client_id >= netplay->connections_size)
return false;
connection = &netplay->connections[client_id];
/* We can only kick connected clients. */
if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED)
return false;
/* Make sure the name matches. */
if (!string_is_equal(client_name, connection->nick))
return false;
netplay_hangup(netplay, connection);
return true;
}
/**
* netplay_driver_ctl
*
@ -8487,157 +8582,219 @@ failure:
*/
bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
{
settings_t *settings = config_get_ptr();
net_driver_state_t *net_st = &networking_driver_st;
netplay_t *netplay = net_st->data;
bool ret = true;
settings_t *settings = config_get_ptr();
net_driver_state_t *net_st = &networking_driver_st;
netplay_t *netplay = net_st->data;
bool ret = true;
if (net_st->in_netplay)
return true;
net_st->in_netplay = true;
if (!netplay)
{
switch (state)
{
case RARCH_NETPLAY_CTL_ENABLE_SERVER:
net_st->netplay_enabled = true;
net_st->netplay_is_client = false;
goto done;
case RARCH_NETPLAY_CTL_ENABLE_CLIENT:
net_st->netplay_enabled = true;
net_st->netplay_is_client = true;
break;
case RARCH_NETPLAY_CTL_DISABLE:
net_st->netplay_enabled = false;
#ifdef HAVE_PRESENCE
{
presence_userdata_t userdata;
userdata.status = PRESENCE_NETPLAY_NETPLAY_STOPPED;
command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata);
}
#endif
goto done;
case RARCH_NETPLAY_CTL_IS_ENABLED:
ret = net_st->netplay_enabled;
goto done;
case RARCH_NETPLAY_CTL_IS_REPLAYING:
case RARCH_NETPLAY_CTL_IS_DATA_INITED:
ret = false;
goto done;
case RARCH_NETPLAY_CTL_IS_SERVER:
ret = net_st->netplay_enabled
&& !net_st->netplay_is_client;
goto done;
case RARCH_NETPLAY_CTL_IS_CONNECTED:
ret = false;
goto done;
case RARCH_NETPLAY_CTL_IS_SPECTATING:
case RARCH_NETPLAY_CTL_IS_PLAYING:
ret = false;
goto done;
default:
goto done;
}
}
switch (state)
{
case RARCH_NETPLAY_CTL_ENABLE_SERVER:
if (netplay)
{
ret = false;
break;
}
net_st->netplay_enabled = true;
net_st->netplay_is_client = false;
break;
case RARCH_NETPLAY_CTL_ENABLE_CLIENT:
case RARCH_NETPLAY_CTL_IS_DATA_INITED:
goto done;
if (netplay)
{
ret = false;
break;
}
net_st->netplay_enabled = true;
net_st->netplay_is_client = true;
break;
case RARCH_NETPLAY_CTL_DISABLE:
ret = false;
goto done;
if (netplay)
{
ret = false;
break;
}
net_st->netplay_enabled = false;
#ifdef HAVE_PRESENCE
{
presence_userdata_t userdata;
userdata.status = PRESENCE_NETPLAY_NETPLAY_STOPPED;
command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata);
}
#endif
break;
case RARCH_NETPLAY_CTL_REFRESH_CLIENT_INFO:
if (!netplay)
{
ret = false;
break;
}
if (!net_st->client_info)
{
net_st->client_info = (netplay_client_info_t*)calloc(
MAX_CLIENTS, sizeof(*net_st->client_info));
if (!net_st->client_info)
{
ret = false;
break;
}
}
net_st->client_info_count = retrieve_client_info(netplay,
net_st->client_info);
break;
case RARCH_NETPLAY_CTL_IS_ENABLED:
goto done;
ret = net_st->netplay_enabled;
break;
case RARCH_NETPLAY_CTL_IS_DATA_INITED:
ret = netplay != NULL;
break;
case RARCH_NETPLAY_CTL_IS_REPLAYING:
ret = netplay->is_replay;
goto done;
ret = netplay && netplay->is_replay;
break;
case RARCH_NETPLAY_CTL_IS_SERVER:
ret = net_st->netplay_enabled
&& !net_st->netplay_is_client;
goto done;
ret = net_st->netplay_enabled && !net_st->netplay_is_client;
break;
case RARCH_NETPLAY_CTL_IS_CONNECTED:
ret = netplay->is_connected;
goto done;
ret = netplay && netplay->is_connected;
break;
case RARCH_NETPLAY_CTL_IS_SPECTATING:
ret = netplay->self_mode == NETPLAY_CONNECTION_SPECTATING;
ret = netplay &&
(netplay->self_mode == NETPLAY_CONNECTION_SPECTATING);
break;
case RARCH_NETPLAY_CTL_IS_PLAYING:
ret = netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE;
ret = netplay &&
(netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE);
break;
case RARCH_NETPLAY_CTL_POST_FRAME:
if (!netplay)
break;
netplay_post_frame(netplay);
/* If we're disconnected, deinitialize */
if (!netplay->is_server && !netplay->connections[0].active)
netplay_disconnect(netplay);
/* If we're disconnected, deinitialize */
if (!netplay->is_server && !netplay->connections[0].active)
netplay_disconnect(netplay);
break;
case RARCH_NETPLAY_CTL_PRE_FRAME:
ret = netplay_pre_frame(
if (netplay)
ret = netplay_pre_frame(
settings->bools.netplay_public_announce,
netplay->mitm_pending != NULL,
netplay);
goto done;
break;
case RARCH_NETPLAY_CTL_GAME_WATCH:
netplay_toggle_play_spectate(netplay);
if (netplay)
netplay_toggle_play_spectate(netplay);
else
ret = false;
break;
case RARCH_NETPLAY_CTL_PLAYER_CHAT:
netplay_input_chat(netplay);
if (netplay)
netplay_input_chat(netplay);
else
ret = false;
break;
case RARCH_NETPLAY_CTL_ALLOW_PAUSE:
ret = netplay->allow_pausing;
ret = !netplay || netplay->allow_pausing;
break;
case RARCH_NETPLAY_CTL_PAUSE:
if (netplay->local_paused != true)
if (netplay && !netplay->local_paused)
netplay_frontend_paused(netplay, true);
break;
case RARCH_NETPLAY_CTL_UNPAUSE:
if (netplay->local_paused != false)
if (netplay && netplay->local_paused)
netplay_frontend_paused(netplay, false);
break;
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
netplay_load_savestate(netplay, (retro_ctx_serialize_info_t*)data, true);
if (netplay)
netplay_load_savestate(netplay,
(retro_ctx_serialize_info_t*)data, true);
break;
case RARCH_NETPLAY_CTL_RESET:
netplay_core_reset(netplay);
if (netplay)
netplay_core_reset(netplay);
break;
case RARCH_NETPLAY_CTL_DISCONNECT:
ret = true;
if (netplay)
netplay_disconnect(netplay);
goto done;
case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL:
netplay_announce_nat_traversal(netplay);
goto done;
case RARCH_NETPLAY_CTL_DESYNC_PUSH:
netplay->desync++;
else
ret = false;
break;
case RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL:
if (netplay)
netplay_announce_nat_traversal(netplay);
break;
case RARCH_NETPLAY_CTL_DESYNC_PUSH:
if (netplay)
netplay->desync++;
break;
case RARCH_NETPLAY_CTL_DESYNC_POP:
if (netplay->desync)
if (netplay && netplay->desync)
{
netplay->desync--;
if (!netplay->desync)
if (!(--netplay->desync))
netplay_load_savestate(netplay, NULL, true);
}
break;
default:
case RARCH_NETPLAY_CTL_KICK_CLIENT:
/* Only the server should be able to kick others. */
if (netplay && netplay->is_server)
{
netplay_client_info_t *client = (netplay_client_info_t*)data;
if (!client)
{
ret = false;
break;
}
if (client->id >= 0 && !string_is_empty(client->name))
ret = kick_client_by_id_and_name(netplay,
client->id, client->name);
else if (client->id >= 0)
ret = kick_client_by_id(netplay, client->id);
else if (!string_is_empty(client->name))
ret = kick_client_by_name(netplay, client->name);
else
ret = false;
}
else
{
ret = false;
}
break;
case RARCH_NETPLAY_CTL_NONE:
default:
ret = false;
break;
}
done:
net_st->in_netplay = false;
return ret;
}

View File

@ -2,7 +2,7 @@
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2016-2017 - Gregor Richards
* Copyright (C) 2021-2021 - Roberto V. Rampim
* Copyright (C) 2021-2022 - Roberto V. Rampim
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
@ -246,29 +246,6 @@ enum rarch_netplay_share_preference
NETPLAY_SHARE_ANALOG_AVERAGE = 0x40
};
/* The current status of a connection */
enum rarch_netplay_connection_mode
{
NETPLAY_CONNECTION_NONE = 0,
NETPLAY_CONNECTION_DELAYED_DISCONNECT,
/* The connection is dead, but data
is still waiting to be forwarded */
/* Initialization: */
NETPLAY_CONNECTION_INIT, /* Waiting for header */
NETPLAY_CONNECTION_PRE_NICK, /* Waiting for nick */
NETPLAY_CONNECTION_PRE_PASSWORD, /* Waiting for password */
NETPLAY_CONNECTION_PRE_INFO, /* Waiting for core/content info */
NETPLAY_CONNECTION_PRE_SYNC, /* Waiting for sync */
/* Ready: */
NETPLAY_CONNECTION_CONNECTED, /* Modes above this are connected */
NETPLAY_CONNECTION_SPECTATING, /* Spectator mode */
NETPLAY_CONNECTION_SLAVE, /* Playing in slave mode */
NETPLAY_CONNECTION_PLAYING /* Normal ready state */
};
enum rarch_netplay_stall_reason
{
NETPLAY_STALL_NONE = 0,