mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-01 12:07:31 +01:00
sceNetAdhocMatching: Run the input and event loops from CoreTiming instead of host threads
Each started context had two host threads, polling every 10 ms of wall time: one sent queued packets, received, and ran hellos, keepalives and timeouts, and the other handed events to the callbacks. A single CoreTiming event now does both for every running context, every 1 ms of emulated time, and Stop does what the threads did on their way out. Matching no longer depends on host scheduling or wall-clock delays, so an accept's ESTABLISHED comes 1-10 ms after SelectTarget at any emulation speed, as on a PSP (pspautotests net/adhoc/pair/matchingaccept). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
074b6bf547
commit
acb2acf0b4
4 files changed
+188
-416
No files matched your search
+5
-7
@@ -515,17 +515,15 @@ typedef struct SceNetAdhocMatchingContext {
|
||||
//HLEHelperThread *matchingThread;
|
||||
int matching_thid;
|
||||
|
||||
// Event Caller Thread
|
||||
std::thread eventThread;
|
||||
//s32_le event_thid;
|
||||
// Event delivery and input handling, run by the matching poll while the context is started.
|
||||
bool eventRunning = false;
|
||||
bool IsMatchingInCB = false;
|
||||
|
||||
// IO Handler Thread
|
||||
std::thread inputThread;
|
||||
//s32_le input_thid;
|
||||
bool inputRunning = false;
|
||||
|
||||
// When the input handling last sent a hello and a keepalive.
|
||||
u64 lastHello = 0;
|
||||
u64 lastPing = 0;
|
||||
|
||||
// Event Caller Thread Message Stack
|
||||
std::recursive_mutex *eventlock; // s32_le event_stack_lock;
|
||||
ThreadMessage *event_stack;
|
||||
|
||||
@@ -395,6 +395,7 @@ void discardMatchingEvents();
|
||||
void DoNetAdhocMatchingInited(PointerWrap &p);
|
||||
void DoNetAdhocMatchingThreads(PointerWrap &p);
|
||||
void ZeroNetAdhocMatchingThreads();
|
||||
void DoNetAdhocMatchingPollEvent(PointerWrap &p, bool saved);
|
||||
|
||||
bool __NetAdhocConnected() {
|
||||
return netAdhocInited && netAdhocctlInited && (adhocctlState == ADHOCCTL_STATE_CONNECTED || adhocctlState == ADHOCCTL_STATE_GAMEMODE);
|
||||
@@ -1352,7 +1353,7 @@ static void StopFriendFinder() {
|
||||
}
|
||||
|
||||
void __NetAdhocDoState(PointerWrap &p) {
|
||||
auto s = p.Section("sceNetAdhoc", 1, 12);
|
||||
auto s = p.Section("sceNetAdhoc", 1, 13);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -1458,6 +1459,7 @@ void __NetAdhocDoState(PointerWrap &p) {
|
||||
} else {
|
||||
adhocctlRequestTimeUs = 0;
|
||||
}
|
||||
DoNetAdhocMatchingPollEvent(p, s >= 13);
|
||||
|
||||
if (p.mode == p.MODE_READ) {
|
||||
// The login after a load starts outside any group.
|
||||
|
||||
+179
-408
@@ -68,6 +68,11 @@ int netAdhocMatchingStarted = 0;
|
||||
// timeout: it already allows keepalive_int * init_count of silence.
|
||||
static constexpr u64 MATCHING_TIMEOUT_EXTRA_US = 0;
|
||||
|
||||
// The contexts' input handling and event delivery run from this CoreTiming event, every
|
||||
// MATCHING_POLL_INTERVAL_US of emulated time.
|
||||
static int matchingPollEvent = -1;
|
||||
static constexpr int MATCHING_POLL_INTERVAL_US = 1000;
|
||||
|
||||
|
||||
void __UpdateMatchingHandler(const MatchingArgs &ArgsPtr) {
|
||||
std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
|
||||
@@ -1163,421 +1168,190 @@ void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * send
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: This really should be a callback or event!
|
||||
* Matching Event Dispatcher Thread
|
||||
* @param args sizeof(SceNetAdhocMatchingContext *)
|
||||
* @param argp SceNetAdhocMatchingContext *
|
||||
* @return Exit Point is never reached...
|
||||
*/
|
||||
int matchingEventThread(int matchingId) {
|
||||
SetCurrentThreadName("MatchingEvent");
|
||||
// Multithreading Lock
|
||||
peerlock.lock();
|
||||
// Cast Context
|
||||
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
|
||||
// Multithreading Unlock
|
||||
peerlock.unlock();
|
||||
// Hands the context's queued events to the game's handler, through __NetMatchingCallbacks.
|
||||
static void matchingDeliverEvents(SceNetAdhocMatchingContext *context) {
|
||||
u32 bufLen = context->rxbuflen;
|
||||
u32 bufAddr = 0;
|
||||
u32_le *args = context->handlerArgs;
|
||||
|
||||
// Log Startup
|
||||
INFO_LOG(Log::sceNet, "EventLoop: Begin of EventLoop[%i] Thread", matchingId);
|
||||
std::lock_guard<std::recursive_mutex> guard(*context->eventlock);
|
||||
while (context->event_stack != NULL) {
|
||||
ThreadMessage *msg = context->event_stack;
|
||||
void *opt = NULL;
|
||||
if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
|
||||
|
||||
// Run while needed...
|
||||
if (context != NULL) {
|
||||
u32 bufLen = context->rxbuflen; //0;
|
||||
u32 bufAddr = 0; //= userMemory.Alloc(bufLen); //context->rxbuf;
|
||||
u32_le * args = context->handlerArgs; //MatchingArgs
|
||||
INFO_LOG(Log::sceNet, "EventLoop[%d]: Matching Event [%d=%s][%s] OptSize=%d", context->id, msg->opcode, getMatchingEventStr(msg->opcode), mac2str(&msg->mac).c_str(), msg->optlen);
|
||||
notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args);
|
||||
|
||||
while (contexts != NULL && context->eventRunning) {
|
||||
// Multithreading Lock
|
||||
peerlock.lock();
|
||||
// Cast Context
|
||||
context = findMatchingContext(matchingId);
|
||||
// Multithreading Unlock
|
||||
peerlock.unlock();
|
||||
|
||||
// Messages on Stack ready for processing
|
||||
while (context != NULL && context->event_stack != NULL) {
|
||||
// Claim Stack
|
||||
context->eventlock->lock();
|
||||
|
||||
// Iterate Message List
|
||||
ThreadMessage * msg = context->event_stack;
|
||||
if (msg != NULL) {
|
||||
// Default Optional Data
|
||||
void* opt = NULL;
|
||||
|
||||
// Grab Optional Data
|
||||
if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage); //&msg[1]
|
||||
|
||||
// Log Matching Events
|
||||
INFO_LOG(Log::sceNet, "EventLoop[%d]: Matching Event [%d=%s][%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode), mac2str(&msg->mac).c_str(), msg->optlen);
|
||||
|
||||
// Unlock to prevent race-condition with other threads due to recursive lock
|
||||
//context->eventlock->unlock();
|
||||
// Call Event Handler
|
||||
//context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
|
||||
// Notify Event Handlers
|
||||
notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args); // If we're using shared Buffer & Args for All Events We should wait for the Mipscall to be fully executed before processing the next event. GTA VCS need this delay/sleep.
|
||||
|
||||
// Give some time before executing the next mipscall to prevent event ACCEPT(6)->ESTABLISH(7) getting reversed After Action ESTABLISH(7)->ACCEPT(6)
|
||||
// Must Not be delayed too long to prevent desync/disconnect. Not longer than the delays on callback's HLE?
|
||||
//sleep_ms(10); //sceKernelDelayThread(10000);
|
||||
|
||||
// Lock again
|
||||
//context->eventlock->lock();
|
||||
|
||||
// Pop the event queue from the front
|
||||
context->event_stack = msg->next;
|
||||
free(msg);
|
||||
msg = NULL;
|
||||
}
|
||||
|
||||
// Unlock Stack
|
||||
context->eventlock->unlock();
|
||||
}
|
||||
|
||||
// Share CPU Time
|
||||
sleep_ms(10, "pro-adhoc-poll-3"); //1 //sceKernelDelayThread(10000);
|
||||
|
||||
// Don't do anything if it's paused, otherwise the log will be flooded
|
||||
while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->eventRunning)
|
||||
sleep_ms(10, "pro-adhoc-event-poll-3");
|
||||
}
|
||||
|
||||
// Process Last Messages
|
||||
if (contexts != NULL && context->event_stack != NULL) {
|
||||
// Claim Stack
|
||||
context->eventlock->lock();
|
||||
|
||||
// Iterate Message List
|
||||
int msg_count = 0;
|
||||
ThreadMessage * msg = context->event_stack;
|
||||
for (; msg != NULL; msg = msg->next) {
|
||||
// Default Optional Data
|
||||
void * opt = NULL;
|
||||
|
||||
// Grab Optional Data
|
||||
if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); //&msg[1]
|
||||
|
||||
INFO_LOG(Log::sceNet, "EventLoop[%d]: Matching Event [EVENT=%d]\n", matchingId, msg->opcode);
|
||||
|
||||
//context->eventlock->unlock();
|
||||
// Original Call Event Handler
|
||||
//context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
|
||||
// Notify Event Handlers
|
||||
notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args);
|
||||
//context->eventlock->lock();
|
||||
msg_count++;
|
||||
}
|
||||
|
||||
// Clear Event Message Stack
|
||||
clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK);
|
||||
|
||||
// Free Stack
|
||||
context->eventlock->unlock();
|
||||
INFO_LOG(Log::sceNet, "EventLoop[%d]: Finished (%d msg)", matchingId, msg_count);
|
||||
}
|
||||
|
||||
// Free memory
|
||||
//if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);
|
||||
|
||||
// Delete Pointer Reference (and notify caller about finished cleanup)
|
||||
//context->eventThread = NULL;
|
||||
context->event_stack = msg->next;
|
||||
free(msg);
|
||||
}
|
||||
|
||||
// Log Shutdown
|
||||
INFO_LOG(Log::sceNet, "EventLoop: End of EventLoop[%i] Thread", matchingId);
|
||||
|
||||
// Return Zero to shut up Compiler
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching IO Handler Thread
|
||||
* @param args sizeof(SceNetAdhocMatchingContext *)
|
||||
* @param argp SceNetAdhocMatchingContext *
|
||||
* @return Exit Point is never reached...
|
||||
*/
|
||||
int matchingInputThread(int matchingId) { // TODO: The MatchingInput thread is using sceNetAdhocPdpRecv & sceNetAdhocPdpSend functions so it might be better to run this on PSP thread instead of real thread
|
||||
SetCurrentThreadName("MatchingInput");
|
||||
// Sends the packets SelectTarget, CancelTarget, SendData and the input handling itself queued.
|
||||
static void matchingSendQueued(SceNetAdhocMatchingContext *context) {
|
||||
std::lock_guard<std::recursive_mutex> guard(*context->inputlock);
|
||||
while (context->input_stack != NULL) {
|
||||
ThreadMessage *msg = context->input_stack;
|
||||
void *opt = NULL;
|
||||
if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
|
||||
|
||||
if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac);
|
||||
|
||||
context->input_stack = msg->next;
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// One pass of a context's input handling: hellos, timeouts and keepalives when they're due, the
|
||||
// queued packets, then everything that has arrived.
|
||||
static void matchingHandleInput(SceNetAdhocMatchingContext *context) {
|
||||
auto n = GetI18NCategory(I18NCat::NETWORKING);
|
||||
// Multithreading Lock
|
||||
peerlock.lock();
|
||||
// Cast Context
|
||||
SceNetAdhocMatchingContext* context = findMatchingContext(matchingId);
|
||||
// Multithreading Unlock
|
||||
peerlock.unlock();
|
||||
SceNetEtherAddr sendermac;
|
||||
u32_le senderport;
|
||||
int rxbuflen;
|
||||
|
||||
// Last Ping
|
||||
u64_le lastping = 0;
|
||||
while (true) {
|
||||
u64 now = CoreTiming::GetGlobalTimeUsScaled();
|
||||
|
||||
// Last Hello
|
||||
u64_le lasthello = 0;
|
||||
|
||||
u64_le now;
|
||||
|
||||
static SceNetEtherAddr sendermac;
|
||||
static u32_le senderport;
|
||||
static int rxbuflen;
|
||||
|
||||
// Log Startup
|
||||
INFO_LOG(Log::sceNet, "InputLoop: Begin of InputLoop[%i] Thread", matchingId);
|
||||
|
||||
// Run while needed...
|
||||
if (context != NULL) {
|
||||
while (contexts != NULL && context->inputRunning) {
|
||||
// Multithreading Lock
|
||||
peerlock.lock();
|
||||
// Cast Context
|
||||
context = findMatchingContext(matchingId);
|
||||
// Multithreading Unlock
|
||||
peerlock.unlock();
|
||||
|
||||
while (context != NULL && context->inputRunning && !Core_IsStepping()) {
|
||||
now = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
|
||||
|
||||
// Hello Message Sending Context with unoccupied Slots
|
||||
if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL)) {
|
||||
// Hello Message Broadcast necessary because of Hello Interval
|
||||
if (context->hello_int > 0)
|
||||
if (static_cast<s64>(now - lasthello) >= static_cast<s64>(context->hello_int)) {
|
||||
// Broadcast Hello Message
|
||||
broadcastHelloMessage(context);
|
||||
|
||||
// Update Hello Timer
|
||||
lasthello = now;
|
||||
}
|
||||
}
|
||||
|
||||
handleDataTimeouts(context, now);
|
||||
|
||||
// A PSP times out a peer exactly keepalive_int * init_count after its last keepalive, not on
|
||||
// its own keepalive tick (pspautotests net/adhoc/pair/vanishtiming).
|
||||
handleTimeout(context);
|
||||
|
||||
// Ping Required
|
||||
if (context->keepalive_int > 0) {
|
||||
if (static_cast<s64>(now - lastping) >= static_cast<s64>(context->keepalive_int)) {
|
||||
// Broadcast Ping Message
|
||||
broadcastPingMessage(context);
|
||||
|
||||
// Update Ping Timer
|
||||
lastping = now;
|
||||
}
|
||||
}
|
||||
|
||||
// Messages on Stack ready for processing
|
||||
if (context->input_stack != NULL) {
|
||||
// Claim Stack
|
||||
context->inputlock->lock();
|
||||
|
||||
// Iterate Message List
|
||||
ThreadMessage* msg = context->input_stack;
|
||||
while (msg != NULL) {
|
||||
// Default Optional Data
|
||||
void* opt = NULL;
|
||||
|
||||
// Grab Optional Data
|
||||
if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
|
||||
|
||||
//context->inputlock->unlock(); // Unlock to prevent race condition when locking peerlock
|
||||
|
||||
// Send Accept Packet
|
||||
if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
// Send Join Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
// Send Cancel Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
// Send Bulk Data Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
// Send Birth Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
|
||||
|
||||
// Send Death Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac);
|
||||
|
||||
// Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and ByePacket here?
|
||||
//else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) sendAbortBulkDataPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
//context->inputlock->lock(); // Lock again
|
||||
|
||||
// Pop the input queue from the front
|
||||
context->input_stack = msg->next;
|
||||
free(msg);
|
||||
msg = context->input_stack;
|
||||
}
|
||||
|
||||
// Free Stack
|
||||
context->inputlock->unlock();
|
||||
}
|
||||
|
||||
// Receive PDP Datagram
|
||||
// FIXME: When using JPCSP + prx files, the "SceNetAdhocMatchingInput" thread is using blocking PdpRecv with infinite(0) timeout, which can be stopped/aborted using SetSocketAlert, while "SceNetAdhocMatchingEvent" thread is using non-blocking for sending
|
||||
rxbuflen = context->rxbuflen;
|
||||
senderport = 0;
|
||||
// Lock the peer first before locking the socket to avoid race condiion
|
||||
peerlock.lock();
|
||||
context->socketlock->lock();
|
||||
int recvresult = sceNetAdhocPdpRecv(currentMIPS, context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK);
|
||||
context->socketlock->unlock();
|
||||
peerlock.unlock();
|
||||
|
||||
// Received Data from a Sender that interests us
|
||||
// Note: There are cases where the sender port might be re-mapped by router or ISP, so we shouldn't check the source port.
|
||||
if (recvresult == 0 && rxbuflen > 0) {
|
||||
// Log Receive Success
|
||||
if (context->rxbuf[0] > 1) {
|
||||
INFO_LOG(Log::sceNet, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0]));
|
||||
}
|
||||
|
||||
// Update Peer Timestamp
|
||||
peerlock.lock();
|
||||
SceNetAdhocctlPeerInfo* peer = findFriend(&sendermac);
|
||||
if (peer != NULL) {
|
||||
now = CoreTiming::GetGlobalTimeUsScaled();
|
||||
s64 delta = now - peer->last_recv;
|
||||
DEBUG_LOG(Log::sceNet, "Timestamp LastRecv Delta: %lld (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac).c_str());
|
||||
if (peer->last_recv != 0) peer->last_recv = std::max(peer->last_recv, now - defaultLastRecvDelta);
|
||||
}
|
||||
else {
|
||||
WARN_LOG(Log::sceNet, "InputLoop[%d]: Unknown Peer[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen);
|
||||
}
|
||||
|
||||
// Show a warning if other player is having their port being re-mapped, thus that other player may have issue with the communication.
|
||||
// Note: That other player may need to switch side between host and join, or reboot their router to solve this issue.
|
||||
if (context->port != senderport && senderport != (*context->peerPort)[sendermac]) {
|
||||
char name[9] = {};
|
||||
if (peer != NULL)
|
||||
truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data);
|
||||
WARN_LOG(Log::sceNet, "InputLoop[%d]: Unknown Source Port from [%s][%s:%u -> %u] (Recved=%i, Length=%i)", matchingId, name, mac2str(&sendermac).c_str(), senderport, context->port, recvresult, rxbuflen);
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, std::string(n->T("AM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 0.0f, "unknowndata");
|
||||
}
|
||||
// Keep tracks of re-mapped peer's ports for further communication.
|
||||
// Note: This will only works if this player were able to receives data on normal port from other players (ie. this player's port wasn't remapped)
|
||||
(*context->peerPort)[sendermac] = senderport;
|
||||
peerlock.unlock();
|
||||
|
||||
// Ping Packet
|
||||
if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_PING) actOnPingPacket(context, &sendermac);
|
||||
|
||||
// Hello Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_HELLO) actOnHelloPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Join Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_JOIN) actOnJoinPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Accept Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_ACCEPT) actOnAcceptPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Cancel Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_CANCEL) actOnCancelPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Bulk Data Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK) actOnBulkDataPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Abort Bulk Data Packet
|
||||
//else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) actOnAbortBulkDataPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Birth Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BIRTH) actOnBirthPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Death Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_DEATH) actOnDeathPacket(context, &sendermac, rxbuflen);
|
||||
|
||||
// Bye Packet
|
||||
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BYE) actOnByePacket(context, &sendermac);
|
||||
|
||||
// Ignore Incoming Trash Data
|
||||
}
|
||||
else
|
||||
break;
|
||||
// Hello Message Sending Context with unoccupied Slots
|
||||
if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL)) {
|
||||
// Hello Message Broadcast necessary because of Hello Interval
|
||||
if (context->hello_int > 0 && static_cast<s64>(now - context->lastHello) >= static_cast<s64>(context->hello_int)) {
|
||||
broadcastHelloMessage(context);
|
||||
context->lastHello = now;
|
||||
}
|
||||
// Share CPU Time
|
||||
sleep_ms(10, "pro-adhoc-4"); //1 //sceKernelDelayThread(10000);
|
||||
|
||||
// Don't do anything if it's paused, otherwise the log will be flooded
|
||||
while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->inputRunning)
|
||||
sleep_ms(10, "pro-adhoc-input-4");
|
||||
}
|
||||
|
||||
if (contexts != NULL) {
|
||||
// Process Last Messages
|
||||
if (context->input_stack != NULL) {
|
||||
// Claim Stack
|
||||
context->inputlock->lock();
|
||||
handleDataTimeouts(context, now);
|
||||
|
||||
// Iterate Message List
|
||||
int msg_count = 0;
|
||||
ThreadMessage* msg = context->input_stack;
|
||||
while (msg != NULL) {
|
||||
// Default Optional Data
|
||||
void* opt = NULL;
|
||||
// A PSP times out a peer exactly keepalive_int * init_count after its last keepalive, not on
|
||||
// its own keepalive tick (pspautotests net/adhoc/pair/vanishtiming).
|
||||
handleTimeout(context);
|
||||
|
||||
// Grab Optional Data
|
||||
if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
|
||||
if (context->keepalive_int > 0 && static_cast<s64>(now - context->lastPing) >= static_cast<s64>(context->keepalive_int)) {
|
||||
broadcastPingMessage(context);
|
||||
context->lastPing = now;
|
||||
}
|
||||
|
||||
// Send Accept Packet
|
||||
if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
|
||||
matchingSendQueued(context);
|
||||
|
||||
// Send Join Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
|
||||
// Receive PDP Datagram
|
||||
rxbuflen = context->rxbuflen;
|
||||
senderport = 0;
|
||||
int recvresult;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> peerGuard(peerlock);
|
||||
std::lock_guard<std::recursive_mutex> socketGuard(*context->socketlock);
|
||||
recvresult = sceNetAdhocPdpRecv(currentMIPS, context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK);
|
||||
}
|
||||
if (recvresult != 0 || rxbuflen <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Send Cancel Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
|
||||
// Received Data from a Sender that interests us
|
||||
// Note: There are cases where the sender port might be re-mapped by router or ISP, so we shouldn't check the source port.
|
||||
if (context->rxbuf[0] > 1) {
|
||||
INFO_LOG(Log::sceNet, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", context->id, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0]));
|
||||
}
|
||||
|
||||
// Send Bulk Data Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
|
||||
// Update Peer Timestamp
|
||||
peerlock.lock();
|
||||
SceNetAdhocctlPeerInfo* peer = findFriend(&sendermac);
|
||||
if (peer != NULL) {
|
||||
now = CoreTiming::GetGlobalTimeUsScaled();
|
||||
s64 delta = now - peer->last_recv;
|
||||
DEBUG_LOG(Log::sceNet, "Timestamp LastRecv Delta: %lld (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac).c_str());
|
||||
if (peer->last_recv != 0) peer->last_recv = std::max(peer->last_recv, now - defaultLastRecvDelta);
|
||||
} else {
|
||||
WARN_LOG(Log::sceNet, "InputLoop[%d]: Unknown Peer[%s:%u] (Recved=%i, Length=%i)", context->id, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen);
|
||||
}
|
||||
|
||||
// Send Birth Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
|
||||
// Show a warning if other player is having their port being re-mapped, thus that other player may have issue with the communication.
|
||||
// Note: That other player may need to switch side between host and join, or reboot their router to solve this issue.
|
||||
if (context->port != senderport && senderport != (*context->peerPort)[sendermac]) {
|
||||
char name[9] = {};
|
||||
if (peer != NULL)
|
||||
truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data);
|
||||
WARN_LOG(Log::sceNet, "InputLoop[%d]: Unknown Source Port from [%s][%s:%u -> %u] (Recved=%i, Length=%i)", context->id, name, mac2str(&sendermac).c_str(), senderport, context->port, recvresult, rxbuflen);
|
||||
g_OSD.Show(OSDType::MESSAGE_WARNING, std::string(n->T("AM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 0.0f, "unknowndata");
|
||||
}
|
||||
// Keep tracks of re-mapped peer's ports for further communication.
|
||||
// Note: This will only works if this player were able to receives data on normal port from other players (ie. this player's port wasn't remapped)
|
||||
(*context->peerPort)[sendermac] = senderport;
|
||||
peerlock.unlock();
|
||||
|
||||
// Send Death Packet
|
||||
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac);
|
||||
|
||||
// Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and ByePacket here?
|
||||
//else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) sendAbortBulkDataPacket(context, &msg->mac, msg->optlen, opt);
|
||||
|
||||
// Pop the input queue from the front
|
||||
context->input_stack = msg->next;
|
||||
free(msg);
|
||||
msg = context->input_stack;
|
||||
msg_count++;
|
||||
}
|
||||
|
||||
// Free Stack
|
||||
context->inputlock->unlock();
|
||||
INFO_LOG(Log::sceNet, "InputLoop[%d]: Finished (%d msg)", matchingId, msg_count);
|
||||
}
|
||||
|
||||
// Clear IO Message Stack
|
||||
clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK);
|
||||
|
||||
// Send Bye Messages. FIXME: Official prx seems to be sending DEATH instead of BYE packet during MatchingStop? But DEATH packet doesn't works with DBZ Team Tag
|
||||
sendByePacket(context);
|
||||
|
||||
// Free Peer List Buffer
|
||||
clearPeerList(context); //deleteAllMembers(context);
|
||||
|
||||
// Delete Pointer Reference (and notify caller about finished cleanup)
|
||||
//context->inputThread = NULL;
|
||||
switch (context->rxbuf[0]) {
|
||||
case PSP_ADHOC_MATCHING_PACKET_PING: actOnPingPacket(context, &sendermac); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_HELLO: actOnHelloPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_JOIN: actOnJoinPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_ACCEPT: actOnAcceptPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_CANCEL: actOnCancelPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_BULK: actOnBulkDataPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_BIRTH: actOnBirthPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_DEATH: actOnDeathPacket(context, &sendermac, rxbuflen); break;
|
||||
case PSP_ADHOC_MATCHING_PACKET_BYE: actOnByePacket(context, &sendermac); break;
|
||||
default: break; // Ignore Incoming Trash Data
|
||||
}
|
||||
}
|
||||
|
||||
// Log Shutdown
|
||||
INFO_LOG(Log::sceNet, "InputLoop: End of InputLoop[%i] Thread", matchingId);
|
||||
|
||||
// Terminate Thread
|
||||
//sceKernelExitDeleteThread(0);
|
||||
|
||||
// Return Zero to shut up Compiler
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Runs every running context's input handling and event delivery, in emulated time, while any is running.
|
||||
static void __NetMatchingPoll(u64 userdata, int cyclesLate) {
|
||||
bool anyRunning = false;
|
||||
SceNetAdhocMatchingContext *next = NULL;
|
||||
for (SceNetAdhocMatchingContext *context = contexts; context != NULL; context = next) {
|
||||
next = context->next;
|
||||
if (context->inputRunning) {
|
||||
matchingHandleInput(context);
|
||||
}
|
||||
if (context->eventRunning) {
|
||||
matchingDeliverEvents(context);
|
||||
}
|
||||
anyRunning = anyRunning || context->inputRunning || context->eventRunning;
|
||||
}
|
||||
if (anyRunning) {
|
||||
CoreTiming::ScheduleEvent(usToCycles(MATCHING_POLL_INTERVAL_US) - cyclesLate, matchingPollEvent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ScheduleMatchingPoll() {
|
||||
if (!CoreTiming::IsScheduled(matchingPollEvent)) {
|
||||
CoreTiming::ScheduleEvent(usToCycles(MATCHING_POLL_INTERVAL_US), matchingPollEvent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DoNetAdhocMatchingPollEvent(PointerWrap &p, bool saved) {
|
||||
if (saved) {
|
||||
Do(p, matchingPollEvent);
|
||||
} else {
|
||||
matchingPollEvent = -1;
|
||||
}
|
||||
CoreTiming::RestoreRegisterEvent(matchingPollEvent, "__NetMatchingPoll", __NetMatchingPoll);
|
||||
// The contexts aren't in the state, so the ones still running here need the poll whatever the state had.
|
||||
if (p.mode == PointerWrap::MODE_READ) {
|
||||
for (SceNetAdhocMatchingContext *context = contexts; context != NULL; context = context->next) {
|
||||
if (context->inputRunning || context->eventRunning) {
|
||||
ScheduleMatchingPoll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// What the input handling does as it stops: sends what's still queued, then the bye.
|
||||
static void matchingStopInput(SceNetAdhocMatchingContext *context) {
|
||||
matchingSendQueued(context);
|
||||
clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK);
|
||||
// FIXME: Official prx seems to be sending DEATH instead of BYE packet during MatchingStop? But DEATH packet doesn't works with DBZ Team Tag
|
||||
sendByePacket(context);
|
||||
clearPeerList(context);
|
||||
}
|
||||
|
||||
void netAdhocMatchingValidateLoopMemory() {
|
||||
if (!matchingThreadHackAddr || (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0)) {
|
||||
@@ -1596,14 +1370,14 @@ int NetAdhocMatching_Stop(int matchingId) {
|
||||
// This will cause using PdpRecv on this socket to return ERROR_NET_ADHOC_SOCKET_ALERTED (Based on Ys vs. Sora no Kiseki when tested with JPCSP + prx files). Is this used to abort inprogress socket activity?
|
||||
NetAdhoc_SetSocketAlert(item->socket, ADHOC_F_ALERTRECV);
|
||||
|
||||
item->inputRunning = false;
|
||||
if (item->inputThread.joinable()) {
|
||||
item->inputThread.join();
|
||||
if (item->inputRunning) {
|
||||
item->inputRunning = false;
|
||||
matchingStopInput(item);
|
||||
}
|
||||
|
||||
item->eventRunning = false;
|
||||
if (item->eventThread.joinable()) {
|
||||
item->eventThread.join();
|
||||
if (item->eventRunning) {
|
||||
item->eventRunning = false;
|
||||
matchingDeliverEvents(item);
|
||||
}
|
||||
|
||||
// Stop fake PSP Thread.
|
||||
@@ -1645,8 +1419,7 @@ int sceNetAdhocMatchingStop(MIPSState *mips, int matchingId) {
|
||||
}
|
||||
|
||||
int NetAdhocMatching_Delete(int matchingId) {
|
||||
// Running threads can't be deleted, so stop them first. This has to happen before we take peerlock,
|
||||
// since the threads take it too.
|
||||
// A running context can't be deleted, so stop it first.
|
||||
SceNetAdhocMatchingContext *item = findMatchingContext(matchingId);
|
||||
if (item && item->running) {
|
||||
NetAdhocMatching_Stop(matchingId);
|
||||
@@ -1955,15 +1728,12 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int
|
||||
//item->matchingThread->Start(matchingId, 0);
|
||||
}
|
||||
|
||||
//Create the threads
|
||||
if (!item->eventRunning) {
|
||||
item->eventRunning = true;
|
||||
item->eventThread = std::thread(matchingEventThread, matchingId);
|
||||
}
|
||||
if (!item->inputRunning) {
|
||||
item->inputRunning = true;
|
||||
item->inputThread = std::thread(matchingInputThread, matchingId);
|
||||
}
|
||||
// The first hello and keepalive go out on the first poll.
|
||||
item->lastHello = 0;
|
||||
item->lastPing = 0;
|
||||
item->eventRunning = true;
|
||||
item->inputRunning = true;
|
||||
ScheduleMatchingPoll();
|
||||
|
||||
item->running = 1;
|
||||
netAdhocMatchingStarted++;
|
||||
@@ -2801,6 +2571,7 @@ void Register_sceNetAdhocMatching() {
|
||||
|
||||
void __NetAdhocMatchingInit() {
|
||||
netAdhocMatchingInited = false;
|
||||
matchingPollEvent = CoreTiming::RegisterEvent("__NetMatchingPoll", __NetMatchingPoll);
|
||||
}
|
||||
|
||||
void __NetAdhocMatchingShutdown() {
|
||||
|
||||
@@ -36,6 +36,7 @@ int NetAdhocMatching_Term();
|
||||
void DoNetAdhocMatchingInited(PointerWrap &p);
|
||||
void DoNetAdhocMatchingThreads(PointerWrap &p);
|
||||
void ZeroNetAdhocMatchingThreads();
|
||||
void DoNetAdhocMatchingPollEvent(PointerWrap &p, bool saved);
|
||||
|
||||
void Register_sceNetAdhocMatching();
|
||||
void __NetAdhocMatchingInit();
|
||||
|
||||
Reference in new issue
Block a user