the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 4155 lines 157 kB view raw
1#include "stdafx.h" 2#include "SQRNetworkManager_Orbis.h" 3#include <net.h> 4#include <sys/socket.h> 5#include <net.h> 6#include <rudp.h> 7#include <np_toolkit.h> 8#include <game_custom_data_dialog.h> 9#include <error_dialog.h> 10#include <system_service.h> 11 12#include "../ps4__np_conf.h" 13#include "Orbis_NPToolkit.h" 14#include "SonyVoiceChat_Orbis.h" 15#include "Common\Network\Sony\SonyHttp.h" 16#include "..\..\..\Minecraft.World\C4JThread.h" 17// #include "..\PS3Extras\PS3Strings.h" 18 19 20int (* SQRNetworkManager_Orbis::s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad) = NULL; 21void * SQRNetworkManager_Orbis::s_SignInCompleteParam = NULL; 22sce::Toolkit::NP::PresenceDetails SQRNetworkManager_Orbis::s_lastPresenceInfo; 23 24__int64 SQRNetworkManager_Orbis::s_lastPresenceTime = 0; 25__int64 SQRNetworkManager_Orbis::s_resendPresenceTime = 0; 26 27bool SQRNetworkManager_Orbis::s_presenceStatusDirty = false; 28bool SQRNetworkManager_Orbis::s_presenceDataDirty = false; 29bool SQRNetworkManager_Orbis::s_signInCompleteCallbackIfFailed = false; 30bool SQRNetworkManager_Orbis::s_signInCompleteCallbackFAIL = false; 31int SQRNetworkManager_Orbis::s_SignInCompleteCallbackPad = -1; 32bool SQRNetworkManager_Orbis::s_SignInCompleteCallbackPending = false; 33long long SQRNetworkManager_Orbis::s_errorDialogClosed = -1; 34long long SQRNetworkManager_Orbis::s_systemDialogClosed = -1; 35SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::s_lastPresenceSyncInfo = { 0 }; 36SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::c_presenceSyncInfoNULL = { 0 }; 37//SceNpBasicAttachmentDataId SQRNetworkManager_Orbis::s_lastInviteIdToRetry = SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID; 38long long SQRNetworkManager_Orbis::s_roomStartTime = 0; 39bool SQRNetworkManager_Orbis::b_inviteRecvGUIRunning = false; 40SQRNetworkManager_Orbis::PresenceSyncInfo* SQRNetworkManager_Orbis::m_gameBootInvite; 41SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::m_gameBootInvite_data; 42bool SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=false; 43bool SQRNetworkManager_Orbis::s_errorDialogRunning=false; 44bool SQRNetworkManager_Orbis::s_bInviteDialogRunning=false; 45 46static const int sc_UserEventHandle = 0; 47 48static const int sc_verbose = false; 49 50int g_numRUDPContextsBound = 0; 51//unsigned int SQRNetworkManager_Orbis::RoomSyncData::playerCount = 0; 52 53// This maps internal to extern states, and needs to match element-by-element the eSQRNetworkManagerInternalState enumerated type 54const SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::m_INTtoEXTStateMappings[SQRNetworkManager_Orbis::SNM_INT_STATE_COUNT] = 55{ 56 SNM_STATE_INITIALISING, // SNM_INT_STATE_UNINITIALISED 57 SNM_STATE_INITIALISING, // SNM_INT_STATE_SIGNING_IN 58 SNM_STATE_INITIALISING, // SNM_INT_STATE_STARTING_CONTEXT 59 SNM_STATE_INITIALISE_FAILED, // SNM_INT_STATE_INITIALISE_FAILED 60 SNM_STATE_IDLE, // SNM_INT_STATE_IDLE 61 SNM_STATE_IDLE, // SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT 62 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT 63 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER 64 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR 65 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_FOUND 66 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT 67 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED 68 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD 69 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND 70 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM 71 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS 72 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED 73 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT 74 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_WAITING_TO_PLAY 75 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT 76 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER 77 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR 78 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_FOUND 79 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT 80 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED 81 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM 82 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED 83 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS 84 SNM_STATE_ENDING, // SNM_INT_STATE_SERVER_DELETING_CONTEXT 85 SNM_STATE_STARTING, // SNM_INT_STATE_STARTING 86 SNM_STATE_PLAYING, // SNM_INT_STATE_PLAYING 87 SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING 88 SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING_FAILED 89 SNM_STATE_ENDING, // SNM_INT_STATE_ENDING 90}; 91 92SQRNetworkManager_Orbis::SQRNetworkManager_Orbis(ISQRNetworkManagerListener *listener) 93{ 94 m_state = SNM_INT_STATE_UNINITIALISED; 95 m_stateExternal = SNM_STATE_INITIALISING; 96 m_nextIdleReasonIsFull = false; 97 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; 98 m_serverContextValid = false; 99 m_isHosting = false; 100 m_currentSmallId = 0; 101 memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); 102 m_listener = listener; 103 m_soc = -1; 104 m_resendExternalRoomDataCountdown = 0; 105 m_matching2initialised = false; 106 m_matchingContextValid = false; 107 m_inviteIndex = 0; 108 m_doBootInviteCheck = true; 109 m_isInSession = false; 110 m_offlineGame = false; 111 m_offlineSQR = false; 112 m_aServerId = NULL; 113 m_gameBootInvite = NULL; 114 m_onlineStatus = false; 115 m_bLinkDisconnected = false; 116 m_bRefreshingRestrictionsForInvite = false; 117 118 InitializeCriticalSection(&m_csRoomSyncData); 119 InitializeCriticalSection(&m_csPlayerState); 120 InitializeCriticalSection(&m_csStateChangeQueue); 121 InitializeCriticalSection(&m_signallingEventListCS); 122 123 int ret = sceKernelCreateEqueue(&m_basicEventQueue, "SQRNetworkManager_Orbis EQ"); 124 assert(ret == SCE_OK); 125 ret = sceKernelAddUserEvent(m_basicEventQueue, sc_UserEventHandle); 126 assert(ret == SCE_OK); 127 128 m_basicEventThread = new C4JThread(&BasicEventThreadProc,this,"Basic Event Handler"); 129 m_basicEventThread->Run(); 130} 131 132// First stage of initialisation. This initialises a few things that don't require the user to be signed in, and then kicks of the network start dialog utility. 133// Initialisation continues in InitialiseAfterOnline once this completes. 134void SQRNetworkManager_Orbis::Initialise() 135{ 136 #define NP_IN_GAME_MESSAGE_POOL_SIZE ( 16 * 1024 ) 137 138 int32_t ret = 0; 139 int32_t libCtxId = 0; 140 ret = sceNpInGameMessageInitialize(NP_IN_GAME_MESSAGE_POOL_SIZE, NULL); 141 assert (ret >= 0); 142 libCtxId = ret; 143 144 assert( m_state == SNM_INT_STATE_UNINITIALISED ); 145 146 147 //Initialize libnetctl 148 ret = sceNetCtlInit(); 149 if( ( ret < 0 /*&& ret != CELL_NET_CTL_ERROR_NOT_TERMINATED*/ ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_CTL_INIT ) ) 150 { 151 app.DebugPrintf("sceNetCtlInit failed with error 0x%08x\n", ret); 152 SetState(SNM_INT_STATE_INITIALISE_FAILED); 153 return; 154 } 155 156 int hid; 157 ret = sceNetCtlRegisterCallback(&NetCtlCallback,this,&hid); 158 159 // Initialise RUDP 160 const int RUDP_POOL_SIZE = (500 * 1024); // TODO - find out what we need, this size is copied from library reference 161 uint8_t *rudp_pool = (uint8_t *)malloc(RUDP_POOL_SIZE); 162 ret = sceRudpInit(rudp_pool, RUDP_POOL_SIZE); 163 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_RUDP_INIT ) ) 164 { 165 app.DebugPrintf("sceRudpInit failed with error 0x%08x\n", ret); 166 SetState(SNM_INT_STATE_INITIALISE_FAILED); 167 return; 168 } 169 170 SetState(SNM_INT_STATE_SIGNING_IN); 171 172 173 SonyHttp::init(); 174 ret = sceNpSetNpTitleId(GetSceNpTitleId(), GetSceNpTitleSecret()); 175 if (ret < 0) 176 { 177 app.DebugPrintf("sceNpSetNpTitleId failed, ret=%x\n", ret); 178 assert(0); 179 } 180 181 ret = sceRudpEnableInternalIOThread(RUDP_THREAD_STACK_SIZE, RUDP_THREAD_PRIORITY); 182 if(ret < 0) 183 { 184 app.DebugPrintf("sceRudpEnableInternalIOThread failed with error code 0x%08x\n", ret); 185 assert(0); 186 } 187 // Already online? the callback won't catch this, so carry on initialising now 188 if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) 189 { 190 InitialiseAfterOnline(); 191 } 192 else 193 { 194 // On Orbis, PSN sign in is only handled by the XMB 195 SetState(SNM_INT_STATE_IDLE); 196 } 197 SonyVoiceChat_Orbis::init(); 198 199 200} 201 202void SQRNetworkManager_Orbis::Terminate() 203{ 204 // If playing, attempt to nicely leave the room before shutting down so that our friends won't still think this game is in progress 205 if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || 206 ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || 207 ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || 208 ( m_state == SNM_INT_STATE_PLAYING ) ) 209 { 210 if( !m_offlineGame ) 211 { 212 LeaveRoom(true); 213 int count = 200; 214 do 215 { 216 Tick(); 217 Sleep(10); 218 count--; 219 } while( ( count > 0 ) && ( m_state != SNM_INT_STATE_IDLE ) ); 220 app.DebugPrintf(CMinecraftApp::USER_RR,"Attempted to leave room, %dms used\n",count * 10); 221 } 222 } 223 224 int ret = sceRudpEnd(); 225 ret = sceNpMatching2Terminate(); 226 // Terminate event thread by sending it a non-zero value for data 227 sceKernelTriggerUserEvent(m_basicEventQueue, sc_UserEventHandle, (void*)1); 228 229 230 do 231 { 232 Sleep(10); 233 } while( m_basicEventThread->isRunning() ); 234} 235 236// Second stage of initialisation, that requires NP Manager to be online & the player to be signed in. This kicks of the creation of a context 237// for Np Matching 2. Initialisation is finally complete when we get a callback to ContextCallback. The SQRNetworkManager_Orbis is then finally moved 238// into SNM_INT_STATE_IDLE at this stage. 239void SQRNetworkManager_Orbis::InitialiseAfterOnline() 240{ 241 SceNpId npId; 242 int option = 0; 243 244 // We should only be doing this if we have come in from an initialisation stage (SQRNetworkManager_Orbis::Initialise) or we've had a network disconnect and are coming in from an offline state. 245 // Don't do anything otherwise - this mainly to catch a bit of a corner case in the initialisation phase where potentially we could register for the callback that would call this with sceNpManagerRegisterCallback. 246 // and could then really quickly go online so that the there becomes two paths (via the callback or SQRNetworkManager_Orbis::Initialise) by which this could be called 247 if( ( m_state != SNM_INT_STATE_SIGNING_IN ) && !(( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ) 248 { 249 // If we aren't going to continue on with this sign-in but are expecting a callback, then let the game know that we have completed the bit we are expecting to do. This 250 // will happen whilst in game, when we want to be able to sign into PSN, but don't expect the full matching stuff to get set up. 251 if( s_SignInCompleteCallbackFn ) 252 { 253 s_SignInCompleteCallbackFn(s_SignInCompleteParam,true,s_SignInCompleteCallbackPad); 254 s_SignInCompleteCallbackFn = NULL; 255 s_SignInCompleteCallbackPad = -1; 256 } 257 return; 258 } 259 260 // Initialize matching2 261 SceNpMatching2InitializeParameter initParam; 262 memset(&initParam,0,sizeof(initParam)); 263 initParam.size = sizeof(initParam); 264 initParam.cpuAffinityMask = 1 << 4; 265 initParam.threadPriority = SCE_KERNEL_PRIO_FIFO_DEFAULT; // This seems to be completely ignored and always set to default anyway 266 initParam.threadStackSize = SCE_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT * 2; 267 initParam.poolSize = SCE_NP_MATCHING2_POOLSIZE_DEFAULT * 2; 268 int ret = sceNpMatching2Initialize(&initParam); 269 270 if( ( ret < 0 && ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) || ForceErrorPoint( SNM_FORCE_ERROR_MATCHING2_INIT ) ) 271 { 272 app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2Initialize failed with error 0x%08x\n", ret); 273 SetState(SNM_INT_STATE_INITIALISE_FAILED); 274 return; 275 } 276 app.DebugPrintf("SQRNetworkManager::InitialiseAfterOnline - matching context is now valid\n"); 277 m_matching2initialised = true; 278 279 // Get NP ID of the signed-in user 280 SceNpId npID; 281 int primaryPad = ProfileManager.GetPrimaryPad(); 282 if(primaryPad >=0 && ProfileManager.IsSignedInLive(primaryPad)) 283 { 284 ProfileManager.GetSceNpId(primaryPad, &npID); 285 } 286 else 287 { 288 app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - Primary pad not signed in to live"); 289 SetState(SNM_INT_STATE_INITIALISE_FAILED); 290 return; 291 } 292 293 SceNpServiceLabel serviceLabel = 0; 294 SceNpMatching2CreateContextParam param; 295 memset(&param, 0, sizeof(param)); 296 param.npId = &npID; 297 param.serviceLabel = serviceLabel; 298 param.size = sizeof(param); 299 300 301 ret = sceNpMatching2CreateContext(&param, &m_matchingContext); 302 303 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT ) ) 304 { 305 app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2CreateContext failed with error 0x%08x\n", ret); 306 SetState(SNM_INT_STATE_INITIALISE_FAILED); 307 return; 308 } 309 m_matchingContextValid = true; 310 311 bool bRet = RegisterCallbacks(); 312 if( ( !bRet ) || ForceErrorPoint( SNM_FORCE_ERROR_REGISTER_CALLBACKS ) ) 313 { 314 app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - RegisterCallbacks failed with error 0x%08x\n", ret); 315 SetState(SNM_INT_STATE_INITIALISE_FAILED); 316 return; 317 } 318 319 // State should be starting context until the callback that this has been created happens 320 SetState(SNM_INT_STATE_STARTING_CONTEXT); 321 322 // Start the context 323 // Set time-out time to 10 seconds 324 ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); 325 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CONTEXT_START_ASYNC ) ) 326 { 327 app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2ContextStart failed with error 0x%08x\n", ret); 328 SetState(SNM_INT_STATE_INITIALISE_FAILED); 329 } 330} 331 332void SQRNetworkManager_Orbis::RefreshChatAndContentRestrictionsReturned_HandleInvite(void *pParam) 333{ 334 SQRNetworkManager_Orbis *netMan = (SQRNetworkManager_Orbis *)pParam; 335 336 netMan->m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), netMan->m_gameBootInvite ); 337 netMan->m_gameBootInvite = NULL; 338 339 netMan->m_bRefreshingRestrictionsForInvite = false; 340} 341 342// General tick function to be called from main game loop - any internal tick functions should be called from here. 343void SQRNetworkManager_Orbis::Tick() 344{ 345 OnlineCheck(); 346 sceNetCtlCheckCallback(); 347 ServerContextTick(); 348 RoomCreateTick(); 349 FriendSearchTick(); 350 TickRichPresence(); 351 TickInviteGUI(); 352 TickNotify(); 353 tickErrorDialog(); 354 SignallingEventsTick(); // process the signalling events here now instead of in the matching2 callback 355 if( ( m_gameBootInvite ) && ( s_safeToRespondToGameBootInvite ) && !m_bRefreshingRestrictionsForInvite ) 356 { 357 m_bRefreshingRestrictionsForInvite = true; 358 ProfileManager.RefreshChatAndContentRestrictions(RefreshChatAndContentRestrictionsReturned_HandleInvite, this); 359 //m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), m_gameBootInvite ); 360 //m_gameBootInvite = NULL; 361 } 362 363 ErrorHandlingTick(); 364 // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, 365 // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. 366 if( m_resendExternalRoomDataCountdown ) 367 { 368 if( m_state == SNM_INT_STATE_PLAYING ) 369 { 370 m_resendExternalRoomDataCountdown--; 371 if( m_resendExternalRoomDataCountdown == 0 ) 372 { 373 UpdateExternalRoomData(); 374 } 375 } 376 else 377 { 378 m_resendExternalRoomDataCountdown = 0; 379 } 380 } 381 382// ProfileManager.SetNetworkStatus(GetOnlineStatus()); 383 384 // Client only - do the final transition to a starting & playing state once we have fully joined the room, And told the game about all the local players so they are also all valid 385 if( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) 386 { 387 if( m_localPlayerJoined == m_localPlayerCount ) 388 { 389 // Since we're now fully joined, we can update our presence info so that our friends could find us in this game. This data was set up 390 // at the point that we joined the game (either from search info, or an invitation). 391 UpdateRichPresenceCustomData(&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); 392 SetState( SNM_INT_STATE_STARTING); 393 SetState( SNM_INT_STATE_PLAYING ); 394 } 395 } 396 397 if( m_state == SNM_INT_STATE_SERVER_DELETING_CONTEXT ) 398 { 399 // make sure we've removed all the remote players and killed the udp connections before we bail out 400 if(m_RudpCtxToPlayerMap.size() == 0) 401 ResetToIdle(); 402 } 403 404 EnterCriticalSection(&m_csStateChangeQueue); 405 while(m_stateChangeQueue.size() > 0 ) 406 { 407 if( m_listener ) 408 { 409 m_listener->HandleStateChange(m_stateChangeQueue.front().m_oldState, m_stateChangeQueue.front().m_newState, m_stateChangeQueue.front().m_idleReasonIsSessionFull); 410 if( m_stateChangeQueue.front().m_newState == SNM_STATE_IDLE ) 411 { 412 m_isInSession = false; 413 } 414 } 415 m_stateExternal = m_stateChangeQueue.front().m_newState; 416 m_stateChangeQueue.pop(); 417 } 418 LeaveCriticalSection(&m_csStateChangeQueue); 419 420 // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it frmo the tick instead 421 if(m_bCallPSNSignInCallback) 422 { 423 // TODO: This may be a sign in attempt for a different pad, need to fix this 424 app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ s_SignInCompleteCallbackFn false ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); 425 426 m_bCallPSNSignInCallback=false; 427 if( s_signInCompleteCallbackIfFailed && s_signInCompleteCallbackFAIL) 428 { 429 s_signInCompleteCallbackIfFailed=false; 430 s_signInCompleteCallbackFAIL=false; 431 if(s_SignInCompleteCallbackFn) 432 { 433 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); 434 s_SignInCompleteCallbackFn = NULL; 435 } 436 s_SignInCompleteCallbackPad = -1; 437 } 438 else if(s_SignInCompleteCallbackFn) 439 { 440 s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, s_SignInCompleteCallbackPad); 441 s_SignInCompleteCallbackFn = NULL; 442 s_SignInCompleteCallbackPad = -1; 443 } 444 } 445 446} 447 448 449void SQRNetworkManager_Orbis::tickErrorDialog() 450{ 451 if(s_errorDialogRunning) 452 { 453 SceErrorDialogStatus s = sceErrorDialogUpdateStatus(); 454 switch (s) 455 { 456 case SCE_ERROR_DIALOG_STATUS_NONE: 457 assert(0); 458 break; 459 460 case SCE_ERROR_DIALOG_STATUS_INITIALIZED: 461 case SCE_ERROR_DIALOG_STATUS_RUNNING: 462// sceErrorDialogClose(); 463 break; 464 465 case SCE_ERROR_DIALOG_STATUS_FINISHED: 466 sceErrorDialogTerminate(); 467 s_errorDialogRunning = false; 468 469 // Start callback timer 470 s_SignInCompleteCallbackPending = true; 471 s_errorDialogClosed = System::currentTimeMillis(); 472 break; 473 } 474 } 475 476 477 // Note: I'll revisit this at some point, there must be a better way 478 // Long story short we need to wait for a bit for the system UI to appear, If it doesn't appear or it 479 // disappears we wait for a bit then check the sign in results. Not perfect but it is safe! 480 481 // If we have a pending callback, try callback 482 if (s_SignInCompleteCallbackPending) 483 { 484 int sinceErrorClosed = System::currentTimeMillis() - s_errorDialogClosed; 485 486 // If error dialog has been gone for a while, look for system dialogue 487 if (sinceErrorClosed > SYSTEM_UI_WAIT_TIME) 488 { 489 // Check if the system UI is currently displayed 490 SceSystemServiceStatus status = SceSystemServiceStatus(); 491 sceSystemServiceGetStatus(&status); 492 bool systemUiDisplayed = status.isInBackgroundExecution || status.isSystemUiOverlaid; 493 494 if (systemUiDisplayed) 495 { 496 // Wait till the system goes away 497 } 498 else 499 { 500 // Start timer if we haven't already 501 if (s_systemDialogClosed <= 0) 502 { 503 s_systemDialogClosed = System::currentTimeMillis(); 504 } 505 else 506 { 507 int sinceSystemClosed = System::currentTimeMillis() - s_systemDialogClosed; 508 509 // If the system dialog has been closed a while, we're ready to callback 510 if (sinceSystemClosed > SYSTEM_UI_WAIT_TIME) 511 { 512 // 4J-PB - don't call this here, let the SQRNetworkManager_Orbis::Tick call it 513 //CallSignInCompleteCallback(); 514 m_bCallPSNSignInCallback=true; 515 516 // Reset 517 s_SignInCompleteCallbackPending = false; 518 s_errorDialogClosed = -1; 519 s_systemDialogClosed = -1; 520 } 521 } 522 } 523 } 524 } 525} 526 527// Detect any states which reflect internal error states, do anything required, and transition away again 528void SQRNetworkManager_Orbis::ErrorHandlingTick() 529{ 530 switch( m_state ) 531 { 532 case SNM_INT_STATE_INITIALISE_FAILED: 533 if( s_SignInCompleteCallbackFn ) 534 { 535 if( s_signInCompleteCallbackIfFailed ) 536 { 537 // 4J-PB - flag the failed sign in complete to be called 538 s_signInCompleteCallbackFAIL=true; 539 m_bCallPSNSignInCallback=true; 540 //s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); 541 } 542 //s_SignInCompleteCallbackFn = NULL; 543 //s_SignInCompleteCallbackPad = -1; 544 } 545 app.DebugPrintf("Network error: SNM_INT_STATE_INITIALISE_FAILED\n"); 546 if( m_isInSession && m_offlineGame) // m_offlineSQR ) // MGH - changed this to m_offlineGame, as m_offlineSQR can be true when running an online game but the init has failed because the servers are down 547 { 548 // This is a fix for an issue where a player attempts (and fails) to sign in, whilst in an offline game. This was setting the state to idle, which in turn 549 // sets the game to Not be in a session anymore (but the game wasn't generally aware of, and so keeps playing). Howoever, the game's connections use 550 // their tick to determine whether to empty their queues or not and so no communications (even though they don't actually use this network manager for local connections) 551 // were happening. 552 SetState(SNM_INT_STATE_PLAYING); 553 } 554 else 555 { 556 m_offlineSQR = true; 557 SetState(SNM_INT_STATE_IDLE); 558 } 559 break; 560 case SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED: 561 app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED\n"); 562 ResetToIdle(); 563 break; 564 case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: 565 app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED\n"); 566 app.SetDisconnectReason(DisconnectPacket::eDisconnect_NetworkError); 567 DeleteServerContext(); 568 break; 569 case SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED: 570 app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED\n"); 571 ResetToIdle(); 572 break; 573 case SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED: 574 app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED\n"); 575 DeleteServerContext(); 576 break; 577 case SNM_INT_STATE_LEAVING_FAILED: 578 app.DebugPrintf("Network error: SNM_INT_STATE_LEAVING_FAILED\n"); 579 if( !m_isHosting ) 580 { 581 RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); 582 } 583 DeleteServerContext(); 584 break; 585 } 586 587} 588 589// Start hosting a game, by creating a room & joining it. We explicity create a server context here (via GetServerContext) as Sony suggest that 590// this means we have greater control of representing when players are actually "online". The creation of the room is carried out in a callback 591// after that server context is made (ServerContextValidCallback_CreateRoom). 592// hostIndex is the index of the user that is hosting the session, and localPlayerMask has bit 0 - 3 set to indicate the full set of local players joining the game. 593// extData and extDataSize define the initial state of room data that is externally visible (eg by players searching for rooms, but not in it) 594void SQRNetworkManager_Orbis::CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline) 595{ 596 // hostIndex should always be in the mask 597 assert( ( ( 1 << hostIndex ) & localPlayerMask ) != 0 ); 598 599 m_isHosting = true; 600 m_joinExtData = extData; 601 m_joinExtDataSize = extDataSize; 602 m_offlineGame = offline; 603 m_resendExternalRoomDataCountdown = 0; 604 m_isInSession= true; 605 606 // Default value for room, which we can use for offlinae games 607 m_room = 0; 608 609 // Initialise room data that will be synchronised. Slot 0 is always reserved for the host. We don't know the 610 // room member until the room is actually created so this will be set/updated at that point 611 memset( &m_roomSyncData, 0, sizeof(m_roomSyncData) ); 612 m_roomSyncData.setPlayerCount(1); 613 m_roomSyncData.players[0].m_smallId = m_currentSmallId++; 614 m_roomSyncData.players[0].m_localIdx = hostIndex; 615 616 // Remove the host player that we've already added, then add any other local players specified in the mask 617 localPlayerMask &= ~( ( 1 << hostIndex ) & localPlayerMask ); 618 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 619 { 620 if( localPlayerMask & ( 1 << i ) ) 621 { 622 m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_smallId = m_currentSmallId++; 623 m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_localIdx = i; 624 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); 625 } 626 } 627 m_localPlayerCount = m_roomSyncData.getPlayerCount(); 628 629 // For offline games, we can jump straight to the state that says we've just created the room (or would have, for an online game) 630 if( m_offlineGame ) 631 { 632 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); 633 } 634 else 635 { 636 // Kick off the sequence of events required for an online game, starting with getting the server context 637 m_isInSession = GetServerContext(); 638 } 639} 640 641// Updates the externally visible data that was associated with the room when it was created with CreateAndJoinRoom. 642void SQRNetworkManager_Orbis::UpdateExternalRoomData() 643{ 644 if( m_offlineGame ) return; 645 if( m_isHosting ) 646 { 647 SceNpMatching2SetRoomDataExternalRequest reqParam; 648 memset( &reqParam, 0, sizeof(reqParam) ); 649 reqParam.roomId = m_room; 650 SceNpMatching2BinAttr roomBinAttr; 651 memset(&roomBinAttr, 0, sizeof(roomBinAttr)); 652 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; 653 roomBinAttr.ptr = m_joinExtData; 654 roomBinAttr.size = m_joinExtDataSize; 655 reqParam.roomBinAttrExternalNum = 1; 656 reqParam.roomBinAttrExternal = &roomBinAttr; 657 658 int ret = sceNpMatching2SetRoomDataExternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); 659 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SetRoomDataExternal returns 0x%x, number of players %d\n",ret,((char *)m_joinExtData)[174]); 660 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA ) ) 661 { 662 // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, 663 // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. 664 m_resendExternalRoomDataCountdown = 60; 665 } 666 } 667} 668 669// Determine if the friend room manager is busy. If it isn't busy, then other operations (searching for a friend, reading the found friend's room lists) may safely be performed 670bool SQRNetworkManager_Orbis::FriendRoomManagerIsBusy() 671{ 672 return (m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE); 673} 674 675// Initiate a search for rooms that the signed in user's friends are in. This is an asynchronous operation, this function returns after it kicks off a search across all game servers 676// for any of the player's friends. 677bool SQRNetworkManager_Orbis::FriendRoomManagerSearch() 678{ 679 if( m_state != SNM_INT_STATE_IDLE ) return false; 680 681 // Don't start another search if we're already searching... 682 if( m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE ) 683 { 684 return false; 685 } 686 687 // Free up any external data that we received from the previous search 688 for( int i = 0; i < m_aFriendSearchResults.size(); i++ ) 689 { 690 if(m_aFriendSearchResults[i].m_RoomExtDataReceived) 691 free(m_aFriendSearchResults[i].m_RoomExtDataReceived); 692 m_aFriendSearchResults[i].m_RoomExtDataReceived = NULL; 693 } 694 695 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT; 696 m_friendCount = 0; 697 m_aFriendSearchResults.clear(); 698 699 // Get friend list - doing this in another thread as it can lock up for a few seconds 700 m_getFriendCountThread = new C4JThread(&GetFriendsThreadProc,this,"GetFriendsThreadProc"); 701 m_getFriendCountThread->Run(); 702 703 return true; 704} 705 706bool SQRNetworkManager_Orbis::FriendRoomManagerSearch2() 707{ 708 SceNpMatching2AttributeId attrId; 709 SceNpMatching2GetUserInfoListRequest reqParam; 710 711 if( m_friendCount == 0 ) 712 { 713 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; 714 return false; 715 } 716 717 if( m_aFriendSearchResults.size() > 0 ) 718 { 719 // If we have some results, then we also want to make sure that we don't have any duplicate rooms here if more than one friend is playing in the same room. 720 unordered_set<SceNpMatching2RoomId> uniqueRooms; 721 for( unsigned int i = 0; i < m_aFriendSearchResults.size(); i++ ) 722 { 723 if(m_aFriendSearchResults[i].m_RoomFound) 724 { 725 uniqueRooms.insert( m_aFriendSearchResults[i].m_RoomId ); 726 } 727 } 728 729 // Tidy the results up further based on this 730 for( unsigned int i = 0; i < m_aFriendSearchResults.size(); ) 731 { 732 if( uniqueRooms.find(m_aFriendSearchResults[i].m_RoomId) == uniqueRooms.end() ) 733 { 734 free(m_aFriendSearchResults[i].m_RoomExtDataReceived); 735 m_aFriendSearchResults[i] = m_aFriendSearchResults.back(); 736 m_aFriendSearchResults.pop_back(); 737 } 738 else 739 { 740 uniqueRooms.erase(m_aFriendSearchResults[i].m_RoomId); 741 i++; 742 } 743 } 744 } 745 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; 746 return true; 747} 748 749void SQRNetworkManager_Orbis::FriendSearchTick() 750{ 751 // Move onto next state if we're done getting our friend count 752 if( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT ) 753 { 754 if( !m_getFriendCountThread->isRunning() ) 755 { 756 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_INFO; 757 delete m_getFriendCountThread; 758 m_getFriendCountThread = NULL; 759 FriendRoomManagerSearch2(); 760 } 761 } 762} 763 764// The handler for basic events can't actually get the events themselves, this has to be done on another thread. Instead, we send a sys_event_t to a queue on This thread, 765// which has a single data item used which we can use to determine whether to terminate this thread or get a basic event & handle that. 766int SQRNetworkManager_Orbis::BasicEventThreadProc( void *lpParameter ) 767{ 768 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; 769 770 int ret = SCE_OK; 771 SceKernelEvent event; 772 int outEv; 773 774 do 775 { 776 ret = sceKernelWaitEqueue(manager->m_basicEventQueue, &event, 1, &outEv, NULL); 777 778 // If the sys_event_t we've sent here from the handler has a non-zero data1 element, this is to signify that we should terminate the thread 779 if( event.udata == 0 ) 780 { 781// int iEvent; 782// SceNpUserInfo from; 783// uint8_t buffer[SCE_NP_BASIC_MAX_MESSAGE_SIZE]; 784// size_t bufferSize = SCE_NP_BASIC_MAX_MESSAGE_SIZE; 785// int ret = sceNpBasicGetEvent(&iEvent, &from, &buffer, &bufferSize); 786// if( ret == 0 ) 787// { 788// if( iEvent == SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION ) 789// { 790// // 4J Stu - Don't do this here as it can be very disruptive to gameplay. Players can bring this up from LoadOrJoinMenu, PauseMenu and InGameInfoMenu 791// //sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); 792// } 793// if( iEvent == SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT ) 794// { 795// SceNpBasicExtendedAttachmentData *result = (SceNpBasicExtendedAttachmentData *)buffer; 796// if(result->userAction == SCE_NP_BASIC_MESSAGE_ACTION_ACCEPT ) 797// { 798// manager->GetInviteDataAndProcess(result->data.id); 799// } 800// } 801// app.DebugPrintf("Incoming basic event of type %d\n",iEvent); 802// } 803 } 804 805 } while(event.udata == 0 ); 806 return 0; 807} 808 809int SQRNetworkManager_Orbis::GetFriendsThreadProc( void* lpParameter ) 810{ 811 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; 812 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::FriendsList> friendList; 813 814 int ret = 0; 815 manager->m_aFriendSearchResults.clear(); 816 manager->m_friendCount = 0; 817 if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) 818 { 819 app.DebugPrintf("getFriendslist failed, not signed into Live! \n"); 820 return 0; 821 } 822 823 sce::Toolkit::NP::FriendInfoRequest requestParam; 824 memset(&requestParam,0,sizeof(requestParam)); 825 requestParam.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_IN_CONTEXT; 826 requestParam.limit = 0; 827 requestParam.offset = 0; 828 requestParam.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); 829 830 bool async = false; 831 ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, &requestParam, async); 832 if(ret != SCE_OK) 833 { 834 app.DebugPrintf("getFriendslist failed! 0x%08x\n", ret); 835 return 0; 836 } 837 838 if (friendList.hasResult()) 839 { 840 sce::Toolkit::NP::FriendsList::const_iterator iter ; 841 int i = 1 ; 842 bool noFriends = true; 843 for (iter = friendList.get()->begin(); iter != friendList.get()->end() ; ++iter,i++) 844 { 845 manager->m_friendCount++; 846 noFriends = false; 847 app.DebugPrintf("------ Friend: %d -----\n",i); 848 app.DebugPrintf("Online Name: %s\n",iter->npid.handle.data); 849 app.DebugPrintf("------------------------\n"); 850 851 852 sce::Toolkit::NP::PresenceRequest presenceRequest; 853 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::PresenceInfo> presenceInfo; 854 memset(&presenceRequest,0,sizeof(presenceRequest)); 855 strncpy(presenceRequest.onlineId.data,iter->npid.handle.data, strlen(iter->npid.handle.data)); 856 presenceRequest.presenceType = SCE_TOOLKIT_NP_PRESENCE_TYPE_INCONTEXT_INFO; 857 presenceRequest.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());; 858 859 860 ret = sce::Toolkit::NP::Presence::Interface::getPresence(&presenceRequest,&presenceInfo,false); 861 if( ret < 0 ) 862 { 863 app.DebugPrintf("getPresence() error. ret = 0x%x\n", ret); 864 } 865 else 866 { 867 app.DebugPrintf("\nPresence Data Retrieved:\n"); 868 app.DebugPrintf("Platform Type: %s\n", presenceInfo.get()->platformType.c_str()); 869 app.DebugPrintf("Online Status: %s\n", (presenceInfo.get()->onlineStatus == SCE_NP_GAME_PRESENCE_STATUS_OFFLINE)?"OFFLINE":"ONLINE"); 870 app.DebugPrintf("Presence Type (Refer to SCE_TOOLKIT_NP_PRESENCE_TYPE_* flags): %d\n", presenceInfo.get()->presenceType); 871 app.DebugPrintf("Title ID: %s\n", presenceInfo.get()->gameInfo.npTitleId.c_str()); 872 app.DebugPrintf("Title Name: %s\n", presenceInfo.get()->gameInfo.npTitleName.c_str()); 873 app.DebugPrintf("Title Status String: %s\n", presenceInfo.get()->gameInfo.gameStatus.c_str()); 874 875 FriendSearchResult result; 876 memcpy(&result.m_NpId, &iter->npid, sizeof(SceNpId)); 877 result.m_RoomFound = false; 878 879 // Only include the friend's game if its the same network id ( this also filters out generally Zeroed PresenceSyncInfo, which we do when we aren't in an active game session) 880// if( presenceDetails.size == sizeof(PresenceSyncInfo) ) 881 { 882 PresenceSyncInfo *pso = (PresenceSyncInfo *)presenceInfo.get()->gameInfo.gameData; 883 if( pso->netVersion == MINECRAFT_NET_VERSION ) 884 { 885 if( !pso->inviteOnly ) 886 { 887 result.m_RoomFound = true; 888 result.m_RoomId = pso->m_RoomId; 889 result.m_ServerId = pso->m_ServerId; 890 891 CPlatformNetworkManagerSony::MallocAndSetExtDataFromSQRPresenceInfo(&result.m_RoomExtDataReceived, pso); 892 manager->m_aFriendSearchResults.push_back(result); 893 } 894 } 895 } 896 897 } 898 } 899 } 900 else if (friendList.hasError()) 901 { 902 app.DebugPrintf( "Error occurred while retrieving FriendsList, ret = 0x%x\n", friendList.getError()); 903 app.DebugPrintf("Check sign-in status\n"); 904 905 } 906 907 return 0; 908} 909 910// Get count of rooms that friends are playing in. Only valid when FriendRoomManagerIsBusy() returns false 911int SQRNetworkManager_Orbis::FriendRoomManagerGetCount() 912{ 913 assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); 914 return m_aFriendSearchResults.size(); 915} 916 917// Get details of a found session that a friend is playing in. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false 918void SQRNetworkManager_Orbis::FriendRoomManagerGetRoomInfo(int idx, SQRNetworkManager_Orbis::SessionSearchResult *searchResult) 919{ 920 assert( idx < m_aFriendSearchResults.size() ); 921 assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); 922 923 searchResult->m_NpId = m_aFriendSearchResults[idx].m_NpId; 924 searchResult->m_sessionId.m_RoomId = m_aFriendSearchResults[idx].m_RoomId; 925 searchResult->m_sessionId.m_ServerId = m_aFriendSearchResults[idx].m_ServerId; 926 searchResult->m_extData = m_aFriendSearchResults[idx].m_RoomExtDataReceived; 927} 928 929// Get overall state of the network manager. 930SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::GetState() 931{ 932 return m_stateExternal;; 933} 934 935bool SQRNetworkManager_Orbis::IsHost() 936{ 937 return m_isHosting; 938} 939 940bool SQRNetworkManager_Orbis::IsReadyToPlayOrIdle() 941{ 942 return (( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || ( m_state == SNM_INT_STATE_PLAYING ) || ( m_state == SNM_INT_STATE_IDLE ) ); 943} 944 945 946// Consider as "in session" from the moment that a game is created or joined, until the point where the game itself has been told via state change that we are now idle. The 947// game code requires IsInSession to return true as soon as it has asked to do one of these things (even if the state system hasn't really caught up with this request yet), and 948// it also requires that it is informed of the state changes leading up to not being in the session, before this should report false. 949bool SQRNetworkManager_Orbis::IsInSession() 950{ 951 return m_isInSession; 952} 953 954// Get count of players currently in the session 955int SQRNetworkManager_Orbis::GetPlayerCount() 956{ 957 return m_roomSyncData.getPlayerCount(); 958} 959 960// Get count of players who are in the session, but not local to this machine 961int SQRNetworkManager_Orbis::GetOnlinePlayerCount() 962{ 963 int onlineCount = 0; 964 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 965 { 966 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) 967 { 968 onlineCount++; 969 } 970 } 971 return onlineCount; 972} 973 974SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByIndex(int idx) 975{ 976 if( idx < MAX_ONLINE_PLAYER_COUNT ) 977 { 978 return GetPlayerIfReady(m_aRoomSlotPlayers[idx]); 979 } 980 else 981 { 982 return NULL; 983 } 984} 985 986SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerBySmallId(int idx) 987{ 988 EnterCriticalSection(&m_csRoomSyncData); 989 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 990 { 991 if( m_roomSyncData.players[i].m_smallId == idx ) 992 { 993 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); 994 LeaveCriticalSection(&m_csRoomSyncData); 995 return player; 996 } 997 } 998 LeaveCriticalSection(&m_csRoomSyncData); 999 return NULL; 1000} 1001 1002SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByXuid(PlayerUID xuid) 1003{ 1004 EnterCriticalSection(&m_csRoomSyncData); 1005 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1006 { 1007 if( m_roomSyncData.players[i].m_UID == xuid ) 1008 { 1009 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); 1010 LeaveCriticalSection(&m_csRoomSyncData); 1011 return player; 1012 } 1013 } 1014 LeaveCriticalSection(&m_csRoomSyncData); 1015 return NULL; 1016} 1017 1018SQRNetworkPlayer *SQRNetworkManager_Orbis::GetLocalPlayerByUserIndex(int idx) 1019{ 1020 EnterCriticalSection(&m_csRoomSyncData); 1021 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1022 { 1023 if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && ( m_roomSyncData.players[i].m_localIdx == idx ) ) 1024 { 1025 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); 1026 LeaveCriticalSection(&m_csRoomSyncData); 1027 return player; 1028 } 1029 } 1030 LeaveCriticalSection(&m_csRoomSyncData); 1031 return NULL; 1032} 1033 1034SQRNetworkPlayer *SQRNetworkManager_Orbis::GetHostPlayer() 1035{ 1036 EnterCriticalSection(&m_csRoomSyncData); 1037 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[0]); 1038 LeaveCriticalSection(&m_csRoomSyncData); 1039 return player; 1040} 1041 1042SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerIfReady(SQRNetworkPlayer *player) 1043{ 1044 if( player == NULL ) return NULL; 1045 1046 if( player->IsReady() ) return player; 1047 1048 return NULL; 1049} 1050 1051// Update state internally 1052void SQRNetworkManager_Orbis::SetState(SQRNetworkManager_Orbis::eSQRNetworkManagerInternalState state) 1053{ 1054 eSQRNetworkManagerState oldState = m_INTtoEXTStateMappings[m_state]; 1055 eSQRNetworkManagerState newState = m_INTtoEXTStateMappings[state]; 1056 bool setIdleReasonSessionFull = false; 1057 if( ( state == SNM_INT_STATE_IDLE ) && m_nextIdleReasonIsFull ) 1058 { 1059 setIdleReasonSessionFull = true; 1060 m_nextIdleReasonIsFull = false; 1061 } 1062 m_state = state; 1063 // Queue any important (ie externally relevant) state changes - we will do a call back for these in our main tick. Don't do it directly here 1064 // as we could be coming from any thread at this stage, with any stack size etc. and so we don't generally want to expect the game to be able to handle itself in such circumstances. 1065 if( ( newState != oldState ) || setIdleReasonSessionFull ) 1066 { 1067 EnterCriticalSection(&m_csStateChangeQueue); 1068 m_stateChangeQueue.push(StateChangeInfo(oldState,newState,setIdleReasonSessionFull)); 1069 LeaveCriticalSection(&m_csStateChangeQueue); 1070 } 1071} 1072 1073void SQRNetworkManager_Orbis::ResetToIdle() 1074{ 1075 app.DebugPrintf("------------------ResetToIdle--------------------\n"); 1076 // If we're the client, remove any networked players properly ( this will destory their rupd context etc.) 1077 if( !m_isHosting ) 1078 { 1079 RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); 1080 } 1081 m_serverContextValid = false; 1082 m_isHosting = false; 1083 m_currentSmallId = 0; 1084 EnterCriticalSection(&m_csRoomSyncData); 1085 for(int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1086 { 1087 delete m_aRoomSlotPlayers[i]; 1088 } 1089 memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); 1090 memset( &m_roomSyncData,0,sizeof(m_roomSyncData)); 1091 LeaveCriticalSection(&m_csRoomSyncData); 1092 SetState(SNM_INT_STATE_IDLE); 1093 assert(m_RudpCtxToPlayerMap.size() == 0); 1094 SonyVoiceChat_Orbis::checkFinished(); 1095} 1096 1097// Join a room that was found with FriendRoomManagerSearch. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false 1098bool SQRNetworkManager_Orbis::JoinRoom(SQRNetworkManager_Orbis::SessionSearchResult *searchResult, int localPlayerMask) 1099{ 1100 // Set up the presence info we would like to synchronise out when we have fully joined the game 1101 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, searchResult->m_extData, searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId); 1102 return JoinRoom(searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId, localPlayerMask, NULL); 1103} 1104 1105// Join room with a specified roomId. This is used when joining from an invite, as well as by the previous method 1106bool SQRNetworkManager_Orbis::JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_Orbis::PresenceSyncInfo *presence) 1107{ 1108 // The presence info will be directly passed in if we are joining from an invite, otherwise it has already been set up. This is synchronised out when we have fully joined the game. 1109 if( presence ) 1110 { 1111 memcpy( &s_lastPresenceSyncInfo, presence, sizeof(PresenceSyncInfo) ); 1112 } 1113 1114 m_isInSession = true; 1115 1116 m_isHosting = false; 1117 m_offlineGame = false; 1118 m_roomToJoin = roomId; 1119 m_localPlayerJoinMask = localPlayerMask; 1120 m_localPlayerCount = 0; 1121 m_localPlayerJoined = 0; 1122 1123 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 1124 { 1125 if( localPlayerMask & ( 1 << i ) ) m_localPlayerCount++; 1126 } 1127 1128 return GetServerContext( serverId ); 1129} 1130 1131void SQRNetworkManager_Orbis::StartGame() 1132{ 1133 assert( ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || (( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ); 1134 1135 SetState( SNM_INT_STATE_STARTING); 1136 SetState( SNM_INT_STATE_PLAYING); 1137} 1138 1139void SQRNetworkManager_Orbis::LeaveRoom(bool bActuallyLeaveRoom) 1140{ 1141 if( m_offlineGame ) 1142 { 1143 if( m_state != SNM_INT_STATE_PLAYING ) return; 1144 1145 SetState(SNM_INT_STATE_LEAVING); 1146 SetState(SNM_INT_STATE_ENDING); 1147 ResetToIdle(); 1148 return; 1149 } 1150 1151 UpdateRichPresenceCustomData(& c_presenceSyncInfoNULL, sizeof(PresenceSyncInfo) ); 1152 1153// SonyVoiceChat::shutdown(); 1154 1155 // Attempt to leave the room if we are in any of the states we could be in if we have successfully created it 1156 if( bActuallyLeaveRoom ) 1157 { 1158 if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || 1159 ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || 1160 ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || 1161 ( m_state == SNM_INT_STATE_PLAYING ) ) 1162 { 1163 SceNpMatching2LeaveRoomRequest reqParam; 1164 memset( &reqParam, 0, sizeof(reqParam) ); 1165 reqParam.roomId = m_room; 1166 1167 SetState(SNM_INT_STATE_LEAVING); 1168 int ret = sceNpMatching2LeaveRoom( m_matchingContext, &reqParam, NULL, &m_leaveRoomRequestId ); 1169 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_LEAVE_ROOM) ) 1170 { 1171 SetState(SNM_INT_STATE_LEAVING_FAILED); 1172 } 1173 } 1174 else if ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ) 1175 { 1176 // Haven't created the room yet, but will have created the server context so need to recover from that 1177 DeleteServerContext(); 1178 } 1179 else 1180 { 1181 SetState(SNM_INT_STATE_IDLE); 1182 } 1183 } 1184 else 1185 { 1186 // We have created a room but have now had some kind of connection error which means that we've been dropped out of the room and it has been destroyed, so 1187 // no need to leave it again since it doesn't exist anymore. Still need to destroy server context which may be valid 1188 DeleteServerContext(); 1189 } 1190} 1191 1192void SQRNetworkManager_Orbis::EndGame() 1193{ 1194} 1195 1196bool SQRNetworkManager_Orbis::SessionHasSpace(int spaceRequired) 1197{ 1198 return( ( m_roomSyncData.getPlayerCount() + spaceRequired ) <= MAX_ONLINE_PLAYER_COUNT ); 1199} 1200 1201bool SQRNetworkManager_Orbis::AddLocalPlayerByUserIndex(int idx) 1202{ 1203 if( m_isHosting ) 1204 { 1205 if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; 1206 1207 // Host's players are always at the start of the sync data, so we just need to find the first entry that isn't us to determine what we want to insert before 1208 int insertAtIdx = m_roomSyncData.getPlayerCount(); 1209 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1210 { 1211 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) 1212 { 1213 insertAtIdx = i; 1214 break; 1215 } 1216 else 1217 { 1218 // Don't add the same local index twice 1219 if( m_roomSyncData.players[i].m_localIdx == idx ) 1220 { 1221 return false; 1222 } 1223 } 1224 } 1225 1226 // Make room for a new entry... 1227 for( int i = m_roomSyncData.getPlayerCount(); i > insertAtIdx; i-- ) 1228 { 1229 m_roomSyncData.players[i] = m_roomSyncData.players[i-1]; 1230 } 1231 m_roomSyncData.players[insertAtIdx].m_localIdx = idx; 1232 m_roomSyncData.players[insertAtIdx].m_roomMemberId = m_localMemberId; 1233 m_roomSyncData.players[insertAtIdx].m_smallId = m_currentSmallId++; 1234 1235 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); 1236 1237 // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. 1238 // This will also create the required new SQRNetworkPlayer and do all the callbacks that requires etc. 1239 MapRoomSlotPlayers(); 1240 1241 // Sync this back out to our networked clients... 1242 SyncRoomData(); 1243 1244 UpdateRemotePlay(); 1245 1246 // no connections being made because we're all on the host, so add this player to the existing connections 1247 SonyVoiceChat_Orbis::connectPlayerToAll(idx); 1248 return true; 1249 } 1250 else 1251 { 1252 // Don't attempt to join if our client's view of the players indicates that there aren't any free slots 1253 if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; 1254 1255 // Add the requested player to the mask of local players currently in the game, and update this data - this 1256 // will also then resync with the server which can respond appropriately 1257 int mask = 1 << idx; 1258 if( m_localPlayerJoinMask & mask ) return false; 1259 1260 m_localPlayerJoinMask |= mask; 1261 1262 SceNpMatching2SetRoomMemberDataInternalRequest reqParam; 1263 SceNpMatching2BinAttr binAttr; 1264 1265 memset(&reqParam, 0, sizeof(reqParam)); 1266 memset(&binAttr, 0, sizeof(binAttr)); 1267 1268 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; 1269 binAttr.ptr = &m_localPlayerJoinMask; 1270 binAttr.size = sizeof(m_localPlayerJoinMask); 1271 1272 reqParam.roomId = m_room; 1273 reqParam.memberId = m_localMemberId; 1274 reqParam.roomMemberBinAttrInternalNum = 1; 1275 reqParam.roomMemberBinAttrInternal = &binAttr; 1276 1277 int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); 1278 1279 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL) ) 1280 { 1281 return false; 1282 } 1283 1284 UpdateRemotePlay(); 1285 1286 // Create the client's end of the rudp connections... note that m_roomSyncData.players[0].m_roomMemberId is always be the host's room member id. 1287 bool rudpOk = CreateRudpConnections(m_room, m_roomSyncData.players[0].m_roomMemberId, mask, m_localMemberId ); 1288 1289 if( rudpOk ) 1290 { 1291 bool ret = CreateVoiceRudpConnections( m_room, m_roomSyncData.players[0].m_roomMemberId, mask); 1292 assert(ret); 1293 return true; 1294 } 1295 else 1296 { 1297 m_localPlayerJoinMask &= (~mask); 1298 return false; 1299 } 1300 } 1301} 1302 1303bool SQRNetworkManager_Orbis::RemoveLocalPlayerByUserIndex(int idx) 1304{ 1305 if( m_isHosting ) 1306 { 1307 EnterCriticalSection(&m_csRoomSyncData); 1308 1309 int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); 1310 1311 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1312 { 1313 if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && 1314 ( m_roomSyncData.players[i].m_localIdx == idx ) ) 1315 { 1316 // Shuffle all remaining entries up... 1317 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); 1318 for( int j = i; j < m_roomSyncData.getPlayerCount(); j++ ) 1319 { 1320 m_roomSyncData.players[j] = m_roomSyncData.players[j+1]; 1321 } 1322 1323 // Zero last element that isn't part of the currently sized array anymore 1324 memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); 1325 1326 // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. 1327 // This will also delete the SQRNetworkPlayer and do all the callbacks that requires etc. 1328 MapRoomSlotPlayers(roomSlotPlayerCount); 1329 m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; 1330 1331 // Sync this back out to our networked clients... 1332 SyncRoomData(); 1333 1334 SonyVoiceChat_Orbis::disconnectLocalPlayer(idx); 1335 1336 LeaveCriticalSection(&m_csRoomSyncData); 1337 return true; 1338 } 1339 } 1340 LeaveCriticalSection(&m_csRoomSyncData); 1341 1342 UpdateRemotePlay(); 1343 1344 return false; 1345 } 1346 else 1347 { 1348 // Remove the requested player from the mask of local players currently in the game, and update this data - this 1349 // will also then resync with the server which can respond appropriately 1350 int mask = 1 << idx; 1351 if( ( m_localPlayerJoinMask & mask ) == 0 ) return false; 1352 1353 m_localPlayerJoinMask &= ~mask; 1354 1355 SceNpMatching2SetRoomMemberDataInternalRequest reqParam; 1356 SceNpMatching2BinAttr binAttr; 1357 1358 memset(&reqParam, 0, sizeof(reqParam)); 1359 memset(&binAttr, 0, sizeof(binAttr)); 1360 1361 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; 1362 binAttr.ptr = &m_localPlayerJoinMask; 1363 binAttr.size = sizeof(m_localPlayerJoinMask); 1364 1365 reqParam.roomId = m_room; 1366 reqParam.memberId = m_localMemberId; 1367 reqParam.roomMemberBinAttrInternalNum = 1; 1368 reqParam.roomMemberBinAttrInternal = &binAttr; 1369 1370 int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); 1371 1372 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2) ) 1373 { 1374 return false; 1375 } 1376 1377 RemoveNetworkPlayers( mask ); 1378 1379 UpdateRemotePlay(); 1380 1381 return true; 1382 } 1383} 1384 1385// Update remote play on multiplayer status 1386void SQRNetworkManager_Orbis::UpdateRemotePlay() 1387{ 1388 int localPlayerCount = 0; 1389 for(int i = 0; i < XUSER_MAX_COUNT; i++) 1390 { 1391 if(GetLocalPlayerByUserIndex(i) != NULL) localPlayerCount++; 1392 } 1393 InputManager.SetLocalMultiplayer(localPlayerCount > 1); 1394} 1395 1396extern uint8_t *mallocAndCreateUTF8ArrayFromString(int iID); 1397 1398// Bring up a Gui to send an invite so a player that the user can select. This invite will contain the room Id so that 1399void SQRNetworkManager_Orbis::SendInviteGUI() 1400{ 1401 if(s_bInviteDialogRunning) 1402 { 1403 app.DebugPrintf("SendInviteGUI - Invite dialog is already running so ignoring request\n"); 1404 return; 1405 } 1406 1407 s_bInviteDialogRunning = true; 1408 1409 //Set invitation information - this is now exactly the same as the presence information that we synchronise out. 1410 1411 // If we joined a game, we'll have already set s_lastPresenceSyncInfo up (whether we came in from an invite, or joining a game we discovered). If we were hosting, 1412 // then we'll need to set this up now from the external dasta. 1413 if( m_isHosting ) 1414 { 1415 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, m_joinExtData, m_room, m_serverId); 1416 } 1417 1418 sce::Toolkit::NP::MessageData messData; 1419 SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); 1420 1421//#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.png" 1422#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.jpg" 1423 1424 char *subject = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_SUBJECT_MAX_18_CHARS); 1425 char *body = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_BODY); 1426 1427 messData.body.assign(body); 1428 messData.dialogFlag = SCE_TOOLKIT_NP_DIALOG_TYPE_USER_EDITABLE; 1429 messData.npIdsCount = 2; // TODO: Set this to the number of available slots 1430 messData.npIds = NULL; 1431 messData.userInfo.userId = userId; 1432 1433 // Set expire to maximum 1434 messData.expireMinutes = 0; 1435 1436 // Now setting session ID 1437 messData.npSessionId = *OrbisNPToolkit::getNPSessionID(); 1438 1439 // Attachment 1440 sce::Toolkit::NP::MessageAttachment attachment = sce::Toolkit::NP::MessageAttachment(); 1441 attachment.setAttachmentData((SceChar8*)&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); // This step is important, allocates space on heap for the data 1442 messData.attachment = attachment.getAttachmentData(); 1443 messData.attachmentSize = attachment.getAttachmentSize(); 1444 1445 messData.dataDescription.assign(body); 1446 messData.dataName.assign(subject); 1447 messData.iconPath.assign(SESSION_IMAGE_PATH); 1448 1449 1450 int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_CUSTOM_DATA); 1451// int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_INVITE); 1452 1453 free(subject); 1454 free(body); 1455 1456 if(ret < SCE_TOOLKIT_NP_SUCCESS ) 1457 { 1458 s_bInviteDialogRunning = false; 1459 app.DebugPrintf("Send Message failed 0x%x ...\n",ret); 1460 assert(0); 1461 return; 1462 } 1463} 1464 1465 1466void SQRNetworkManager_Orbis::RecvInviteGUI() 1467{ 1468 int ret = sceGameCustomDataDialogInitialize(); 1469 if(ret != SCE_OK) 1470 { 1471 app.DebugPrintf("sceGameCustomDataDialogInitialize() failed. ret = 0x%x\n", ret); 1472 } 1473 else 1474 { 1475 1476 SceGameCustomDataDialogParam dialogParam; 1477 SceGameCustomDataDialogDataParam dataParam; 1478 1479 sceGameCustomDataDialogParamInit( &dialogParam ); 1480 memset( &dataParam, 0x00, sizeof( SceGameCustomDataDialogDataParam ) ); 1481 dialogParam.mode = SCE_GAME_CUSTOM_DATA_DIALOG_MODE_RECV; 1482 dialogParam.dataParam = &dataParam; 1483 dialogParam.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); 1484 ret = sceGameCustomDataDialogOpen( &dialogParam ); 1485 1486 if( SCE_OK != ret ) 1487 { 1488 app.DebugPrintf("sceGameCustomDataDialogOpen() failed. ret = 0x%x\n", ret); 1489 } 1490 else 1491 { 1492 b_inviteRecvGUIRunning = true; 1493 } 1494 } 1495} 1496 1497 1498void SQRNetworkManager_Orbis::TickInviteGUI() 1499{ 1500 1501 if(b_inviteRecvGUIRunning) 1502 { 1503 SceCommonDialogStatus status = sceGameCustomDataDialogUpdateStatus(); 1504 1505 if( SCE_COMMON_DIALOG_STATUS_FINISHED == status ) 1506 { 1507 SceGameCustomDataDialogOnlineIdList sentOnlineIdList; 1508 memset( &sentOnlineIdList, 0x0, sizeof(SceGameCustomDataDialogOnlineIdList)); 1509 SceGameCustomDataDialogResult dialogResult; 1510 memset( &dialogResult, 0x0, sizeof(SceGameCustomDataDialogResult) ); 1511 dialogResult.sentOnlineIds = &sentOnlineIdList; 1512 1513 int32_t ret = sceGameCustomDataDialogGetResult( &dialogResult ); 1514 1515 if( SCE_OK != ret ) 1516 { 1517 app.DebugPrintf( "***** sceGameCustomDataDialogGetResult error:0x%x\n", ret); 1518 } 1519 sceGameCustomDataDialogClose(); 1520 sceGameCustomDataDialogTerminate(); 1521 b_inviteRecvGUIRunning = false; 1522 } 1523 } 1524} 1525 1526// Get the data for an invite into a statically allocated array of invites, and pass a pointer of this back up to the game. Elements in the array are used in a circular fashion, to save any issues with handling freeing of this invite data as the 1527// qnet equivalent of this seems to just assume that the data persists forever. 1528void SQRNetworkManager_Orbis::GetInviteDataAndProcess(sce::Toolkit::NP::MessageAttachment* pInvite) 1529{ 1530 1531 app.DebugPrintf("GameCustomData attachment size : %d\n", pInvite->getAttachmentSize()); 1532 if(pInvite->getAttachmentSize() == sizeof(m_gameBootInvite_data)) 1533 { 1534 memcpy(&m_gameBootInvite_data, pInvite->getAttachmentData(), sizeof(m_gameBootInvite_data)); 1535 m_gameBootInvite = &m_gameBootInvite_data; 1536 } 1537// InvitationInfoRequest requestInfo; 1538// sce::Toolkit::NP::Utilities::Future< NpSessionInvitationInfo > inviteInfo; 1539// 1540// requestInfo.invitationId = pInvite->invitationId; 1541// requestInfo.userInfo.npId = pInvite->onlineId; 1542// int err = sce::Toolkit::NP::Sessions::Interface::getInvitationInfo(&requestInfo, &inviteInfo, false); 1543// if(err == SCE_OK) 1544// { 1545// if(inviteInfo.hasResult()) 1546// { 1547// inviteInfo.get()-> 1548// } 1549// else if(inviteInfo.hasError()) 1550// { 1551// app.DebugPrintf("inviteInfo.getError() 0x%08x", inviteInfo.getError()); 1552// } 1553// } 1554// else 1555// { 1556// app.DebugPrintf("getInvitationInfo error 0x%08x", err); 1557// } 1558// 1559// 1560// 1561// INVITE_INFO *invite = &m_inviteReceived[m_inviteIndex]; 1562// m_inviteIndex = ( m_inviteIndex + 1 ) % MAX_SIMULTANEOUS_INVITES; 1563// size_t dataSize = sizeof(INVITE_INFO); 1564// int ret = sceNpBasicRecvMessageAttachmentLoad(id, invite, &dataSize); 1565// 1566// // If we fail ( which we might do if we aren't online at this point) then zero the invite information and we'll try and get it later after (possibly) signing in 1567// if( ret != 0 ) 1568// { 1569// memset(invite, 0, sizeof( INVITE_INFO ) ); 1570// s_lastInviteIdToRetry = id; 1571// } 1572// 1573// m_gameBootInvite = invite; 1574} 1575 1576// bool SQRNetworkManager_Orbis::UpdateInviteData(SQRNetworkManager_Orbis::PresenceSyncInfo *invite) 1577// { 1578// // size_t dataSize = sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo); 1579// // int ret = sceNpBasicRecvMessageAttachmentLoad(s_lastInviteIdToRetry, invite, &dataSize); 1580// // return (ret == 0); 1581// } 1582 1583// This method is a helper used in MapRoomSlotPlayers - tries to find a player that matches: 1584// (1) the playerType 1585// (2) if playerType is remote, memberId 1586// (3) localPlayerIdx 1587// The reason we don't care about memberid when the player isn't remote is that it doesn't matter (since we know the player is either on this machine, or it is the host and there's only one of those), 1588// and there's a period when starting up the host game where it doesn't accurately know the memberId for its own local players 1589void SQRNetworkManager_Orbis::FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId) 1590{ 1591 for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) 1592 { 1593 if( ((*it)->m_type == playerType ) && ( (*it)->m_localPlayerIdx == localPlayerIdx ) ) 1594 { 1595 if( ( playerType != SQRNetworkPlayer::SNP_TYPE_REMOTE ) || ( (*it)->m_roomMemberId == memberId ) ) 1596 { 1597 SQRNetworkPlayer *player = *it; 1598 m_vecTempPlayers.erase(it); 1599 m_aRoomSlotPlayers[ slot ] = player; 1600 return; 1601 } 1602 } 1603 } 1604 // Create the player - non-network players can be considered complete as soon as we create them as we aren't waiting on their network connections becoming complete, so can flag them as such and notify via callback 1605 PlayerUID *pUID = NULL; 1606 PlayerUID localUID; 1607 if( ( playerType == SQRNetworkPlayer::SNP_TYPE_LOCAL ) || 1608 (m_isHosting && ( playerType == SQRNetworkPlayer::SNP_TYPE_HOST )) ) 1609 { 1610 // Local players can establish their UID at this point 1611 ProfileManager.GetXUID(localPlayerIdx,&localUID,true); 1612 pUID = &localUID; 1613 } 1614 SQRNetworkPlayer *player = new SQRNetworkPlayer(this, (SQRNetworkPlayer::eSQRNetworkPlayerType)playerType, m_isHosting, memberId, localPlayerIdx, 0, pUID ); 1615 // For offline games, set name directly from gamertag as the PlayerUID will be full of zeroes. 1616 if( m_offlineGame ) 1617 { 1618 player->SetName(ProfileManager.GetGamertag(localPlayerIdx)); 1619 } 1620 NonNetworkPlayerComplete( player, smallId); 1621 m_aRoomSlotPlayers[ slot ] = player; 1622 HandlePlayerJoined( player ); 1623} 1624 1625// For data sending on the local machine, used to send between host and localplayers on the host 1626void SQRNetworkManager_Orbis::LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize) 1627{ 1628 assert(m_isHosting); 1629 if(m_listener) 1630 { 1631 m_listener->HandleDataReceived( playerFrom, playerTo, (unsigned char *)data, dataSize ); 1632 } 1633} 1634 1635int SQRNetworkManager_Orbis::GetSessionIndex(SQRNetworkPlayer *player) 1636{ 1637 int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); 1638 for( int i = 0; i < roomSlotPlayerCount; i++ ) 1639 { 1640 if( m_aRoomSlotPlayers[i] == player ) return i; 1641 } 1642 return 0; 1643} 1644 1645// Updates m_aRoomSlotPlayers, based on what is in m_roomSyncData. This needs to be updated when room members join & leave, and when any SQRNetworkPlayer is created externally that this should be mapping to 1646void SQRNetworkManager_Orbis::MapRoomSlotPlayers(int roomSlotPlayerCount/*=-1*/) 1647{ 1648 EnterCriticalSection(&m_csRoomSyncData); 1649 1650 // If we pass an explicit roomSlotPlayerCount, it is because we are removing a player, and this is the count of slots that there were *before* the removal. 1651 bool zeroLastSlot = false; 1652 if( roomSlotPlayerCount == -1 ) 1653 { 1654 roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); 1655 } 1656 else 1657 { 1658 zeroLastSlot = true; 1659 } 1660 1661 if( m_isHosting ) 1662 { 1663 for( int i = 0; i < roomSlotPlayerCount; i++ ) 1664 { 1665 if( m_aRoomSlotPlayers[i] ) 1666 { 1667 // On host, remote players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. 1668 // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer 1669 if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_REMOTE ) 1670 { 1671 m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); 1672 m_aRoomSlotPlayers[i] = NULL; 1673 } 1674 } 1675 } 1676 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1677 { 1678 if( i == 0 ) 1679 { 1680 // Special case - slot 0 is always the host 1681 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); 1682 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData 1683 } 1684 else 1685 { 1686 if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) 1687 { 1688 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_LOCAL, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); 1689 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData 1690 } 1691 else 1692 { 1693 m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); 1694 // If we're the host, then we allocated the small id so can flag now if we've got a player to flag... 1695 if( m_aRoomSlotPlayers[i] ) 1696 { 1697 NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); 1698 } 1699 } 1700 } 1701 } 1702 1703 if( zeroLastSlot ) 1704 { 1705 if( roomSlotPlayerCount ) 1706 { 1707 m_aRoomSlotPlayers[ roomSlotPlayerCount - 1 ] = 0; 1708 } 1709 } 1710 1711 // Also update the externally visible room data for the current slots 1712 if (m_listener ) 1713 { 1714 m_listener->HandleResyncPlayerRequest(m_aRoomSlotPlayers); 1715 } 1716 } 1717 else 1718 { 1719 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1720 { 1721 if( m_aRoomSlotPlayers[i] ) 1722 { 1723 // On clients, local players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. 1724 // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer 1725 if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_LOCAL ) 1726 { 1727 m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); 1728 m_aRoomSlotPlayers[i] = NULL; 1729 } 1730 } 1731 } 1732 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1733 { 1734 if( i == 0 ) 1735 { 1736 // Special case - slot 0 is always the host 1737 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); 1738 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data 1739 } 1740 else 1741 { 1742 if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) 1743 { 1744 // This player is local to this machine - don't bother setting UID from sync data, as it will already have been set accurately when we (locally) made this player 1745 m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); 1746 // If we've got the room sync data back from the server, then we've got our smallId. Set flag for this. 1747 if( m_aRoomSlotPlayers[i] ) 1748 { 1749 NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); 1750 } 1751 } 1752 else 1753 { 1754 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_REMOTE, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); 1755 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data 1756 } 1757 } 1758 } 1759 } 1760 // Clear up any non-network players that are no longer required - this would be a good point to notify of players leaving when we support that 1761 // FindOrCreateNonNetworkPlayer will have pulled any players that we Do need out of m_vecTempPlayers, so the ones that are remaining are no longer in the game 1762 for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) 1763 { 1764 if( m_listener ) 1765 { 1766 m_listener->HandlePlayerLeaving(*it); 1767 } 1768 delete (*it); 1769 } 1770 m_vecTempPlayers.clear(); 1771 1772 LeaveCriticalSection(&m_csRoomSyncData); 1773} 1774 1775// On host, update the room sync data with UIDs that are in the players 1776void SQRNetworkManager_Orbis::UpdateRoomSyncUIDsFromPlayers() 1777{ 1778 EnterCriticalSection(&m_csRoomSyncData); 1779 if( m_isHosting ) 1780 { 1781 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1782 { 1783 if( m_aRoomSlotPlayers[i] ) 1784 { 1785 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); 1786 } 1787 } 1788 } 1789 1790 LeaveCriticalSection(&m_csRoomSyncData); 1791} 1792 1793// On the client, move UIDs from the room sync data out to the players. 1794void SQRNetworkManager_Orbis::UpdatePlayersFromRoomSyncUIDs() 1795{ 1796 EnterCriticalSection(&m_csRoomSyncData); 1797 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1798 { 1799 if( m_aRoomSlotPlayers[i] ) 1800 { 1801 if( i == 0 ) 1802 { 1803 // Special case - slot 0 is always the host 1804 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); 1805 } 1806 else 1807 { 1808 // Don't sync local players as we already set those up with their UID in the first place... 1809 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) 1810 { 1811 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); 1812 } 1813 } 1814 } 1815 } 1816 LeaveCriticalSection(&m_csRoomSyncData); 1817} 1818 1819// Host only - add remote players to our internal storage of player slots, and synchronise this with other room members. 1820bool SQRNetworkManager_Orbis::AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull/*==NULL*/ ) 1821{ 1822 assert( m_isHosting ); 1823 1824 EnterCriticalSection(&m_csRoomSyncData); 1825 1826 // Establish whether we have enough room to add the players 1827 int addCount = 0; 1828 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) 1829 { 1830 if( playerMask & ( 1 << i ) ) 1831 { 1832 addCount++; 1833 } 1834 } 1835 1836 if( ( m_roomSyncData.getPlayerCount() + addCount ) > MAX_ONLINE_PLAYER_COUNT ) 1837 { 1838 if( isFull ) 1839 { 1840 *isFull = true; 1841 } 1842 LeaveCriticalSection(&m_csRoomSyncData); 1843 return false; 1844 } 1845 1846 // We want to keep all players from a particular machine together, so search through the room sync data to see if we can find 1847 // any pre-existing players from this machine. 1848 int firstIdx = -1; 1849 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 1850 { 1851 if( m_roomSyncData.players[i].m_roomMemberId == memberId ) 1852 { 1853 firstIdx = i; 1854 break; 1855 } 1856 } 1857 1858 // We'll just be inserting at the end unless we've got a pre-existing player to insert after. Even then there might be no following 1859 // players. 1860 int insertIdx = m_roomSyncData.getPlayerCount(); 1861 if( firstIdx > -1 ) 1862 { 1863 for( int i = firstIdx; i < m_roomSyncData.getPlayerCount(); i++ ) 1864 { 1865 if( m_roomSyncData.players[i].m_roomMemberId != memberId ) 1866 { 1867 insertIdx = i; 1868 break; 1869 } 1870 } 1871 } 1872 1873 // Add all remote players determined from the player mask to our own slots of active players 1874 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 1875 { 1876 if( playerMask & ( 1 << i ) ) 1877 { 1878 // Shift any following players along... 1879 for( int j = m_roomSyncData.getPlayerCount(); j > insertIdx; j-- ) 1880 { 1881 m_roomSyncData.players[j] = m_roomSyncData.players[j-1]; 1882 } 1883 PlayerSyncData *player = &m_roomSyncData.players[ insertIdx ]; 1884 player->m_smallId = m_currentSmallId++; 1885 player->m_roomMemberId = memberId; 1886 player->m_localIdx = i; 1887 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); 1888 insertIdx++; 1889 } 1890 } 1891 1892 // Update mapping from the room slot players to SQRNetworkPlayer instances 1893 MapRoomSlotPlayers(); 1894 1895 // And then synchronise this out to all other machines 1896 SyncRoomData(); 1897 1898 LeaveCriticalSection(&m_csRoomSyncData); 1899 1900 return true; 1901} 1902 1903// Host only - remove all remote players belonging to the supplied memberId, and in the supplied mask, and synchronise this with other room members 1904void SQRNetworkManager_Orbis::RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask ) 1905{ 1906 assert( m_isHosting ); 1907 EnterCriticalSection(&m_csRoomSyncData); 1908 1909 // Remove any applicable players, keeping remaining players in order 1910 for( int i = 0; i < m_roomSyncData.getPlayerCount(); ) 1911 { 1912 if( ( m_roomSyncData.players[ i ].m_roomMemberId == memberId ) && ( ( 1 << m_roomSyncData.players[ i ].m_localIdx ) & mask ) ) 1913 { 1914 SQRNetworkPlayer *player = GetPlayerFromRoomMemberAndLocalIdx( memberId, m_roomSyncData.players[ i ].m_localIdx ); 1915 if( player ) 1916 { 1917 // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) 1918 int ctx = player->m_rudpCtx; 1919 int err = sceRudpTerminate( ctx ); 1920 assert(err == SCE_OK); 1921 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); 1922 g_numRUDPContextsBound--; 1923 if( m_listener ) 1924 { 1925 m_listener->HandlePlayerLeaving(player); 1926 } 1927 // Delete the player itself and the mapping from context to player map as this context is no longer valid 1928 delete player; 1929 m_RudpCtxToPlayerMap.erase(ctx); 1930 1931 removePlayerFromVoiceChat(player); 1932 } 1933 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); 1934 // Shuffled entries up into the space that we have just created 1935 for( int j = i ; j < m_roomSyncData.getPlayerCount(); j++ ) 1936 { 1937 m_roomSyncData.players[j] = m_roomSyncData.players[j + 1]; 1938 m_aRoomSlotPlayers[j] = m_aRoomSlotPlayers[j + 1]; 1939 } 1940 // Zero last element, that isn't part of the currently sized array anymore 1941 memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); 1942 m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; 1943 } 1944 else 1945 { 1946 i++; 1947 } 1948 } 1949 LeaveCriticalSection(&m_csRoomSyncData); 1950 1951 // Update mapping from the room slot players to SQRNetworkPlayer instances 1952 MapRoomSlotPlayers(); 1953 1954 1955 // And then synchronise this out to all other machines 1956 SyncRoomData(); 1957 1958// if(GetOnlinePlayerCount() == 0) 1959// SonyVoiceChat::shutdown(); 1960} 1961 1962// Client only - remove all network players matching the supplied mask 1963void SQRNetworkManager_Orbis::RemoveNetworkPlayers( int mask ) 1964{ 1965 assert( !m_isHosting ); 1966 1967 for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); ) 1968 { 1969 SQRNetworkPlayer *player = it->second; 1970 if( (player->m_roomMemberId == m_localMemberId ) && ( ( 1 << player->m_localPlayerIdx ) & mask ) ) 1971 { 1972 // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) 1973 int ctx = it->first; 1974 int err = sceRudpTerminate( ctx ); 1975 g_numRUDPContextsBound--; 1976 assert(err == SCE_OK); 1977 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); 1978 if( m_listener ) 1979 { 1980 m_listener->HandlePlayerLeaving(player); 1981 } 1982 // Delete any reference to this player from the player mappings 1983 for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) 1984 { 1985 if( m_aRoomSlotPlayers[i] == player ) 1986 { 1987 m_aRoomSlotPlayers[i] = NULL; 1988 } 1989 } 1990 // And delete the reference from the ctx->player map 1991 it = m_RudpCtxToPlayerMap.erase(it); 1992 1993 removePlayerFromVoiceChat(player); 1994 1995 // Delete the player itself and the mapping from context to player map as this context is no longer valid 1996 delete player; 1997 } 1998 else 1999 { 2000 it++; 2001 } 2002 } 2003 2004} 2005 2006// Host only - update the memberId of the local players, and synchronise with other room members 2007void SQRNetworkManager_Orbis::SetLocalPlayersAndSync() 2008{ 2009 assert( m_isHosting ); 2010 for( int i = 0; i < m_localPlayerCount; i++ ) 2011 { 2012 m_roomSyncData.players[i].m_roomMemberId = m_localMemberId; 2013 } 2014 2015 // Update mapping from the room slot players to SQRNetworkPlayer instances 2016 MapRoomSlotPlayers(); 2017 2018 // And then synchronise this out to all other machines 2019 SyncRoomData(); 2020 2021} 2022 2023// Host only - sync the room data with other machines 2024void SQRNetworkManager_Orbis::SyncRoomData() 2025{ 2026 if( m_offlineGame ) return; 2027 2028 UpdateRoomSyncUIDsFromPlayers(); 2029 2030 SceNpMatching2SetRoomDataInternalRequest reqParam; 2031 memset( &reqParam, 0, sizeof(reqParam) ); 2032 reqParam.roomId = m_room; 2033 SceNpMatching2BinAttr roomBinAttr; 2034 memset(&roomBinAttr, 0, sizeof(roomBinAttr)); 2035 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; 2036 roomBinAttr.ptr = &m_roomSyncData; 2037 roomBinAttr.size = sizeof( m_roomSyncData ); 2038 reqParam.roomBinAttrInternalNum = 1; 2039 reqParam.roomBinAttrInternal = &roomBinAttr; 2040 sceNpMatching2SetRoomDataInternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); 2041} 2042 2043// Check if the matching context is valid, and if not attempt to create one. If to do this requires starting an asynchronous process, then sets the internal state to the state passed in 2044// before doing this. 2045// Returns true on success. 2046bool SQRNetworkManager_Orbis::GetMatchingContext(eSQRNetworkManagerInternalState asyncState) 2047{ 2048 if( m_matchingContextValid ) return true; 2049 2050 int ret = 0; 2051 if( !m_matching2initialised) 2052 { 2053 2054 SceNpMatching2InitializeParameter param; 2055 memset(&param, 0, sizeof(param)); 2056 param.size = sizeof(param); 2057 ret = sceNpMatching2Initialize(&param); 2058 } 2059 if( (ret < 0) && (ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) ) 2060 { 2061 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2Init2 failed with code 0x%08x\n", ret); 2062 return false; 2063 } 2064 m_matching2initialised = true; 2065 2066 // Get NP ID of the signed-in user 2067 SceNpId npId; 2068 /*ret = */ProfileManager.GetSceNpId(ProfileManager.GetPrimaryPad(), &npId); 2069 2070 2071 // Create context 2072 SceNpServiceLabel serviceLabel = 0; 2073 SceNpMatching2CreateContextParam param; 2074 memset(&param, 0, sizeof(param)); 2075 param.npId = &npId; 2076 param.serviceLabel = serviceLabel; 2077 param.size = sizeof(param); 2078 ret = sceNpMatching2CreateContext(&param, &m_matchingContext); 2079 if( ret < 0 ) 2080 { 2081 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2CreateContext failed with code 0x%08x\n", ret); 2082 return false; 2083 } 2084 if( ret < 0 ) return false; 2085 2086 if( !RegisterCallbacks() ) 2087 { 2088 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - RegisterCallbacks failed\n"); 2089 return false; 2090 } 2091 2092 // Set internal state & kick off async process that will actually start the context. 2093 SetState(asyncState); 2094 ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); 2095 if( ret < 0 ) 2096 { 2097 // Put state back so that the caller isn't expecting a callback from sceNpMatching2ContextStartAsync completing to happen 2098 SetState(SNM_INT_STATE_IDLE); 2099 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2ContextStartAsync failed with code 0x%08x\n", ret); 2100 return false; 2101 } 2102 2103 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - matching context is now valid\n"); 2104 m_matchingContextValid = true; 2105 return true; 2106} 2107 2108// Starts the process of obtaining a server context. This is an asynchronous operation, at the end of which (if successful), we'll be creating 2109// a room. General procedure followed here is as suggested by Sony - we get a list of servers, then pick a random one, and see if it is available. 2110// If not we just cycle round trying other random ones until we either find an available one or fail. 2111bool SQRNetworkManager_Orbis::GetServerContext() 2112{ 2113 assert(m_state == SNM_INT_STATE_IDLE); 2114 assert(m_serverContextValid == false); 2115 2116 // Check that the matching context is valid & recreate if necessary 2117 if( !GetMatchingContext(SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT) ) return false; 2118 // If this caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes 2119 // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) 2120 if( m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) return true; 2121 2122 return GetServerContext2(); 2123} 2124 2125// Code split out from previous method, so we can also call from creating matching context if required 2126bool SQRNetworkManager_Orbis::GetServerContext2() 2127{ 2128 2129 m_aServerId = (SceNpMatching2ServerId *)realloc( m_aServerId, sizeof(SceNpMatching2ServerId) * 1 ); 2130 int err = sceNpMatching2GetServerId(m_matchingContext, m_aServerId); 2131 if(err != SCE_OK) 2132 { 2133 m_serverCount = 0; 2134 assert(0); 2135 } 2136 m_serverCount = 1; 2137 2138 SetState(SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER); 2139 return SelectRandomServer(); 2140} 2141 2142// Overloaded method for (as before) obtaining a server context. This version is so that can also get a server context for a specific server rather than a random one, 2143// using mainly the same code by making a single element list. This is used when joining an existing room. 2144bool SQRNetworkManager_Orbis::GetServerContext(SceNpMatching2ServerId serverId) 2145{ 2146 assert(m_state == SNM_INT_STATE_IDLE); 2147 assert(m_serverContextValid == false); 2148 2149 // Check that the matching context is valid & recreate if necessary 2150 if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) 2151 { 2152 app.DebugPrintf("SQRNetworkManager::GetServerContext - Failed due to no matching context\n"); 2153 return false; 2154 } 2155 2156 // 4J Stu - If this state is set, then we have successfully created a new context but it won't have started yet 2157 // Therefore the sceNpMatching2GetServerIdListLocal call will fail. If we just skip this check everything should be good. 2158// if( m_state != SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) 2159// { 2160// // Get list of server IDs of servers allocated to the application. We don't actually need to do this, but it is as good a way as any to try a matching2 service and check that 2161// // the context *really* is valid. 2162// int serverCount = sceNpMatching2GetServerIdListLocal( m_matchingContext, NULL, 0 ); 2163// // If an error is returned here, we need to destroy and recerate our server - if this goes ok we should come back through this path again 2164// if( ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment 2165// ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error 2166// { 2167// sceNpMatching2DestroyContext(m_matchingContext); 2168// m_matchingContextValid = false; 2169// if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) return false; 2170// } 2171// } 2172 m_serverCount = 1; 2173 m_totalServerCount = m_serverCount; 2174 m_aServerId = (SceNpMatching2ServerId *)realloc(m_aServerId, sizeof(SceNpMatching2ServerId) * m_serverCount ); 2175 m_aServerId[0] = serverId; 2176 2177 // If one of the previous GetMatchingContext calls caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes 2178 // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) 2179 if( m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) return true; 2180 2181 SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); 2182 return SelectRandomServer(); 2183} 2184 2185// Tick to update the search for a server which is available, for the creation of a server context. 2186void SQRNetworkManager_Orbis::ServerContextTick() 2187{ 2188 switch( m_state ) 2189 { 2190 case SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER: 2191 case SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER: 2192 break; 2193 case SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR: 2194 case SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR: 2195 // Attempt to keep searching if a single server failed 2196 SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER:SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); 2197 if(!SelectRandomServer()) 2198 { 2199 SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED:SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); 2200 } 2201 break; 2202 case SNM_INT_STATE_HOSTING_SERVER_FOUND: 2203 m_serverContextValid = true; 2204 ServerContextValid_CreateRoom(); 2205 break; 2206 2207 case SNM_INT_STATE_JOINING_SERVER_FOUND: 2208 m_serverContextValid = true; 2209 ServerContextValid_JoinRoom(); 2210 break; 2211 default: 2212 break; 2213 } 2214} 2215 2216// Tick the process of creating a room. 2217void SQRNetworkManager_Orbis::RoomCreateTick() 2218{ 2219 switch( m_state ) 2220 { 2221 case SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD: 2222 break; 2223 case SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND: 2224 { 2225 SceNpMatching2CreateJoinRoomRequest reqParam; 2226 SceNpMatching2SignalingOptParam optSignalingParam; 2227 SceNpMatching2BinAttr roomBinAttrExt; 2228 SceNpMatching2BinAttr roomBinAttr; 2229 memset(&reqParam, 0, sizeof(reqParam)); 2230 memset(&optSignalingParam, 0, sizeof( optSignalingParam) ); 2231 memset(&roomBinAttr, 0, sizeof(roomBinAttr)); 2232 memset(&roomBinAttrExt, 0, sizeof(roomBinAttrExt)); 2233 2234 reqParam.worldId = m_worldId; 2235 reqParam.flagAttr = SCE_NP_MATCHING2_ROOM_FLAG_ATTR_NAT_TYPE_RESTRICTION; 2236 reqParam.sigOptParam = &optSignalingParam; 2237 reqParam.maxSlot = MAX_ONLINE_PLAYER_COUNT; 2238 reqParam.roomBinAttrInternalNum = 1; 2239 reqParam.roomBinAttrInternal = &roomBinAttr; 2240 reqParam.roomBinAttrExternalNum = 1; 2241 reqParam.roomBinAttrExternal = &roomBinAttrExt; 2242 2243 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; 2244 roomBinAttr.ptr = &m_roomSyncData; 2245 roomBinAttr.size = sizeof( m_roomSyncData ); 2246 2247 roomBinAttrExt.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; 2248 roomBinAttrExt.ptr = m_joinExtData; 2249 roomBinAttrExt.size = m_joinExtDataSize; 2250 2251 optSignalingParam.type = SCE_NP_MATCHING2_SIGNALING_TYPE_MESH; 2252 optSignalingParam.hubMemberId = 0; // Room owner is the hub of the star 2253 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM); 2254 app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room start\n"); 2255 s_roomStartTime = System::currentTimeMillis(); 2256 int ret = sceNpMatching2CreateJoinRoom( m_matchingContext, &reqParam, NULL, &m_createRoomRequestId ); 2257 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_JOIN_ROOM) ) 2258 { 2259 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); 2260 } 2261 } 2262 break; 2263 case SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM: 2264 break; 2265 case SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS: 2266 SetState(SNM_INT_STATE_HOSTING_WAITING_TO_PLAY); 2267 2268 // Now we know the local member id we can update our local players 2269 SetLocalPlayersAndSync(); 2270 break; 2271 case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: 2272 break; 2273 default: 2274 break; 2275 } 2276} 2277 2278// For a player using the network to communicate, flag as having its connection complete. This wraps the player's own functionality, so that we can determine if this 2279// call is transitioning us from not ready to ready, and call a registered callback. 2280void SQRNetworkManager_Orbis::NetworkPlayerConnectionComplete(SQRNetworkPlayer *player) 2281{ 2282 EnterCriticalSection(&m_csPlayerState); 2283 bool wasReady = player->IsReady(); 2284 bool wasClientReady = player->HasConnectionAndSmallId(); 2285 player->ConnectionComplete(); 2286 bool isReady = player->IsReady(); 2287 bool isClientReady = player->HasConnectionAndSmallId(); 2288 if( !m_isHosting ) 2289 { 2290 // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end 2291 if( ( !wasClientReady ) && ( isClientReady ) ) 2292 { 2293 player->ConfirmReady(); 2294 isReady = true; 2295 } 2296 } 2297 LeaveCriticalSection(&m_csPlayerState); 2298 2299 if( ( !wasReady ) && ( isReady ) ) 2300 { 2301 HandlePlayerJoined( player ); 2302 } 2303} 2304 2305// For a player using the network to communicate, set its small id, thereby flagging it as having one allocated 2306void SQRNetworkManager_Orbis::NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId) 2307{ 2308 EnterCriticalSection(&m_csPlayerState); 2309 bool wasReady = player->IsReady(); 2310 bool wasClientReady = player->HasConnectionAndSmallId(); 2311 player->SmallIdAllocated(smallId); 2312 bool isReady = player->IsReady(); 2313 bool isClientReady = player->HasConnectionAndSmallId(); 2314 2315 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Small ID allocated\n"); 2316 if( !m_isHosting ) 2317 { 2318 // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end 2319 if( ( !wasClientReady ) && ( isClientReady ) ) 2320 { 2321 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Confirm ready\n"); 2322 player->ConfirmReady(); 2323 isReady = true; 2324 } 2325 } 2326 LeaveCriticalSection(&m_csPlayerState); 2327 2328 if( ( !wasReady ) && ( isReady ) ) 2329 { 2330 HandlePlayerJoined( player ); 2331 } 2332} 2333 2334// On host, for a player using the network to communicate, confirm that its small id has now been received back 2335void SQRNetworkManager_Orbis::NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player, void *data) 2336{ 2337 EnterCriticalSection(&m_csPlayerState); 2338 SQRNetworkPlayer::InitSendData *ISD = (SQRNetworkPlayer::InitSendData *)data; 2339 bool wasReady = player->IsReady(); 2340 player->InitialDataReceived(ISD); 2341 bool isReady = player->IsReady(); 2342 LeaveCriticalSection(&m_csPlayerState); 2343 // Sync room data back out as we've updated a player's UID here 2344 SyncRoomData(); 2345 2346 if( ( !wasReady ) && ( isReady ) ) 2347 { 2348 HandlePlayerJoined( player ); 2349 } 2350} 2351 2352// For non-network players, flag that it is complete/ready, and assign its small id. We don't want to call any callbacks for these, as that can be explicitly done when local players are added. 2353// Also, we dynamically destroy & recreate local players quite a lot when remapping player slots which would create a lot of messages we don't want. 2354void SQRNetworkManager_Orbis::NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId) 2355{ 2356 player->ConnectionComplete(); 2357 player->SmallIdAllocated(smallId); 2358} 2359 2360void SQRNetworkManager_Orbis::HandlePlayerJoined(SQRNetworkPlayer *player) 2361{ 2362 if( m_listener ) 2363 { 2364 m_listener->HandlePlayerJoined( player ); 2365 } 2366 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> HandlePlayerJoined\n"); 2367 2368 // On client, keep a count of how many local players we have told the game about. We can only transition to telling the game that we are playing once the room is set up And all the local players are valid to use. 2369 if( !m_isHosting ) 2370 { 2371 if( player->IsLocal() ) 2372 { 2373 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LocalPlayerJoined++\n"); 2374 2375 m_localPlayerJoined++; 2376 } 2377 } 2378} 2379 2380// Selects a random server from the current list, removes that server so it won't be searched for again, and then kick off an attempt to find out if that particular server is available. 2381bool SQRNetworkManager_Orbis::SelectRandomServer() 2382{ 2383 assert( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ); 2384 2385 if( m_serverCount == 0 ) 2386 { 2387 SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED : SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); 2388 app.DebugPrintf("SQRNetworkManager::SelectRandomServer - Server count is 0\n"); 2389 return false; 2390 } 2391 2392 // not really selecting a random server, as we've already been allocated one, but calling this to match PS3 2393 int serverIdx; 2394 serverIdx = 0; 2395 m_serverCount--; 2396 m_aServerId[serverIdx] = m_aServerId[m_serverCount]; 2397 2398 // This server is available 2399 SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_FOUND : SNM_INT_STATE_JOINING_SERVER_FOUND); 2400 m_serverId = m_aServerId[serverIdx]; 2401 2402 return true; 2403} 2404 2405// Delete the current server context. Should be called when finished with the current host or client game session. 2406void SQRNetworkManager_Orbis::DeleteServerContext() 2407{ 2408 // No server context on PS4, so we just set the state, and then we'll check all the UDP connections have shutdown before setting to idle 2409 if( m_serverContextValid ) 2410 { 2411 m_serverContextValid = false; 2412 SetState(SNM_INT_STATE_SERVER_DELETING_CONTEXT); 2413 } 2414} 2415 2416// Creates a set of Rudp connections by the "active open" method. This requires that both ends of the connection call cellRudpInitiate to fully create a connection. We 2417// create one connection per local play on any remote machine. 2418// 2419// peerMemberId is the room member Id of the remote end of the connection 2420// playersMemberId is the room member Id that the players belong to 2421// ie for the host (when matching incoming connections), these will be the same thing... and for the client, peerMemberId will be the host, whereas playersMemberId will be itself 2422 2423 2424std::string getIPAddressString(SceNetInAddr add) 2425{ 2426 char str[32]; 2427 unsigned char *vals = (unsigned char*)&add.s_addr; 2428 sprintf(str, "%d.%d.%d.%d", (int)vals[0], (int)vals[1], (int)vals[2], (int)vals[3]); 2429 return std::string(str); 2430} 2431 2432bool SQRNetworkManager_Orbis::CreateSocket() 2433{ 2434 // First get details of the UDPP2P connection that has been established 2435 int connStatus; 2436 SceNetSockaddrIn sinp2pLocal, sinp2pPeer; 2437 SceNpMatching2SignalingNetInfo netInfo; 2438 2439 // Local end first... 2440 memset(&sinp2pLocal, 0, sizeof(sinp2pLocal)); 2441 memset(&netInfo, 0 , sizeof(netInfo)); 2442 netInfo.size = sizeof(netInfo); 2443 int ret = sceNpMatching2SignalingGetLocalNetInfo(&netInfo); 2444 if( ret < 0 ) return false; 2445 sinp2pLocal.sin_len = sizeof(sinp2pLocal); 2446 sinp2pLocal.sin_family = AF_INET; 2447 sinp2pLocal.sin_port = sceNetHtons(SCE_NP_PORT); 2448 sinp2pLocal.sin_addr = netInfo.localAddr; 2449 2450 2451 // Set vport for both ends of connection 2452 sinp2pLocal.sin_vport = sceNetHtons(1); 2453 2454 // Create socket & bind 2455 ret = sceNetSocket("rupdSocket", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM_P2P, 0); 2456 assert(ret >= 0); 2457 m_soc = ret; 2458 int optval = 1; 2459 ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USECRYPTO, &optval, sizeof(optval)); 2460 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_0) ) return false; 2461 ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USESIGNATURE, &optval, sizeof(optval)); 2462 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_1) ) return false; 2463 ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_NBIO, &optval, sizeof(optval)); 2464 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_2) ) return false; 2465 2466 ret = sceNetBind(m_soc, (SceNetSockaddr *)&sinp2pLocal, sizeof(sinp2pLocal)); 2467 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SOCK_BIND) ) return false; 2468 return true; 2469 2470} 2471 2472 2473bool SQRNetworkManager_Orbis::CreateVoiceRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask) 2474{ 2475 SceNetSockaddrIn sinp2pPeer; 2476 SceNpMatching2SignalingNetInfo netInfo; 2477 int connStatus; 2478 2479 memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); 2480 sinp2pPeer.sin_len = sizeof(sinp2pPeer); 2481 sinp2pPeer.sin_family = AF_INET; 2482 int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); 2483 sinp2pPeer.sin_vport = sceNetHtons(1); 2484 2485 2486 ret = 0; 2487 // Create socket & bind, if we don't already have one 2488 if( m_soc == -1 ) 2489 { 2490 if(CreateSocket() == false) 2491 return false; 2492 } 2493 2494 // create this connection if we don't have it already 2495 SQRVoiceConnection* pConnection = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); 2496 if(pConnection == NULL) 2497 { 2498 2499 // Create an Rudp context for the voice connection, this will happen regardless of whether the peer is client or host 2500 int rudpCtx; 2501 ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); 2502 if(ret < 0){ app.DebugPrintf("sceRudpCreateContext failed : 0x%08x\n", ret); assert(0); } 2503 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; 2504 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); 2505 2506 // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. 2507 ret = sceRudpBind( rudpCtx, m_soc , 5, SCE_RUDP_MUXMODE_P2P ); 2508 if(ret < 0){ app.DebugPrintf("sceRudpBind %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } 2509 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; 2510 g_numRUDPContextsBound++; 2511 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); 2512 2513 ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); 2514 if(ret < 0){ app.DebugPrintf("sceRudpInitiate %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } 2515 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; 2516 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); 2517 2518 app.DebugPrintf("-----------------------------\n"); 2519 app.DebugPrintf("Voice rudp context created %d connected to %s\n", rudpCtx, getIPAddressString(sinp2pPeer.sin_addr).c_str()); 2520 app.DebugPrintf("-----------------------------\n"); 2521 2522 pConnection = SonyVoiceChat_Orbis::addRemoteConnection(rudpCtx, peerMemberId); 2523 } 2524 2525 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 2526 { 2527 bool bMaskVal = ( playerMask & ( 1 << i ) ); 2528 2529 if(bMaskVal || GetLocalPlayerByUserIndex(i)) 2530 SonyVoiceChat_Orbis::connectPlayer(pConnection, i); 2531 } 2532 return true; 2533} 2534 2535 2536 2537bool SQRNetworkManager_Orbis::CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersMemberId) 2538{ 2539 // First get details of the UDPP2P connection that has been established 2540 int connStatus; 2541 SceNetSockaddrIn sinp2pPeer; 2542 2543 // get the peer 2544 memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); 2545 sinp2pPeer.sin_len = sizeof(sinp2pPeer); 2546 sinp2pPeer.sin_family = AF_INET; 2547 2548 int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); 2549 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SignalingGetConnectionStatus returned 0x%x, connStatus %d peer add:%s peer port:0x%x\n",ret, connStatus,getIPAddressString(sinp2pPeer.sin_addr).c_str(),sinp2pPeer.sin_port); 2550 2551 // Set vport 2552 sinp2pPeer.sin_vport = sceNetHtons(1); 2553 2554 // Create socket & bind, if we don't already have one 2555 if( m_soc == -1 ) 2556 { 2557 if(CreateSocket() == false) 2558 return false; 2559 } 2560 2561 // Create an Rudp context for each local player that is required. These can be used as individual virtual connections between room members (ie consoles), which are multiplexed 2562 // over the socket we have just made 2563 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 2564 { 2565 if( ( playerMask & ( 1 << i ) ) == 0 ) continue; 2566 2567 int rudpCtx; 2568 2569 // Socket for the local network node created, now can create an Rupd context. 2570 ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); 2571 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; 2572 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); 2573 2574 if( m_isHosting ) 2575 { 2576 m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_REMOTE, true, playersMemberId, i, rudpCtx, NULL ); 2577 } 2578 else 2579 { 2580 // Local players can establish their UID at this point 2581 PlayerUID localUID; 2582 ProfileManager.GetXUID(i,&localUID,true); 2583 2584 m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_LOCAL, false, m_localMemberId, i, rudpCtx, &localUID ); 2585 } 2586 2587 // If we've created a player, then we want to try and patch up any connections that we should have to it 2588 MapRoomSlotPlayers(); 2589 2590 // TODO - set any non-default options for the context. By default, the context is set to have delivery critical and order critical both on 2591 2592 // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. 2593 ret = sceRudpBind( rudpCtx, m_soc , 1 + i, SCE_RUDP_MUXMODE_P2P ); 2594 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; 2595 g_numRUDPContextsBound++; 2596 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); 2597 2598 2599 ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); 2600 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; 2601 app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); 2602 } 2603 return true; 2604} 2605 2606 2607SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRudpCtx(int rudpCtx) 2608{ 2609 AUTO_VAR(it,m_RudpCtxToPlayerMap.find(rudpCtx)); 2610 if( it != m_RudpCtxToPlayerMap.end() ) 2611 { 2612 return it->second; 2613 } 2614 return NULL; 2615} 2616 2617 2618 2619SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx) 2620{ 2621 for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); it++ ) 2622 { 2623 if( (it->second->m_roomMemberId == roomMember ) && ( it->second->m_localPlayerIdx == localIdx ) ) 2624 { 2625 return it->second; 2626 } 2627 } 2628 return NULL; 2629} 2630 2631 2632// This is called as part of the general initialisation of the network manager, to register any callbacks that the sony libraries require. 2633// Returns true if all were registered successfully. 2634bool SQRNetworkManager_Orbis::RegisterCallbacks() 2635{ 2636 // Register RUDP event handler 2637 int ret = sceRudpSetEventHandler(RudpEventCallback, this); 2638 if (ret < 0) 2639 { 2640 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - cellRudpSetEventHandler failed with code 0x%08x\n", ret); 2641 return false; 2642 } 2643 2644 // Register the context callback function 2645 ret = sceNpMatching2RegisterContextCallback(ContextCallback, this); 2646 if (ret < 0) 2647 { 2648 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterContextCallback failed with code 0x%08x\n", ret); 2649 return false; 2650 } 2651 2652 // Register the default request callback & parameters 2653 SceNpMatching2RequestOptParam optParam; 2654 2655 memset(&optParam, 0, sizeof(optParam)); 2656 optParam.cbFunc = DefaultRequestCallback; 2657 optParam.cbFuncArg = this; 2658 optParam.timeout = (30 * 1000 * 1000); 2659 optParam.appReqId = 0; 2660 2661 ret = sceNpMatching2SetDefaultRequestOptParam(m_matchingContext, &optParam); 2662 if (ret < 0) 2663 { 2664 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2SetDefaultRequestOptParam failed with code 0x%08x\n", ret); 2665 return false; 2666 } 2667 2668 // Register signalling callback 2669 ret = sceNpMatching2RegisterSignalingCallback(m_matchingContext, SignallingCallback, this); 2670 if (ret < 0) 2671 { 2672 return false; 2673 } 2674 2675 // Register room event callback 2676 ret = sceNpMatching2RegisterRoomEventCallback(m_matchingContext, RoomEventCallback, this); 2677 if (ret < 0) 2678 { 2679 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterRoomEventCallback failed with code 0x%08x\n", ret); 2680 return false; 2681 } 2682 2683 return true; 2684} 2685 2686extern bool g_bBootedFromInvite; 2687 2688 2689// This is an implementation of SceNpMatching2ContextCallback. Used to determine whether the matching 2 context is valid or not. 2690void SQRNetworkManager_Orbis::ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg) 2691{ 2692 app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback id:%d, event:%d, eventCause:%d, errorCode:0x%08x\n", id, event, eventCause, errorCode); 2693 2694 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 2695 if (id != manager->m_matchingContext) 2696 { 2697 return; 2698 } 2699 2700 switch( event ) 2701 { 2702 case SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED: 2703 if(errorCode < 0) 2704 { 2705 if(manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT || 2706 manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT || 2707 manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) 2708 { 2709 // matching context failed to start (this can happen when you block the IP addresses of the matching servers on your router 2710 // agent-0101.ww.sp-int.matching.playstation.net (198.107.157.191) 2711 // static-resource.sp-int.community.playstation.net (203.105.77.140) 2712 app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - Error\n"); 2713 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 2714 break; 2715 } 2716 } 2717 2718 // Some special cases to detect when this event is coming in, in case we had to start the matching context because there wasn't a valid context when we went to get a server context. These two 2719 // responses here complete what should then happen to get the server context in each case (for hosting or joining a game) 2720 if( manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) 2721 { 2722 manager->SetState( SNM_INT_STATE_IDLE ); 2723 manager->GetExtDataForRoom(0, NULL, NULL, NULL); 2724 break; 2725 } 2726 2727 if( manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) 2728 { 2729 manager->GetServerContext2(); 2730 break; 2731 } 2732 if( manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) 2733 { 2734 manager->SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); 2735 manager->SelectRandomServer(); 2736 break; 2737 } 2738 if ( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) 2739 { 2740 manager->ServerContextValid_CreateRoom(); 2741 break; 2742 } 2743 // Normal handling of context starting, from standard initialisation procedure 2744 assert( manager->m_state == SNM_INT_STATE_STARTING_CONTEXT ); 2745 if (errorCode < 0) 2746 { 2747 app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED failed with error 0x%08x\n", errorCode); 2748 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 2749 } 2750 else 2751 { 2752 manager->m_offlineSQR = false; 2753 manager->SetState(SNM_INT_STATE_IDLE); 2754 2755 // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it from the tick instead 2756 m_bCallPSNSignInCallback=true; 2757 app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ m_bCallPSNSignInCallback true ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); 2758 2759 // if(s_SignInCompleteCallbackFn) 2760 // { 2761 // s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, 0); 2762 // s_SignInCompleteCallbackFn = NULL; 2763 // } 2764 2765 2766 2767 // Check to see if we were booted from an invite. Only do this once, the first time we have all our networking stuff set up on boot-up 2768 if( manager->m_doBootInviteCheck ) 2769 { 2770// ORBIS_STUBBED; 2771// unsigned int type, attributes; 2772// CellGameContentSize gameSize;` 2773// char dirName[CELL_GAME_DIRNAME_SIZE]; 2774// 2775// if( g_bBootedFromInvite ) 2776// { 2777// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); 2778// manager->m_doBootInviteCheck = false; 2779// } 2780 } 2781 } 2782 break; 2783 case SCE_NP_MATCHING2_CONTEXT_EVENT_STOPPED: 2784 if( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) 2785 { 2786 sceNpMatching2ContextStart(manager->m_matchingContext, (10*1000*1000)); 2787 break; 2788 } 2789 assert(false); 2790 break; 2791 case SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER: 2792 2793 app.DebugPrintf("SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER\n"); 2794 app.DebugPrintf("eventCause=%u, errorCode=0x%08x\n", eventCause, errorCode); 2795 2796 sceNpMatching2DestroyContext(manager->m_matchingContext); 2797 manager->m_matchingContextValid = false; 2798 manager->m_offlineSQR = true; 2799 2800 if(manager->m_state == SNM_INT_STATE_STARTING_CONTEXT) 2801 { 2802 // MGH - to fix potential issue with a bad state assert on loading a level 2803 app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER failed\n"); 2804 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 2805 } 2806 switch(errorCode) 2807 { 2808 case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: 2809 case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: 2810 app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_ERROR_LATEST_PATCH_PKG_EXIST failed\n"); 2811 // 4J-PB - need to fail the init, so the external state will be set to idle, and we can continue offline 2812 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 2813 break; 2814 } 2815 2816 break; 2817 } 2818} 2819 2820// This is an implementation of SceNpMatching2RequestCallback. This callback is used by default for any matching 2 request functions. 2821void SQRNetworkManager_Orbis::DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg) 2822{ 2823 int ret; 2824 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 2825 if( id != manager->m_matchingContext ) return; 2826 2827 // MGH - added this here for the NAT failure, should only happen for the join request, but without being able to test we can't be sure 2828 if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) 2829 { 2830 app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); 2831 } 2832 2833 switch( event ) 2834 { 2835 // This is the response to sceNpMatching2GetWorldInfoList, which is called as part of the process to create a room (which needs a world to be created in). We aren't anticipating 2836 // using worlds in a meaningful way so just getting the first world we find on the server here, and then advancing the state so that the tick can get on with the rest of the process. 2837 case SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST: 2838 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) 2839 { 2840 // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here 2841 break; 2842 } 2843 assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD ); 2844 if( errorCode == 0 ) 2845 { 2846 if( data != 0 ) 2847 { 2848 // Currently just using first world - this may well be all that we need anyway 2849 SceNpMatching2GetWorldInfoListResponse *pWorldList = (SceNpMatching2GetWorldInfoListResponse *)data; 2850 if( pWorldList->worldNum >= 1 ) 2851 { 2852 manager->m_worldId = pWorldList->world[0].worldId; 2853 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND); 2854 break; 2855 } 2856 } 2857 } 2858 // We get this error when starting a new game after a disconnect occurred in a previous game with at least one remote player. Fix by stopping/starting the matching context. 2859 // We stop the context here, which is picked up in the callback, and started again. Then the start event is picked up and reattempts the sceNpMatching2GetWorldInfoList. 2860 if( errorCode == SCE_NET_ERROR_RESOLVER_ENODNS ) 2861 { 2862 sceNpMatching2ContextStop(manager->m_matchingContext); 2863 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT); 2864 break; 2865 } 2866 app.DebugPrintf("SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST failed, errorCode 0x%x, data %d\n", errorCode, data); 2867 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); 2868 break; 2869 // This is the response to sceNpMatching2CreateJoinRoom, which if successful means that we are just about ready to move to an online state as host of a game. The final 2870 // transition actually occurs in the create room tick, on detecting that the state has transitioned to SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS here. 2871 case SCE_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM: 2872 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) 2873 { 2874 // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here 2875 break; 2876 } 2877 app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room complete, time taken %d, error 0x%x\n",System::currentTimeMillis()-s_roomStartTime, errorCode); 2878 assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ); 2879 if( errorCode == 0 ) 2880 { 2881 if( data != 0 ) 2882 { 2883 SceNpMatching2CreateJoinRoomResponse *roomData = (SceNpMatching2CreateJoinRoomResponse *)data; 2884 manager->m_localMemberId = roomData->memberList.me->memberId; 2885 2886 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); 2887 manager->m_room = roomData->roomDataInternal->roomId; 2888 break; 2889 } 2890 } 2891 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); 2892 break; 2893 // This is the response to sceNpMatching2JoinRoom, which is called as the final stage of the process started when calling the JoinRoom method. If this is successful, then 2894 // the state can change to SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS. We can transition out of that state once we have told the application that all the local players 2895 // have joined. 2896 case SCE_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM: 2897 assert( manager->m_state == SNM_INT_STATE_JOINING_JOIN_ROOM); 2898 if( errorCode == 0 ) 2899 { 2900 if( data != 0 ) 2901 { 2902 SceNpMatching2JoinRoomResponse *roomData = (SceNpMatching2JoinRoomResponse *)data; 2903 2904 manager->m_localMemberId = roomData->memberList.me->memberId; 2905 manager->m_room = roomData->roomDataInternal->roomId; 2906// SonyVoiceChat::init(manager); 2907 // Copy over initial room sync data 2908 for( int i = 0; i < roomData->roomDataInternal->roomBinAttrInternalNum; i++ ) 2909 { 2910 if( roomData->roomDataInternal->roomBinAttrInternal[i].data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) 2911 { 2912 assert( roomData->roomDataInternal->roomBinAttrInternal[i].data.size == sizeof( manager->m_roomSyncData ) ); 2913 memcpy( &manager->m_roomSyncData, roomData->roomDataInternal[i].roomBinAttrInternal[0].data.ptr, sizeof( manager->m_roomSyncData ) ); 2914 2915// manager->UpdatePlayersFromRoomSyncUIDs(); 2916 // Update mapping from the room slot players to SQRNetworkPlayer instances 2917 manager->MapRoomSlotPlayers(); 2918 break; 2919 } 2920 } 2921 manager->SetState(SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS); 2922 break; 2923 } 2924 } 2925 manager->SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); 2926 if(errorCode == SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL) // MGH - added to fix "host has exited" error when 2 players go after the final slot 2927 { 2928 Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), DisconnectPacket::eDisconnect_ServerFull); 2929 } 2930 break; 2931 // This is the response to sceNpMatching2GetRoomMemberDataInternal.This only happens on the host, as a response to an incoming connection being established, when we 2932 // kick off the request for room member internal data so that we can determine what local players that remote machine is intending to bring into the game. At this point we can 2933 // activate the host end of each of the Rupd connection that this machine requires. We can also update our player slot data (which gets syncronised back out to other room members) at this point. 2934 case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_MEMBER_DATA_INTERNAL: 2935 if( errorCode == 0 ) 2936 { 2937 2938 if( data != 0 ) 2939 { 2940 SceNpMatching2GetRoomMemberDataInternalResponse *pRoomMemberData = (SceNpMatching2GetRoomMemberDataInternalResponse *)data; 2941 assert( pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternalNum == 1 ); 2942 2943 if( manager->m_isHosting ) 2944 { 2945 int playerMask = *((int *)(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr)); 2946 2947 bool isFull = false; 2948 bool success1 = manager->AddRemotePlayersAndSync( pRoomMemberData->roomMemberDataInternal->memberId, playerMask, &isFull ); 2949 bool success2; 2950 if( success1 ) 2951 { 2952 success2 = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, playerMask, pRoomMemberData->roomMemberDataInternal->memberId); 2953 if( success2 ) 2954 { 2955 bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, 0); 2956 assert(ret == true); 2957 break; 2958 } 2959 } 2960 // Something has gone wrong adding these players to the room - kick out the player 2961 SceNpMatching2KickoutRoomMemberRequest reqParam; 2962 SceNpMatching2PresenceOptionData optParam; 2963 memset(&reqParam,0,sizeof(reqParam)); 2964 reqParam.roomId = manager->m_room; 2965 reqParam.target = pRoomMemberData->roomMemberDataInternal->memberId; 2966 // Set flag to indicate whether we were kicked for being out of room or not 2967 reqParam.optData.data[0] = isFull ? 1 : 0; 2968 reqParam.optData.len = 1; 2969 int ret = sceNpMatching2KickoutRoomMember(manager->m_matchingContext, &reqParam, NULL, &manager->m_kickRequestId); 2970 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2KickoutRoomMember returns error 0x%x\n",ret); 2971 } 2972 else 2973 { 2974 if(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr == NULL) 2975 { 2976 // the host doesn't send out data, so this must be the host we're connecting to 2977 2978 // If we are the client, then we locally know what Rupd connections we need (from m_localPlayerJoinMask) and can kick this off. 2979 manager->m_hostMemberId = pRoomMemberData->roomMemberDataInternal->memberId; 2980 bool ret = manager->CreateRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask, manager->m_localMemberId); 2981 if( ret == false ) 2982 { 2983 manager->DeleteServerContext(); 2984 } 2985 else 2986 { 2987 bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); 2988 assert(ret == true); 2989 } 2990 } 2991 else 2992 { 2993 // client <-> client 2994 bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); 2995 assert(ret == true); 2996 } 2997 } 2998 2999 } 3000 } 3001 break; 3002 case SCE_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM: 3003 // This is the response to sceNpMatching2LeaveRoom - from the Sony docs, this doesn't ever fail so no need to do error checking here 3004// SonyVoiceChat::signalDisconnected(); 3005 assert(manager->m_state == SNM_INT_STATE_LEAVING ); 3006 manager->DeleteServerContext(); 3007 break; 3008 // This is the response to SceNpMatching2GetRoomDataExternalListRequest, which happens when we request the full details of a room we are interested in joining 3009 case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_EXTERNAL_LIST: 3010 if( errorCode == 0 ) 3011 { 3012 if( data != 0 ) 3013 { 3014 SceNpMatching2GetRoomDataExternalListResponse *pExternalData = (SceNpMatching2GetRoomDataExternalListResponse *)data; 3015 SceNpMatching2RoomDataExternal *pRoomExtData = pExternalData->roomDataExternal; 3016 if( pExternalData->roomDataExternalNum == 1 ) 3017 { 3018 if(pRoomExtData->roomBinAttrExternalNum == 1 ) 3019 { 3020 memcpy(manager->m_pExtDataToUpdate, pRoomExtData->roomBinAttrExternal[0].ptr,pRoomExtData->roomBinAttrExternal[0].size); 3021 manager->m_FriendSessionUpdatedFn(true, manager->m_pParamFriendSessionUpdated); 3022 } 3023 else 3024 { 3025 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); 3026 } 3027 } 3028 else 3029 { 3030 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); 3031 } 3032 } 3033 else 3034 { 3035 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); 3036 } 3037 } 3038 else 3039 { 3040 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); 3041 } 3042 break; 3043 case SCE_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL: 3044 if( ( errorCode != 0 ) || manager->ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK) ) 3045 { 3046 app.DebugPrintf(CMinecraftApp::USER_RR,"Error updating external data 0x%x (from SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal event in callback)\n",errorCode); 3047 // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, 3048 // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. 3049 manager->m_resendExternalRoomDataCountdown = 60; 3050 } 3051 break; 3052 }; 3053} 3054 3055void SQRNetworkManager_Orbis::RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, const void *data, void *arg) 3056{ 3057 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3058 3059 bool gotEventData = false; 3060 switch( event ) 3061 { 3062 case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED: 3063 break; 3064 case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT: 3065 break; 3066 case SCE_NP_MATCHING2_ROOM_EVENT_KICKEDOUT: 3067 { 3068// SonyVoiceChat::signalRoomKickedOut(); 3069 // We've been kicked out. This server has rejected our attempt to join, most likely because there wasn't enough space in the server to have us. There's a flag set 3070 // so we can determine which thing has happened 3071// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo ); 3072// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataUpdateInfo, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo); 3073// app.DebugPrintf(CMinecraftApp::USER_RR,"SCE_NP_MATCHING2_ROOM_EVENT_Kickedout, sceNpMatching2GetEventData returning 0x%x\n",ret); 3074 3075 bool bIsFull = false; 3076 if( ( data ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) 3077 { 3078 gotEventData = true; 3079 SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)(data); 3080 if( pUpdateInfo->optData.len == 1 ) 3081 { 3082 if( pUpdateInfo->optData.data[0] == 1 ) 3083 { 3084 bIsFull = true; 3085 } 3086 } 3087 } 3088 app.DebugPrintf(CMinecraftApp::USER_RR,"IsFull determined to be %d\n",bIsFull); 3089 if( bIsFull ) 3090 { 3091 manager->m_nextIdleReasonIsFull = true; 3092 } 3093 manager->ResetToIdle(); 3094 } 3095 break; 3096 case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_DESTROYED: 3097// SonyVoiceChat::signalRoomDestroyed(); 3098 3099 { 3100 SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)data; 3101 app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed\n"); 3102 if( pUpdateInfo ) 3103 { 3104 app.DebugPrintf("Further info: Error 0x%x, cause %d\n",pUpdateInfo->errorCode,pUpdateInfo->eventCause); 3105 } 3106 // If we're hosting, then handle this a bit like a disconnect, in that we will shift the game into an offline game - but don't need to actually leave the room 3107 // since that has been destroyed and so isn't there to be left anymore. Don't do this if we are disconnected though, as we've already handled this. 3108 if( ( manager->m_isHosting ) && !manager->m_bLinkDisconnected ) 3109 { 3110 // MGH - we're not receiving an SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD after this so we have to remove all the remote players 3111 while(manager->m_RudpCtxToPlayerMap.size()) 3112 { 3113 SQRNetworkPlayer* pRemotePlayer = manager->m_RudpCtxToPlayerMap.begin()->second; 3114 manager->RemoveRemotePlayersAndSync( pRemotePlayer->m_roomMemberId, 15 ); 3115 } 3116 3117 if(pUpdateInfo && (pUpdateInfo->eventCause==SCE_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT)) 3118 { 3119 manager->m_listener->HandleDisconnect(true,true); 3120 } 3121 else 3122 { 3123 manager->m_listener->HandleDisconnect(true); 3124 } 3125 } 3126 } 3127 break; 3128 case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_OWNER_CHANGED: 3129 break; 3130 case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_DATA_INTERNAL: 3131 // We are using the room internal data to synchronise the player data stored in m_roomSyncData from the host to clients. 3132 // The host is the thing creating the internal room data, so it doesn't need to update itself. 3133 if( !manager->m_isHosting ) 3134 { 3135// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo ); 3136// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataInternal, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo); 3137 if( ( data) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) 3138 { 3139 gotEventData = true; 3140 SceNpMatching2RoomDataInternalUpdateInfo *pRoomData = (SceNpMatching2RoomDataInternalUpdateInfo *)(data); 3141 for(int i = 0; i < pRoomData->newRoomBinAttrInternalNum; i++) 3142 { 3143 if( pRoomData->newRoomBinAttrInternal[i]->data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) 3144 { 3145 assert( pRoomData->newRoomBinAttrInternal[i]->data.size == sizeof( manager->m_roomSyncData ) ); 3146 memcpy( &manager->m_roomSyncData, pRoomData->newRoomBinAttrInternal[i]->data.ptr, sizeof( manager->m_roomSyncData ) ); 3147 3148// manager->UpdatePlayersFromRoomSyncUIDs(); 3149 // Update mapping from the room slot players to SQRNetworkPlayer instances 3150 manager->MapRoomSlotPlayers(); 3151#if 0 3152 { 3153 printf("New player sync data arrived\n"); 3154 for(int i = 0; i < manager->m_roomSyncData.getPlayerCount(); i++ ) 3155 { 3156 printf("%d: small %d, machine %d, local %d\n",i, manager->m_roomSyncData.players[i].m_smallId, manager->m_roomSyncData.players[i].m_roomMemberId, manager->m_roomSyncData.players[i].m_localIdx); 3157 } 3158 } 3159#endif 3160 break; 3161 } 3162 } 3163 break; 3164 } 3165 // TODO - handle error here? What could we do? 3166 } 3167 3168 break; 3169 case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL: 3170 app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_ROOM_MEMBER_DATA_INTERNAL\n"); 3171 3172 if( /*( errorCode == 0 ) && */(!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1) ) ) 3173 { 3174 // We'll get this sync'd round all the connected clients, but we only care about it on the host where we can use it to work out if any RUDP connections need to be made or released 3175 if( manager->m_isHosting ) 3176 { 3177// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); 3178// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); 3179 if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2) ) ) 3180 { 3181 gotEventData = true; 3182 SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); 3183 assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); 3184 3185 int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); 3186 int oldMask = manager->GetOldMask( pRoomMemberData->newRoomMemberDataInternal->memberId ); 3187 int addedMask = manager->GetAddedMask(playerMask, oldMask ); 3188 int removedMask = manager->GetRemovedMask(playerMask, oldMask ); 3189 3190 if( addedMask != 0 ) 3191 { 3192 bool success = manager->AddRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask ); 3193 if( success ) 3194 { 3195 success = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask, pRoomMemberData->newRoomMemberDataInternal->memberId); 3196 } 3197 if( ( !success ) || (manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3) ) ) 3198 { 3199 // Failed for some reason - signal back to the client that this is the case, by updating its internal data back again, rather than have 3200 // it wait for a timeout on its rudp connection initialisation. 3201 SceNpMatching2SetRoomMemberDataInternalRequest reqParam; 3202 SceNpMatching2BinAttr binAttr; 3203 3204 memset(&reqParam, 0, sizeof(reqParam)); 3205 memset(&binAttr, 0, sizeof(binAttr)); 3206 3207 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; 3208 binAttr.ptr = &oldMask; 3209 binAttr.size = sizeof(oldMask); 3210 3211 reqParam.roomId = manager->m_room; 3212 reqParam.memberId = pRoomMemberData->newRoomMemberDataInternal->memberId; 3213 reqParam.roomMemberBinAttrInternalNum = 1; 3214 reqParam.roomMemberBinAttrInternal = &binAttr; 3215 3216 int ret = sceNpMatching2SetRoomMemberDataInternal( manager->m_matchingContext, &reqParam, NULL, &manager->m_setRoomMemberInternalDataRequestId ); 3217 } 3218 else 3219 { 3220 success = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, 0); 3221 assert(success); 3222 } 3223 3224 } 3225 3226 if( removedMask != 0 ) 3227 { 3228 manager->RemoveRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, removedMask ); 3229 } 3230 3231 break; 3232 } 3233 } 3234 else 3235 { 3236 // If, as a client, we receive an updated room member data this could be for two reason. 3237 // (1) Another client in the game has updated their own data as someone has joined/left the session 3238 // (2) The server has set someone's data back, due to a failed attempt to join a game 3239 // We're only interested in scenario (2), when the data that has been updated is our own, in which case we know to abandon creating rudp connections etc. for a new player 3240// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); 3241// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); 3242 if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4) ) ) 3243 { 3244 gotEventData = true; 3245 SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); 3246 assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); 3247 if( pRoomMemberData->newRoomMemberDataInternal->memberId == manager->m_localMemberId ) 3248 { 3249 int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); 3250 if( playerMask != manager->m_localPlayerJoinMask ) 3251 { 3252 int playersToRemove = manager->m_localPlayerJoinMask & (~playerMask); 3253 manager->RemoveNetworkPlayers( playersToRemove ); 3254 if( manager->m_listener ) 3255 { 3256 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) 3257 { 3258 if( playersToRemove & ( 1 << i ) ) 3259 { 3260 manager->m_listener->HandleAddLocalPlayerFailed(i); 3261 break; 3262 } 3263 } 3264 } 3265 } 3266 } 3267 } 3268 3269 } 3270 } 3271 break; 3272 case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_SIGNALING_OPT_PARAM: 3273 break; 3274 }; 3275 3276// // If we didn't get the event data, then we need to clear it, or the system even queue will overflow 3277// if( !gotEventData ) 3278// { 3279// sceNpMatching2ClearEventData(manager->m_matchingContext, eventKey); 3280// } 3281} 3282 3283 3284 3285// This is an implementation of SceNpMatching2SignalingCallback. We configure our too automatically create a star network of connections with the host at the hub, and can respond here to 3286// the connections being set up to layer sockets and Rudp on top. 3287void SQRNetworkManager_Orbis::SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code, void *arg) 3288{ 3289 // MGH - changed this to queue up the signalling events from the callback and process them later on the server thread 3290 3291 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3292 EnterCriticalSection(&manager->m_signallingEventListCS); 3293 SignallingEvent ev; 3294 ev.ctxId = ctxId; 3295 ev.roomId = roomId; 3296 ev.peerMemberId = peerMemberId; 3297 ev.event = event; 3298 ev.error_code = error_code; 3299 manager->m_signallingEventList.push_back(ev); 3300 LeaveCriticalSection(&manager->m_signallingEventListCS); 3301} 3302 3303 3304 3305 3306void SQRNetworkManager_Orbis::ProcessSignallingEvent(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code) 3307{ 3308 switch( event ) 3309 { 3310 case SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD: 3311 { 3312 if( m_isHosting ) 3313 { 3314 // Remove any players associated with this peer 3315 RemoveRemotePlayersAndSync( peerMemberId, 15 ); 3316 } 3317 else if(peerMemberId == m_hostMemberId) 3318 { 3319 // Host has left the game... so its all over for this client too. Finish everything up now, including deleting the server context which belongs to this gaming session 3320 // This also might be a response to a request to leave the game from our end too so don't need to do anything in that case 3321 if( m_state != SNM_INT_STATE_LEAVING ) 3322 { 3323 DeleteServerContext(); 3324 ResetToIdle(); 3325 } 3326 } 3327 else 3328 { 3329 // we've lost connection to another client (voice only) so kill the voice connection 3330 // no players left on the remote machine once we remove this one 3331 SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); 3332 if(pVoice) 3333 SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); 3334 } 3335 } 3336 break; 3337 3338 case SCE_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED: 3339 { 3340 // MGH - changed this to always get the data now, as we need to know if the connecting peer is the host or not 3341 // If we're the host, then we need to get the data associated with the connecting peer to know what connections we should be trying to match. So 3342 // the actual creation of connections happens when the response for this request is processed. 3343 3344 SceNpMatching2GetRoomMemberDataInternalRequest reqParam; 3345 memset( &reqParam, 0, sizeof(reqParam)); 3346 reqParam.roomId = roomId; 3347 reqParam.memberId = peerMemberId; 3348 SceNpMatching2AttributeId attrs[1] = {SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID}; 3349 reqParam.attrId = attrs; 3350 reqParam.attrIdNum = 1; 3351 3352 sceNpMatching2GetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_roomMemberDataRequestId); 3353 } 3354 break; 3355 } 3356} 3357 3358void SQRNetworkManager_Orbis::SignallingEventsTick() 3359{ 3360 EnterCriticalSection(&m_signallingEventListCS); 3361 for(int i=0;i<m_signallingEventList.size(); i++) 3362 { 3363 SignallingEvent& ev = m_signallingEventList[i]; 3364 ProcessSignallingEvent(ev.ctxId, ev.roomId, ev.peerMemberId, ev.event, ev.error_code); 3365 } 3366 m_signallingEventList.clear(); 3367 LeaveCriticalSection(&m_signallingEventListCS); 3368 3369} 3370 3371 3372 3373// Implementation of SceNpBasicEventHandler 3374int SQRNetworkManager_Orbis::BasicEventCallback(int event, int retCode, uint32_t reqId, void *arg) 3375{ 3376 ORBIS_STUBBED; 3377// SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3378// // We aren't allowed to actually get the event directly from this callback, so send our own internal event to a thread dedicated to doing this 3379// sceKernelTriggerUserEvent(m_basicEventQueue, sc_UserEventHandle, NULL); 3380 3381 return 0; 3382} 3383 3384// Implementation of SceNpManagerCallback 3385void SQRNetworkManager_Orbis::OnlineCheck() 3386{ 3387 bool bSignedIn = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()); 3388 if(GetOnlineStatus() == false) 3389 { 3390 if(bSignedIn) 3391 InitialiseAfterOnline(); 3392 } 3393 else 3394 { 3395 // If it's an online game, and the primary profile is no longer signed into LIVE then we act as if disconnected 3396 if( (bSignedIn == false) && m_isInSession && !g_NetworkManager.IsLocalGame() ) 3397 { 3398 app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_EthernetDisconnected); 3399 } 3400 3401 } 3402 UpdateOnlineStatus(bSignedIn); 3403} 3404 3405// Implementation of CellSysutilCallback 3406void SQRNetworkManager_Orbis::SysUtilCallback(uint64_t status, uint64_t param, void *userdata) 3407{ 3408// SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)userdata; 3409// struct CellNetCtlNetStartDialogResult netstart_result; 3410// int ret = 0; 3411// netstart_result.size = sizeof(netstart_result); 3412// switch(status) 3413// { 3414// case CELL_SYSUTIL_NET_CTL_NETSTART_FINISHED: 3415// ret = cellNetCtlNetStartDialogUnloadAsync(&netstart_result); 3416// if(ret < 0) 3417// { 3418// manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 3419// if( s_SignInCompleteCallbackFn ) 3420// { 3421// if( s_signInCompleteCallbackIfFailed ) 3422// { 3423// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); 3424// } 3425// s_SignInCompleteCallbackFn = NULL; 3426// } 3427// return; 3428// } 3429// 3430// if( netstart_result.result != 0 ) 3431// { 3432// // Failed, or user may have decided not to sign in - maybe need to differentiate here 3433// manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); 3434// if( s_SignInCompleteCallbackFn ) 3435// { 3436// if( s_signInCompleteCallbackIfFailed ) 3437// { 3438// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); 3439// } 3440// s_SignInCompleteCallbackFn = NULL; 3441// } 3442// } 3443// 3444// break; 3445// case CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED: 3446// break; 3447// case CELL_SYSUTIL_NP_INVITATION_SELECTED: 3448// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); 3449// break; 3450// default: 3451// break; 3452// } 3453} 3454 3455// Implementation of CellRudpContextEventHandler. This is associate with an Rudp context every time one is created, and can be used to determine the status of each 3456// Rudp connection. We create one context/connection per local player on the non-hosting consoles. 3457void SQRNetworkManager_Orbis::RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg) 3458{ 3459 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3460 switch(event_id) 3461 { 3462 case SCE_RUDP_CONTEXT_EVENT_CLOSED: 3463 { 3464 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_CLOSED\n"); 3465 SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); 3466 if(pVoice) 3467 { 3468 pVoice->m_bConnected = false; 3469 } 3470 else 3471 { 3472 app.DebugPrintf(CMinecraftApp::USER_RR,"RUDP closed - event error 0x%x\n",error_code); 3473 if( !manager->m_isHosting ) 3474 { 3475 if( manager->m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) 3476 { 3477 manager->LeaveRoom(true); 3478 } 3479 } 3480 } 3481 } 3482 break; 3483 case SCE_RUDP_CONTEXT_EVENT_ESTABLISHED: 3484 { 3485 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ESTABLISHED\n"); 3486 3487 SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); 3488 if( player ) 3489 { 3490 // Flag connection stage as being completed for this player 3491 manager->NetworkPlayerConnectionComplete(player); 3492 } 3493 else 3494 { 3495 SonyVoiceChat_Orbis::setConnected(ctx_id); 3496 } 3497 } 3498 break; 3499 case SCE_RUDP_CONTEXT_EVENT_ERROR: 3500 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ERROR\n"); 3501 break; 3502 case SCE_RUDP_CONTEXT_EVENT_WRITABLE: 3503 { 3504 SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); 3505 // This event signifies that room has opened up in the write buffer, so attempt to send something 3506 if( player ) 3507 { 3508 player->SendMoreInternal(); 3509 } 3510 else 3511 { 3512 SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); 3513 assert(pVoice); 3514 } 3515 } 3516 break; 3517 case SCE_RUDP_CONTEXT_EVENT_READABLE: 3518 if( manager->m_listener ) 3519 { 3520 SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); 3521 if(pVoice) 3522 { 3523 pVoice->readRemoteData(); 3524 } 3525 else 3526 { 3527 unsigned int dataSize = sceRudpGetSizeReadable(ctx_id); 3528 // If we're the host, and this player hasn't yet had its small id confirmed, then the first byte sent to us should be this id 3529 if( manager->m_isHosting ) 3530 { 3531 SQRNetworkPlayer *playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); 3532 if( playerFrom && !playerFrom->HasSmallIdConfirmed() ) 3533 { 3534 if( dataSize >= sizeof(SQRNetworkPlayer::InitSendData) ) 3535 { 3536 SQRNetworkPlayer::InitSendData ISD; 3537 int bytesRead = sceRudpRead( ctx_id, &ISD, sizeof(SQRNetworkPlayer::InitSendData), 0, NULL ); 3538 if( bytesRead == sizeof(SQRNetworkPlayer::InitSendData) ) 3539 { 3540 manager->NetworkPlayerInitialDataReceived(playerFrom, &ISD); 3541 dataSize -= sizeof(SQRNetworkPlayer::InitSendData); 3542 } 3543 else 3544 { 3545 assert(false); 3546 } 3547 } 3548 else 3549 { 3550 assert(false); 3551 } 3552 } 3553 } 3554 3555 if( dataSize > 0 ) 3556 { 3557 unsigned char *data = new unsigned char [ dataSize ]; 3558 int bytesRead = sceRudpRead( ctx_id, data, dataSize, 0, NULL ); 3559 if( bytesRead > 0 ) 3560 { 3561 SQRNetworkPlayer *playerFrom, *playerTo; 3562 if( manager->m_isHosting ) 3563 { 3564 // Data always going from a remote player, to the host 3565 playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); 3566 playerTo = manager->m_aRoomSlotPlayers[0]; 3567 } 3568 else 3569 { 3570 // Data always going from host player, to a local player 3571 playerFrom = manager->m_aRoomSlotPlayers[0]; 3572 playerTo = manager->GetPlayerFromRudpCtx( ctx_id ); 3573 } 3574 if( ( playerFrom != NULL ) && ( playerTo != NULL ) ) 3575 { 3576 manager->m_listener->HandleDataReceived( playerFrom, playerTo, data, bytesRead ); 3577 } 3578 } 3579 delete [] data; 3580 } 3581 } 3582 } 3583 break; 3584 case SCE_RUDP_CONTEXT_EVENT_FLUSHED: 3585 app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_FLUSHED\n"); 3586 break; 3587 } 3588} 3589 3590// Implementation of CellRudpEventHandler 3591int SQRNetworkManager_Orbis::RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg) 3592{ 3593 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3594 if( event_id == SCE_RUDP_EVENT_SOCKET_RELEASED ) 3595 { 3596 assert( soc == manager->m_soc ); 3597 sceNetSocketClose(soc); 3598 manager->m_soc = -1; 3599 } 3600 return 0; 3601} 3602 3603void SQRNetworkManager_Orbis::NetCtlCallback(int eventType, void *arg) 3604{ 3605 SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; 3606 // Oddly, the disconnect event comes in with a new state of "CELL_NET_CTL_STATE_Connecting"... looks like the event is more important than the state to 3607 // determine what has just happened 3608 if( eventType == SCE_NET_CTL_EVENT_TYPE_DISCONNECTED)// CELL_NET_CTL_EVENT_LINK_DISCONNECTED ) 3609 { 3610 manager->m_bLinkDisconnected = true; 3611 manager->m_listener->HandleDisconnect(false); 3612 } 3613 else //if( event == CELL_NET_CTL_EVENT_ESTABLISH ) 3614 { 3615 manager->m_bLinkDisconnected = false; 3616 } 3617 3618} 3619 3620// Called when the context has been created, and we are intending to create a room. 3621void SQRNetworkManager_Orbis::ServerContextValid_CreateRoom() 3622{ 3623 // First find a world 3624 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD); 3625 3626 SceNpMatching2GetWorldInfoListRequest reqParam; 3627 3628 // Request parameters 3629 memset(&reqParam, 0, sizeof(reqParam)); 3630 reqParam.serverId = m_serverId; 3631 3632 int ret = -1; 3633 if( !ForceErrorPoint(SNM_FORCE_ERROR_GET_WORLD_INFO_LIST) ) 3634 { 3635 ret = sceNpMatching2GetWorldInfoList( m_matchingContext, &reqParam, NULL, &m_getWorldRequestId); 3636 } 3637 if (ret < 0) 3638 { 3639 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); 3640 return; 3641 } 3642} 3643 3644// Called when the context has been created, and we are intending to join a pre-existing room. 3645void SQRNetworkManager_Orbis::ServerContextValid_JoinRoom() 3646{ 3647// assert( m_state == SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT ); 3648 3649 SetState(SNM_INT_STATE_JOINING_JOIN_ROOM); 3650 3651 // Join the room, passing the local player mask as initial binary data so that the host knows what local players are here 3652 SceNpMatching2JoinRoomRequest reqParam; 3653 SceNpMatching2BinAttr binAttr; 3654 memset(&reqParam, 0, sizeof(reqParam)); 3655 memset(&binAttr, 0, sizeof(binAttr)); 3656 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; 3657 binAttr.ptr = &m_localPlayerJoinMask; 3658 binAttr.size = sizeof(m_localPlayerJoinMask); 3659 3660 reqParam.roomId = m_roomToJoin; 3661 reqParam.roomMemberBinAttrInternalNum = 1; 3662 reqParam.roomMemberBinAttrInternal = &binAttr; 3663 3664 int ret = sceNpMatching2JoinRoom( m_matchingContext, &reqParam, NULL, &m_joinRoomRequestId ); 3665 if ( (ret < 0) || ForceErrorPoint(SNM_FORCE_ERROR_JOIN_ROOM) ) 3666 { 3667 if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) 3668 { 3669 app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); 3670 } 3671 SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); 3672 } 3673} 3674 3675const SceNpCommunicationId* SQRNetworkManager_Orbis::GetSceNpCommsId() 3676{ 3677 return &s_npCommunicationId; 3678} 3679 3680const SceNpCommunicationSignature* SQRNetworkManager_Orbis::GetSceNpCommsSig() 3681{ 3682 return &s_npCommunicationSignature; 3683} 3684 3685const SceNpTitleId* SQRNetworkManager_Orbis::GetSceNpTitleId() 3686{ 3687 return &s_npTitleId; 3688} 3689 3690const SceNpTitleSecret* SQRNetworkManager_Orbis::GetSceNpTitleSecret() 3691{ 3692 return &s_npTitleSecret; 3693} 3694 3695int SQRNetworkManager_Orbis::GetOldMask(SceNpMatching2RoomMemberId memberId) 3696{ 3697 int oldMask = 0; 3698 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) 3699 { 3700 if( m_roomSyncData.players[i].m_roomMemberId == memberId ) 3701 { 3702 oldMask |= (1 << m_roomSyncData.players[i].m_localIdx); 3703 } 3704 } 3705 return oldMask; 3706} 3707 3708int SQRNetworkManager_Orbis::GetAddedMask(int newMask, int oldMask) 3709{ 3710 return newMask & ~oldMask; 3711} 3712 3713int SQRNetworkManager_Orbis::GetRemovedMask(int newMask, int oldMask) 3714{ 3715 return oldMask & ~newMask; 3716} 3717 3718 3719void SQRNetworkManager_Orbis::GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) 3720{ 3721 static SceNpMatching2GetRoomDataExternalListRequest reqParam; 3722 static SceNpMatching2RoomId aRoomId[1]; 3723 static SceNpMatching2AttributeId attr[1]; 3724 3725 // All parameters will be NULL if this is being called a second time, after creating a new matching context via one of the paths below (using GetMatchingContext). 3726 // NULL parameters therefore basically represents an attempt to retry the last sceNpMatching2GetRoomDataExternalList 3727 if( extData != NULL ) 3728 { 3729 aRoomId[0] = roomId; 3730 attr[0] = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; 3731 3732 memset(&reqParam, 0, sizeof(reqParam)); 3733 reqParam.roomId = aRoomId; 3734 reqParam.roomIdNum = 1; 3735 reqParam.attrIdNum = 1; 3736 reqParam.attrId = attr; 3737 3738 m_FriendSessionUpdatedFn = FriendSessionUpdatedFn; 3739 m_pParamFriendSessionUpdated = pParam; 3740 m_pExtDataToUpdate = extData; 3741 } 3742 3743 // Check there's a valid matching context and possibly recreate here 3744 if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) 3745 { 3746 // No matching context, and failed to try and make one. We're really broken here. 3747 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); 3748 return; 3749 } 3750 3751 // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. 3752 if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) 3753 { 3754 app.DebugPrintf("Having to recreate matching context, setting state to SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT\n"); 3755 return; 3756 } 3757 3758 int ret = sceNpMatching2GetRoomDataExternalList( m_matchingContext, &reqParam, NULL, &m_roomDataExternalListRequestId ); 3759 3760 // If we hadn't properly detected that a matching context was unvailable, we might still get an error indicating that it is from the previous call. Handle similarly, but we need 3761 // to destroy the context first. 3762 if( ret == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) // Also checking for this as a means of simulating the previous error 3763 { 3764 sceNpMatching2DestroyContext(m_matchingContext); 3765 m_matchingContextValid = false; 3766 if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) 3767 { 3768 // No matching context, and failed to try and make one. We're really broken here. 3769 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); 3770 return; 3771 }; 3772 // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. 3773 if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) 3774 { 3775 return; 3776 } 3777 } 3778 3779 if( ret != 0 ) 3780 { 3781 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); 3782 } 3783} 3784 3785 3786#ifdef _CONTENT_PACKAGE 3787bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError error) 3788{ 3789 return false; 3790} 3791#else 3792bool SQRNetworkManager_Orbis::aForceError[SNM_FORCE_ERROR_COUNT] = 3793{ 3794 false, // SNM_FORCE_ERROR_NP2_INIT 3795 false, // SNM_FORCE_ERROR_NET_INITIALIZE_NETWORK 3796 false, // SNM_FORCE_ERROR_NET_CTL_INIT 3797 false, // SNM_FORCE_ERROR_RUDP_INIT 3798 false, // SNM_FORCE_ERROR_NET_START_DIALOG 3799 false, // SNM_FORCE_ERROR_MATCHING2_INIT 3800 false, // SNM_FORCE_ERROR_REGISTER_NP_CALLBACK 3801 false, // SNM_FORCE_ERROR_GET_NPID 3802 false, // SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT 3803 false, // SNM_FORCE_ERROR_REGISTER_CALLBACKS 3804 false, // SNM_FORCE_ERROR_CONTEXT_START_ASYNC 3805 false, // SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA 3806 false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY_COUNT 3807 false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY 3808 false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST 3809 false, // SNM_FORCE_ERROR_LEAVE_ROOM 3810 false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL 3811 false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2 3812 false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT 3813 false, // SNM_FORCE_ERROR_CREATE_JOIN_ROOM 3814 false, // SNM_FORCE_ERROR_GET_SERVER_INFO 3815 false, // SNM_FORCE_ERROR_DELETE_SERVER_CONTEXT 3816 false, // SNM_FORCE_ERROR_SETSOCKOPT_0 3817 false, // SNM_FORCE_ERROR_SETSOCKOPT_1 3818 false, // SNM_FORCE_ERROR_SETSOCKOPT_2 3819 false, // SNM_FORCE_ERROR_SOCK_BIND 3820 false, // SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT 3821 false, // SNM_FORCE_ERROR_RUDP_BIND 3822 false, // SNM_FORCE_ERROR_RUDP_INIT2 3823 false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA 3824 false, // SNM_FORCE_ERROR_GET_SERVER_INFO_DATA 3825 false, // SNM_FORCE_ERROR_GET_WORLD_INFO_DATA 3826 false, // SNM_FORCE_ERROR_GET_CREATE_JOIN_ROOM_DATA 3827 false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST_DATA 3828 false, // SNM_FORCE_ERROR_GET_JOIN_ROOM_DATA 3829 false, // SNM_FORCE_ERROR_GET_ROOM_MEMBER_DATA_INTERNAL 3830 false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA2 3831 false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK 3832 false, // SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK 3833 false, // SNM_FORCE_ERROR_UPDATED_ROOM_DATA 3834 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1 3835 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2 3836 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3 3837 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4 3838 false, // SNM_FORCE_ERROR_GET_WORLD_INFO_LIST 3839 false, // SNM_FORCE_ERROR_JOIN_ROOM 3840}; 3841 3842bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError err) 3843{ 3844 return aForceError[err]; 3845} 3846#endif 3847 3848 3849int ErrorPSNDisconnectedDialogReturned(void *pParam, int iPad, const C4JStorage::EMessageResult iResult) 3850{ 3851 //SQRNetworkManager_Orbis::CallSignInCompleteCallback(); 3852 SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=true; 3853 3854 return 0; 3855} 3856 3857void SQRNetworkManager_Orbis::AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed/*=false*/, int iPad/*=-1*/) 3858{ 3859 s_SignInCompleteCallbackFn = SignInCompleteCallbackFn; 3860 s_signInCompleteCallbackIfFailed = callIfFailed; 3861 s_SignInCompleteParam = pParam; 3862 s_SignInCompleteCallbackPad = iPad; 3863 3864 // If pad isn't set, get primary pad 3865 iPad = iPad == -1 ? ProfileManager.GetPrimaryPad() : iPad; 3866 3867 if(ProfileManager.isSignedInPSN(iPad) == false) 3868 { 3869 bool bOpenedDialog = false; 3870 int NPError = ProfileManager.getNPAvailability(iPad); 3871 if(NPError == SCE_NP_ERROR_SIGNED_OUT || NPError == SCE_NP_ERROR_NOT_SIGNED_UP) 3872 { 3873 int32_t ret=sceErrorDialogInitialize(); 3874 if ( ret==SCE_OK ) 3875 { 3876 SceErrorDialogParam dialogParameter; 3877 sceErrorDialogParamInitialize( &dialogParameter ); 3878 dialogParameter.errorCode = SCE_NP_ERROR_SIGNED_OUT; // for force display. 3879 dialogParameter.userId = ProfileManager.getUserID(iPad); 3880 ret = sceErrorDialogOpen( &dialogParameter ); 3881 if( ret < 0 ) 3882 { 3883 app.DebugPrintf("sceErrorDialogOpen failed : 0x%08x\n",ret); 3884 assert(0); 3885 } 3886 else 3887 { 3888 bOpenedDialog = true; 3889 app.DebugPrintf("sceErrorDialogOpen s_errorDialogRunning\n"); 3890 s_errorDialogRunning = true; 3891 } 3892 } 3893 } 3894 3895 if(!bOpenedDialog) 3896 { 3897 // This shouldn't happen generally 3898 assert(0); 3899 3900 if(s_SignInCompleteCallbackFn) // MGH - added after crash on PS4 3901 { 3902 if( s_signInCompleteCallbackIfFailed ) 3903 { 3904 m_bCallPSNSignInCallback=true; 3905 s_signInCompleteCallbackFAIL=true; 3906 3907 //s_signInCompleteCallbackIfFailed=false; 3908 //s_SignInCompleteCallbackFn(s_SignInCompleteParam, false, iPad); 3909 } 3910 //s_SignInCompleteCallbackFn = NULL; 3911 } 3912 } 3913 } 3914 else if(ProfileManager.isConnectedToPSN(iPad) == false) 3915 { 3916 // we're signed into PSN, but we don't have a net connection, throw up a lan error 3917 UINT uiIDA[1]; 3918 uiIDA[0]=IDS_CONFIRM_OK; 3919 ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA,1,iPad,ErrorPSNDisconnectedDialogReturned,pParam, app.GetStringTable()); 3920 3921 } 3922} 3923 3924int SQRNetworkManager_Orbis::SetRichPresence(const void *data) 3925{ 3926 const sce::Toolkit::NP::PresenceDetails *newPresenceInfo = (const sce::Toolkit::NP::PresenceDetails *)data; 3927 3928 s_lastPresenceInfo.status = newPresenceInfo->status; 3929 s_lastPresenceInfo.userInfo = newPresenceInfo->userInfo; 3930 3931 s_presenceStatusDirty = true; 3932 SendLastPresenceInfo(); 3933 3934 // Return as if no error happened no matter what, as we'll be resending ourselves if we need to and don't want the calling system to retry 3935 return 0; 3936} 3937 3938void SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(void *data, unsigned int dataBytes) 3939{ 3940 assert(dataBytes <= SCE_TOOLKIT_NP_IN_GAME_PRESENCE_DATA_SIZE_MAX ); 3941 memcpy(s_lastPresenceInfo.data, data, dataBytes); 3942 s_lastPresenceInfo.size = dataBytes; 3943 3944 s_presenceDataDirty = true; 3945 SendLastPresenceInfo(); 3946} 3947 3948void SQRNetworkManager_Orbis::TickRichPresence() 3949{ 3950 if( s_resendPresenceTime ) 3951 { 3952 if( s_resendPresenceTime < System::currentTimeMillis() ) 3953 { 3954 s_resendPresenceTime = 0; 3955 SendLastPresenceInfo(); 3956 } 3957 } 3958} 3959 3960void SQRNetworkManager_Orbis::SendLastPresenceInfo() 3961{ 3962 // Don't attempt to send if we are already waiting to resend 3963 if( s_resendPresenceTime ) return; 3964 3965 // If we are trying to send at a rate faster than we should be, then use the resend mechanism to send at an appropriate time 3966 if( ( System::currentTimeMillis() - s_lastPresenceTime ) < MIN_PRESENCE_RESEND_TIME ) 3967 { 3968 s_resendPresenceTime = s_lastPresenceTime + MIN_PRESENCE_RESEND_TIME; 3969 return; 3970 } 3971 3972 // On PS4 we can't set the status and the data at the same time 3973 unsigned int options = 0; 3974 if( s_presenceDataDirty ) 3975 { 3976 // Prioritise data over status as it is critical to discovering the network game 3977 s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_DATA; 3978 } 3979 else if( s_presenceStatusDirty ) 3980 { 3981 s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_STATUS; 3982 } 3983 else 3984 { 3985 return; // nothing to be done. 3986 } 3987 3988 int err = -1; 3989 // check if we're connected to the PSN first 3990 if(ProfileManager.IsSignedInLive(ProfileManager.getQuadrant(s_lastPresenceInfo.userInfo.userId))) 3991 { 3992 err = sce::Toolkit::NP::Presence::Interface::setPresence(&s_lastPresenceInfo); 3993 app.DebugPrintf("Updating presence type %d @ %dms\n",s_lastPresenceInfo.presenceType,(System::currentTimeMillis()%1000000)); 3994 s_lastPresenceTime = System::currentTimeMillis(); 3995 } 3996 3997 if( err == SCE_TOOLKIT_NP_SUCCESS ) 3998 { 3999 // Successfully sent something 4000 if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) 4001 { 4002 s_presenceDataDirty = false; 4003 } 4004 else 4005 { 4006 s_presenceStatusDirty = false; 4007 } 4008 } 4009 4010 // If there's still work to be done, use resend mechanism to do it 4011 if( s_presenceDataDirty || s_presenceStatusDirty ) 4012 { 4013 s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; 4014 } 4015} 4016 4017void SQRNetworkManager_Orbis::SetPresenceFailedCallback() 4018{ 4019 // Check the last presence type to be sent 4020 if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) 4021 { 4022 s_presenceDataDirty = true; 4023 } 4024 else 4025 { 4026 s_presenceStatusDirty = true; 4027 } 4028 s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; 4029} 4030 4031 4032 4033void SQRNetworkManager_Orbis::SetPresenceDataStartHostingGame() 4034{ 4035 if( m_offlineGame ) 4036 { 4037 SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&c_presenceSyncInfoNULL, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); 4038 } 4039 else 4040 { 4041 SQRNetworkManager_Orbis::PresenceSyncInfo presenceInfo; 4042 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData( &presenceInfo, m_joinExtData, m_room, m_serverId ); 4043 SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&presenceInfo, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); 4044// OrbisNPToolkit::createNPSession(); 4045 } 4046} 4047 4048int SQRNetworkManager_Orbis::GetJoiningReadyPercentage() 4049{ 4050 if ( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ) 4051 { 4052 int completed = ( m_totalServerCount - m_serverCount ) - 1; 4053 int pc = ( completed * 100 ) / m_totalServerCount; 4054 if( pc < 0 ) pc = 0; 4055 if( pc > 100 ) pc = 100; 4056 return pc; 4057 } 4058 else 4059 { 4060 return 100; 4061 } 4062} 4063 4064void SQRNetworkManager_Orbis::removePlayerFromVoiceChat( SQRNetworkPlayer* pPlayer ) 4065{ 4066 if(pPlayer->IsLocal()) 4067 { 4068 4069 SonyVoiceChat_Orbis::disconnectLocalPlayer(pPlayer->GetLocalPlayerIndex()); 4070 } 4071 else 4072 { 4073 int numRemotePlayersLeft = 0; 4074 for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) 4075 { 4076 if( m_aRoomSlotPlayers[i] ) 4077 { 4078 if( m_aRoomSlotPlayers[i] != pPlayer ) 4079 { 4080 if(m_aRoomSlotPlayers[i]->m_roomMemberId == pPlayer->m_roomMemberId) 4081 numRemotePlayersLeft++; 4082 } 4083 } 4084 } 4085 if(numRemotePlayersLeft == 0) 4086 { 4087 // no players left on the remote machine once we remove this one 4088 SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(pPlayer->m_roomMemberId); 4089 //assert(pVoice); 4090 if(pVoice) 4091 SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); 4092 } 4093 } 4094} 4095 4096// While we're in online gameplay notify Sony accordingly (every 5 seconds) 4097void SQRNetworkManager_Orbis::TickNotify() 4098{ 4099 if (g_NetworkManager.IsInSession() && !g_NetworkManager.IsLocalGame()) 4100 { 4101 long long currentTime = System::currentTimeMillis(); 4102 4103 // Note: interval at which to notify Sony of realtime play, according to docs an interval greater than 1 sec is bad 4104 // but in testing NP debug was happy with 5 seconds, to be on the safe side call it 0.75 seconds 4105 int notifyInterval = 750; 4106 4107 if (currentTime - m_lastNotifyTime > notifyInterval) 4108 { 4109 m_lastNotifyTime = currentTime; 4110 for(int i = 0; i < XUSER_MAX_COUNT; i++) 4111 { 4112 if (ProfileManager.IsSignedInLive(i)) 4113 { 4114 NotifyRealtimePlusFeature(i); 4115 } 4116 } 4117 } 4118 } 4119} 4120 4121// Notify plus feature for given quadrant 4122void SQRNetworkManager_Orbis::NotifyRealtimePlusFeature(int iQuadrant) 4123{ 4124 SceNpNotifyPlusFeatureParameter param = SceNpNotifyPlusFeatureParameter(); 4125 param.userId = ProfileManager.getUserID(iQuadrant); 4126 param.size = sizeof(SceNpNotifyPlusFeatureParameter); 4127 param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; 4128 ZeroMemory(param.padding, sizeof(char) * 4); 4129 ZeroMemory(param.reserved, sizeof(uint8_t) * 32); 4130 4131 int err = sceNpNotifyPlusFeature(&param); 4132 if (err != SCE_OK) 4133 { 4134 app.DebugPrintf("SQRNetworkManager_Orbis::NotifyRealtimePlusFeature: sceNpNotifyPlusFeature failed (0x%x)\n", err); 4135 assert(0); 4136 } 4137} 4138 4139// void SQRNetworkManager_Orbis::CallSignInCompleteCallback() 4140// { 4141// // If there's a callback 4142// if( s_SignInCompleteCallbackFn) 4143// { 4144// app.DebugPrintf("============ Calling CallSignInCompleteCallback and s_SignInCompleteCallbackFn is OK\n"); 4145// bool isSignedIn = ProfileManager.IsSignedInLive(s_SignInCompleteCallbackPad); 4146// 4147// s_SignInCompleteCallbackFn(s_SignInCompleteParam, isSignedIn, s_SignInCompleteCallbackPad); 4148// s_SignInCompleteCallbackFn = NULL; 4149// s_SignInCompleteCallbackPad = -1; 4150// } 4151// else 4152// { 4153// app.DebugPrintf("============ Calling CallSignInCompleteCallback but s_SignInCompleteCallbackFn is NULL\n"); 4154// } 4155//}