Merge pull request #22845 from hrydgard/adhoc-4-ctl-thread

Ad hoc 4/9: Run the adhocctl client on the CPU thread, don't wait for the login
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-30 07:33:44 +01:00
commit 86e4b2db02
4 files changed
+264 -199

No files matched your search

+221 -166
View File
@@ -24,6 +24,7 @@
#include "ppsspp_config.h"
#include <algorithm>
#include <condition_variable>
#include <mutex>
#include <cstring>
@@ -114,7 +115,6 @@ std::map<uint32_t, in_addr> localIpCache;
SceNetAdhocctlParameter parameter;
SceNetAdhocctlAdhocId product_code;
std::thread friendFinderThread;
std::recursive_mutex peerlock;
AdhocSocket* adhocSockets[MAX_SOCKET];
bool isOriPort = false;
@@ -1392,104 +1392,58 @@ static bool should_abort_adhocctl_connection(void *_arg) {
return !friendFinderRunning;
}
// TODO: We should probably change this thread into PSPThread (or merging it into the existing AdhocThread PSPThread) as there are too many global vars being used here which also being used within some HLEs
int friendFinder() {
SetCurrentThreadName("FriendFinder");
auto n = GetI18NCategory(I18NCat::NETWORKING);
// Resolving the server and logging in can take seconds, so they run on a thread of their own.
// friendFinderPoll() starts it, and takes over the session once it's done.
static std::thread adhocctlLoginThread;
static std::atomic<bool> adhocctlLoginDone(false);
// Written by the login thread before it sets adhocctlLoginDone.
static void *adhocctlLoginHandle = nullptr;
static bool adhocctlLoginRunning = false;
static bool adhocServerResolved = false;
// Whether the client has logged in since friendFinderStart(), so the next login is after a lost connection.
static bool adhocctlLoggedIn = false;
// Guarded by adhocctlMutex, since the keepalive thread pings too.
static uint64_t lastAdhocctlPing = 0;
// Chat Packet
SceNetAdhocctlChatPacketC2S chat;
chat.base.opcode = OPCODE_CHAT;
// The poll only runs while emulation does, so while it's paused (the pause menu, a debugger) this
// keeps the server from dropping the client, as the old friendFinder thread did.
static std::thread adhocctlKeepaliveThread;
static std::mutex adhocctlKeepaliveMutex;
static std::condition_variable adhocctlKeepaliveCond;
static bool adhocctlKeepaliveStop = false;
// Last Ping Time
uint64_t lastping = 0;
// Last Time Reception got updated
uint64_t lastreceptionupdate = 0;
uint64_t now;
// Log Startup
INFO_LOG(Log::sceNet, "FriendFinder: Begin of Friend Finder Thread");
static const auto maintain_adhocctl_client = [] (SceNetAdhocctlAdhocId *adhoc_id) -> AdhocctlClientStatus {
static void AdhocctlKeepalive() {
SetCurrentThreadName("AdhocctlKeepalive");
while (true) {
{
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
if (adhocctlHandle) {
return AdhocctlClientStatus::ALIVE;
std::unique_lock<std::mutex> lock(adhocctlKeepaliveMutex);
adhocctlKeepaliveCond.wait_for(lock, std::chrono::milliseconds(500), [] { return adhocctlKeepaliveStop; });
if (adhocctlKeepaliveStop) {
return;
}
}
auto n = GetI18NCategory(I18NCat::NETWORKING);
int iResult = 0;
auto server_ip = GetAdhocServerIP();
struct adhocctl_addr_v4 adhocctl_addr;
adhocctl_addr.ip = server_ip.in.sin_addr.s_addr;
adhocctl_addr.port = server_ip.in.sin_port;
struct adhocctl_addr_v4 _adhocctl_bind_addr = {0};
struct adhocctl_addr_v4 *adhocctl_bind_addr = NULL;
// If Server is at localhost and we are in P2P mode, try to bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by adhocctl server
// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
if (isLocalServer && !serverHasRelay) {
_adhocctl_bind_addr.ip = g_localhostIP.in.sin_addr.s_addr;
_adhocctl_bind_addr.port = 0;
adhocctl_bind_addr = &_adhocctl_bind_addr;
}
// Default/Initial Network Parameters
memset(&parameter, 0, sizeof(parameter));
strncpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
parameter.channel = g_Config.iWlanAdhocChannel;
// Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11)
if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel
//getLocalMac(&parameter.bssid.mac_addr);
// Default ProductId
product_code.type = adhoc_id->type;
memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
// Don't need to connect if AdhocServer DNS was not resolved
if (server_ip.in.sin_addr.s_addr == INADDR_NONE) {
return AdhocctlClientStatus::DEAD;
}
SceNetEtherAddr address;
getLocalMac(&address);
// Connect to Adhoc Server
void *new_adhocctl_handle = create_adhocctl_v1_session_v4(&adhocctl_addr, adhocctl_bind_addr, (char *)product_code.data, (char *)parameter.nickname.data, (char *)address.data, netAdhocUseUdp, should_abort_adhocctl_connection, NULL);
if (!new_adhocctl_handle) {
ERROR_LOG(Log::sceNet, "Failed connecting to adhocctl server [%s/%s:%u]", g_Config.sProAdhocServer.c_str(), ip2str(server_ip.in.sin_addr).c_str(), ntohs(server_ip.in.sin_port));
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")));
return AdhocctlClientStatus::DEAD;
}
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
if (adhocctlHandle) {
// we have two friend finders?? DO NOT try to create adhocctl client outside of this lambda
ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__);
_dbg_assert_(false);
killAdhocctlClient(false, adhocctl_guard);
const uint64_t now = time_now_d() * 1000000.0;
// A failed ping is left for the poll to notice and handle.
if (adhocctlHandle && static_cast<s64>(now - lastAdhocctlPing) >= PSP_ADHOCCTL_PING_TIMEOUT && adhocctl_ping(adhocctlHandle) == ADHOCCTL_CALL_SUCCESS) {
lastAdhocctlPing = now;
}
adhocctlHandle = new_adhocctl_handle;
int new_adhocctl_id = 2;
if (new_adhocctl_id == adhocctlId) {
new_adhocctl_id++;
}
adhocctlId = new_adhocctl_id;
}
}
// warm the local ip cache
sockaddr_in addr;
getLocalIp(&addr);
static void StopAdhocctlKeepalive() {
{
std::lock_guard<std::mutex> lock(adhocctlKeepaliveMutex);
adhocctlKeepaliveStop = true;
}
adhocctlKeepaliveCond.notify_one();
if (adhocctlKeepaliveThread.joinable()) {
adhocctlKeepaliveThread.join();
}
}
return AdhocctlClientStatus::SPAWNED;
};
static void ResolveAdhocServer() {
auto n = GetI18NCategory(I18NCat::NETWORKING);
// Resolve and cache AdhocServer DNS
addrinfo* resolved = nullptr;
@@ -1518,101 +1472,215 @@ int friendFinder() {
if (resolved) {
clearLocalIpCache();
}
}
// Finder Loop. The flag was set by whoever started us, and cleared to stop us.
while (friendFinderRunning) {
// Acquire Network Lock
//_acquireNetworkLock();
static void AdhocctlLogin(bool resolve, bool bind, struct adhocctl_addr_v4 bindAddr, SceNetAdhocctlAdhocId productCode, SceNetAdhocctlNickname nickname, SceNetEtherAddr mac, bool useUdp) {
SetCurrentThreadName("AdhocctlLogin");
if (resolve) {
ResolveAdhocServer();
}
// Reconnect when disconnected while Adhocctl is still inited
void *handle = nullptr;
auto server_ip = GetAdhocServerIP();
// Don't need to connect if AdhocServer DNS was not resolved
if (server_ip.in.sin_addr.s_addr != INADDR_NONE) {
struct adhocctl_addr_v4 adhocctl_addr;
adhocctl_addr.ip = server_ip.in.sin_addr.s_addr;
adhocctl_addr.port = server_ip.in.sin_port;
// Connect to Adhoc Server
handle = create_adhocctl_v1_session_v4(&adhocctl_addr, bind ? &bindAddr : NULL, (char *)productCode.data, (char *)nickname.data, (char *)mac.data, useUdp, should_abort_adhocctl_connection, NULL);
if (!handle && friendFinderRunning) {
auto n = GetI18NCategory(I18NCat::NETWORKING);
ERROR_LOG(Log::sceNet, "Failed connecting to adhocctl server [%s/%s:%u]", g_Config.sProAdhocServer.c_str(), ip2str(server_ip.in.sin_addr).c_str(), ntohs(server_ip.in.sin_port));
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")));
}
}
adhocctlLoginHandle = handle;
adhocctlLoginDone = true;
}
static void StartAdhocctlLogin() {
// Default/Initial Network Parameters
memset(&parameter, 0, sizeof(parameter));
strncpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
parameter.channel = g_Config.iWlanAdhocChannel;
// Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11)
if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel
// If Server is at localhost and we are in P2P mode, try to bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by adhocctl server
// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
struct adhocctl_addr_v4 bindAddr = {0};
const bool bind = isLocalServer && !serverHasRelay;
if (bind) {
bindAddr.ip = g_localhostIP.in.sin_addr.s_addr;
bindAddr.port = 0;
}
SceNetEtherAddr address;
getLocalMac(&address);
adhocctlLoginDone = false;
adhocctlLoginHandle = nullptr;
adhocctlLoginRunning = true;
adhocctlLoginThread = std::thread(AdhocctlLogin, !adhocServerResolved, bind, bindAddr, product_code, parameter.nickname, address, netAdhocUseUdp);
adhocServerResolved = true;
}
static void FinishAdhocctlLogin() {
adhocctlLoginThread.join();
adhocctlLoginRunning = false;
void *handle = adhocctlLoginHandle;
adhocctlLoginHandle = nullptr;
// sceNetAdhocctlScan & Create puts this back to true, which will trigger retry
isAdhocctlNeedLogin = false;
if (!handle) {
g_adhocServerConnected = false;
return;
}
{
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
if (adhocctlHandle) {
// Only the login thread creates sessions, one at a time.
ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__);
_dbg_assert_(false);
killAdhocctlClient(false, adhocctl_guard);
}
adhocctlHandle = handle;
int new_adhocctl_id = 2;
if (new_adhocctl_id == adhocctlId) {
new_adhocctl_id++;
}
adhocctlId = new_adhocctl_id;
// warm the local ip cache
sockaddr_in addr;
getLocalIp(&addr);
}
g_adhocServerConnected = true;
INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized");
if (!adhocctlLoggedIn) {
// The first login after sceNetAdhocctlInit. A request the game made meanwhile is waiting for it,
// and still busy.
adhocctlLoggedIn = true;
return;
}
// At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
netAdhocGameModeEntered = false;
isAdhocctlBusy = false;
}
void friendFinderStart() {
adhocServerResolved = false;
adhocctlLoggedIn = false;
{
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
lastAdhocctlPing = 0;
}
// In case the client was dropped without friendFinderStop(), as __NetAdhocInit() does.
StopAdhocctlKeepalive();
adhocctlKeepaliveStop = false;
adhocctlKeepaliveThread = std::thread(AdhocctlKeepalive);
INFO_LOG(Log::sceNet, "FriendFinder: Begin");
}
void friendFinderStop() {
StopAdhocctlKeepalive();
// friendFinderRunning is already false, which makes a login in progress give up.
if (adhocctlLoginThread.joinable()) {
adhocctlLoginThread.join();
}
if (adhocctlLoginHandle) {
destroy_adhocctl_session(adhocctlLoginHandle);
adhocctlLoginHandle = nullptr;
}
adhocctlLoginRunning = false;
// Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm)
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
INFO_LOG(Log::sceNet, "FriendFinder: End");
}
void friendFinderPoll() {
if (!friendFinderRunning) {
return;
}
if (adhocctlLoginRunning && adhocctlLoginDone) {
FinishAdhocctlLogin();
}
// Reconnect when disconnected while Adhocctl is still inited
if (!adhocctlLoginRunning) {
if (netAdhocctlInited && isAdhocctlNeedLogin && g_Config.bEnableWlan) {
AdhocctlClientStatus status = maintain_adhocctl_client(&product_code);
switch (status) {
case AdhocctlClientStatus::SPAWNED: {
g_adhocServerConnected = true;
INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized");
// At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
netAdhocGameModeEntered = false;
isAdhocctlBusy = false;
break;
}
case AdhocctlClientStatus::DEAD: {
g_adhocServerConnected = false;
break;
}
case AdhocctlClientStatus::ALIVE: {
break;
}
default: {
ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__);
_dbg_assert_(false);
bool alive;
{
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
alive = adhocctlHandle != NULL;
}
if (alive) {
isAdhocctlNeedLogin = false;
} else {
// isAdhocctlNeedLogin stays set until the login is done, so the requests waiting for it keep waiting.
StartAdhocctlLogin();
}
} else {
// sceNetAdhocctlScan & Create puts this back to true, which will trigger retry
isAdhocctlNeedLogin = false;
}
}
// sceNetAdhocctlScan & Create puts this back to true, which will trigger retry
isAdhocctlNeedLogin = false;
uint64_t now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable
now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable
// Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players
if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {
netAdhocGameModeEntered = false;
// adhocctl's own TIMEOUT (pspautotests net/adhoc/gamemode).
notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_TIMEOUT);
}
auto friend_finder_wait = [] {
// This delay time should be 100ms when there is an event otherwise 500ms ?
sleep_ms(10, "pro-adhoc-poll-2"); // Using 1ms for faster response just like AdhocServer?
// Don't do anything if it's paused, otherwise the log will be flooded
while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning) {
sleep_ms(10, "pro-adhoc-paused-poll-2");
}
};
// Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players
if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {
netAdhocGameModeEntered = false;
// adhocctl's own TIMEOUT (pspautotests net/adhoc/gamemode).
notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_TIMEOUT);
}
// Handle everything the server has sent since the last poll.
for (int i = 0; i < 64; i++) {
struct adhocctl_event adhocctl_event;
bool wait_and_continue = false;
do {
{
// we only want a lock until an event is fetched, so that we don't hold adhocctl & peer the same time and rig outselves into weird hold and wait down the road
const std::lock_guard<std::mutex> adhocctl_guard(adhocctlMutex);
if (!adhocctlHandle) {
wait_and_continue = true;
break;
return;
}
// Ping Server
// original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT)
if (static_cast<s64>(now - lastping) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet
if (static_cast<s64>(now - lastAdhocctlPing) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet
enum adhocctl_call_status ping_status = adhocctl_ping(adhocctlHandle);
if (ping_status != ADHOCCTL_CALL_SUCCESS) {
ERROR_LOG(Log::sceNet, "FriendFinder: adhocctl client error during ping");
killAdhocctlClient(true, adhocctl_guard);
wait_and_continue = true;
break;
} else {
// Update Ping Time
lastping = now;
VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
return;
}
// Update Ping Time
lastAdhocctlPing = now;
VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
}
adhocctl_get_event(adhocctlHandle, &adhocctl_event);
if (adhocctl_event.type == ADHOCCTL_CLIENT_EVENT_ERROR) {
ERROR_LOG(Log::sceNet, "FriendFinder: adhocctl client error during event fetch");
killAdhocctlClient(true, adhocctl_guard);
wait_and_continue = true;
break;
return;
}
} while (false);
}
if (wait_and_continue) {
friend_finder_wait();
continue;
if (adhocctl_event.type == ADHOCCTL_CLIENT_EVENT_WOULD_BLOCK) {
return;
}
switch (adhocctl_event.type) {
@@ -1826,20 +1894,7 @@ int friendFinder() {
_dbg_assert_(false);
}
friend_finder_wait();
}
// Groups/Networks should be deallocated isn't?
// Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm)
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
friendFinderRunning = false;
// Log Shutdown
INFO_LOG(Log::sceNet, "FriendFinder: End of Friend Finder Thread");
// Return Success
return 0;
}
int getActivePeerCount(const bool excludeTimedout) {
+7 -8
View File
@@ -780,7 +780,6 @@ extern int adhocctlId; // for tracking whether there was an instance swap
extern std::mutex adhocctlMutex;
extern SceNetAdhocctlParameter parameter;
extern SceNetAdhocctlAdhocId product_code;
extern std::thread friendFinderThread;
extern std::recursive_mutex peerlock;
extern AdhocSocket* adhocSockets[MAX_SOCKET];
@@ -968,13 +967,13 @@ void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count = nullpt
void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count = nullptr);
/**
* Friend Finder Thread (Receives Peer Information)
* @param args Length of argp in Bytes (Unused)
* @param argp Argument (Unused)
* @return Unused Value - Return 0
*/
int friendFinder();
// The adhocctl client: logs in to the Adhoc Server (on a thread of its own) and handles what the
// server sends. friendFinderPoll() runs on the CPU thread, from a CoreTiming event, while
// friendFinderRunning is set.
void friendFinderStart();
void friendFinderPoll();
// Call after clearing friendFinderRunning.
void friendFinderStop();
/**
* Find Free Matching ID
+35 -25
View File
@@ -95,6 +95,7 @@ std::deque<std::pair<u32, u32>> adhocctlEvents;
std::map<int, AdhocctlHandler> adhocctlHandlers;
int adhocctlNotifyEvent = -1;
int adhocctlStateEvent = -1;
int adhocctlPollEvent = -1;
int adhocSocketNotifyEvent = -1;
std::map<int, AdhocctlRequest> adhocctlRequests;
std::map<u64, AdhocSocketRequest> adhocSocketRequests;
@@ -531,6 +532,16 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) {
}
}
// How often the adhocctl client looks at what the Adhoc Server sent, in emulated time.
static const int ADHOCCTL_POLL_INTERVAL_US = 1000;
static void __AdhocctlPoll(u64 userdata, int cyclesLate) {
friendFinderPoll();
if (friendFinderRunning) {
CoreTiming::ScheduleEvent(usToCycles(ADHOCCTL_POLL_INTERVAL_US) - cyclesLate, adhocctlPollEvent, 0);
}
}
static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
SceUID threadID = userdata >> 32;
int uid = (int)(userdata & 0xFFFFFFFF);
@@ -1885,25 +1896,20 @@ static void SetupAdhocRelayMode() {
}
static void StartFriendFinder() {
// TODO: Merging friendFinder (real) thread to AdhocThread (fake) thread on PSP side
if (!friendFinderRunning) {
// Set before the thread starts, not by it: shutting down before it got going used to clear
// the flag first, and then the thread set it again and never stopped, hanging the join.
if (friendFinderThread.joinable()) {
friendFinderThread.join();
}
friendFinderStart();
friendFinderRunning = true;
friendFinderThread = std::thread(friendFinder);
}
if (!CoreTiming::IsScheduled(adhocctlPollEvent)) {
CoreTiming::ScheduleEvent(usToCycles(ADHOCCTL_POLL_INTERVAL_US), adhocctlPollEvent, 0);
}
}
// Stops the friendFinder thread and drops the connection to the Adhoc Server, leaving the PSP side
// Stops the adhocctl client and drops the connection to the Adhoc Server, leaving the PSP side
// (handlers, AdhocThread) alone.
static void StopFriendFinder() {
friendFinderRunning = false;
if (friendFinderThread.joinable()) {
friendFinderThread.join();
}
friendFinderStop();
int32_t peercount = 0;
freeFriendsRecursive(friends, &peercount);
@@ -1914,7 +1920,7 @@ static void StopFriendFinder() {
}
void __NetAdhocDoState(PointerWrap &p) {
auto s = p.Section("sceNetAdhoc", 1, 10);
auto s = p.Section("sceNetAdhoc", 1, 11);
if (!s)
return;
@@ -2007,6 +2013,12 @@ void __NetAdhocDoState(PointerWrap &p) {
// The login after a load sends it, so a state loaded into a fresh boot needs it.
p.DoVoid(&product_code, (int)sizeof(product_code));
}
if (s >= 11) {
Do(p, adhocctlPollEvent);
} else {
adhocctlPollEvent = -1;
}
CoreTiming::RestoreRegisterEvent(adhocctlPollEvent, "__AdhocctlPoll", __AdhocctlPoll);
if (p.mode == p.MODE_READ) {
// Discard leftover events
@@ -2040,6 +2052,10 @@ void __NetAdhocDoState(PointerWrap &p) {
}
isAdhocctlNeedLogin = false;
}
// A state from before the poll event, or one saved with the client stopped, has none queued.
if (netAdhocctlInited) {
StartFriendFinder();
}
if (!netAdhocInited && cur_netAdhocInited) {
// cleanup adhoc
if (aemuPostofficeEnetThread.joinable()) {
@@ -2063,6 +2079,7 @@ void __AdhocNotifInit() {
adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify);
gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify);
adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState);
adhocctlPollEvent = CoreTiming::RegisterEvent("__AdhocctlPoll", __AdhocctlPoll);
adhocctlRequests.clear();
adhocSocketRequests.clear();
@@ -2090,7 +2107,7 @@ void __NetAdhocInit() {
__AdhocServerInit();
// Create built-in AdhocServer Thread. The flag is set here rather than by the thread, so that a
// shutdown that clears it before the thread gets going can't be undone (see friendFinder).
// shutdown that clears it before the thread gets going can't be undone.
adhocServerRunning = false;
if (adhocServerThread.joinable()) {
adhocServerThread.join();
@@ -2227,14 +2244,9 @@ int sceNetAdhocctlInit(MIPSState *mips, int stackSize, int prio, u32 productAddr
StartFriendFinder();
// Need to make sure to be connected to Adhoc Server (indicated by networkInited) before returning to prevent GTA VCS failed to create/join a group and unable to see any game room
int us = adhocDefaultDelay;
if (g_Config.bEnableWlan && !g_adhocServerConnected) {
AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} };
return hleLogDebugOrWarn(Log::sceNet, WaitBlockingAdhocctlSocket(dummyreq, us, "adhocctl init"));
}
// Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time?
hleEatMicro(us);
// Init doesn't wait for the login to the Adhoc Server, and a Create, Connect or Scan right after it
// works, as on a PSP (pspautotests net/adhoc/ctlinit): those requests wait for the login instead.
hleEatMicro(adhocDefaultDelay);
return hleLogDebug(Log::sceNet, 0);
}
@@ -3861,11 +3873,9 @@ int NetAdhocctl_Term() {
NetAdhocctl_Disconnect();
}
// Terminate Adhoc Threads
// Stop the adhocctl client
friendFinderRunning = false;
if (friendFinderThread.joinable()) {
friendFinderThread.join();
}
friendFinderStop();
// TODO: May need to block current thread to make sure all Adhocctl callbacks have been fully executed before terminating Adhoc PSPThread (ie. threadAdhocID).
+1
View File
@@ -670,6 +670,7 @@ tests_next = [
"net/http/http",
"net/adhoc/adhocctl",
"net/adhoc/alerts",
"net/adhoc/ctlinit",
"net/adhoc/discover",
"net/adhoc/gamemode",
"net/adhoc/limits",