Support multiple controllers for local multiplayer (#1583)

* Support multiple controllers for local co-op
This commit is contained in:
Felipe
2026-10-17 08:15:22 -03:00
committed by GitHub
parent 890393fbe6
commit 415387f00d
46 changed files with 664 additions and 196 deletions
+54 -27
View File
@@ -1,12 +1,14 @@
#include "common/abi.h"
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include "common/localization.h"
#include "libs/controller.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "loader/symbolDatabase.h"
#include <cstdio>
#include <cstring>
#include <string>
namespace Libs {
@@ -39,6 +41,15 @@ struct UserServiceGamePresets {
static_assert(sizeof(UserServiceGamePresets) == 40);
static bool IsPlayerLoggedIn(int player) {
return player == 0 || (player > 0 && Controller::IsPlayerConnected(player));
}
static int GetLoggedInPlayer(int user_id) {
const int player = Config::GetPlayerFromUserId(user_id);
return IsPlayerLoggedIn(player) ? player : -1;
}
static KYTY_SYSV_ABI int UserServiceInitialize(const void* /*params*/) {
PRINT_NAME();
@@ -66,13 +77,16 @@ static KYTY_SYSV_ABI int UserServiceGetEvent(SceUserServiceEvent* event) {
EXIT_NOT_IMPLEMENTED(event == nullptr);
static bool logged_in = false;
static bool logged_in[Config::MAX_PLAYERS] = {};
if (!logged_in) {
logged_in = true;
event->event_type = UserServiceEventTypeLogin;
event->user_id = Config::GetUserId();
return OK;
for (int player = 0; player < Config::MAX_PLAYERS; player++) {
const bool connected = IsPlayerLoggedIn(player);
if (connected != logged_in[player]) {
logged_in[player] = connected;
event->event_type = connected ? UserServiceEventTypeLogin : UserServiceEventTypeLogout;
event->user_id = Config::GetPlayerUserId(player);
return OK;
}
}
return USER_SERVICE_ERROR_NO_EVENT;
@@ -83,24 +97,36 @@ static KYTY_SYSV_ABI int UserServiceGetLoginUserIdList(UserServiceLoginUserIdLis
EXIT_NOT_IMPLEMENTED(user_id_list == nullptr);
user_id_list->user_id[0] = Config::GetUserId();
user_id_list->user_id[1] = -1;
user_id_list->user_id[2] = -1;
user_id_list->user_id[3] = -1;
int count = 0;
for (int player = 0; player < Config::MAX_PLAYERS; player++) {
if (IsPlayerLoggedIn(player)) {
user_id_list->user_id[count++] = Config::GetPlayerUserId(player);
}
}
while (count < Config::MAX_PLAYERS) {
user_id_list->user_id[count++] = -1;
}
return OK;
}
static KYTY_SYSV_ABI int UserServiceGetUserName(int user_id, char* name, size_t size) {
if (user_id != Config::GetUserId()) {
if (name == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
const int player = GetLoggedInPlayer(user_id);
if (player < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
EXIT_NOT_IMPLEMENTED(size < 5);
const auto& user_name = Config::GetUserName();
int s = snprintf(name, size, "%s", user_name.c_str());
EXIT_NOT_IMPLEMENTED(static_cast<size_t>(s) >= size);
const auto user_name =
player > 0 ? std::string(Common::Localization::Get(Config::GetConsoleLanguage(), "user.guest")) +
" " + std::to_string(player + 1)
: Config::GetUserName();
if (size <= user_name.size()) {
return USER_SERVICE_ERROR_BUFFER_TOO_SHORT;
}
std::memcpy(name, user_name.c_str(), user_name.size() + 1);
return OK;
}
@@ -111,11 +137,12 @@ static KYTY_SYSV_ABI int UserServiceGetUserNumber(int user_id, int32_t* number)
if (number == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
const int player = GetLoggedInPlayer(user_id);
if (player < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
*number = 1;
*number = player + 1;
return OK;
}
@@ -126,7 +153,7 @@ static KYTY_SYSV_ABI int UserServiceGetGamePresets(int user_id, UserServiceGameP
if (presets == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -147,7 +174,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityVibration(int user_id, int32
if (vibration == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -163,7 +190,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityTriggerEffect(int user_
if (trigger_effect == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -178,7 +205,7 @@ static KYTY_SYSV_ABI int UserServiceGetAgeLevel(int user_id, uint32_t* age_level
if (age_level == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -194,7 +221,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityChatTranscription(int u
if (chat_transcription == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -210,7 +237,7 @@ UserServiceGetAccessibilityPressAndHoldDelay(int user_id, int32_t* press_and_hol
if (press_and_hold_delay == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -226,7 +253,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityZoomEnabled(int user_id
if (zoom_enabled == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
@@ -242,7 +269,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityZoomFollowFocus(int use
if (zoom_follow_focus == nullptr) {
return USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
if (user_id != Config::GetUserId()) {
if (GetLoggedInPlayer(user_id) < 0) {
return USER_SERVICE_ERROR_NOT_LOGGED_IN;
}