mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
patch lobby chat
This commit is contained in:
@@ -42,6 +42,18 @@ remains useful for debugging and for hosts that prefer fixed lag.
|
||||
|
||||
## Seats vs session slots
|
||||
|
||||
Lobby chat is masked for profanity and slurs on every path: the server
|
||||
filters relayed lines, and every client runs recomp-net's
|
||||
`rnet_chat_filter_apply` on each line it puts in its ring -- so LAN rooms
|
||||
(host-relayed `MOTK5 CHAT`) are filtered without a server. The list is
|
||||
`lib/recomp-net/data/chat_filter_words.txt`; `RNET_CHAT_FILTER=0` disables
|
||||
the client pass.
|
||||
|
||||
The lobby browser also carries a per-game server chat (server op
|
||||
`server_chat`, relayed to every client listed for the same title) and lists
|
||||
only the players online for this title; both are online-only, since a LAN
|
||||
room has no server. The same emoji and profanity handling applies.
|
||||
|
||||
Players can move themselves in the lobby and ask to swap seats (online via
|
||||
the server's `seat_move` / `seat_swap_request` / `seat_swap_answer`; on LAN
|
||||
via `MOTK5 SEATMOVE` / `SWAPASK` / `SWAPANS` / `SWAPRES` relayed by the host),
|
||||
|
||||
+1
-1
Submodule lib/recomp-net updated: e9c8e9377c...30772f6e78
@@ -68,6 +68,9 @@ typedef struct PsxLobbyOnlinePlayer {
|
||||
/* First 8 characters of the player's connection id: enough to tell
|
||||
* "which row is me" without publishing whole ids to browsers. */
|
||||
char tag[12];
|
||||
/* The title that player is browsing for; rows of other titles are
|
||||
* dropped at parse time when this client has a game identity. */
|
||||
char game_name[PSX_LOBBY_NAME_LEN];
|
||||
} PsxLobbyOnlinePlayer;
|
||||
#define PSX_LOBBY_MAX_ONLINE 64
|
||||
|
||||
@@ -398,6 +401,12 @@ void psx_lobby_seat_swap_clear(void);
|
||||
/* Lobby chat. send: 0 when queued (the line appears via the server echo).
|
||||
* count/get read the ring, oldest first; cleared on create/join/leave. */
|
||||
int psx_lobby_send_chat(const char *text);
|
||||
|
||||
/* Server chat: per-game, outside any room (op server_chat). Its own ring,
|
||||
* cleared on disconnect; lines are masked on arrival like lobby chat. */
|
||||
int psx_lobby_send_server_chat(const char *text);
|
||||
int psx_lobby_server_chat_count(void);
|
||||
int psx_lobby_server_chat_get(int index, PsxLobbyChatMsg *out);
|
||||
int psx_lobby_chat_count(void);
|
||||
int psx_lobby_chat_get(int index, PsxLobbyChatMsg *out);
|
||||
void psx_lobby_chat_clear(void);
|
||||
|
||||
@@ -56,6 +56,9 @@ extern "C" void psx_event_step_conservative_env_init(void);
|
||||
#include "psx_netplay_rb.h"
|
||||
#include "psx_selfcheck.h"
|
||||
#include "psx_lobby_client.h"
|
||||
#if defined(PSX_HAS_RECOMP_NET)
|
||||
#include "recomp_net/chat_filter.h" /* chat profanity mask, LAN rooms too */
|
||||
#endif
|
||||
#include "spu.h"
|
||||
#include "audio_trace.h"
|
||||
#include "spu_shadow.h"
|
||||
@@ -8084,6 +8087,11 @@ namespace {
|
||||
std::snprintf(m.player_id, sizeof(m.player_id), "%s", player_id ? player_id : "");
|
||||
std::snprintf(m.from, sizeof(m.from), "%s", from ? from : "");
|
||||
std::snprintf(m.text, sizeof(m.text), "%s", text);
|
||||
/* A LAN room has no server to mask for it: every peer masks the
|
||||
* line as it lands in the ring, the host included. */
|
||||
#if defined(PSX_HAS_RECOMP_NET)
|
||||
if (!is_system) (void)rnet_chat_filter_apply(m.text, sizeof(m.text));
|
||||
#endif
|
||||
m.is_system = is_system ? 1 : 0;
|
||||
++g_lnch_lan_chat_seq;
|
||||
}
|
||||
@@ -10032,6 +10040,31 @@ namespace {
|
||||
out->seq = msg.seq;
|
||||
return 1;
|
||||
}
|
||||
/* Server chat: per-game, online only. A LAN room has no server and no
|
||||
* wider audience, so the panel is hidden there (send refuses, count 0). */
|
||||
int ae_np_server_chat_send(void*, const char* text) {
|
||||
if (g_lnch_hosting_lan || g_lnch_joined_lan || !psx_lobby_connected()) return -1;
|
||||
char line[256];
|
||||
ae_np_chat_sanitize(text, line, sizeof(line));
|
||||
if (!line[0]) return -1;
|
||||
return psx_lobby_send_server_chat(line);
|
||||
}
|
||||
int ae_np_server_chat_count(void*) {
|
||||
if (g_lnch_hosting_lan || g_lnch_joined_lan) return 0;
|
||||
return psx_lobby_server_chat_count();
|
||||
}
|
||||
int ae_np_server_chat_get(void*, int index, RecompLauncherCNetplayChatMessage* out) {
|
||||
if (!out) return 0;
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
PsxLobbyChatMsg msg{};
|
||||
if (!psx_lobby_server_chat_get(index, &msg)) return 0;
|
||||
std::snprintf(out->from, sizeof(out->from), "%s", msg.from);
|
||||
std::snprintf(out->text, sizeof(out->text), "%s", msg.text);
|
||||
out->is_local = msg.is_local;
|
||||
out->is_system = msg.is_system;
|
||||
out->seq = msg.seq;
|
||||
return 1;
|
||||
}
|
||||
int ae_np_guest_memcard_set(void*, int allow) {
|
||||
if (g_lnch_joined_lan) return -1;
|
||||
if (!g_lnch_hosting_lan && psx_lobby_in_lobby() && !psx_lobby_is_host())
|
||||
@@ -12014,6 +12047,9 @@ namespace {
|
||||
g_lnch_netplay_callbacks.host_can_spectate = ae_np_host_can_spectate;
|
||||
g_lnch_netplay_callbacks.online_count = ae_np_online_count;
|
||||
g_lnch_netplay_callbacks.online_get = ae_np_online_get;
|
||||
g_lnch_netplay_callbacks.server_chat_send = ae_np_server_chat_send;
|
||||
g_lnch_netplay_callbacks.server_chat_count = ae_np_server_chat_count;
|
||||
g_lnch_netplay_callbacks.server_chat_get = ae_np_server_chat_get;
|
||||
g_lnch_netplay_callbacks.seat_move_self = ae_np_seat_move_self;
|
||||
g_lnch_netplay_callbacks.seat_swap_request = ae_np_seat_swap_request;
|
||||
g_lnch_netplay_callbacks.seat_swap_incoming = ae_np_seat_swap_incoming;
|
||||
|
||||
@@ -74,6 +74,9 @@ int psx_lobby_set_match_caps(const PsxLobbyMatchCaps *c) { (void)c; return -1;
|
||||
int psx_lobby_member_count(void) { return 0; }
|
||||
int psx_lobby_member_get(int index, PsxLobbyMember *out) { (void)index; (void)out; return 0; }
|
||||
int psx_lobby_member_latency_ms(int slot) { (void)slot; return -1; }
|
||||
int psx_lobby_send_server_chat(const char *text) { (void)text; return -1; }
|
||||
int psx_lobby_server_chat_count(void) { return 0; }
|
||||
int psx_lobby_server_chat_get(int index, PsxLobbyChatMsg *out) { (void)index; (void)out; return 0; }
|
||||
void psx_lobby_resume_waiting_room_rtt(void) {}
|
||||
int psx_lobby_member_is_host(const PsxLobbyMember *member)
|
||||
{
|
||||
@@ -149,6 +152,7 @@ void psx_lobby_clear_launch_pending(void) {}
|
||||
#include "recomp_net/ice_rtt.h"
|
||||
#include "recomp_net/lan_beacon.h"
|
||||
#include "recomp_net/rtt_probe.h"
|
||||
#include "recomp_net/chat_filter.h"
|
||||
#include "host_time.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -226,6 +230,11 @@ typedef struct {
|
||||
int chat_head;
|
||||
int chat_count;
|
||||
uint32_t chat_seq;
|
||||
/* Server (per-game) chat: a second ring with its own sequence. */
|
||||
PsxLobbyChatMsg schat[PSX_LOBBY_CHAT_RING];
|
||||
int schat_head;
|
||||
int schat_count;
|
||||
uint32_t schat_seq;
|
||||
char pending_tx[8][2048];
|
||||
int pending_n;
|
||||
/* Inbound ICE signals (WS op:signal). */
|
||||
@@ -1319,6 +1328,13 @@ static void lobby_list_parse_players(const char *json)
|
||||
sizeof(g_lc.online[n].lobby_name));
|
||||
g_lc.online[n].hosting = json_get_bool(chunk, "hosting", 0);
|
||||
json_get_str(chunk, "tag", g_lc.online[n].tag, sizeof(g_lc.online[n].tag));
|
||||
json_get_str(chunk, "game_name", g_lc.online[n].game_name,
|
||||
sizeof(g_lc.online[n].game_name));
|
||||
/* Players of another title are not "online" for this one. A row
|
||||
* with no title yet (a client that has not listed) is kept. */
|
||||
if (g_lc.filter_game_name[0] && g_lc.online[n].game_name[0] &&
|
||||
strcmp(g_lc.online[n].game_name, g_lc.filter_game_name) != 0)
|
||||
{ p = end; continue; }
|
||||
if (g_lc.online[n].display_name[0]) ++n;
|
||||
p = end;
|
||||
}
|
||||
@@ -1785,12 +1801,39 @@ static void chat_push(const char *player_id, const char *from, const char *text,
|
||||
snprintf(m->player_id, sizeof(m->player_id), "%s", player_id ? player_id : "");
|
||||
snprintf(m->from, sizeof(m->from), "%s", from ? from : "");
|
||||
snprintf(m->text, sizeof(m->text), "%s", text);
|
||||
/* Masked on arrival, whatever relayed it: the server already did this,
|
||||
* an older server did not, and the rule is that nothing unmasked is
|
||||
* ever shown. */
|
||||
if (!is_system) (void)rnet_chat_filter_apply(m->text, sizeof(m->text));
|
||||
m->is_system = is_system ? 1 : 0;
|
||||
m->is_local = (!is_system && g_lc.player_id[0] && player_id &&
|
||||
strcmp(player_id, g_lc.player_id) == 0) ? 1 : 0;
|
||||
m->seq = ++g_lc.chat_seq;
|
||||
}
|
||||
|
||||
static void schat_push(const char *player_id, const char *from, const char *text)
|
||||
{
|
||||
PsxLobbyChatMsg *m;
|
||||
int idx;
|
||||
if (!text || !text[0]) return;
|
||||
if (g_lc.schat_count < PSX_LOBBY_CHAT_RING) {
|
||||
idx = (g_lc.schat_head + g_lc.schat_count) % PSX_LOBBY_CHAT_RING;
|
||||
g_lc.schat_count++;
|
||||
} else {
|
||||
idx = g_lc.schat_head;
|
||||
g_lc.schat_head = (g_lc.schat_head + 1) % PSX_LOBBY_CHAT_RING;
|
||||
}
|
||||
m = &g_lc.schat[idx];
|
||||
memset(m, 0, sizeof(*m));
|
||||
snprintf(m->player_id, sizeof(m->player_id), "%s", player_id ? player_id : "");
|
||||
snprintf(m->from, sizeof(m->from), "%s", from ? from : "");
|
||||
snprintf(m->text, sizeof(m->text), "%s", text);
|
||||
(void)rnet_chat_filter_apply(m->text, sizeof(m->text));
|
||||
m->is_local = (g_lc.player_id[0] && player_id &&
|
||||
strcmp(player_id, g_lc.player_id) == 0) ? 1 : 0;
|
||||
m->seq = ++g_lc.schat_seq;
|
||||
}
|
||||
|
||||
void psx_lobby_chat_clear(void)
|
||||
{
|
||||
g_lc.chat_head = 0;
|
||||
@@ -1805,9 +1848,15 @@ static void handle_server_json(const char *json)
|
||||
json_get_str(json, "op", op, sizeof(op));
|
||||
if (strcmp(op, "welcome") == 0) {
|
||||
json_get_str(json, "player_id", g_lc.player_id, sizeof(g_lc.player_id));
|
||||
if (g_lc.display_name[0]) {
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "{\"op\":\"hello\",\"display_name\":\"%s\"}", g_lc.display_name);
|
||||
{
|
||||
/* Say who we are AND what we are playing in the first message:
|
||||
* the server scopes players-online and server chat by title, and
|
||||
* waiting for a `list` to tell it left a client that chatted
|
||||
* first with no title at all. Title only -- never the version. */
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"{\"op\":\"hello\",\"display_name\":\"%s\",\"game_name\":\"%s\"}",
|
||||
g_lc.display_name, g_lc.filter_game_name);
|
||||
queue_send(msg);
|
||||
}
|
||||
queue_list_request();
|
||||
@@ -2197,6 +2246,19 @@ static void handle_server_json(const char *json)
|
||||
g_lc.swap_out = json_get_bool(json, "accept", 0) ? 2 : -1;
|
||||
return;
|
||||
}
|
||||
if (strcmp(op, "server_chat") == 0) {
|
||||
char text[PSX_LOBBY_CHAT_TEXT_LEN];
|
||||
char from_id[PSX_LOBBY_ID_LEN];
|
||||
char from[PSX_LOBBY_NAME_LEN];
|
||||
text[0] = '\0';
|
||||
from_id[0] = '\0';
|
||||
from[0] = '\0';
|
||||
json_get_str(json, "text", text, sizeof(text));
|
||||
json_get_str(json, "from_player_id", from_id, sizeof(from_id));
|
||||
json_get_str(json, "from", from, sizeof(from));
|
||||
schat_push(from_id, from, text);
|
||||
return;
|
||||
}
|
||||
if (strcmp(op, "chat") == 0) {
|
||||
char text[PSX_LOBBY_CHAT_TEXT_LEN];
|
||||
char from_id[PSX_LOBBY_ID_LEN];
|
||||
@@ -3573,6 +3635,37 @@ int psx_lobby_chat_count(void)
|
||||
return g_lc.chat_count;
|
||||
}
|
||||
|
||||
int psx_lobby_send_server_chat(const char *text)
|
||||
{
|
||||
char esc[PSX_LOBBY_CHAT_TEXT_LEN * 2 + 8];
|
||||
char msg[PSX_LOBBY_CHAT_TEXT_LEN * 2 + 256];
|
||||
int n;
|
||||
if (!psx_lobby_connected()) return -1;
|
||||
if (!text || !text[0]) return -1;
|
||||
json_escape(text, esc, sizeof(esc));
|
||||
/* Carry the title on the line itself: the server scopes by it, and this
|
||||
* works even against a server that has not seen our `list` yet. */
|
||||
n = snprintf(msg, sizeof(msg),
|
||||
"{\"op\":\"server_chat\",\"game_name\":\"%s\",\"text\":\"%s\"}",
|
||||
g_lc.filter_game_name, esc);
|
||||
if (n < 0 || (size_t)n >= sizeof(msg)) return -1;
|
||||
queue_send(msg);
|
||||
flush_pending();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int psx_lobby_server_chat_count(void)
|
||||
{
|
||||
return g_lc.schat_count;
|
||||
}
|
||||
|
||||
int psx_lobby_server_chat_get(int index, PsxLobbyChatMsg *out)
|
||||
{
|
||||
if (!out || index < 0 || index >= g_lc.schat_count) return 0;
|
||||
*out = g_lc.schat[(g_lc.schat_head + index) % PSX_LOBBY_CHAT_RING];
|
||||
return 1;
|
||||
}
|
||||
|
||||
int psx_lobby_chat_get(int index, PsxLobbyChatMsg *out)
|
||||
{
|
||||
if (!out || index < 0 || index >= g_lc.chat_count) return 0;
|
||||
|
||||
Reference in New Issue
Block a user