mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Blocks current thread to synchronize initial GameMode data. Fixes desync issue when GameMode multiplayer mission is being started.
This commit is contained in:
parent
a47cb101bb
commit
4eca124710
4 changed files with 123 additions and 46 deletions
|
@ -1405,6 +1405,12 @@ int friendFinder(){
|
|||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT);
|
||||
}
|
||||
|
||||
// Handle Packets
|
||||
if (rxpos > 0) {
|
||||
// BSSID Packet
|
||||
|
@ -1430,8 +1436,8 @@ int friendFinder(){
|
|||
// Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back
|
||||
gameModeMacs.push_back(localMac);
|
||||
|
||||
if (gameModeMacs.size() >= requiredGameModeMacs.size()) {
|
||||
//adhocctlState = ADHOCCTL_STATE_GAMEMODE;
|
||||
// FIXME: OPCODE_CONNECT_BSSID only triggered once, but the timing of ADHOCCTL_EVENT_GAME notification could be too soon, since there could be more players that need to join before the event should be notified
|
||||
if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) {
|
||||
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1533,10 +1539,9 @@ int friendFinder(){
|
|||
}
|
||||
|
||||
// From JPCSP: Join complete when all the required MACs have joined
|
||||
if (requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {
|
||||
if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {
|
||||
// TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side!
|
||||
//gameModeMacs = requiredGameModeMacs;
|
||||
//adhocctlState = ADHOCCTL_STATE_GAMEMODE;
|
||||
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1582,13 +1587,14 @@ int friendFinder(){
|
|||
// Log Incoming Peer Delete Request
|
||||
INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request...");
|
||||
|
||||
/*if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
|
||||
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
|
||||
auto peer = findFriendByIP(packet->ip);
|
||||
gameModeMacs.erase(std::remove_if(gameModeMacs.begin(), gameModeMacs.end(),
|
||||
[peer](SceNetEtherAddr const& e) {
|
||||
return isMacMatch(&e, &peer->mac_addr);
|
||||
}), gameModeMacs.end());
|
||||
}*/
|
||||
for (auto& gma : replicaGameModeAreas)
|
||||
if (isMacMatch(&gma.mac, &peer->mac_addr)) {
|
||||
gma.updateTimestamp = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't?
|
||||
deleteFriendByIP(packet->ip);
|
||||
|
|
|
@ -114,6 +114,7 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode ==
|
|||
#define ADHOC_GAMEMODE_PORT 31000
|
||||
#define GAMEMODE_UPDATE_INTERVAL 500 // 12000 usec on JPCSP, but lower value works better on BattleZone (in order to get full speed 60 FPS)
|
||||
#define GAMEMODE_INIT_DELAY 10000
|
||||
#define GAMEMODE_SYNC_TIMEOUT 250000
|
||||
|
||||
// GameMode Type
|
||||
#define ADHOCCTL_GAMETYPE_1A 1
|
||||
|
@ -326,6 +327,7 @@ typedef struct GameModeArea {
|
|||
//int socket; // PDP socket?
|
||||
u64 updateTimestamp;
|
||||
int dataUpdated;
|
||||
int dataSent;
|
||||
SceNetEtherAddr mac;
|
||||
u8* data; // upto "size" bytes started from "addr" ?
|
||||
} PACK GameModeArea;
|
||||
|
|
|
@ -66,6 +66,7 @@ bool netAdhocctlInited;
|
|||
bool networkInited = false;
|
||||
|
||||
bool netAdhocGameModeEntered = false;
|
||||
int netAdhocEnterGameModeTimeout = 15000000; // 15 sec as default timeout, to wait for all players to join
|
||||
|
||||
bool netAdhocMatchingInited;
|
||||
int netAdhocMatchingStarted = 0;
|
||||
|
@ -147,37 +148,79 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) {
|
|||
SceUID threadID = userdata >> 32;
|
||||
int uid = (int)(userdata & 0xFFFFFFFF);
|
||||
|
||||
// TODO: May be we should check for timeout too? since EnterGammeMode have timeout arg
|
||||
if (IsGameModeActive()) {
|
||||
auto sock = adhocSockets[gameModeSocket - 1];
|
||||
|
||||
// Send Master data
|
||||
if (masterGameModeArea.dataUpdated) {
|
||||
for (auto& gma : replicaGameModeAreas) {
|
||||
if (IsGameModeActive() && IsSocketReady(sock->data.pdp.id, false, true) > 0) {
|
||||
int sent = sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, ADHOC_GAMEMODE_PORT, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK);
|
||||
if (sent >= 0) {
|
||||
masterGameModeArea.dataUpdated = 0;
|
||||
DEBUG_LOG(SCENET, "GameMode Sent %d bytes to %s", masterGameModeArea.size, mac2str(&gma.mac).c_str());
|
||||
// Need to make sure all replicas have been created before we start syncing data
|
||||
if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
|
||||
// Send Master data
|
||||
if (masterGameModeArea.dataUpdated) {
|
||||
int sentcount = 0;
|
||||
for (auto& gma : replicaGameModeAreas) {
|
||||
if (!gma.dataSent && IsSocketReady(sock->data.pdp.id, false, true) > 0) {
|
||||
int sent = sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, ADHOC_GAMEMODE_PORT, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK);
|
||||
if (sent != ERROR_NET_ADHOC_WOULD_BLOCK) {
|
||||
gma.dataSent = 1;
|
||||
DEBUG_LOG(SCENET, "GameMode: Master data Sent %d bytes to Area #%d [%s]", masterGameModeArea.size, gma.id, mac2str(&gma.mac).c_str());
|
||||
sentcount++;
|
||||
}
|
||||
}
|
||||
else if (gma.dataSent) sentcount++;
|
||||
}
|
||||
if (sentcount == replicaGameModeAreas.size())
|
||||
masterGameModeArea.dataUpdated = 0;
|
||||
}
|
||||
// Need to sync (send + recv) all players initial data (data from CreateMaster) after Master + All Replicas are created, and before the first UpdateMaster / UpdateReplica is called for Star Wars The Force Unleashed to show the correct players color on minimap (also prevent Starting issue on other GameMode games)
|
||||
else {
|
||||
u32 error;
|
||||
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
|
||||
if (error == 0 && waitID == sock->data.pdp.id) {
|
||||
// Resume thread after all replicas data have been received
|
||||
int recvd = 0;
|
||||
for (auto& gma : replicaGameModeAreas) {
|
||||
// Either replicas new data has been received or that player has been disconnected
|
||||
if (gma.dataUpdated || gma.updateTimestamp == 0)
|
||||
recvd++;
|
||||
}
|
||||
// Resume blocked thread
|
||||
u64 now = CoreTiming::GetGlobalTimeUsScaled();
|
||||
if (recvd == replicaGameModeAreas.size()) {
|
||||
u32 waitVal = __KernelGetWaitValue(threadID, error);
|
||||
if (error == 0) {
|
||||
DEBUG_LOG(SCENET, "GameMode: Resuming Thread %d after Master data Synced (Result = %08x)", threadID, waitVal);
|
||||
__KernelResumeThreadFromWait(threadID, waitVal);
|
||||
}
|
||||
else
|
||||
ERROR_LOG(SCENET, "GameMode: Error (%08x) on WaitValue %d ThreadID %d", error, waitVal, threadID);
|
||||
}
|
||||
// Attempt to Re-Send initial Master data (in case previous packets were lost)
|
||||
else if (static_cast<s64>(now - masterGameModeArea.updateTimestamp) > GAMEMODE_SYNC_TIMEOUT) {
|
||||
DEBUG_LOG(SCENET, "GameMode: Attempt to Re-Send Master data after Sync Timeout (%d us)", GAMEMODE_SYNC_TIMEOUT);
|
||||
// Reset Sent marker on players who haven't replied yet (except disconnected players)
|
||||
for (auto& gma : replicaGameModeAreas)
|
||||
if (!gma.dataUpdated && gma.updateTimestamp != 0)
|
||||
gma.dataSent = 0;
|
||||
masterGameModeArea.updateTimestamp = now;
|
||||
masterGameModeArea.dataUpdated = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recv new Replica data. We shouldn't do too much activity on a single event and just try again later after a short delay (1ms or lower, to prevent long sync with max players)
|
||||
if (IsGameModeActive() && IsSocketReady(sock->data.pdp.id, true, false) > 0) {
|
||||
SceNetEtherAddr sendermac;
|
||||
s32_le senderport = ADHOC_GAMEMODE_PORT;
|
||||
s32_le bufsz = uid; // GAMEMODE_BUFFER_SIZE;
|
||||
int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK);
|
||||
if (ret >= 0 && bufsz > 0) {
|
||||
for (auto& gma : replicaGameModeAreas) {
|
||||
if (IsMatch(gma.mac, sendermac)) {
|
||||
DEBUG_LOG(SCENET, "GameMode Received %d bytes of new Data for Area [%s]", bufsz, mac2str(&sendermac).c_str());
|
||||
memcpy(gma.data, gameModeBuffer, std::min(gma.size, bufsz));
|
||||
gma.dataUpdated = 1;
|
||||
gma.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled();
|
||||
break;
|
||||
// Recv new Replica data when available
|
||||
if (IsSocketReady(sock->data.pdp.id, true, false) > 0) {
|
||||
SceNetEtherAddr sendermac;
|
||||
s32_le senderport = ADHOC_GAMEMODE_PORT;
|
||||
s32_le bufsz = uid; // GAMEMODE_BUFFER_SIZE;
|
||||
int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK);
|
||||
if (ret >= 0 && bufsz > 0) {
|
||||
for (auto& gma : replicaGameModeAreas) {
|
||||
if (IsMatch(gma.mac, sendermac)) {
|
||||
DEBUG_LOG(SCENET, "GameMode: Replica data Received %d bytes for Area #%d [%s]", bufsz, gma.id, mac2str(&sendermac).c_str());
|
||||
memcpy(gma.data, gameModeBuffer, std::min(gma.size, bufsz));
|
||||
gma.dataUpdated = 1;
|
||||
gma.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -969,10 +1012,12 @@ void __NetAdhocDoState(PointerWrap &p) {
|
|||
if (s >= 8) {
|
||||
Do(p, isAdhocctlBusy);
|
||||
Do(p, netAdhocGameModeEntered);
|
||||
Do(p, netAdhocEnterGameModeTimeout);
|
||||
}
|
||||
else {
|
||||
isAdhocctlBusy = false;
|
||||
netAdhocGameModeEntered = false;
|
||||
netAdhocEnterGameModeTimeout = 15000000;
|
||||
}
|
||||
|
||||
if (p.mode == p.MODE_READ) {
|
||||
|
@ -2534,6 +2579,7 @@ int NetAdhocctl_Create(const char* groupName) {
|
|||
// Send Packet
|
||||
int iResult = send(metasocket, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);
|
||||
int error = errno;
|
||||
adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
|
||||
|
||||
if (iResult == SOCKET_ERROR && error != EAGAIN && error != EWOULDBLOCK) {
|
||||
ERROR_LOG(SCENET, "Socket error (%i) when sending", error);
|
||||
|
@ -2681,6 +2727,7 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n
|
|||
adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE;
|
||||
adhocConnectionType = ADHOC_CREATE;
|
||||
netAdhocGameModeEntered = true;
|
||||
netAdhocEnterGameModeTimeout = timeout;
|
||||
return NetAdhocctl_Create(group_name);
|
||||
}
|
||||
|
||||
|
@ -2742,6 +2789,7 @@ static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char *
|
|||
adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE;
|
||||
adhocConnectionType = ADHOC_JOIN;
|
||||
netAdhocGameModeEntered = true;
|
||||
netAdhocEnterGameModeTimeout = timeout;
|
||||
return hleLogDebug(SCENET, NetAdhocctl_Create(group_name), "");
|
||||
}
|
||||
|
||||
|
@ -4007,6 +4055,7 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) {
|
|||
if (size < 0 || !Memory::IsValidAddress(dataAddr))
|
||||
return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
|
||||
|
||||
hleEatMicro(1000);
|
||||
SceNetEtherAddr localMac;
|
||||
getLocalMac(&localMac);
|
||||
u8* buf = (u8*)realloc(gameModeBuffer, size);
|
||||
|
@ -4016,14 +4065,21 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) {
|
|||
u8* data = (u8*)malloc(size);
|
||||
if (data) {
|
||||
Memory::Memcpy(data, dataAddr, size);
|
||||
masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, localMac, data };
|
||||
masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, 0, localMac, data };
|
||||
// Socket's buffer size should fit the largest size from master/replicas, should we waited until master & all replicas to be created first before creating the socket? (ie. the first time UpdateMaster being called?)
|
||||
gameModeSocket = sceNetAdhocPdpCreate((const char*)&localMac, ADHOC_GAMEMODE_PORT, size, 0);
|
||||
StartGameModeScheduler(size);
|
||||
}
|
||||
|
||||
hleEatMicro(GAMEMODE_INIT_DELAY);
|
||||
return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0?
|
||||
// Block current thread to sync initial master data
|
||||
if (gameModeSocket > 0 && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
|
||||
auto sock = adhocSockets[gameModeSocket - 1];
|
||||
__KernelWaitCurThread(WAITTYPE_NET, sock->data.pdp.id, 0, 0, false, "syncing master data");
|
||||
DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread());
|
||||
}
|
||||
return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0?
|
||||
}
|
||||
|
||||
return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4059,7 +4115,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s
|
|||
// MAC address already existed!
|
||||
if (it != replicaGameModeAreas.end()) {
|
||||
WARN_LOG(SCENET, "sceNetAdhocGameModeCreateReplica - [%s] is already existed (id: %d)", mac2str((SceNetEtherAddr*)mac).c_str(), it->id);
|
||||
return it->id;
|
||||
return it->id; // ERROR_NET_ADHOC_ALREADY_CREATED
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
@ -4067,11 +4123,20 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s
|
|||
if (data) {
|
||||
Memory::Memcpy(data, dataAddr, size);
|
||||
//int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, size, 0);
|
||||
GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, *(SceNetEtherAddr*)mac, data };
|
||||
GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, 0, *(SceNetEtherAddr*)mac, data };
|
||||
replicaGameModeAreas.push_back(gma);
|
||||
ret = gma.id;
|
||||
ret = gma.id; // Valid id for replica is higher than 0?
|
||||
|
||||
// Block current thread to sync initial master data
|
||||
if (gameModeSocket > 0 && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
|
||||
auto sock = adhocSockets[gameModeSocket - 1];
|
||||
__KernelWaitCurThread(WAITTYPE_NET, sock->data.pdp.id, ret, 0, false, "syncing master data");
|
||||
DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread());
|
||||
}
|
||||
return hleLogSuccessInfoI(SCENET, ret, "success");
|
||||
}
|
||||
return hleLogSuccessInfoI(SCENET, ret, "success"); // valid id for replica started from 1?
|
||||
|
||||
return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
|
||||
}
|
||||
|
||||
static int sceNetAdhocGameModeUpdateMaster() {
|
||||
|
@ -4089,8 +4154,11 @@ static int sceNetAdhocGameModeUpdateMaster() {
|
|||
Memory::Memcpy(masterGameModeArea.data, masterGameModeArea.addr, masterGameModeArea.size);
|
||||
masterGameModeArea.dataUpdated = 1;
|
||||
masterGameModeArea.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled();
|
||||
// Reset sent marker
|
||||
for (auto& gma : replicaGameModeAreas)
|
||||
gma.dataSent = 0;
|
||||
}
|
||||
|
||||
|
||||
hleEatMicro(1000);
|
||||
return 0;
|
||||
}
|
||||
|
@ -4143,7 +4211,7 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) {
|
|||
GameModeUpdateInfo* gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr);
|
||||
gmuinfo->length = sizeof(GameModeUpdateInfo);
|
||||
if (gma.data && gma.dataUpdated) {
|
||||
memcpy(Memory::GetPointer(gma.addr), gma.data, gma.size);
|
||||
Memory::Memcpy(gma.addr, gma.data, gma.size);
|
||||
gma.dataUpdated = 0;
|
||||
gmuinfo->updated = 1;
|
||||
gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
|
||||
|
@ -5314,10 +5382,10 @@ void __NetTriggerCallbacks()
|
|||
u32_le args[3] = { 0, 0, 0 };
|
||||
args[0] = flags;
|
||||
args[1] = error;
|
||||
u64 now = (u64)(time_now_d() * 1000000.0);
|
||||
|
||||
// FIXME: When Joining a group, Do we need to wait for group creator's peer data before triggering the callback to make sure the game not to thinks we're the group creator?
|
||||
u64 now = (u64)(time_now_d() * 1000000.0);
|
||||
if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || now - adhocctlStartTime > adhocDefaultTimeout)
|
||||
if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || static_cast<s64>(now - adhocctlStartTime) > adhocDefaultTimeout)
|
||||
{
|
||||
// Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state
|
||||
if (actionAfterAdhocMipsCall < 0) {
|
||||
|
|
|
@ -99,6 +99,7 @@ extern bool netAdhocInited;
|
|||
extern bool netAdhocctlInited;
|
||||
extern bool networkInited;
|
||||
extern bool netAdhocGameModeEntered;
|
||||
extern int netAdhocEnterGameModeTimeout;
|
||||
extern int adhocDefaultTimeout; //3000000 usec
|
||||
extern int adhocDefaultDelay; //10000
|
||||
extern int adhocExtraDelay; //20000
|
||||
|
|
Loading…
Add table
Reference in a new issue