Files
MoHadiShibli 546e04e066 Plugin: fix lag, crashes and user handling; add settings and security
RemotePad NG 1.0.0, a maintained fork of remotePad 1.2.0 by xfangfang.

Fixes:
- Input lag that grew to seconds in games that read one sample per frame:
  stick-only updates now replace the newest unread sample, presses and
  releases still get their own (xfangfang/remotePad#7).
- Crashes: the notify RPC used client text as a format string, a heap
  overflow in the ini parser, a too small server thread stack, unload
  crashes and leaks, a startup race, unchecked buffers from games
  (xfangfang/remotePad#9).
- Hex user ids were read as decimal, so several pads got the wrong users
  (xfangfang/remotePad#2).
- scePadRead returned shifted data with more than one queued sample; held
  buttons looked released between updates; buttons stayed pressed when a
  client went away.
- Lowercase "connection: upgrade" headers were refused; TCP_NODELAY.
- ini parsing: section order, UTF-8 BOM, "]" in values.

New:
- Settings module: everything the page can change is applied live and
  saved to remote_pad.ini, for all games or one game; lock_settings=1.
- Guests join when a device picks their pad, and pads look connected only
  while a device drives them (join_on_start / always_connected restore the
  old behavior).
- Configured PS4 users who aren't signed in play as guests.
- Share mode (userN_share): add a pad to a user's own DualShock.
- WebSocket protocol 2: claim, leave, status, settings, settings.set and
  live status notifications; protocol 1 clients keep working.
- Network security: pages from other sites are refused, settings and
  notifications need the console's own page opened by IP address (or a
  non-browser client), at most 16 connections and 64 KB buffered per
  connection, and the page is served with a content security policy.
- Clearer start notification with the address to open; logs go to the
  kernel log; guests are named after their pad (Remote1..4).
2026-10-02 23:38:32 +03:00

134 lines
5.2 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <orbis/_types/user.h>
#include <orbis/_types/pthread.h>
#ifndef REMOTE_PAD_USER_H
#define REMOTE_PAD_USER_H
#define REMOTE_PAD_MAX_USERS 4
#define REMOTE_PAD_VIRTUAL_USER_ID 0x20000000 // + index: the guest id of each pad
typedef struct RemoteUser {
int32_t index;
// The id games see now: realId while that user is signed in on the console, guestId otherwise
int32_t userId;
// Id of this pad's guest player (REMOTE_PAD_VIRTUAL_USER_ID + index)
int32_t guestId;
// remote_pad.ini userN_id: a real PS4 user whose controller this pad takes over while that user is
// signed in. When the user isn't signed in, the pad plays as a guest.
int32_t realId;
char userName[ORBIS_USER_SERVICE_MAX_USER_NAME_LENGTH + 1];
bool enabled;
// realId is set
bool isConfiguredId;
// userName comes from remote_pad.ini (otherwise real users keep their console name)
bool hasCustomName;
// realId is in the console's own login user list, so the pad plays as that user
bool isSystemUserId;
// remote_pad.ini userN_share: the real user's own controller keeps working and this pad's input is
// added to it, instead of this pad replacing it
bool shareController;
// position in the login user list the game sees (player number - 1), -1 when the game doesn't see this user
int32_t slot;
OrbisUserServiceUserColor color;
// Guests only: a device picked this pad, so the guest joins the game (see joinOnStart)
bool joined;
// Guests only: the guest left the game and stays out until it joins again
bool left;
/// private
// whether a login event has been returned for the guest, and with which id
bool isLoggedIn;
int32_t loggedInId;
} RemoteUser;
// The original (unhooked) user service functions
typedef struct RemoteUserSystem {
int32_t (*getUserColor)(int32_t userId, OrbisUserServiceUserColor *color);
int32_t (*getUserName)(int32_t userId, char *username, size_t size);
// Console's own list, without remote users (optional)
int32_t (*getLoginUserIdList)(OrbisUserServiceLoginUserIdList *list);
// Every user registered on the console; games may not be allowed to read it (optional)
int32_t (*getRegisteredUserIdList)(OrbisUserServiceRegisteredUserIdList *list);
} RemoteUserSystem;
typedef struct RemoteUserService {
int32_t (*init)(void);
int32_t (*term)(void);
// User with this guest id or real id
RemoteUser *(*getUser)(int32_t userId);
// Enabled user with this guest id or real id, NULL when the id is not handled by remotePad
RemoteUser *(*getActiveUser)(int32_t userId);
RemoteUser *(*getUserByIndex)(int32_t index);
// Fail when the system should answer (unknown user or a signed-in real user without a configured name)
int32_t (*getUserName)(int32_t userId, char *username, size_t size);
// Name for the user with this index (3 to 16 characters); NULL or "" goes back to the default name
int32_t (*setUserName)(int32_t index, const char *username);
// The pad with this index takes over this real user while it is signed in; 0 = always a guest
void (*setRealId)(int32_t index, int32_t realId);
void (*setEnabled)(int32_t index, bool enabled);
void (*setShare)(int32_t index, bool share);
// Fail when the system should answer (unknown user or a signed-in real user)
int32_t (*getUserColor)(int32_t userId, OrbisUserServiceUserColor *color);
// Takes the console's login list, marks the signed-in real users and puts guests into the empty slots.
// Returns true when the users the game sees changed.
bool (*updateLoginList)(OrbisUserServiceLoginUserIdList *list, const RemoteUserSystem *system);
// Whether the slots are known (updateLoginList ran since the last invalidateLoginList)
bool (*hasLoginList)(void);
void (*invalidateLoginList)(void);
// Next login/logout event for a guest, false when there is nothing to report
bool (*nextEvent)(OrbisUserServiceEvent *event);
// The guest player of this pad joins / leaves the game. Returns true when that changes anything.
bool (*join)(int32_t index);
bool (*leave)(int32_t index);
RemoteUser users[REMOTE_PAD_MAX_USERS];
OrbisPthreadMutex mutex;
OrbisUserServiceLoginUserIdList systemList;
bool hasSystemList;
// The original user service functions (set by the plugin)
const RemoteUserSystem *system;
// remote_pad.ini join_on_start: guest players are in the game from the start (the old behavior)
// instead of joining when a device picks their pad
bool joinOnStart;
} RemoteUserService;
// Whether the pad plays as its real (configured and signed-in) user rather than as a guest
static inline bool playsAsRealUser(const RemoteUser *user) {
return user->isConfiguredId && user->isSystemUserId;
}
// Whether the pad adds its input to the real user's own controller (share mode) rather than replacing it
static inline bool sharesController(const RemoteUser *user) {
return user->shareController && playsAsRealUser(user);
}
RemoteUserService *initRemoteUserService(void);
RemoteUserService *getRemoteUserService(void);
void termRemoteUserService(RemoteUserService *);
#endif //REMOTE_PAD_USER_H