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