mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-03 13:01:55 +01:00
Merge pull request #22846 from hrydgard/adhoc-5-ctl-timing
Ad hoc 5/9: Accurate PSP timing for adhocctl events
This commit is contained in:
2 files changed
+71
-19
No files matched your search
+60
-17
@@ -96,6 +96,40 @@ std::map<int, AdhocctlHandler> adhocctlHandlers;
|
||||
int adhocctlNotifyEvent = -1;
|
||||
int adhocctlStateEvent = -1;
|
||||
int adhocctlPollEvent = -1;
|
||||
// When the game made its last request to the Adhoc Server, in emulated time. Its event isn't delivered
|
||||
// earlier than on a PSP (see PspEventDelayUs).
|
||||
static u64 adhocctlRequestTimeUs = 0;
|
||||
// Whether the server has us in a group. The state the game sees only changes when the event is
|
||||
// delivered, so a Disconnect can come while a Create or Connect is still on its way.
|
||||
static bool adhocctlInServerGroup = false;
|
||||
|
||||
// How long after the request a PSP delivers an event (pspautotests net/adhoc/timing and
|
||||
// net/adhoc/pair/timing). The state changes along with it. Over the internet the server's answer can
|
||||
// come later, and then the event waits for it.
|
||||
static int PspEventDelayUs(u32 flags) {
|
||||
switch (flags) {
|
||||
case ADHOCCTL_EVENT_CONNECT:
|
||||
switch (adhocConnectionType) {
|
||||
case ADHOC_CREATE: return 1110000;
|
||||
case ADHOC_JOIN: return 530000;
|
||||
// Connect looks for the group first: about 3.5 s to join one, 4 s to create it.
|
||||
default: return getActivePeerCount() > 0 ? 3500000 : 4000000;
|
||||
}
|
||||
case ADHOCCTL_EVENT_SCAN: return 1015000;
|
||||
case ADHOCCTL_EVENT_DISCONNECT: return 105000;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// A PSP returns from Create, Connect, Join and Scan within about 30 µs (pspautotests net/adhoc/timing).
|
||||
// The request goes out to the server then, and the call returns once it's sent.
|
||||
static const int ADHOCCTL_REQUEST_US = 30;
|
||||
|
||||
static int RemainingEventDelayUs(u32 flags) {
|
||||
s64 left = (s64)(adhocctlRequestTimeUs + PspEventDelayUs(flags)) - (s64)CoreTiming::GetGlobalTimeUs();
|
||||
return left > 0 ? (int)left : 0;
|
||||
}
|
||||
|
||||
int adhocSocketNotifyEvent = -1;
|
||||
std::map<int, AdhocctlRequest> adhocctlRequests;
|
||||
std::map<u64, AdhocSocketRequest> adhocSocketRequests;
|
||||
@@ -571,6 +605,9 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
|
||||
switch(req.opcode) {
|
||||
case OPCODE_CONNECT:{
|
||||
call_status = adhocctl_connect(adhocctlHandle, (char *)req.group.data);
|
||||
if (call_status == ADHOCCTL_CALL_SUCCESS) {
|
||||
adhocctlInServerGroup = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPCODE_SCAN:{
|
||||
@@ -682,6 +719,7 @@ int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* re
|
||||
|
||||
u64 param = ((u64)__KernelGetCurThread()) << 32 | uid;
|
||||
adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
|
||||
adhocctlRequestTimeUs = CoreTiming::GetGlobalTimeUs();
|
||||
adhocctlRequests[uid] = request;
|
||||
CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param);
|
||||
__KernelWaitCurThread(WAITTYPE_NET, uid, request.opcode, 0, false, reason);
|
||||
@@ -1910,6 +1948,7 @@ static void StartFriendFinder() {
|
||||
static void StopFriendFinder() {
|
||||
friendFinderRunning = false;
|
||||
friendFinderStop();
|
||||
adhocctlInServerGroup = false;
|
||||
|
||||
int32_t peercount = 0;
|
||||
freeFriendsRecursive(friends, &peercount);
|
||||
@@ -1920,7 +1959,7 @@ static void StopFriendFinder() {
|
||||
}
|
||||
|
||||
void __NetAdhocDoState(PointerWrap &p) {
|
||||
auto s = p.Section("sceNetAdhoc", 1, 11);
|
||||
auto s = p.Section("sceNetAdhoc", 1, 12);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -2019,8 +2058,15 @@ void __NetAdhocDoState(PointerWrap &p) {
|
||||
adhocctlPollEvent = -1;
|
||||
}
|
||||
CoreTiming::RestoreRegisterEvent(adhocctlPollEvent, "__AdhocctlPoll", __AdhocctlPoll);
|
||||
if (s >= 12) {
|
||||
Do(p, adhocctlRequestTimeUs);
|
||||
} else {
|
||||
adhocctlRequestTimeUs = 0;
|
||||
}
|
||||
|
||||
if (p.mode == p.MODE_READ) {
|
||||
// The login after a load starts outside any group.
|
||||
adhocctlInServerGroup = false;
|
||||
// Discard leftover events
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
|
||||
@@ -3596,7 +3642,7 @@ int sceNetAdhocctlScan(MIPSState *mips) {
|
||||
|
||||
if (friendFinderRunning) {
|
||||
AdhocctlRequest req = { OPCODE_SCAN, {0} };
|
||||
return hleLogDebugOrError(Log::sceNet, WaitBlockingAdhocctlSocket(req, us, "adhocctl scan"));
|
||||
return hleLogDebugOrError(Log::sceNet, WaitBlockingAdhocctlSocket(req, ADHOCCTL_REQUEST_US, "adhocctl scan"));
|
||||
}
|
||||
else {
|
||||
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
|
||||
@@ -3762,15 +3808,15 @@ u32 NetAdhocctl_Disconnect() {
|
||||
// Library initialized
|
||||
if (netAdhocctlInited) {
|
||||
int iResult, error;
|
||||
// We might need to have at least 16ms (1 frame?) delay before the game calls the next Adhocctl syscall for Tekken 6 not to stuck when exiting Lobby
|
||||
hleEatMicro(16667);
|
||||
|
||||
if (isAdhocctlBusy && CoreTiming::IsScheduled(adhocctlNotifyEvent)) {
|
||||
return SCE_NET_ADHOCCTL_ERROR_BUSY;
|
||||
}
|
||||
// Returns at once on a PSP, and DISCONNECT follows after about 105 ms (pspautotests net/adhoc/timing).
|
||||
adhocctlRequestTimeUs = CoreTiming::GetGlobalTimeUs();
|
||||
|
||||
// Connected State (Adhoc Mode). Attempting to leave a group while not in a group will be kicked out by Adhoc Server (ie. some games tries to disconnect more than once within a short time)
|
||||
if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) {
|
||||
if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED || adhocctlInServerGroup) {
|
||||
adhocctlInServerGroup = false;
|
||||
isAdhocctlBusy = true;
|
||||
|
||||
// Clear Network Name
|
||||
@@ -3865,7 +3911,7 @@ static u32 sceNetAdhocctlDelHandler(u32 handlerID) {
|
||||
|
||||
int NetAdhocctl_Term() {
|
||||
if (netAdhocctlInited) {
|
||||
if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) {
|
||||
if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED || adhocctlInServerGroup) {
|
||||
// Note: This might block current thread if the first attempt to send OPCODE_DISCONNECT to AdhocServer failed with EAGAIN error
|
||||
if (netAdhocGameModeEntered)
|
||||
NetAdhocctl_ExitGameMode();
|
||||
@@ -3876,6 +3922,7 @@ int NetAdhocctl_Term() {
|
||||
// Stop the adhocctl client
|
||||
friendFinderRunning = false;
|
||||
friendFinderStop();
|
||||
adhocctlInServerGroup = false;
|
||||
|
||||
// TODO: May need to block current thread to make sure all Adhocctl callbacks have been fully executed before terminating Adhoc PSPThread (ie. threadAdhocID).
|
||||
|
||||
@@ -4071,10 +4118,9 @@ int NetAdhocctl_Create(const char *groupName) {
|
||||
//setConnectionStatus(1);
|
||||
|
||||
// Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
|
||||
int us = adhocDefaultDelay;
|
||||
if (friendFinderRunning) {
|
||||
AdhocctlRequest req = { OPCODE_CONNECT, parameter.group_name };
|
||||
return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect");
|
||||
return WaitBlockingAdhocctlSocket(req, ADHOCCTL_REQUEST_US, "adhocctl connect");
|
||||
}
|
||||
//Faking success, to prevent Full Auto 2 from freezing while Initializing Network
|
||||
else {
|
||||
@@ -4091,7 +4137,7 @@ int NetAdhocctl_Create(const char *groupName) {
|
||||
}
|
||||
}
|
||||
|
||||
hleEatMicro(us);
|
||||
hleEatMicro(adhocDefaultDelay);
|
||||
// Return Success
|
||||
// FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line.
|
||||
return 0;
|
||||
@@ -6304,18 +6350,15 @@ void __NetTriggerCallbacks()
|
||||
switch (flags) {
|
||||
case ADHOCCTL_EVENT_CONNECT:
|
||||
newState = ADHOCCTL_STATE_CONNECTED;
|
||||
if (adhocConnectionType == ADHOC_CREATE)
|
||||
delayus = adhocEventDelay; // May affects Dissidia 012 and GTA VCS
|
||||
else if (adhocConnectionType == ADHOC_CONNECT)
|
||||
delayus = adhocEventDelay / 2;
|
||||
delayus = RemainingEventDelayUs(flags);
|
||||
break;
|
||||
case ADHOCCTL_EVENT_SCAN: // notified only when scan completed?
|
||||
case ADHOCCTL_EVENT_SCAN:
|
||||
newState = ADHOCCTL_STATE_DISCONNECTED;
|
||||
//delayus = adhocEventDelay / 2;
|
||||
delayus = RemainingEventDelayUs(flags);
|
||||
break;
|
||||
case ADHOCCTL_EVENT_DISCONNECT:
|
||||
newState = ADHOCCTL_STATE_DISCONNECTED;
|
||||
delayus = adhocDefaultDelay; // Tekken 5 expects AdhocctlDisconnect to be done within ~17ms (a frame?)
|
||||
delayus = RemainingEventDelayUs(flags);
|
||||
break;
|
||||
case ADHOCCTL_EVENT_GAME:
|
||||
{
|
||||
|
||||
+11
-2
@@ -61,17 +61,21 @@ AdhocServerRelayMode = 1
|
||||
def run_test(headless, test, extra, relay_ini):
|
||||
roles = roles_of(test)
|
||||
memstick = os.path.join(os.path.dirname(headless), "memstick")
|
||||
# A test that hasn't been recorded on PSPs yet has no expected files; then just show what each instance prints.
|
||||
compare = all(os.path.exists(os.path.join(TESTS_DIR, "%s_%s.expected" % (test, role))) for role in roles)
|
||||
procs = []
|
||||
for i, role in enumerate(roles):
|
||||
cmd = [headless, "--root", os.path.join(ROOT, "pspautotests") + "/", "--compare", "--timeout-wall=%d" % TIMEOUT,
|
||||
cmd = [headless, "--root", os.path.join(ROOT, "pspautotests") + "/", "--timeout-wall=%d" % TIMEOUT,
|
||||
"--graphics=software", "--memstick=" + memstick, "--realtime", "--instance=%d" % (i + 1)]
|
||||
if compare:
|
||||
cmd.append("--compare")
|
||||
if relay_ini:
|
||||
cmd.append("--appendconfig=" + relay_ini)
|
||||
if i == 0:
|
||||
cmd.append("--adhoc-server")
|
||||
cmd += extra
|
||||
cmd.append(os.path.join(TESTS_DIR, "%s_%s.prx" % (test, role)))
|
||||
procs.append((role, subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)))
|
||||
procs.append((role, subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT if compare else subprocess.DEVNULL)))
|
||||
if i == 0:
|
||||
# Let the host's built-in server come up before the clients log in to it.
|
||||
time.sleep(1.0)
|
||||
@@ -79,6 +83,11 @@ def run_test(headless, test, extra, relay_ini):
|
||||
passed = True
|
||||
for role, proc in procs:
|
||||
out, _ = proc.communicate(timeout=TIMEOUT + 30)
|
||||
if not compare:
|
||||
print("OUTPUT %s_%s (no expected file):" % (test, role))
|
||||
sys.stdout.write(out.decode("utf-8", "replace"))
|
||||
passed = False
|
||||
continue
|
||||
ok = proc.returncode == 0
|
||||
passed = passed and ok
|
||||
print("%s %s_%s" % ("PASS" if ok else "FAIL", test, role))
|
||||
|
||||
Reference in new issue
Block a user