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_PS3.h"
3#include "Common/Network/Sony/SQRNetworkPlayer.h"
4#ifdef __PS3__
5#include <sys/random_number.h>
6#include <sys/event.h>
7#include <cell/rudp.h>
8#include <sysutil/sysutil_gamecontent.h>
9#else
10#include <rudp.h>
11#include <np_toolkit.h>
12#endif
13#include "../Passphrase/ps3__np_conf.h"
14#ifdef __PS3__
15#include "SonyVoiceChat.h"
16#include "Common/Network/Sony/SonyHttp.h"
17#endif
18#include "..\..\..\Minecraft.World\C4JThread.h"
19#include "..\PS3Extras\PS3Strings.h"
20#include "PS3\Network\SonyRemoteStorage_PS3.h"
21
22int (* SQRNetworkManager_PS3::s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad) = NULL;
23void * SQRNetworkManager_PS3::s_SignInCompleteParam = NULL;
24SceNpBasicPresenceDetails2 SQRNetworkManager_PS3::s_lastPresenceInfo = { 0 };
25int SQRNetworkManager_PS3::s_resendPresenceCountdown = 0;
26bool SQRNetworkManager_PS3::s_presenceStatusDirty = false;
27bool SQRNetworkManager_PS3::s_presenceDataDirty = false;
28bool SQRNetworkManager_PS3::s_signInCompleteCallbackIfFailed = false;
29SQRNetworkManager_PS3::PresenceSyncInfo SQRNetworkManager_PS3::s_lastPresenceSyncInfo = { 0 };
30SQRNetworkManager_PS3::PresenceSyncInfo SQRNetworkManager_PS3::c_presenceSyncInfoNULL = { 0 };
31SceNpBasicAttachmentDataId SQRNetworkManager_PS3::s_lastInviteIdToRetry = SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID;
32long long SQRNetworkManager_PS3::s_roomStartTime = 0;
33bool SQRNetworkManager_PS3::m_bCallPSNSignInCallback=false;
34
35
36//unsigned int SQRNetworkManager_PS3::RoomSyncData::playerCount = 0;
37
38// This maps internal to extern states, and needs to match element-by-element the eSQRNetworkManagerInternalState enumerated type
39const SQRNetworkManager_PS3::eSQRNetworkManagerState SQRNetworkManager_PS3::m_INTtoEXTStateMappings[SQRNetworkManager_PS3::SNM_INT_STATE_COUNT] =
40{
41 SNM_STATE_INITIALISING, // SNM_INT_STATE_UNINITIALISED
42 SNM_STATE_INITIALISING, // SNM_INT_STATE_SIGNING_IN
43 SNM_STATE_INITIALISING, // SNM_INT_STATE_STARTING_CONTEXT
44 SNM_STATE_INITIALISE_FAILED, // SNM_INT_STATE_INITIALISE_FAILED
45 SNM_STATE_IDLE, // SNM_INT_STATE_IDLE
46 SNM_STATE_IDLE, // SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT
47 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT
48 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER
49 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR
50 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_FOUND
51 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT
52 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED
53 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD
54 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND
55 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM
56 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS
57 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED
58 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT
59 SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_WAITING_TO_PLAY
60 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT
61 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER
62 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR
63 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_FOUND
64 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT
65 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED
66 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM
67 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED
68 SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS
69 SNM_STATE_ENDING, // SNM_INT_STATE_SERVER_DELETING_CONTEXT
70 SNM_STATE_STARTING, // SNM_INT_STATE_STARTING
71 SNM_STATE_PLAYING, // SNM_INT_STATE_PLAYING
72 SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING
73 SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING_FAILED
74 SNM_STATE_ENDING, // SNM_INT_STATE_ENDING
75};
76
77SQRNetworkManager_PS3::SQRNetworkManager_PS3(ISQRNetworkManagerListener *listener)
78{
79 m_state = SNM_INT_STATE_UNINITIALISED;
80 m_stateExternal = SNM_STATE_INITIALISING;
81 m_nextIdleReasonIsFull = false;
82 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE;
83 m_searchResultCount = 0;
84 m_serverContextValid = false;
85 m_isHosting = false;
86 m_currentSmallId = 0;
87 memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) );
88 memset( m_aSearchResultRoomExtDataReceived, 0, sizeof( void *) * MAX_FRIENDS );
89 m_listener = listener;
90 m_soc = -1;
91 m_resendExternalRoomDataCountdown = 0;
92 m_matching2initialised = false;
93 m_matchingContextValid = false;
94 m_inviteIndex = 0;
95 m_doBootInviteCheck = true;
96 m_isInSession = false;
97 m_offlineGame = false;
98 m_offlineSQR = false;
99 m_aServerId = NULL;
100 m_gameBootInvite = NULL;
101 m_onlineStatus = false;
102 m_bLinkDisconnected = false;
103
104 InitializeCriticalSection(&m_csRoomSyncData);
105 InitializeCriticalSection(&m_csPlayerState);
106 InitializeCriticalSection(&m_csStateChangeQueue);
107
108 sys_event_port_create(&m_basicEventPort, SYS_EVENT_PORT_LOCAL, SYS_EVENT_PORT_NO_NAME);
109
110 sys_event_queue_attribute_t queue_attr = {SYS_SYNC_PRIORITY, SYS_PPU_QUEUE};
111 sys_event_queue_create(&m_basicEventQueue, &queue_attr, SYS_EVENT_QUEUE_LOCAL, 127);
112 sys_event_port_connect_local(m_basicEventPort, m_basicEventQueue);
113
114 m_basicEventThread = new C4JThread(&BasicEventThreadProc,this,"Basic Event Handler");
115 m_basicEventThread->Run();
116}
117
118// 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.
119// Initialisation continues in InitialiseAfterOnline once this completes.
120void SQRNetworkManager_PS3::Initialise()
121{
122#ifdef __PS3__
123
124#ifdef __PS3__
125 sceNpBasicRegisterHandler( &s_npCommunicationId, BasicEventCallback, this );
126#endif
127
128 assert( m_state == SNM_INT_STATE_UNINITIALISED );
129
130 //Initialize network
131 int ret = sys_net_initialize_network();
132 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_INITIALIZE_NETWORK ) )
133 {
134 SetState(SNM_INT_STATE_INITIALISE_FAILED);
135 return;
136 }
137
138 //Initialize libnetctl
139 ret = cellNetCtlInit();
140#else
141 int ret = sceNetCtlInit();
142#endif
143 if( ( ret < 0 && ret != CELL_NET_CTL_ERROR_NOT_TERMINATED ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_CTL_INIT ) )
144 {
145 SetState(SNM_INT_STATE_INITIALISE_FAILED);
146 return;
147 }
148
149 int hid;
150 ret = cellNetCtlAddHandler(&NetCtlCallback,this,&hid);
151
152 // Initialise RUDP
153#ifdef __PS3__
154 ret = cellRudpInit(NULL);
155#else
156 const int RUDP_POOL_SIZE = (500 * 1024); // TODO - find out what we need, this size is copied from library reference
157 uint8_t *rudp_pool = (uint8_t *)malloc(RUDP_POOL_SIZE);
158 ret = sceRudpInit(rudp_pool, RUDP_POOL_SIZE);
159#endif
160 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_RUDP_INIT ) )
161 {
162 SetState(SNM_INT_STATE_INITIALISE_FAILED);
163 return;
164 }
165
166#ifdef __PS3__
167 ret = cellRudpEnableInternalIOThread(RUDP_THREAD_STACK_SIZE, RUDP_THREAD_PRIORITY);
168
169 // Register for sys util callbacks... we need this for finding out what state the network dialog stuff has got to
170
171 ret = cellSysutilRegisterCallback( 0, SysUtilCallback, this );
172
173 // Kick off the Network Start Dialog Utility. This interacts with the user to make sure they are signed in & ready for network play.
174 struct CellNetCtlNetStartDialogParam netstart_param;
175
176 memset(&netstart_param, 0, sizeof(netstart_param));
177 netstart_param.size = sizeof(netstart_param);
178 netstart_param.type = CELL_NET_CTL_NETSTART_TYPE_NP;
179
180 SetState(SNM_INT_STATE_SIGNING_IN);
181 ret = cellNetCtlNetStartDialogLoadAsync(&netstart_param);
182 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_START_DIALOG ) )
183 {
184 SetState(SNM_INT_STATE_INITIALISE_FAILED);
185 return;
186 }
187 SonyHttp::init();
188#else
189 ret = sceRudpEnableInternalIOThread(RUDP_THREAD_STACK_SIZE, RUDP_THREAD_PRIORITY);
190#endif
191
192 ret = sceNpManagerRegisterCallback(ManagerCallback, this);
193 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_REGISTER_NP_CALLBACK ) )
194 {
195 SetState(SNM_INT_STATE_INITIALISE_FAILED);
196 return;
197 }
198
199 int status;
200 ret = sceNpManagerGetStatus( &status );
201 UpdateOnlineStatus(status);
202
203 // Already online? the callback won't catch this, so carry on initialising now
204 if( ( ret == 0 ) && ( status == SCE_NP_MANAGER_STATUS_ONLINE ) )
205 {
206 InitialiseAfterOnline();
207 }
208}
209
210void SQRNetworkManager_PS3::Terminate()
211{
212 // If playing, attempt to nicely leave the room before shutting down so that our friends won't still think this game is in progress
213 if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) ||
214 ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) ||
215 ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) ||
216 ( m_state == SNM_INT_STATE_PLAYING ) )
217 {
218 if( !m_offlineGame )
219 {
220 LeaveRoom(true);
221 int count = 200;
222 do
223 {
224 Tick();
225 Sleep(10);
226 count--;
227 } while( ( count > 0 ) && ( m_state != SNM_INT_STATE_IDLE ) );
228 app.DebugPrintf(CMinecraftApp::USER_RR,"Attempted to leave room, %dms used\n",count * 10);
229 }
230 }
231
232 int ret = cellRudpEnd();
233 ret = sceNpMatching2Term2();
234 // Terminate event thread by sending it a non-zero value for data1
235 sys_event_port_send(m_basicEventPort, 1, 0, 0);
236
237 do
238 {
239 Sleep(10);
240 } while( m_basicEventThread->isRunning() );
241}
242
243// 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
244// for Np Matching 2. Initialisation is finally complete when we get a callback to ContextCallback. The SQRNetworkManager_PS3 is then finally moved
245// into SNM_INT_STATE_IDLE at this stage.
246void SQRNetworkManager_PS3::InitialiseAfterOnline()
247{
248 SceNpId npId;
249 int option = 0;
250
251 // We should only be doing this if we have come in from an initialisation stage (SQRNetworkManager_PS3::Initialise) or we've had a network disconnect and are coming in from an offline state.
252 // 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.
253 // and could then really quickly go online so that the there becomes two paths (via the callback or SQRNetworkManager_PS3::Initialise) by which this could be called
254 if( ( m_state != SNM_INT_STATE_SIGNING_IN ) && !(( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) )
255 {
256 // 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
257 // 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.
258 if( s_SignInCompleteCallbackFn )
259 {
260 s_SignInCompleteCallbackFn(s_SignInCompleteParam,true,0);
261 s_SignInCompleteCallbackFn = NULL;
262 }
263 return;
264 }
265
266 // Initialize matching2 with default settings
267#ifdef __PS3__
268 int ret = 0;
269 if( !m_matching2initialised)
270 {
271 ret = sceNpMatching2Init2(0, 0, NULL);
272 }
273#else
274 SceNpMatching2InitializeParameter initParam;
275 memset(&initParam,0,sizeof(initParam));
276 initParam.size = sizeof(initParam);
277 int ret = sceNpMatching2Initialize(&initParam);
278#endif
279 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_MATCHING2_INIT ) )
280 {
281 SetState(SNM_INT_STATE_INITIALISE_FAILED);
282 return;
283 }
284 app.DebugPrintf("SQRNetworkManager::InitialiseAfterOnline - matching context is now valid\n");
285 m_matching2initialised = true;
286
287 // Get NP ID of the signed-in user
288 ret = sceNpManagerGetNpId(&npId);
289 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_GET_NPID ) )
290 {
291 SetState(SNM_INT_STATE_INITIALISE_FAILED);
292 return;
293 }
294
295 // Use Online Name and avatar to identify user
296 option = (SCE_NP_MATCHING2_CONTEXT_OPTION_USE_ONLINENAME | SCE_NP_MATCHING2_CONTEXT_OPTION_USE_AVATARURL);
297
298 // Create context
299 ret = sceNpMatching2CreateContext(
300 &npId, &s_npCommunicationId, &s_npCommunicationPassphrase, &m_matchingContext, option);
301 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT ) )
302 {
303 SetState(SNM_INT_STATE_INITIALISE_FAILED);
304 return;
305 }
306 app.DebugPrintf("SQRNetworkManager::InitialiseAfterOnline - matching context is now valid\n");
307 m_matchingContextValid = true;
308
309 bool bRet = RegisterCallbacks();
310 if( ( !bRet ) || ForceErrorPoint( SNM_FORCE_ERROR_REGISTER_CALLBACKS ) )
311 {
312 SetState(SNM_INT_STATE_INITIALISE_FAILED);
313 return;
314 }
315
316 // State should be starting context until the callback that this has been created happens
317 SetState(SNM_INT_STATE_STARTING_CONTEXT);
318
319 // Start the context
320 // Set time-out time to 10 seconds
321 ret = sceNpMatching2ContextStartAsync(m_matchingContext, (10*1000*1000));
322 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CONTEXT_START_ASYNC ) )
323 {
324 SetState(SNM_INT_STATE_INITIALISE_FAILED);
325 }
326}
327
328
329// General tick function to be called from main game loop - any internal tick functions should be called from here.
330void SQRNetworkManager_PS3::Tick()
331{
332 cellSysutilCheckCallback();
333 ServerContextTick();
334 RoomCreateTick();
335 SonyVoiceChat::tick();
336 FriendSearchTick();
337 TickRichPresence();
338
339 if( ( m_gameBootInvite ) && ( s_safeToRespondToGameBootInvite ) )
340 {
341 m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), m_gameBootInvite );
342 m_gameBootInvite = NULL;
343 }
344
345 ErrorHandlingTick();
346 // 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,
347 // 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.
348 if( m_resendExternalRoomDataCountdown )
349 {
350 if( m_state == SNM_INT_STATE_PLAYING )
351 {
352 m_resendExternalRoomDataCountdown--;
353 if( m_resendExternalRoomDataCountdown == 0 )
354 {
355 UpdateExternalRoomData();
356 }
357 }
358 else
359 {
360 m_resendExternalRoomDataCountdown = 0;
361 }
362 }
363
364 ProfileManager.SetNetworkStatus(GetOnlineStatus());
365
366 // 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
367 if( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS )
368 {
369 if( m_localPlayerJoined == m_localPlayerCount )
370 {
371 // 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
372 // at the point that we joined the game (either from search info, or an invitation).
373 UpdateRichPresenceCustomData(&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo));
374 SetState( SNM_INT_STATE_STARTING);
375 SetState( SNM_INT_STATE_PLAYING );
376 }
377 }
378
379 EnterCriticalSection(&m_csStateChangeQueue);
380 while(m_stateChangeQueue.size() > 0 )
381 {
382 if( m_listener )
383 {
384 m_listener->HandleStateChange(m_stateChangeQueue.front().m_oldState, m_stateChangeQueue.front().m_newState, m_stateChangeQueue.front().m_idleReasonIsSessionFull);
385 if( m_stateChangeQueue.front().m_newState == SNM_STATE_IDLE )
386 {
387 m_isInSession = false;
388 }
389 }
390 m_stateExternal = m_stateChangeQueue.front().m_newState;
391 m_stateChangeQueue.pop();
392 }
393 LeaveCriticalSection(&m_csStateChangeQueue);
394
395 // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it frmo the tick instead
396 if(m_bCallPSNSignInCallback)
397 {
398 m_bCallPSNSignInCallback=false;
399 if( s_signInCompleteCallbackIfFailed )
400 {
401 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0);
402 s_SignInCompleteCallbackFn = NULL;
403 }
404 else if(s_SignInCompleteCallbackFn)
405 {
406 s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, 0);
407 s_SignInCompleteCallbackFn = NULL;
408 }
409 }
410}
411
412// Detect any states which reflect internal error states, do anything required, and transition away again
413void SQRNetworkManager_PS3::ErrorHandlingTick()
414{
415 switch( m_state )
416 {
417 case SNM_INT_STATE_INITIALISE_FAILED:
418 if( s_SignInCompleteCallbackFn )
419 {
420 if( s_signInCompleteCallbackIfFailed )
421 {
422 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0);
423 }
424 s_SignInCompleteCallbackFn = NULL;
425 }
426 app.DebugPrintf("Network error: SNM_INT_STATE_INITIALISE_FAILED\n");
427 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
428 {
429 // 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
430 // 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
431 // 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)
432 // were happening.
433 SetState(SNM_INT_STATE_PLAYING);
434 }
435 else
436 {
437 m_offlineSQR = true;
438 SetState(SNM_INT_STATE_IDLE);
439 }
440 break;
441 case SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED:
442 app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED\n");
443 ResetToIdle();
444 break;
445 case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED:
446 app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED\n");
447 DeleteServerContext();
448 break;
449 case SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED:
450 app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED\n");
451 ResetToIdle();
452 break;
453 case SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED:
454 app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED\n");
455 DeleteServerContext();
456 break;
457 case SNM_INT_STATE_LEAVING_FAILED:
458 app.DebugPrintf("Network error: SNM_INT_STATE_LEAVING_FAILED\n");
459 DeleteServerContext();
460 break;
461 }
462
463}
464
465// Start hosting a game, by creating a room & joining it. We explicity create a server context here (via GetServerContext) as Sony suggest that
466// this means we have greater control of representing when players are actually "online". The creation of the room is carried out in a callback
467// after that server context is made (ServerContextValidCallback_CreateRoom).
468// 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.
469// extData and extDataSize define the initial state of room data that is externally visible (eg by players searching for rooms, but not in it)
470void SQRNetworkManager_PS3::CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline)
471{
472 // hostIndex should always be in the mask
473 assert( ( ( 1 << hostIndex ) & localPlayerMask ) != 0 );
474
475 m_isHosting = true;
476 m_joinExtData = extData;
477 m_joinExtDataSize = extDataSize;
478 m_offlineGame = offline;
479 m_resendExternalRoomDataCountdown = 0;
480 m_isInSession= true;
481
482 // Default value for room, which we can use for offline games
483 m_room = 0;
484
485 // Initialise room data that will be synchronised. Slot 0 is always reserved for the host. We don't know the
486 // room member until the room is actually created so this will be set/updated at that point
487 memset( &m_roomSyncData, 0, sizeof(m_roomSyncData) );
488 m_roomSyncData.setPlayerCount(1);
489 m_roomSyncData.players[0].m_smallId = m_currentSmallId++;
490 m_roomSyncData.players[0].m_localIdx = hostIndex;
491
492 // Remove the host player that we've already added, then add any other local players specified in the mask
493 localPlayerMask &= ~( ( 1 << hostIndex ) & localPlayerMask );
494 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ )
495 {
496 if( localPlayerMask & ( 1 << i ) )
497 {
498 m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_smallId = m_currentSmallId++;
499 m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_localIdx = i;
500 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1);
501 }
502 }
503 m_localPlayerCount = m_roomSyncData.getPlayerCount();
504
505 // 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)
506 if( m_offlineGame )
507 {
508 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS);
509 }
510 else
511 {
512 // Kick off the sequence of events required for an online game, starting with getting the server context
513 m_isInSession = GetServerContext();
514 }
515}
516
517// Updates the externally visible data that was associated with the room when it was created with CreateAndJoinRoom.
518void SQRNetworkManager_PS3::UpdateExternalRoomData()
519{
520 if( m_offlineGame ) return;
521 if( m_isHosting )
522 {
523 SceNpMatching2SetRoomDataExternalRequest reqParam;
524 memset( &reqParam, 0, sizeof(reqParam) );
525 reqParam.roomId = m_room;
526 SceNpMatching2BinAttr roomBinAttr;
527 memset(&roomBinAttr, 0, sizeof(roomBinAttr));
528 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID;
529 roomBinAttr.ptr = m_joinExtData;
530 roomBinAttr.size = m_joinExtDataSize;
531 reqParam.roomBinAttrExternalNum = 1;
532 reqParam.roomBinAttrExternal = &roomBinAttr;
533
534 int ret = sceNpMatching2SetRoomDataExternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId );
535 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SetRoomDataExternal returns 0x%x, number of players %d\n",ret,((char *)m_joinExtData)[174]);
536 if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA ) )
537 {
538 // 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,
539 // 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.
540 m_resendExternalRoomDataCountdown = 60;
541 }
542 }
543}
544
545// 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
546bool SQRNetworkManager_PS3::FriendRoomManagerIsBusy()
547{
548 return (m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE);
549}
550
551// 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
552// for any of the player's friends.
553bool SQRNetworkManager_PS3::FriendRoomManagerSearch()
554{
555 if( m_state != SNM_INT_STATE_IDLE ) return false;
556
557 // Don't start another search if we're already searching...
558 if( m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE )
559 {
560 return false;
561 }
562
563 // Free up any external data that we received from the previous search
564 for( int i = 0; i < m_searchResultCount; i++ )
565 {
566 free(m_aSearchResultRoomExtDataReceived[i]);
567 m_aSearchResultRoomExtDataReceived[i] = NULL;
568 }
569
570 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT;
571 m_friendCount = 0;
572 m_searchResultCount = 0;
573
574 // Get friend list - doing this in another thread as it can lock up for a few seconds
575 m_getFriendCountThread = new C4JThread(&GetFriendsThreadProc,this,"GetFriendsThreadProc");
576 m_getFriendCountThread->Run();
577
578 return true;
579}
580
581bool SQRNetworkManager_PS3::FriendRoomManagerSearch2()
582{
583 SceNpMatching2AttributeId attrId;
584 SceNpMatching2GetUserInfoListRequest reqParam;
585
586 if( m_friendCount == 0 )
587 {
588 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE;
589 return false;
590 }
591
592 if( m_searchResultCount )
593 {
594 // 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.
595 unordered_set<SceNpMatching2RoomId> uniqueRooms;
596 for( unsigned int i = 0; i < m_searchResultCount; i++ )
597 {
598 if(m_aSearchResultRoomFound[ i ])
599 {
600 uniqueRooms.insert( m_aSearchResultRoomId[ i ] );
601 }
602 }
603
604 // Tidy the results up further based on this
605 for( unsigned int i = 0; i < m_searchResultCount; )
606 {
607 if( uniqueRooms.find(m_aSearchResultRoomId[i]) == uniqueRooms.end() )
608 {
609 m_searchResultCount--;
610 free(m_aSearchResultRoomExtDataReceived[i]);
611 m_aSearchResultNpId[ i ] = m_aSearchResultNpId[ m_searchResultCount ];
612 m_aSearchResultRoomId[ i ] = m_aSearchResultRoomId[ m_searchResultCount ];
613 m_aSearchResultServerId[ i ] = m_aSearchResultServerId[ m_searchResultCount ];
614 m_aSearchResultRoomFound[ i ] = m_aSearchResultRoomFound[ m_searchResultCount ];
615 m_aSearchResultRoomExtDataReceived[ i ] = m_aSearchResultRoomExtDataReceived[ m_searchResultCount ];
616 }
617 else
618 {
619 uniqueRooms.erase(m_aSearchResultRoomId[i]);
620 i++;
621 }
622 }
623 }
624 else
625 {
626 // Done, with no results found
627 m_searchResultCount = 0;
628 }
629 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE;
630 return true;
631}
632
633void SQRNetworkManager_PS3::FriendSearchTick()
634{
635 // Move onto next state if we're done getting our friend count
636 if( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT )
637 {
638 if( !m_getFriendCountThread->isRunning() )
639 {
640 m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_INFO;
641 delete m_getFriendCountThread;
642 m_getFriendCountThread = NULL;
643 FriendRoomManagerSearch2();
644 }
645 }
646}
647
648// 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,
649// 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.
650int SQRNetworkManager_PS3::BasicEventThreadProc( void *lpParameter )
651{
652 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)lpParameter;
653
654 sys_event_t event;
655 do
656 {
657 sys_event_queue_receive(manager->m_basicEventQueue, &event, 0);
658
659 // 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
660 if( event.data1 == 0 )
661 {
662 int iEvent;
663 SceNpUserInfo from;
664 uint8_t buffer[SCE_NP_BASIC_MAX_MESSAGE_SIZE];
665 size_t bufferSize = SCE_NP_BASIC_MAX_MESSAGE_SIZE;
666 int ret = sceNpBasicGetEvent(&iEvent, &from, &buffer, &bufferSize);
667 if( ret == 0 )
668 {
669 if( iEvent == SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION )
670 {
671 // 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
672 //sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID);
673 }
674 if( iEvent == SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT )
675 {
676 SceNpBasicExtendedAttachmentData *result = (SceNpBasicExtendedAttachmentData *)buffer;
677 if(result->userAction == SCE_NP_BASIC_MESSAGE_ACTION_ACCEPT )
678 {
679 manager->GetInviteDataAndProcess(result->data.id);
680 }
681 }
682 app.DebugPrintf("Incoming basic event of type %d\n",iEvent);
683 }
684 }
685
686 } while(event.data1 == 0 );
687 return 0;
688}
689
690int SQRNetworkManager_PS3::GetFriendsThreadProc( void* lpParameter )
691{
692 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)lpParameter;
693
694 int ret = sceNpBasicGetFriendListEntryCount(&manager->m_friendCount);
695 if( ( ret < 0 ) || manager->ForceErrorPoint( SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY_COUNT ) )
696 {
697 // 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
698 manager->m_friendCount = 0;
699 }
700
701
702 // There shouldn't ever be more than 100 friends returned but limit here just in case
703 if( manager->m_friendCount > 100 ) manager->m_friendCount = 100;
704
705 // 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
706 for( unsigned int i = 0; i < manager->m_friendCount; i++ )
707 {
708 static SceNpBasicPresenceDetails2 presenceDetails;
709 static SceNpUserInfo userInfo;
710
711 memset(&userInfo, 0x00, sizeof(userInfo));
712 memset(&presenceDetails, 0x00, sizeof(presenceDetails));
713 presenceDetails.struct_size = sizeof(presenceDetails);
714
715 int ret = sceNpBasicGetFriendPresenceByIndex2( i, &userInfo, &presenceDetails, 0 );
716
717 if( ( ret == 0 ) && ( !manager->ForceErrorPoint( SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY ) ) )
718 {
719 memcpy(&manager->m_aSearchResultNpId[ manager->m_searchResultCount ], &userInfo.userId, sizeof(SceNpId));
720 manager->m_aSearchResultRoomFound[ manager->m_searchResultCount ] = false;
721
722 // 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)
723 if( presenceDetails.size == sizeof(PresenceSyncInfo) )
724 {
725 PresenceSyncInfo *pso = (PresenceSyncInfo *)presenceDetails.data;
726 if( pso->netVersion == MINECRAFT_NET_VERSION )
727 {
728 if( !pso->inviteOnly )
729 {
730 manager->m_aSearchResultRoomFound[manager->m_searchResultCount] = true;
731 manager->m_aSearchResultRoomId[manager->m_searchResultCount] = pso->m_RoomId;
732 manager->m_aSearchResultServerId[manager->m_searchResultCount] = pso->m_ServerId;
733
734 CPlatformNetworkManagerSony::MallocAndSetExtDataFromSQRPresenceInfo(&manager->m_aSearchResultRoomExtDataReceived[manager->m_searchResultCount], pso);
735 manager->m_searchResultCount++;
736 }
737 }
738 }
739 }
740 }
741 return 0;
742}
743
744// Get count of rooms that friends are playing in. Only valid when FriendRoomManagerIsBusy() returns false
745int SQRNetworkManager_PS3::FriendRoomManagerGetCount()
746{
747 assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE );
748 return m_searchResultCount;
749}
750
751// Get details of a found session that a friend is playing in. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false
752void SQRNetworkManager_PS3::FriendRoomManagerGetRoomInfo(int idx, SQRNetworkManager_PS3::SessionSearchResult *searchResult)
753{
754 assert( idx < m_searchResultCount );
755 assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE );
756
757 searchResult->m_NpId = m_aSearchResultNpId[idx];
758 searchResult->m_sessionId.m_RoomId = m_aSearchResultRoomId[idx];
759 searchResult->m_sessionId.m_ServerId = m_aSearchResultServerId[idx];
760 searchResult->m_extData = m_aSearchResultRoomExtDataReceived[idx];
761}
762
763// Get overall state of the network manager.
764SQRNetworkManager_PS3::eSQRNetworkManagerState SQRNetworkManager_PS3::GetState()
765{
766 return m_stateExternal;;
767}
768
769bool SQRNetworkManager_PS3::IsHost()
770{
771 return m_isHosting;
772}
773
774bool SQRNetworkManager_PS3::IsReadyToPlayOrIdle()
775{
776 return (( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || ( m_state == SNM_INT_STATE_PLAYING ) || ( m_state == SNM_INT_STATE_IDLE ) );
777}
778
779
780// 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
781// 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
782// it also requires that it is informed of the state changes leading up to not being in the session, before this should report false.
783bool SQRNetworkManager_PS3::IsInSession()
784{
785 return m_isInSession;
786}
787
788// Get count of players currently in the session
789int SQRNetworkManager_PS3::GetPlayerCount()
790{
791 return m_roomSyncData.getPlayerCount();
792}
793
794// Get count of players who are in the session, but not local to this machine
795int SQRNetworkManager_PS3::GetOnlinePlayerCount()
796{
797 int onlineCount = 0;
798 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
799 {
800 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId )
801 {
802 onlineCount++;
803 }
804 }
805 return onlineCount;
806}
807
808SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerByIndex(int idx)
809{
810 if( idx < MAX_ONLINE_PLAYER_COUNT )
811 {
812 return GetPlayerIfReady(m_aRoomSlotPlayers[idx]);
813 }
814 else
815 {
816 return NULL;
817 }
818}
819
820SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerBySmallId(int idx)
821{
822 EnterCriticalSection(&m_csRoomSyncData);
823 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
824 {
825 if( m_roomSyncData.players[i].m_smallId == idx )
826 {
827 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]);
828 LeaveCriticalSection(&m_csRoomSyncData);
829 return player;
830 }
831 }
832 LeaveCriticalSection(&m_csRoomSyncData);
833 return NULL;
834}
835
836SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerByXuid(PlayerUID xuid)
837{
838 EnterCriticalSection(&m_csRoomSyncData);
839 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
840 {
841 if( m_roomSyncData.players[i].m_UID == xuid )
842 {
843 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]);
844 LeaveCriticalSection(&m_csRoomSyncData);
845 return player;
846 }
847 }
848 LeaveCriticalSection(&m_csRoomSyncData);
849 return NULL;
850}
851
852SQRNetworkPlayer *SQRNetworkManager_PS3::GetLocalPlayerByUserIndex(int idx)
853{
854 EnterCriticalSection(&m_csRoomSyncData);
855 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
856 {
857 if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && ( m_roomSyncData.players[i].m_localIdx == idx ) )
858 {
859 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]);
860 LeaveCriticalSection(&m_csRoomSyncData);
861 return player;
862 }
863 }
864 LeaveCriticalSection(&m_csRoomSyncData);
865 return NULL;
866}
867
868SQRNetworkPlayer *SQRNetworkManager_PS3::GetHostPlayer()
869{
870 EnterCriticalSection(&m_csRoomSyncData);
871 SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[0]);
872 LeaveCriticalSection(&m_csRoomSyncData);
873 return player;
874}
875
876SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerIfReady(SQRNetworkPlayer *player)
877{
878 if( player == NULL ) return NULL;
879
880 if( player->IsReady() ) return player;
881
882 return NULL;
883}
884
885// Update state internally
886void SQRNetworkManager_PS3::SetState(SQRNetworkManager_PS3::eSQRNetworkManagerInternalState state)
887{
888 eSQRNetworkManagerState oldState = m_INTtoEXTStateMappings[m_state];
889 eSQRNetworkManagerState newState = m_INTtoEXTStateMappings[state];
890 bool setIdleReasonSessionFull = false;
891 if( ( state == SNM_INT_STATE_IDLE ) && m_nextIdleReasonIsFull )
892 {
893 setIdleReasonSessionFull = true;
894 m_nextIdleReasonIsFull = false;
895 }
896 m_state = state;
897 // 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
898 // 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.
899 if( ( newState != oldState ) || setIdleReasonSessionFull )
900 {
901 EnterCriticalSection(&m_csStateChangeQueue);
902 m_stateChangeQueue.push(StateChangeInfo(oldState,newState,setIdleReasonSessionFull));
903 LeaveCriticalSection(&m_csStateChangeQueue);
904 }
905}
906
907void SQRNetworkManager_PS3::ResetToIdle()
908{
909 // If we're the client, remove any networked players properly ( this will destory their rupd context etc.)
910 if( !m_isHosting )
911 {
912 RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1);
913 }
914 m_serverContextValid = false;
915 m_isHosting = false;
916 m_currentSmallId = 0;
917 EnterCriticalSection(&m_csRoomSyncData);
918 for(int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
919 {
920 delete m_aRoomSlotPlayers[i];
921 }
922 memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) );
923 memset( &m_roomSyncData,0,sizeof(m_roomSyncData));
924 LeaveCriticalSection(&m_csRoomSyncData);
925 SetState(SNM_INT_STATE_IDLE);
926}
927
928// Join a room that was found with FriendRoomManagerSearch. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false
929bool SQRNetworkManager_PS3::JoinRoom(SQRNetworkManager_PS3::SessionSearchResult *searchResult, int localPlayerMask)
930{
931 // Set up the presence info we would like to synchronise out when we have fully joined the game
932 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, searchResult->m_extData, searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId);
933 return JoinRoom(searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId, localPlayerMask, NULL);
934}
935
936// Join room with a specified roomId. This is used when joining from an invite, as well as by the previous method
937bool SQRNetworkManager_PS3::JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_PS3::PresenceSyncInfo *presence)
938{
939 // 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.
940 if( presence )
941 {
942 memcpy( &s_lastPresenceSyncInfo, presence, sizeof(PresenceSyncInfo) );
943 }
944
945 m_isInSession = true;
946
947 m_isHosting = false;
948 m_offlineGame = false;
949 m_roomToJoin = roomId;
950 m_localPlayerJoinMask = localPlayerMask;
951 m_localPlayerCount = 0;
952 m_localPlayerJoined = 0;
953
954 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ )
955 {
956 if( localPlayerMask & ( 1 << i ) ) m_localPlayerCount++;
957 }
958
959 return GetServerContext( serverId );
960}
961
962void SQRNetworkManager_PS3::StartGame()
963{
964 assert( ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || (( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) );
965
966 SetState( SNM_INT_STATE_STARTING);
967 SetState( SNM_INT_STATE_PLAYING);
968}
969
970void SQRNetworkManager_PS3::LeaveRoom(bool bActuallyLeaveRoom)
971{
972 if( m_offlineGame )
973 {
974 if( m_state != SNM_INT_STATE_PLAYING ) return;
975
976 SetState(SNM_INT_STATE_LEAVING);
977 SetState(SNM_INT_STATE_ENDING);
978 ResetToIdle();
979 return;
980 }
981
982 UpdateRichPresenceCustomData(& c_presenceSyncInfoNULL, sizeof(PresenceSyncInfo) );
983
984 SonyVoiceChat::shutdown();
985
986 // Attempt to leave the room if we are in any of the states we could be in if we have successfully created it
987 if( bActuallyLeaveRoom )
988 {
989 if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) ||
990 ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) ||
991 ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) ||
992 ( m_state == SNM_INT_STATE_PLAYING ) )
993 {
994 SceNpMatching2LeaveRoomRequest reqParam;
995 memset( &reqParam, 0, sizeof(reqParam) );
996 reqParam.roomId = m_room;
997
998 SetState(SNM_INT_STATE_LEAVING);
999 int ret = sceNpMatching2LeaveRoom( m_matchingContext, &reqParam, NULL, &m_leaveRoomRequestId );
1000 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_LEAVE_ROOM) )
1001 {
1002 SetState(SNM_INT_STATE_LEAVING_FAILED);
1003 }
1004 }
1005 else if ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM )
1006 {
1007 // Haven't created the room yet, but will have created the server context so need to recover from that
1008 DeleteServerContext();
1009 }
1010 else
1011 {
1012 SetState(SNM_INT_STATE_IDLE);
1013 }
1014 }
1015 else
1016 {
1017 // 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
1018 // no need to leave it again since it doesn't exist anymore. Still need to destroy server context which may be valid
1019 DeleteServerContext();
1020 }
1021}
1022
1023void SQRNetworkManager_PS3::EndGame()
1024{
1025}
1026
1027bool SQRNetworkManager_PS3::SessionHasSpace(int spaceRequired)
1028{
1029 return( ( m_roomSyncData.getPlayerCount() + spaceRequired ) <= MAX_ONLINE_PLAYER_COUNT );
1030}
1031
1032bool SQRNetworkManager_PS3::AddLocalPlayerByUserIndex(int idx)
1033{
1034 if( m_isHosting )
1035 {
1036 if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false;
1037
1038 // 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
1039 int insertAtIdx = m_roomSyncData.getPlayerCount();
1040 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1041 {
1042 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId )
1043 {
1044 insertAtIdx = i;
1045 break;
1046 }
1047 else
1048 {
1049 // Don't add the same local index twice
1050 if( m_roomSyncData.players[i].m_localIdx == idx )
1051 {
1052 return false;
1053 }
1054 }
1055 }
1056
1057 // Make room for a new entry...
1058 for( int i = m_roomSyncData.getPlayerCount(); i > insertAtIdx; i-- )
1059 {
1060 m_roomSyncData.players[i] = m_roomSyncData.players[i-1];
1061 }
1062 m_roomSyncData.players[insertAtIdx].m_localIdx = idx;
1063 m_roomSyncData.players[insertAtIdx].m_roomMemberId = m_localMemberId;
1064 m_roomSyncData.players[insertAtIdx].m_smallId = m_currentSmallId++;
1065
1066 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1);
1067
1068 // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers.
1069 // This will also create the required new SQRNetworkPlayer and do all the callbacks that requires etc.
1070 MapRoomSlotPlayers();
1071
1072 // Sync this back out to our networked clients...
1073 SyncRoomData();
1074 return true;
1075 }
1076 else
1077 {
1078 // Don't attempt to join if our client's view of the players indicates that there aren't any free slots
1079 if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false;
1080
1081 // Add the requested player to the mask of local players currently in the game, and update this data - this
1082 // will also then resync with the server which can respond appropriately
1083 int mask = 1 << idx;
1084 if( m_localPlayerJoinMask & mask ) return false;
1085
1086 m_localPlayerJoinMask |= mask;
1087
1088 SceNpMatching2SetRoomMemberDataInternalRequest reqParam;
1089 SceNpMatching2BinAttr binAttr;
1090
1091 memset(&reqParam, 0, sizeof(reqParam));
1092 memset(&binAttr, 0, sizeof(binAttr));
1093
1094 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID;
1095 binAttr.ptr = &m_localPlayerJoinMask;
1096 binAttr.size = sizeof(m_localPlayerJoinMask);
1097
1098 reqParam.roomId = m_room;
1099 reqParam.memberId = m_localMemberId;
1100 reqParam.roomMemberBinAttrInternalNum = 1;
1101 reqParam.roomMemberBinAttrInternal = &binAttr;
1102
1103 int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId );
1104
1105 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL) )
1106 {
1107 return false;
1108 }
1109
1110 // 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.
1111 bool rudpOk = CreateRudpConnections(m_room, m_roomSyncData.players[0].m_roomMemberId, mask, m_localMemberId );
1112
1113 if( rudpOk )
1114 {
1115 return true;
1116 }
1117 else
1118 {
1119 m_localPlayerJoinMask &= (~mask);
1120 return false;
1121 }
1122 }
1123}
1124
1125bool SQRNetworkManager_PS3::RemoveLocalPlayerByUserIndex(int idx)
1126{
1127 if( m_isHosting )
1128 {
1129 EnterCriticalSection(&m_csRoomSyncData);
1130
1131 int roomSlotPlayerCount = m_roomSyncData.getPlayerCount();
1132
1133 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1134 {
1135 if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) &&
1136 ( m_roomSyncData.players[i].m_localIdx == idx ) )
1137 {
1138 // Shuffle all remaining entries up...
1139 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1);
1140 for( int j = i; j < m_roomSyncData.getPlayerCount(); j++ )
1141 {
1142 m_roomSyncData.players[j] = m_roomSyncData.players[j+1];
1143 }
1144
1145 // Zero last element that isn't part of the currently sized array anymore
1146 memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData));
1147
1148 // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers.
1149 // This will also delete the SQRNetworkPlayer and do all the callbacks that requires etc.
1150 MapRoomSlotPlayers(roomSlotPlayerCount);
1151 m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL;
1152
1153 // Sync this back out to our networked clients...
1154 SyncRoomData();
1155
1156 LeaveCriticalSection(&m_csRoomSyncData);
1157 return true;
1158 }
1159 }
1160 LeaveCriticalSection(&m_csRoomSyncData);
1161 return false;
1162 }
1163 else
1164 {
1165 // Remove the requested player from the mask of local players currently in the game, and update this data - this
1166 // will also then resync with the server which can respond appropriately
1167 int mask = 1 << idx;
1168 if( ( m_localPlayerJoinMask & mask ) == 0 ) return false;
1169
1170 m_localPlayerJoinMask &= ~mask;
1171
1172 SceNpMatching2SetRoomMemberDataInternalRequest reqParam;
1173 SceNpMatching2BinAttr binAttr;
1174
1175 memset(&reqParam, 0, sizeof(reqParam));
1176 memset(&binAttr, 0, sizeof(binAttr));
1177
1178 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID;
1179 binAttr.ptr = &m_localPlayerJoinMask;
1180 binAttr.size = sizeof(m_localPlayerJoinMask);
1181
1182 reqParam.roomId = m_room;
1183 reqParam.memberId = m_localMemberId;
1184 reqParam.roomMemberBinAttrInternalNum = 1;
1185 reqParam.roomMemberBinAttrInternal = &binAttr;
1186
1187 int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId );
1188
1189 if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2) )
1190 {
1191 return false;
1192 }
1193
1194 RemoveNetworkPlayers( mask );
1195
1196 return true;
1197 }
1198}
1199
1200// 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
1201void SQRNetworkManager_PS3::SendInviteGUI()
1202{
1203 SceNpBasicMessageDetails msg;
1204
1205 //Set invitation information - this is now exactly the same as the presence information that we synchronise out.
1206
1207 // 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,
1208 // then we'll need to set this up now from the external dasta.
1209 if( m_isHosting )
1210 {
1211 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, m_joinExtData, m_room, m_serverId);
1212 }
1213
1214 memset(&msg, 0, sizeof(msg));
1215 msg.msgId = 0;
1216 msg.mainType = SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE;
1217 msg.subType = SCE_NP_BASIC_MESSAGE_INVITE_SUBTYPE_ACTION_ACCEPT;
1218 msg.msgFeatures = SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE;
1219 msg.npids = NULL;
1220 msg.count = 0;
1221
1222 uint8_t *subject = mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_SUBJECT_MAX_18_CHARS);
1223 uint8_t *body = mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_BODY);
1224 msg.subject = (const char *)subject;
1225 msg.body = (const char *)body;
1226 msg.data = &s_lastPresenceSyncInfo;
1227 msg.size = sizeof(PresenceSyncInfo);
1228
1229 int ret = sceNpBasicSendMessageGui(&msg, SYS_MEMORY_CONTAINER_ID_INVALID);
1230 free(subject);
1231 free(body);
1232 if (ret < 0)
1233 {
1234 app.DebugPrintf("Failed %08x\n", ret);
1235 //E Error handling
1236 }
1237}
1238
1239// 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
1240// qnet equivalent of this seems to just assume that the data persists forever.
1241void SQRNetworkManager_PS3::GetInviteDataAndProcess(SceNpBasicAttachmentDataId id)
1242{
1243 INVITE_INFO *invite = &m_inviteReceived[m_inviteIndex];
1244 m_inviteIndex = ( m_inviteIndex + 1 ) % MAX_SIMULTANEOUS_INVITES;
1245 size_t dataSize = sizeof(INVITE_INFO);
1246 int ret = sceNpBasicRecvMessageAttachmentLoad(id, invite, &dataSize);
1247
1248 // If we fail ( which we might do if we aren't online at this point) then zero the invite information and we'll try and get it later after (possibly) signing in
1249 if( ret != 0 )
1250 {
1251 memset(invite, 0, sizeof( INVITE_INFO ) );
1252 s_lastInviteIdToRetry = id;
1253 }
1254
1255 m_gameBootInvite = invite;
1256}
1257
1258bool SQRNetworkManager_PS3::UpdateInviteData(SQRNetworkManager_PS3::PresenceSyncInfo *invite)
1259{
1260 size_t dataSize = sizeof(SQRNetworkManager_PS3::PresenceSyncInfo);
1261 int ret = sceNpBasicRecvMessageAttachmentLoad(s_lastInviteIdToRetry, invite, &dataSize);
1262 return (ret == 0);
1263}
1264
1265// This method is a helper used in MapRoomSlotPlayers - tries to find a player that matches:
1266// (1) the playerType
1267// (2) if playerType is remote, memberId
1268// (3) localPlayerIdx
1269// 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),
1270// and there's a period when starting up the host game where it doesn't accurately know the memberId for its own local players
1271void SQRNetworkManager_PS3::FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId)
1272{
1273 for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ )
1274 {
1275 if( ((*it)->m_type == playerType ) && ( (*it)->m_localPlayerIdx == localPlayerIdx ) )
1276 {
1277 if( ( playerType != SQRNetworkPlayer::SNP_TYPE_REMOTE ) || ( (*it)->m_roomMemberId == memberId ) )
1278 {
1279 SQRNetworkPlayer *player = *it;
1280 m_vecTempPlayers.erase(it);
1281 m_aRoomSlotPlayers[ slot ] = player;
1282 return;
1283 }
1284 }
1285 }
1286 // 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
1287 PlayerUID *pUID = NULL;
1288 PlayerUID localUID;
1289 if( ( playerType == SQRNetworkPlayer::SNP_TYPE_LOCAL ) ||
1290 m_isHosting && ( playerType == SQRNetworkPlayer::SNP_TYPE_HOST ) )
1291 {
1292 // Local players can establish their UID at this point
1293 ProfileManager.GetXUID(localPlayerIdx,&localUID,true);
1294 pUID = &localUID;
1295 }
1296 SQRNetworkPlayer *player = new SQRNetworkPlayer(this, (SQRNetworkPlayer::eSQRNetworkPlayerType)playerType, m_isHosting, memberId, localPlayerIdx, 0, pUID );
1297 // For offline games, set name directly from gamertag as the PlayerUID will be full of zeroes.
1298 if( m_offlineGame )
1299 {
1300 player->SetName(ProfileManager.GetGamertag(localPlayerIdx));
1301 }
1302 NonNetworkPlayerComplete( player, smallId);
1303 m_aRoomSlotPlayers[ slot ] = player;
1304 HandlePlayerJoined( player );
1305}
1306
1307// For data sending on the local machine, used to send between host and localplayers on the host
1308void SQRNetworkManager_PS3::LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize)
1309{
1310 assert(m_isHosting);
1311 if(m_listener)
1312 {
1313 m_listener->HandleDataReceived( playerFrom, playerTo, (unsigned char *)data, dataSize );
1314 }
1315}
1316
1317int SQRNetworkManager_PS3::GetSessionIndex(SQRNetworkPlayer *player)
1318{
1319 int roomSlotPlayerCount = m_roomSyncData.getPlayerCount();
1320 for( int i = 0; i < roomSlotPlayerCount; i++ )
1321 {
1322 if( m_aRoomSlotPlayers[i] == player ) return i;
1323 }
1324 return 0;
1325}
1326
1327// 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
1328void SQRNetworkManager_PS3::MapRoomSlotPlayers(int roomSlotPlayerCount/*=-1*/)
1329{
1330 EnterCriticalSection(&m_csRoomSyncData);
1331
1332 // 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.
1333 bool zeroLastSlot = false;
1334 if( roomSlotPlayerCount == -1 )
1335 {
1336 roomSlotPlayerCount = m_roomSyncData.getPlayerCount();
1337 }
1338 else
1339 {
1340 zeroLastSlot = true;
1341 }
1342
1343 if( m_isHosting )
1344 {
1345 for( int i = 0; i < roomSlotPlayerCount; i++ )
1346 {
1347 if( m_aRoomSlotPlayers[i] )
1348 {
1349 // 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.
1350 // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer
1351 if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_REMOTE )
1352 {
1353 m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]);
1354 m_aRoomSlotPlayers[i] = NULL;
1355 }
1356 }
1357 }
1358 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1359 {
1360 if( i == 0 )
1361 {
1362 // Special case - slot 0 is always the host
1363 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId);
1364 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData
1365 }
1366 else
1367 {
1368 if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId )
1369 {
1370 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_LOCAL, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId);
1371 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData
1372 }
1373 else
1374 {
1375 m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx );
1376 // If we're the host, then we allocated the small id so can flag now if we've got a player to flag...
1377 if( m_aRoomSlotPlayers[i] )
1378 {
1379 NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId);
1380 }
1381 }
1382 }
1383 }
1384
1385 if( zeroLastSlot )
1386 {
1387 if( roomSlotPlayerCount )
1388 {
1389 m_aRoomSlotPlayers[ roomSlotPlayerCount - 1 ] = 0;
1390 }
1391 }
1392
1393 // Also update the externally visible room data for the current slots
1394 if (m_listener )
1395 {
1396 m_listener->HandleResyncPlayerRequest(m_aRoomSlotPlayers);
1397 }
1398 }
1399 else
1400 {
1401 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1402 {
1403 if( m_aRoomSlotPlayers[i] )
1404 {
1405 // 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.
1406 // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer
1407 if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_LOCAL )
1408 {
1409 m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]);
1410 m_aRoomSlotPlayers[i] = NULL;
1411 }
1412 }
1413 }
1414 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1415 {
1416 if( i == 0 )
1417 {
1418 // Special case - slot 0 is always the host
1419 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId);
1420 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data
1421 }
1422 else
1423 {
1424 if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId )
1425 {
1426 // 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
1427 m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx );
1428 // If we've got the room sync data back from the server, then we've got our smallId. Set flag for this.
1429 if( m_aRoomSlotPlayers[i] )
1430 {
1431 NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId);
1432 }
1433 }
1434 else
1435 {
1436 FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_REMOTE, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId);
1437 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data
1438 }
1439 }
1440 }
1441 }
1442 // 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
1443 // 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
1444 for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ )
1445 {
1446 if( m_listener )
1447 {
1448 m_listener->HandlePlayerLeaving(*it);
1449 }
1450 delete (*it);
1451 }
1452 m_vecTempPlayers.clear();
1453
1454 LeaveCriticalSection(&m_csRoomSyncData);
1455}
1456
1457// On host, update the room sync data with UIDs that are in the players
1458void SQRNetworkManager_PS3::UpdateRoomSyncUIDsFromPlayers()
1459{
1460 EnterCriticalSection(&m_csRoomSyncData);
1461 if( m_isHosting )
1462 {
1463 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1464 {
1465 if( m_aRoomSlotPlayers[i] )
1466 {
1467 m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID();
1468 }
1469 }
1470 }
1471
1472 LeaveCriticalSection(&m_csRoomSyncData);
1473}
1474
1475// On the client, move UIDs from the room sync data out to the players.
1476void SQRNetworkManager_PS3::UpdatePlayersFromRoomSyncUIDs()
1477{
1478 EnterCriticalSection(&m_csRoomSyncData);
1479 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1480 {
1481 if( m_aRoomSlotPlayers[i] )
1482 {
1483 if( i == 0 )
1484 {
1485 // Special case - slot 0 is always the host
1486 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID);
1487 }
1488 else
1489 {
1490 // Don't sync local players as we already set those up with their UID in the first place...
1491 if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId )
1492 {
1493 m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID);
1494 }
1495 }
1496 }
1497 }
1498 LeaveCriticalSection(&m_csRoomSyncData);
1499}
1500
1501// Host only - add remote players to our internal storage of player slots, and synchronise this with other room members.
1502bool SQRNetworkManager_PS3::AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull/*==NULL*/ )
1503{
1504 assert( m_isHosting );
1505
1506 EnterCriticalSection(&m_csRoomSyncData);
1507
1508 // Establish whether we have enough room to add the players
1509 int addCount = 0;
1510 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
1511 {
1512 if( playerMask & ( 1 << i ) )
1513 {
1514 addCount++;
1515 }
1516 }
1517
1518 if( ( m_roomSyncData.getPlayerCount() + addCount ) > MAX_ONLINE_PLAYER_COUNT )
1519 {
1520 if( isFull )
1521 {
1522 *isFull = true;
1523 }
1524 LeaveCriticalSection(&m_csRoomSyncData);
1525 return false;
1526 }
1527
1528 // We want to keep all players from a particular machine together, so search through the room sync data to see if we can find
1529 // any pre-existing players from this machine.
1530 int firstIdx = -1;
1531 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
1532 {
1533 if( m_roomSyncData.players[i].m_roomMemberId == memberId )
1534 {
1535 firstIdx = i;
1536 break;
1537 }
1538 }
1539
1540 // 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
1541 // players.
1542 int insertIdx = m_roomSyncData.getPlayerCount();
1543 if( firstIdx > -1 )
1544 {
1545 for( int i = firstIdx; i < m_roomSyncData.getPlayerCount(); i++ )
1546 {
1547 if( m_roomSyncData.players[i].m_roomMemberId != memberId )
1548 {
1549 insertIdx = i;
1550 break;
1551 }
1552 }
1553 }
1554
1555 // Add all remote players determined from the player mask to our own slots of active players
1556 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ )
1557 {
1558 if( playerMask & ( 1 << i ) )
1559 {
1560 // Shift any following players along...
1561 for( int j = m_roomSyncData.getPlayerCount(); j > insertIdx; j-- )
1562 {
1563 m_roomSyncData.players[j] = m_roomSyncData.players[j-1];
1564 }
1565 PlayerSyncData *player = &m_roomSyncData.players[ insertIdx ];
1566 player->m_smallId = m_currentSmallId++;
1567 player->m_roomMemberId = memberId;
1568 player->m_localIdx = i;
1569 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1);
1570 insertIdx++;
1571 }
1572 }
1573
1574 // Update mapping from the room slot players to SQRNetworkPlayer instances
1575 MapRoomSlotPlayers();
1576
1577 // And then synchronise this out to all other machines
1578 SyncRoomData();
1579
1580 LeaveCriticalSection(&m_csRoomSyncData);
1581
1582 SonyVoiceChat::init(this);
1583
1584 return true;
1585}
1586
1587// Host only - remove all remote players belonging to the supplied memberId, and in the supplied mask, and synchronise this with other room members
1588void SQRNetworkManager_PS3::RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask )
1589{
1590 assert( m_isHosting );
1591 EnterCriticalSection(&m_csRoomSyncData);
1592
1593 // Remove any applicable players, keeping remaining players in order
1594 for( int i = 0; i < m_roomSyncData.getPlayerCount(); )
1595 {
1596 if( ( m_roomSyncData.players[ i ].m_roomMemberId == memberId ) && ( ( 1 << m_roomSyncData.players[ i ].m_localIdx ) & mask ) )
1597 {
1598 SQRNetworkPlayer *player = GetPlayerFromRoomMemberAndLocalIdx( memberId, m_roomSyncData.players[ i ].m_localIdx );
1599 if( player )
1600 {
1601 // Get Rudp context for this player, close that context down ( which will in turn close the socket if required)
1602 int ctx = player->m_rudpCtx;
1603 cellRudpTerminate( ctx );
1604 if( m_listener )
1605 {
1606 m_listener->HandlePlayerLeaving(player);
1607 }
1608 // Delete the player itself and the mapping from context to player map as this context is no longer valid
1609 delete player;
1610 m_RudpCtxToPlayerMap.erase(ctx);
1611 }
1612 m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1);
1613 // Shuffled entries up into the space that we have just created
1614 for( int j = i ; j < m_roomSyncData.getPlayerCount(); j++ )
1615 {
1616 m_roomSyncData.players[j] = m_roomSyncData.players[j + 1];
1617 m_aRoomSlotPlayers[j] = m_aRoomSlotPlayers[j + 1];
1618 }
1619 // Zero last element, that isn't part of the currently sized array anymore
1620 memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData));
1621 m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL;
1622 }
1623 else
1624 {
1625 i++;
1626 }
1627 }
1628 LeaveCriticalSection(&m_csRoomSyncData);
1629
1630 // Update mapping from the room slot players to SQRNetworkPlayer instances
1631 MapRoomSlotPlayers();
1632
1633
1634 // And then synchronise this out to all other machines
1635 SyncRoomData();
1636
1637 if(GetOnlinePlayerCount() == 0)
1638 SonyVoiceChat::shutdown();
1639}
1640
1641// Client only - remove all network players matching the supplied mask
1642void SQRNetworkManager_PS3::RemoveNetworkPlayers( int mask )
1643{
1644 assert( !m_isHosting );
1645
1646 for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); )
1647 {
1648 SQRNetworkPlayer *player = it->second;
1649 if( (player->m_roomMemberId == m_localMemberId ) && ( ( 1 << player->m_localPlayerIdx ) & mask ) )
1650 {
1651 // Get Rudp context for this player, close that context down ( which will in turn close the socket if required)
1652 int ctx = it->first;
1653 cellRudpTerminate( ctx );
1654 if( m_listener )
1655 {
1656 m_listener->HandlePlayerLeaving(player);
1657 }
1658 // Delete any reference to this player from the player mappings
1659 for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ )
1660 {
1661 if( m_aRoomSlotPlayers[i] == player )
1662 {
1663 m_aRoomSlotPlayers[i] = NULL;
1664 }
1665 }
1666 // And delete the reference from the ctx->player map
1667 it = m_RudpCtxToPlayerMap.erase(it);
1668
1669 // Delete the player itself and the mapping from context to player map as this context is no longer valid
1670 delete player;
1671 }
1672 else
1673 {
1674 it++;
1675 }
1676 }
1677
1678}
1679
1680// Host only - update the memberId of the local players, and synchronise with other room members
1681void SQRNetworkManager_PS3::SetLocalPlayersAndSync()
1682{
1683 assert( m_isHosting );
1684 for( int i = 0; i < m_localPlayerCount; i++ )
1685 {
1686 m_roomSyncData.players[i].m_roomMemberId = m_localMemberId;
1687 }
1688
1689 // Update mapping from the room slot players to SQRNetworkPlayer instances
1690 MapRoomSlotPlayers();
1691
1692 // And then synchronise this out to all other machines
1693 SyncRoomData();
1694
1695}
1696
1697// Host only - sync the room data with other machines
1698void SQRNetworkManager_PS3::SyncRoomData()
1699{
1700 if( m_offlineGame ) return;
1701
1702 UpdateRoomSyncUIDsFromPlayers();
1703
1704 SceNpMatching2SetRoomDataInternalRequest reqParam;
1705 memset( &reqParam, 0, sizeof(reqParam) );
1706 reqParam.roomId = m_room;
1707 SceNpMatching2BinAttr roomBinAttr;
1708 memset(&roomBinAttr, 0, sizeof(roomBinAttr));
1709 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID;
1710 roomBinAttr.ptr = &m_roomSyncData;
1711 roomBinAttr.size = sizeof( m_roomSyncData );
1712 reqParam.roomBinAttrInternalNum = 1;
1713 reqParam.roomBinAttrInternal = &roomBinAttr;
1714 sceNpMatching2SetRoomDataInternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId );
1715}
1716
1717// 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
1718// before doing this.
1719// Returns true on success.
1720bool SQRNetworkManager_PS3::GetMatchingContext(eSQRNetworkManagerInternalState asyncState)
1721{
1722 if( m_matchingContextValid ) return true;
1723
1724 int ret = 0;
1725 if( !m_matching2initialised)
1726 {
1727 ret = sceNpMatching2Init2(0, 0, NULL);
1728 }
1729 if( ret < 0 )
1730 {
1731 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2Init2 failed with code 0x%08x\n", ret);
1732 return false;
1733 }
1734 m_matching2initialised = true;
1735
1736 // Get NP ID of the signed-in user
1737 SceNpId npId;
1738 ret = sceNpManagerGetNpId(&npId);
1739 if( ret < 0 )
1740 {
1741 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpManagerGetNpId failed with code 0x%08x\n", ret);
1742 return false;
1743 }
1744
1745 // Use Online Name and avatar to identify user
1746 int option = (SCE_NP_MATCHING2_CONTEXT_OPTION_USE_ONLINENAME | SCE_NP_MATCHING2_CONTEXT_OPTION_USE_AVATARURL);
1747
1748 // Create context
1749 ret = sceNpMatching2CreateContext(
1750 &npId, &s_npCommunicationId, &s_npCommunicationPassphrase, &m_matchingContext, option);
1751 if( ret < 0 )
1752 {
1753 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2CreateContext failed with code 0x%08x\n", ret);
1754 return false;
1755 }
1756
1757 if( !RegisterCallbacks() )
1758 {
1759 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - RegisterCallbacks failed\n");
1760 return false;
1761 }
1762
1763 // Set internal state & kick off async process that will actually start the context.
1764 SetState(asyncState);
1765 ret = sceNpMatching2ContextStartAsync(m_matchingContext, (10*1000*1000));
1766 if( ret < 0 )
1767 {
1768 // Put state back so that the caller isn't expecting a callback from sceNpMatching2ContextStartAsync completing to happen
1769 SetState(SNM_INT_STATE_IDLE);
1770 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2ContextStartAsync failed with code 0x%08x\n", ret);
1771 return false;
1772 }
1773
1774 app.DebugPrintf("SQRNetworkManager::GetMatchingContext - matching context is now valid\n");
1775 m_matchingContextValid = true;
1776 return true;
1777}
1778
1779// Starts the process of obtaining a server context. This is an asynchronous operation, at the end of which (if successful), we'll be creating
1780// 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.
1781// If not we just cycle round trying other random ones until we either find an available one or fail.
1782bool SQRNetworkManager_PS3::GetServerContext()
1783{
1784 assert(m_state == SNM_INT_STATE_IDLE);
1785 assert(m_serverContextValid == false);
1786
1787 // Check that the matching context is valid & recreate if necessary
1788 if( !GetMatchingContext(SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT) ) return false;
1789 // 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
1790 // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received )
1791 if( m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) return true;
1792
1793 return GetServerContext2();
1794}
1795
1796// Code split out from previous method, so we can also call from creating matching context if required
1797bool SQRNetworkManager_PS3::GetServerContext2()
1798{
1799 // Get list of server IDs of servers allocated to the application
1800 int serverCount = sceNpMatching2GetServerIdListLocal( m_matchingContext, NULL, 0 );
1801 // 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
1802 if( ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment
1803 ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error
1804 {
1805 sceNpMatching2DestroyContext(m_matchingContext);
1806 m_matchingContextValid = false;
1807 if( !GetMatchingContext(SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT) ) return false;
1808 // 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
1809 // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received )
1810 if( m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT )
1811 {
1812 return true;
1813 }
1814 else
1815 {
1816 return false;
1817 }
1818 }
1819 if( serverCount <= 0 )
1820 {
1821 return false;
1822 }
1823 m_aServerId = (SceNpMatching2ServerId *)realloc( m_aServerId, sizeof(SceNpMatching2ServerId) * serverCount );
1824 m_serverCount = sceNpMatching2GetServerIdListLocal(m_matchingContext, m_aServerId, serverCount);
1825 m_totalServerCount = m_serverCount;
1826 if (m_serverCount < 0)
1827 {
1828 return false;
1829 }
1830
1831 SetState(SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER);
1832 return SelectRandomServer();
1833}
1834
1835// 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,
1836// using mainly the same code by making a single element list. This is used when joining an existing room.
1837bool SQRNetworkManager_PS3::GetServerContext(SceNpMatching2ServerId serverId)
1838{
1839 assert(m_state == SNM_INT_STATE_IDLE);
1840 assert(m_serverContextValid == false);
1841
1842 // Check that the matching context is valid & recreate if necessary
1843 if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) )
1844 {
1845 app.DebugPrintf("SQRNetworkManager::GetServerContext - Failed due to no matching context\n");
1846 return false;
1847 }
1848
1849 // 4J Stu - If this state is set, then we have successfully created a new context but it won't have started yet
1850 // Therefore the sceNpMatching2GetServerIdListLocal call will fail. If we just skip this check everything should be good.
1851 if( m_state != SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT )
1852 {
1853 // 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
1854 // the context *really* is valid.
1855 int serverCount = sceNpMatching2GetServerIdListLocal( m_matchingContext, NULL, 0 );
1856 // 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
1857 if( ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment
1858 ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error
1859 {
1860 app.DebugPrintf("SQRNetworkManager::GetServerContext - sceNpMatching2GetServerIdListLocal failed with error 0x%08x\n", serverCount);
1861 int ret = sceNpMatching2DestroyContext(m_matchingContext);
1862 app.DebugPrintf("SQRNetworkManager::GetServerContext - sceNpMatching2DestroyContext returned 0x%08x\n", ret);
1863 m_matchingContextValid = false;
1864 if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) )
1865 {
1866 app.DebugPrintf("SQRNetworkManager::GetServerContext - Failed due to no matching context after recreating\n");
1867 return false;
1868 }
1869 }
1870 }
1871
1872 m_serverCount = 1;
1873 m_totalServerCount = m_serverCount;
1874 m_aServerId = (SceNpMatching2ServerId *)realloc(m_aServerId, sizeof(SceNpMatching2ServerId) * m_serverCount );
1875 m_aServerId[0] = serverId;
1876
1877 // 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
1878 // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received )
1879 if( m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) return true;
1880
1881 SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER);
1882 return SelectRandomServer();
1883}
1884
1885// Tick to update the search for a server which is available, for the creation of a server context.
1886void SQRNetworkManager_PS3::ServerContextTick()
1887{
1888 switch( m_state )
1889 {
1890 case SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER:
1891 case SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER:
1892 break;
1893 case SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR:
1894 case SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR:
1895 // Attempt to keep searching if a single server failed
1896 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);
1897 if(!SelectRandomServer())
1898 {
1899 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);
1900 }
1901 break;
1902 case SNM_INT_STATE_HOSTING_SERVER_FOUND:
1903 case SNM_INT_STATE_JOINING_SERVER_FOUND:
1904 {
1905 SceNpMatching2CreateServerContextRequest reqParam;
1906 reqParam.serverId = m_serverId;
1907 SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_FOUND)?SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT:SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT);
1908 // Found a server - now try and create a context for it
1909 int ret = sceNpMatching2CreateServerContext( m_matchingContext, &reqParam, NULL, &m_serverContextRequestId );
1910 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT) )
1911 {
1912 SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_FOUND)?SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED:SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED);
1913 }
1914 }
1915 break;
1916 default:
1917 break;
1918 }
1919}
1920
1921// Tick the process of creating a room.
1922void SQRNetworkManager_PS3::RoomCreateTick()
1923{
1924 switch( m_state )
1925 {
1926 case SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD:
1927 break;
1928 case SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND:
1929 {
1930 SceNpMatching2CreateJoinRoomRequest reqParam;
1931 SceNpMatching2SignalingOptParam optSignalingParam;
1932 SceNpMatching2BinAttr roomBinAttrExt;
1933 SceNpMatching2BinAttr roomBinAttr;
1934 memset(&reqParam, 0, sizeof(reqParam));
1935 memset(&optSignalingParam, 0, sizeof( optSignalingParam) );
1936 memset(&roomBinAttr, 0, sizeof(roomBinAttr));
1937 memset(&roomBinAttrExt, 0, sizeof(roomBinAttrExt));
1938
1939 reqParam.worldId = m_worldId;
1940 reqParam.flagAttr = SCE_NP_MATCHING2_ROOM_FLAG_ATTR_NAT_TYPE_RESTRICTION;
1941 reqParam.sigOptParam = &optSignalingParam;
1942 reqParam.maxSlot = MAX_ONLINE_PLAYER_COUNT;
1943 reqParam.roomBinAttrInternalNum = 1;
1944 reqParam.roomBinAttrInternal = &roomBinAttr;
1945 reqParam.roomBinAttrExternalNum = 1;
1946 reqParam.roomBinAttrExternal = &roomBinAttrExt;
1947
1948 roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID;
1949 roomBinAttr.ptr = &m_roomSyncData;
1950 roomBinAttr.size = sizeof( m_roomSyncData );
1951
1952 roomBinAttrExt.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID;
1953 roomBinAttrExt.ptr = m_joinExtData;
1954 roomBinAttrExt.size = m_joinExtDataSize;
1955
1956 optSignalingParam.type = SCE_NP_MATCHING2_SIGNALING_TYPE_STAR;
1957 optSignalingParam.hubMemberId = 0; // Room owner is the hub of the star
1958 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM);
1959 app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room start\n");
1960 s_roomStartTime = System::currentTimeMillis();
1961 int ret = sceNpMatching2CreateJoinRoom( m_matchingContext, &reqParam, NULL, &m_createRoomRequestId );
1962 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_JOIN_ROOM) )
1963 {
1964 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED);
1965 }
1966 }
1967 break;
1968 case SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM:
1969 break;
1970 case SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS:
1971 SetState(SNM_INT_STATE_HOSTING_WAITING_TO_PLAY);
1972
1973 // Now we know the local member id we can update our local players
1974 SetLocalPlayersAndSync();
1975 break;
1976 case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED:
1977 break;
1978 default:
1979 break;
1980 }
1981}
1982
1983// 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
1984// call is transitioning us from not ready to ready, and call a registered callback.
1985void SQRNetworkManager_PS3::NetworkPlayerConnectionComplete(SQRNetworkPlayer *player)
1986{
1987 EnterCriticalSection(&m_csPlayerState);
1988 bool wasReady = player->IsReady();
1989 bool wasClientReady = player->HasConnectionAndSmallId();
1990 player->ConnectionComplete();
1991 bool isReady = player->IsReady();
1992 bool isClientReady = player->HasConnectionAndSmallId();
1993 if( !m_isHosting )
1994 {
1995 // 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
1996 if( ( !wasClientReady ) && ( isClientReady ) )
1997 {
1998 player->ConfirmReady();
1999 isReady = true;
2000 }
2001 }
2002 LeaveCriticalSection(&m_csPlayerState);
2003
2004 if( ( !wasReady ) && ( isReady ) )
2005 {
2006 HandlePlayerJoined( player );
2007 }
2008}
2009
2010// For a player using the network to communicate, set its small id, thereby flagging it as having one allocated
2011void SQRNetworkManager_PS3::NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId)
2012{
2013 EnterCriticalSection(&m_csPlayerState);
2014 bool wasReady = player->IsReady();
2015 bool wasClientReady = player->HasConnectionAndSmallId();
2016 player->SmallIdAllocated(smallId);
2017 bool isReady = player->IsReady();
2018 bool isClientReady = player->HasConnectionAndSmallId();
2019 if( !m_isHosting )
2020 {
2021 // 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
2022 if( ( !wasClientReady ) && ( isClientReady ) )
2023 {
2024 player->ConfirmReady();
2025 isReady = true;
2026 }
2027 }
2028 LeaveCriticalSection(&m_csPlayerState);
2029
2030 if( ( !wasReady ) && ( isReady ) )
2031 {
2032 HandlePlayerJoined( player );
2033 }
2034}
2035
2036// On host, for a player using the network to communicate, confirm that its small id has now been received back
2037void SQRNetworkManager_PS3::NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player, void *data)
2038{
2039 EnterCriticalSection(&m_csPlayerState);
2040 SQRNetworkPlayer::InitSendData *ISD = (SQRNetworkPlayer::InitSendData *)data;
2041 bool wasReady = player->IsReady();
2042 player->InitialDataReceived(ISD);
2043 bool isReady = player->IsReady();
2044 LeaveCriticalSection(&m_csPlayerState);
2045 // Sync room data back out as we've updated a player's UID here
2046 SyncRoomData();
2047
2048 if( ( !wasReady ) && ( isReady ) )
2049 {
2050 HandlePlayerJoined( player );
2051 }
2052}
2053
2054// 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.
2055// 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.
2056void SQRNetworkManager_PS3::NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId)
2057{
2058 player->ConnectionComplete();
2059 player->SmallIdAllocated(smallId);
2060}
2061
2062void SQRNetworkManager_PS3::HandlePlayerJoined(SQRNetworkPlayer *player)
2063{
2064 if( m_listener )
2065 {
2066 m_listener->HandlePlayerJoined( player );
2067 }
2068 // 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.
2069 if( !m_isHosting )
2070 {
2071 if( player->IsLocal() )
2072 {
2073 m_localPlayerJoined++;
2074 }
2075 }
2076}
2077
2078// 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.
2079bool SQRNetworkManager_PS3::SelectRandomServer()
2080{
2081 assert( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) );
2082
2083 if( m_serverCount == 0 )
2084 {
2085 SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED : SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED);
2086 app.DebugPrintf("SQRNetworkManager::SelectRandomServer - Server count is 0\n");
2087 return false;
2088 }
2089
2090 int serverIdx;
2091
2092 sys_get_random_number(&serverIdx, 4);
2093 serverIdx &= 0x7fffffff;
2094 serverIdx = serverIdx % m_serverCount;
2095
2096 SceNpMatching2GetServerInfoRequest reqParam;
2097
2098 //E Request parameters
2099 memset(&reqParam, 0, sizeof(reqParam));
2100 reqParam.serverId = m_aServerId[serverIdx]; //E Specify ID of target server
2101
2102 // Remove this server from ones to try, if we need to try more than one to find an available one
2103 m_serverCount--;
2104 m_aServerId[serverIdx] = m_aServerId[m_serverCount];
2105
2106 // Kick off the search - we'll get a callback to DefaultRequestCallback with the result from this
2107 app.DebugPrintf(CMinecraftApp::USER_RR,"Kicking off sceNpMatching2GetServerInfo for server id %d\n",reqParam.serverId);
2108 int ret = sceNpMatching2GetServerInfo( m_matchingContext, &reqParam, NULL, &m_serverSearchRequestId);
2109 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_GET_SERVER_INFO) )
2110 {
2111 // Jump straight to an error state for this server
2112 SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR : SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR);
2113 app.DebugPrintf("SQRNetworkManager::SelectRandomServer - An error occured : 0x%08x\n", ret);
2114 return false;
2115 }
2116 return true;
2117}
2118
2119// Delete the current server context. Should be called when finished with the current host or client game session.
2120void SQRNetworkManager_PS3::DeleteServerContext()
2121{
2122 if( m_serverContextValid )
2123 {
2124 SceNpMatching2DeleteServerContextRequest reqParam;
2125 reqParam.serverId = m_serverId;
2126 m_serverContextValid = false;
2127 SetState(SNM_INT_STATE_SERVER_DELETING_CONTEXT);
2128 int ret = sceNpMatching2DeleteServerContext( m_matchingContext, &reqParam, NULL, &m_serverContextRequestId );
2129 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_DELETE_SERVER_CONTEXT) )
2130 {
2131 ResetToIdle();
2132 }
2133 }
2134 else
2135 {
2136 ResetToIdle();
2137 }
2138}
2139
2140// 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
2141// create one connection per local play on any remote machine.
2142//
2143// peerMemberId is the room member Id of the remote end of the connection
2144// playersMemberId is the room member Id that the players belong to
2145// 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
2146
2147bool SQRNetworkManager_PS3::CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersMemberId)
2148{
2149 // First get details of the UDPP2P connection that has been established
2150 int connStatus;
2151 sockaddr_in_p2p sinp2pLocal, sinp2pPeer;
2152 SceNpMatching2SignalingNetInfo netInfo;
2153
2154 // Local end first...
2155 memset(&sinp2pLocal, 0, sizeof(sinp2pLocal));
2156 memset(&netInfo, 0 , sizeof(netInfo));
2157 netInfo.size = sizeof(netInfo);
2158 int ret = sceNpMatching2SignalingGetLocalNetInfo(&netInfo);
2159 if( ret < 0 ) return false;
2160 sinp2pLocal.sin_family = AF_INET;
2161 sinp2pLocal.sin_port = htons(SCE_NP_PORT);
2162// sinp2pLocal.sin_addr = netInfo.localAddr;
2163
2164 // ... and then the peer
2165 memset(&sinp2pPeer, 0, sizeof(sinp2pPeer));
2166 sinp2pPeer.sin_family = AF_INET;
2167
2168 ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port);
2169 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SignalingGetConnectionStatus returned 0x%x, connStatus %d peer add:0x%x peer port:0x%x\n",ret, connStatus,sinp2pPeer.sin_addr,sinp2pPeer.sin_port);
2170
2171
2172 // Set vport for both ends of connection
2173 sinp2pLocal.sin_vport = htons(1);
2174 sinp2pPeer.sin_vport = htons(1);
2175
2176 // Create socket & bind, if we don't already have one
2177 if( m_soc == -1 )
2178 {
2179 ret = socket(AF_INET, SOCK_DGRAM_P2P, 0);
2180 m_soc = ret;
2181 int optval = 1;
2182 ret = setsockopt(m_soc, SOL_SOCKET, SO_USECRYPTO, &optval, sizeof(optval));
2183 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_0) ) return false;
2184 ret = setsockopt(m_soc, SOL_SOCKET, SO_USESIGNATURE, &optval, sizeof(optval));
2185 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_1) ) return false;
2186 ret = setsockopt(m_soc, SOL_SOCKET, SO_NBIO, &optval, sizeof(optval));
2187 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_2) ) return false;
2188
2189 ret = bind(m_soc, (struct sockaddr *)&sinp2pLocal, sizeof(sinp2pLocal));
2190 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SOCK_BIND) ) return false;
2191 }
2192
2193 // 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
2194 // over the socket we have just made
2195 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ )
2196 {
2197 if( ( playerMask & ( 1 << i ) ) == 0 ) continue;
2198
2199 int rudpCtx;
2200
2201 // Socket for the local network node created, now can create an Rupd context.
2202 ret = cellRudpCreateContext( RudpContextCallback, this, &rudpCtx );
2203 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false;
2204 if( m_isHosting )
2205 {
2206 m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_REMOTE, true, playersMemberId, i, rudpCtx, NULL );
2207 }
2208 else
2209 {
2210 // Local players can establish their UID at this point
2211 PlayerUID localUID;
2212 ProfileManager.GetXUID(i,&localUID,true);
2213
2214 m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_LOCAL, false, m_localMemberId, i, rudpCtx, &localUID );
2215 }
2216
2217 // If we've created a player, then we want to try and patch up any connections that we should have to it
2218 MapRoomSlotPlayers();
2219
2220 // TODO - set any non-default options for the context. By default, the context is set to have delivery critical and order critical both on
2221
2222 // 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.
2223 ret = cellRudpBind( rudpCtx, m_soc , 1 + i, CELL_RUDP_MUXMODE_P2P );
2224 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false;
2225
2226 ret = cellRudpInitiate( rudpCtx, (struct sockaddr *)&sinp2pPeer, sizeof(sinp2pPeer), 0);
2227 if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false;
2228 }
2229 return true;
2230}
2231
2232
2233SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerFromRudpCtx(int rudpCtx)
2234{
2235 AUTO_VAR(it,m_RudpCtxToPlayerMap.find(rudpCtx));
2236 if( it != m_RudpCtxToPlayerMap.end() )
2237 {
2238 return it->second;
2239 }
2240 return NULL;
2241}
2242
2243SQRNetworkPlayer *SQRNetworkManager_PS3::GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx)
2244{
2245 for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); it++ )
2246 {
2247 if( (it->second->m_roomMemberId == roomMember ) && ( it->second->m_localPlayerIdx == localIdx ) )
2248 {
2249 return it->second;
2250 }
2251 }
2252 return NULL;
2253}
2254
2255
2256// This is called as part of the general initialisation of the network manager, to register any callbacks that the sony libraries require.
2257// Returns true if all were registered successfully.
2258bool SQRNetworkManager_PS3::RegisterCallbacks()
2259{
2260 // Register RUDP event handler
2261 int ret = cellRudpSetEventHandler(RudpEventCallback, this);
2262 if (ret < 0)
2263 {
2264 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - cellRudpSetEventHandler failed with code 0x%08x\n", ret);
2265 return false;
2266 }
2267
2268 // Register the context callback function
2269 ret = sceNpMatching2RegisterContextCallback(m_matchingContext, ContextCallback, this);
2270 if (ret < 0)
2271 {
2272 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterContextCallback failed with code 0x%08x\n", ret);
2273 return false;
2274 }
2275
2276 // Register the default request callback & parameters
2277 SceNpMatching2RequestOptParam optParam;
2278
2279 memset(&optParam, 0, sizeof(optParam));
2280 optParam.cbFunc = DefaultRequestCallback;
2281 optParam.cbFuncArg = this;
2282 optParam.timeout = (30 * 1000 * 1000);
2283 optParam.appReqId = 0;
2284
2285 ret = sceNpMatching2SetDefaultRequestOptParam(m_matchingContext, &optParam);
2286 if (ret < 0)
2287 {
2288 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2SetDefaultRequestOptParam failed with code 0x%08x\n", ret);
2289 return false;
2290 }
2291
2292 // Register signalling callback
2293 ret = sceNpMatching2RegisterSignalingCallback(m_matchingContext, SignallingCallback, this);
2294 if (ret < 0)
2295 {
2296 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterSignalingCallback failed with code 0x%08x\n", ret);
2297 return false;
2298 }
2299
2300 // Register room event callback
2301 ret = sceNpMatching2RegisterRoomEventCallback(m_matchingContext, RoomEventCallback, this);
2302 if (ret < 0)
2303 {
2304 app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterRoomEventCallback failed with code 0x%08x\n", ret);
2305 return false;
2306 }
2307
2308 return true;
2309}
2310
2311extern bool g_bBootedFromInvite;
2312
2313// This is an implementation of SceNpMatching2ContextCallback. Used to determine whether the matching 2 context is valid or not.
2314void SQRNetworkManager_PS3::ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg)
2315{
2316 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
2317 if (id != manager->m_matchingContext)
2318 {
2319 return;
2320 }
2321
2322 switch( event )
2323 {
2324 case SCE_NP_MATCHING2_CONTEXT_EVENT_Start:
2325 if(errorCode < 0)
2326 {
2327 if(manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ||
2328 manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ||
2329 manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT)
2330 {
2331
2332 // matching context failed to start (this can happen when you block the IP addresses of the matching servers on your router
2333 // agent-0101.ww.sp-int.matching.playstation.net (198.107.157.191)
2334 // static-resource.sp-int.community.playstation.net (203.105.77.140)
2335 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED);
2336 break;
2337 }
2338 }
2339 // 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
2340 // responses here complete what should then happen to get the server context in each case (for hosting or joining a game)
2341 if( manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT )
2342 {
2343
2344 manager->SetState( SNM_INT_STATE_IDLE );
2345 manager->GetExtDataForRoom(0, NULL, NULL, NULL);
2346 break;
2347 }
2348
2349 if( manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT )
2350 {
2351 manager->GetServerContext2();
2352 break;
2353 }
2354 if( manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT )
2355 {
2356 manager->SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER);
2357 manager->SelectRandomServer();
2358 break;
2359 }
2360 // Normal handling of context starting, from standard initialisation procedure
2361 assert( manager->m_state == SNM_INT_STATE_STARTING_CONTEXT );
2362 if (errorCode < 0)
2363 {
2364 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED);
2365 }
2366 else
2367 {
2368 manager->m_offlineSQR = false;
2369 manager->SetState(SNM_INT_STATE_IDLE);
2370
2371 // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it from the tick instead
2372 m_bCallPSNSignInCallback=true;
2373// if(s_SignInCompleteCallbackFn)
2374// {
2375// s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, 0);
2376// s_SignInCompleteCallbackFn = NULL;
2377// }
2378
2379
2380 // 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
2381 if( manager->m_doBootInviteCheck )
2382 {
2383 unsigned int type, attributes;
2384 CellGameContentSize gameSize;
2385 char dirName[CELL_GAME_DIRNAME_SIZE];
2386
2387 if( g_bBootedFromInvite )
2388 {
2389 manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA);
2390 manager->m_doBootInviteCheck = false;
2391 }
2392 }
2393 }
2394 break;
2395 case SCE_NP_MATCHING2_CONTEXT_EVENT_Stop:
2396 assert(false);
2397 break;
2398 case SCE_NP_MATCHING2_CONTEXT_EVENT_StartOver:
2399 sceNpMatching2DestroyContext(manager->m_matchingContext);
2400 manager->m_matchingContextValid = false;
2401 manager->m_offlineSQR = true;
2402 break;
2403 }
2404}
2405
2406// This is an implementation of SceNpMatching2RequestCallback. This callback is used by default for any matching 2 request functions.
2407#ifdef __PS3__
2408void SQRNetworkManager_PS3::DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, SceNpMatching2EventKey eventKey, int errorCode, size_t dataSize, void *arg)
2409#else
2410void SQRNetworkManager_PS3::DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg)
2411#endif
2412{
2413 int ret;
2414 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
2415 if( id != manager->m_matchingContext ) return;
2416
2417 // PS3 requires a call to get any data that is passed with the callback. On PS3, the data is just passed in directly - do the getting of data here on PS3 to get it into the same state as the PS4
2418#ifdef __PS3__
2419 void *data = 0;
2420 bool dataError = false;
2421
2422 if( errorCode == 0 )
2423 {
2424 switch( event )
2425 {
2426 case SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo:
2427 assert(dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetServerInfo);
2428 ret = sceNpMatching2GetEventData( id, eventKey, (void *)(manager->cServerInfoStorage), dataSize );
2429 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_SERVER_INFO_DATA) )
2430 {
2431 data = (void *)manager->cServerInfoStorage;
2432 }
2433 else
2434 {
2435 dataError = true;
2436 }
2437 break;
2438 case SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList:
2439 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetWorldInfoList );
2440 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cWorldListStorage), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetWorldInfoList);
2441 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_WORLD_INFO_DATA) )
2442 {
2443 data = (void *)manager->cWorldListStorage;
2444 }
2445 else
2446 {
2447 dataError = true;
2448 }
2449 break;
2450 case SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom:
2451 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_CreateJoinRoom );
2452 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cCreateJoinRoomStorage), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_CreateJoinRoom);
2453 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_CREATE_JOIN_ROOM_DATA) )
2454 {
2455 data = (void *)manager->cCreateJoinRoomStorage;
2456 }
2457 else
2458 {
2459 dataError = true;
2460 }
2461 break;
2462 case SCE_NP_MATCHING2_REQUEST_EVENT_GetUserInfoList:
2463 assert(dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetUserInfoList);
2464 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cUserInfoListStorage), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetUserInfoList);
2465 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_USER_INFO_LIST_DATA) )
2466 {
2467 data = (void *)manager->cUserInfoListStorage;
2468 }
2469 else
2470 {
2471 dataError = true;
2472 }
2473 break;
2474 case SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom:
2475 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_JoinRoom);
2476 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cJoinRoomStorage), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_JoinRoom);
2477 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_JOIN_ROOM_DATA) )
2478 {
2479 data = (void *)manager->cJoinRoomStorage;
2480 }
2481 else
2482 {
2483 dataError = true;
2484 }
2485 break;
2486 case SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataInternal:
2487 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomMemberDataInternal );
2488 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternal), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomMemberDataInternal);
2489 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_ROOM_MEMBER_DATA_INTERNAL) )
2490 {
2491 data = (void *)manager->cRoomMemberDataInternal;
2492 }
2493 else
2494 {
2495 dataError = true;
2496 }
2497 break;
2498 case SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList:
2499 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataExternalList );
2500 ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomDataExternalList), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataExternalList);
2501 if ( (ret >= 0) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA2) )
2502 {
2503 data = (void *)manager->cRoomDataExternalList;
2504 }
2505 else
2506 {
2507 dataError = true;
2508 }
2509 break;
2510 }
2511 }
2512
2513 // If there was some problem getting data, then silently ignore as we don't want to go any further with a NULL data pointer, and this will just be indicating that the networking context
2514 // is invalid which will be picked up elsewhere to shut everything down properly
2515 if( dataError )
2516 {
2517 return;
2518 }
2519
2520 // If we didn't get the event data, then we need to clear it, or the system even queue will overflow
2521 if( data == 0 )
2522 {
2523 sceNpMatching2ClearEventData(manager->m_matchingContext, eventKey);
2524 }
2525#endif
2526
2527 switch( event )
2528 {
2529 // This is the responese to a call to sceNpMatching2GetServerInfo, used when we are trying to establish an available server to get an explicit server context for, which is the process
2530 // kicked off by the GetServerContext methods.
2531 case SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo:
2532 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT )
2533 {
2534 // 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
2535 break;
2536 }
2537 assert( (manager->m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (manager->m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) );
2538 app.DebugPrintf(CMinecraftApp::USER_RR,"Response to server search received, errorCode 0x%x\n",errorCode);
2539 if( errorCode == 0 )
2540 {
2541 if( data != 0 )
2542 {
2543 SceNpMatching2GetServerInfoResponse *pServerInfo = (SceNpMatching2GetServerInfoResponse *)data;
2544 if (pServerInfo->server.status == SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE)
2545 {
2546 app.DebugPrintf(CMinecraftApp::USER_RR,"Server status available\n");
2547 // This server is available
2548 manager->SetState((manager->m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_FOUND : SNM_INT_STATE_JOINING_SERVER_FOUND);
2549 manager->m_serverId = pServerInfo->server.serverId;
2550 break;
2551 }
2552 }
2553 }
2554 // A problem of some kind with this particular server - let the tick decide what to do
2555 manager->SetState((manager->m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR : SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR);
2556 break;
2557 // 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
2558 // 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.
2559 case SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList:
2560 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT )
2561 {
2562 // 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
2563 break;
2564 }
2565 assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD );
2566 if( errorCode == 0 )
2567 {
2568 if( data != 0 )
2569 {
2570 // Currently just using first world - this may well be all that we need anyway
2571 SceNpMatching2GetWorldInfoListResponse *pWorldList = (SceNpMatching2GetWorldInfoListResponse *)data;
2572 if( pWorldList->worldNum >= 1 )
2573 {
2574 manager->m_worldId = pWorldList->world[0].worldId;
2575 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND);
2576 break;
2577 }
2578 }
2579 }
2580 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED);
2581 break;
2582 // 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
2583 // transition actually occurs in the create room tick, on detecting that the state has transitioned to SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS here.
2584 case SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom:
2585 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT )
2586 {
2587 // 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
2588 break;
2589 }
2590 app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room complete, time taken %d, error 0x%x\n",System::currentTimeMillis()-s_roomStartTime, errorCode);
2591 assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM );
2592 if( errorCode == 0 )
2593 {
2594 if( data != 0 )
2595 {
2596 SceNpMatching2CreateJoinRoomResponse *roomData = (SceNpMatching2CreateJoinRoomResponse *)data;
2597 manager->m_localMemberId = roomData->roomDataInternal->memberList.me->memberId;
2598
2599 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS);
2600 manager->m_room = roomData->roomDataInternal->roomId;
2601 break;
2602 }
2603 }
2604 manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED);
2605 break;
2606 // This is the response to sceNpMatching2CreateServerContext, which is the final thing that happens in the chain of states kicked off by GetServerContext, and happens
2607 // after we've found an available server. If this context creation is successful, then we can proceed to create or join a room - we can determine which to
2608 // do by virtue of the current state.
2609 case SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext:
2610 if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT )
2611 {
2612 // 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
2613 break;
2614 }
2615 assert( ( manager->m_state == SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT ) || ( manager->m_state == SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT ) );
2616 if( manager->m_state == SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT )
2617 {
2618 if(( errorCode == 0 ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK) )
2619 {
2620 manager->m_serverContextValid = true;
2621 manager->ServerContextValid_CreateRoom();
2622 break;
2623 }
2624 manager->SetState(SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED);
2625 }
2626 else
2627 {
2628 if(( errorCode == 0 ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK) )
2629 {
2630 manager->m_serverContextValid = true;
2631 manager->ServerContextValid_JoinRoom();
2632 break;
2633 }
2634 manager->SetState(SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED);
2635 }
2636 break;
2637 // This is the response to sceNpMatching2DeleteServerContext.
2638 case SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext:
2639// assert( manager->m_state == SNM_INT_STATE_SERVER_DELETING_CONTEXT );
2640 manager->ResetToIdle();
2641 break;
2642 // 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
2643 // 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
2644 // have joined.
2645 case SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom:
2646 assert( manager->m_state == SNM_INT_STATE_JOINING_JOIN_ROOM);
2647 if( errorCode == 0 )
2648 {
2649 if( data != 0 )
2650 {
2651 SceNpMatching2JoinRoomResponse *roomData = (SceNpMatching2JoinRoomResponse *)data;
2652
2653 manager->m_localMemberId = roomData->roomDataInternal->memberList.me->memberId;
2654 manager->m_room = roomData->roomDataInternal->roomId;
2655 SonyVoiceChat::init(manager);
2656 // Copy over initial room sync data
2657 for( int i = 0; i < roomData->roomDataInternal->roomBinAttrInternalNum; i++ )
2658 {
2659 if( roomData->roomDataInternal->roomBinAttrInternal[i].data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID )
2660 {
2661 assert( roomData->roomDataInternal->roomBinAttrInternal[i].data.size == sizeof( manager->m_roomSyncData ) );
2662 memcpy( &manager->m_roomSyncData, roomData->roomDataInternal[i].roomBinAttrInternal[0].data.ptr, sizeof( manager->m_roomSyncData ) );
2663
2664// manager->UpdatePlayersFromRoomSyncUIDs();
2665 // Update mapping from the room slot players to SQRNetworkPlayer instances
2666 manager->MapRoomSlotPlayers();
2667 break;
2668 }
2669 }
2670 manager->SetState(SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS);
2671 break;
2672 }
2673 }
2674 manager->SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED);
2675 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
2676 {
2677 Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), DisconnectPacket::eDisconnect_ServerFull);
2678 }
2679 break;
2680 // This is the response to sceNpMatching2GetRoomMemberDataInternal.This only happens on the host, as a response to an incoming connection being established, when we
2681 // 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
2682 // 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.
2683 case SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataInternal:
2684 if( errorCode == 0 )
2685 {
2686 assert( manager->m_isHosting );
2687 if( data != 0 )
2688 {
2689 SceNpMatching2GetRoomMemberDataInternalResponse *pRoomMemberData = (SceNpMatching2GetRoomMemberDataInternalResponse *)data;
2690 assert( pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternalNum == 1 );
2691 int playerMask = *((int *)(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr));
2692
2693 bool isFull = false;
2694 bool success = manager->AddRemotePlayersAndSync( pRoomMemberData->roomMemberDataInternal->memberId, playerMask, &isFull );
2695 if( success )
2696 {
2697 success = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, playerMask, pRoomMemberData->roomMemberDataInternal->memberId);
2698 if( success ) break;
2699 }
2700 // Something has gone wrong adding these players to the room - kick out the player
2701 SceNpMatching2KickoutRoomMemberRequest reqParam;
2702 SceNpMatching2PresenceOptionData optParam;
2703 memset(&reqParam,0,sizeof(reqParam));
2704 reqParam.roomId = manager->m_room;
2705 reqParam.target = pRoomMemberData->roomMemberDataInternal->memberId;
2706 // Set flag to indicate whether we were kicked for being out of room or not
2707 reqParam.optData.data[0] = isFull ? 1 : 0;
2708 reqParam.optData.len = 1;
2709 int ret = sceNpMatching2KickoutRoomMember(manager->m_matchingContext, &reqParam, NULL, &manager->m_kickRequestId);
2710 app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2KickoutRoomMember returns error 0x%x\n",ret);
2711 break;
2712 }
2713 }
2714 break;
2715 case SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom:
2716 // This is the response to sceNpMatching2LeaveRoom - from the Sony docs, this doesn't ever fail so no need to do error checking here
2717 SonyVoiceChat::signalDisconnected();
2718 assert(manager->m_state == SNM_INT_STATE_LEAVING );
2719 manager->DeleteServerContext();
2720 break;
2721 // This is the response to SceNpMatching2GetRoomDataExternalListRequest, which happens when we request the full details of a room we are interested in joining
2722 case SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList:
2723 if( errorCode == 0 )
2724 {
2725 if( data != 0 )
2726 {
2727 SceNpMatching2GetRoomDataExternalListResponse *pExternalData = (SceNpMatching2GetRoomDataExternalListResponse *)data;
2728 SceNpMatching2RoomDataExternal *pRoomExtData = pExternalData->roomDataExternal;
2729 if( pExternalData->roomDataExternalNum == 1 )
2730 {
2731 if(pRoomExtData->roomBinAttrExternalNum == 1 )
2732 {
2733 memcpy(manager->m_pExtDataToUpdate, pRoomExtData->roomBinAttrExternal[0].ptr,pRoomExtData->roomBinAttrExternal[0].size);
2734 manager->m_FriendSessionUpdatedFn(true, manager->m_pParamFriendSessionUpdated);
2735 }
2736 else
2737 {
2738 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated);
2739 }
2740 }
2741 else
2742 {
2743 manager->m_FriendSessionUpdatedFn(true, manager->m_pParamFriendSessionUpdated);
2744 }
2745 }
2746 else
2747 {
2748 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated);
2749 }
2750 }
2751 else
2752 {
2753 manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated);
2754 }
2755 break;
2756 case SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal:
2757 if( ( errorCode != 0 ) || manager->ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK) )
2758 {
2759 app.DebugPrintf(CMinecraftApp::USER_RR,"Error updating external data 0x%x (from SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal event in callback)\n",errorCode);
2760 // 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,
2761 // 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.
2762 manager->m_resendExternalRoomDataCountdown = 60;
2763 }
2764 break;
2765 };
2766}
2767
2768// This is an implementation of SceNpMatching2RoomEventCallback.
2769#ifdef __PS3__
2770void SQRNetworkManager_PS3::RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, SceNpMatching2EventKey eventKey, int errorCode, size_t dataSize, void *arg)
2771#else
2772void SQRNetworkManager_PS3::RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, int errorCode, const void *data, void *arg)
2773#endif
2774{
2775 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
2776
2777 bool gotEventData = false;
2778 switch( event )
2779 {
2780 case SCE_NP_MATCHING2_ROOM_EVENT_MemberJoined:
2781 break;
2782 case SCE_NP_MATCHING2_ROOM_EVENT_MemberLeft:
2783 break;
2784 case SCE_NP_MATCHING2_ROOM_EVENT_Kickedout:
2785 {
2786 SonyVoiceChat::signalRoomKickedOut();
2787 // 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
2788 // so we can determine which thing has happened
2789 assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo );
2790 int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataUpdateInfo, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo);
2791 app.DebugPrintf(CMinecraftApp::USER_RR,"SCE_NP_MATCHING2_ROOM_EVENT_Kickedout, sceNpMatching2GetEventData returning 0x%x\n",ret);
2792
2793 bool bIsFull = false;
2794 if( ( ret >= 0 ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) )
2795 {
2796 gotEventData = true;
2797 SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)(manager->cRoomDataUpdateInfo);
2798 if( pUpdateInfo->optData.len == 1 )
2799 {
2800 if( pUpdateInfo->optData.data[0] == 1 )
2801 {
2802 bIsFull = true;
2803 }
2804 }
2805 }
2806 app.DebugPrintf(CMinecraftApp::USER_RR,"IsFull determined to be %d\n",bIsFull);
2807 if( bIsFull )
2808 {
2809 manager->m_nextIdleReasonIsFull = true;
2810 }
2811 manager->ResetToIdle();
2812 }
2813 break;
2814 case SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed:
2815 {
2816 SonyVoiceChat::signalRoomDestroyed();
2817 SceNpMatching2RoomUpdateInfo *pUpdateInfo=NULL;
2818
2819 if( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo )
2820 {
2821 int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataUpdateInfo, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo);
2822 app.DebugPrintf("Room destroyed, error code 0x%x\n",errorCode);
2823 app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed, sceNpMatching2GetEventData returning 0x%x\n",ret);
2824 if( ret >= 0 )
2825 {
2826 pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)(manager->cRoomDataUpdateInfo);
2827 app.DebugPrintf("Further info: Error 0x%x, cause %d\n",pUpdateInfo->errorCode,pUpdateInfo->eventCause);
2828 }
2829 }
2830
2831 // 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
2832 // 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.
2833 if( ( manager->m_isHosting ) && !manager->m_bLinkDisconnected )
2834 {
2835 // MGH - we're not receiving an SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD after this so we have to remove all the remote players
2836 while(manager->m_RudpCtxToPlayerMap.size())
2837 {
2838 SQRNetworkPlayer* pRemotePlayer = manager->m_RudpCtxToPlayerMap.begin()->second;
2839 manager->RemoveRemotePlayersAndSync( pRemotePlayer->m_roomMemberId, 15 );
2840 }
2841
2842 if(pUpdateInfo && (pUpdateInfo->eventCause==SCE_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT))
2843 {
2844 manager->m_listener->HandleDisconnect(true,true);
2845 }
2846 else
2847 {
2848 manager->m_listener->HandleDisconnect(true);
2849 }
2850 }
2851
2852 }
2853 break;
2854 case SCE_NP_MATCHING2_ROOM_EVENT_RoomOwnerChanged:
2855 break;
2856 case SCE_NP_MATCHING2_ROOM_EVENT_UpdatedRoomDataInternal:
2857 // We are using the room internal data to synchronise the player data stored in m_roomSyncData from the host to clients.
2858 // The host is the thing creating the internal room data, so it doesn't need to update itself.
2859 if( !manager->m_isHosting )
2860 {
2861 assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo );
2862 int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataInternal, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo);
2863 if( ( ret >= 0 ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) )
2864 {
2865 gotEventData = true;
2866 SceNpMatching2RoomDataInternalUpdateInfo *pRoomData = (SceNpMatching2RoomDataInternalUpdateInfo *)(manager->cRoomDataInternal);
2867 for(int i = 0; i < pRoomData->newRoomBinAttrInternalNum; i++)
2868 {
2869 if( pRoomData->newRoomBinAttrInternal[i]->data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID )
2870 {
2871 assert( pRoomData->newRoomBinAttrInternal[i]->data.size == sizeof( manager->m_roomSyncData ) );
2872 memcpy( &manager->m_roomSyncData, pRoomData->newRoomBinAttrInternal[i]->data.ptr, sizeof( manager->m_roomSyncData ) );
2873
2874// manager->UpdatePlayersFromRoomSyncUIDs();
2875 // Update mapping from the room slot players to SQRNetworkPlayer instances
2876 manager->MapRoomSlotPlayers();
2877#if 0
2878 {
2879 printf("New player sync data arrived\n");
2880 for(int i = 0; i < manager->m_roomSyncData.getPlayerCount(); i++ )
2881 {
2882 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);
2883 }
2884 }
2885#endif
2886 break;
2887 }
2888 }
2889 break;
2890 }
2891 // TODO - handle error here? What could we do?
2892 }
2893
2894 break;
2895 case SCE_NP_MATCHING2_ROOM_EVENT_UpdatedRoomMemberDataInternal:
2896 if( ( errorCode == 0 ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1) ) )
2897 {
2898 // 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
2899 if( manager->m_isHosting )
2900 {
2901 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo );
2902 int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo);
2903 if( ( ret >= 0 ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2) ) )
2904 {
2905 gotEventData = true;
2906 SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(manager->cRoomMemberDataInternalUpdate);
2907 assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 );
2908
2909 int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr));
2910 int oldMask = manager->GetOldMask( pRoomMemberData->newRoomMemberDataInternal->memberId );
2911 int addedMask = manager->GetAddedMask(playerMask, oldMask );
2912 int removedMask = manager->GetRemovedMask(playerMask, oldMask );
2913
2914 if( addedMask != 0 )
2915 {
2916 bool success = manager->AddRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask );
2917 if( success )
2918 {
2919 success = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask, pRoomMemberData->newRoomMemberDataInternal->memberId);
2920 }
2921 if( ( !success ) || (manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3) ) )
2922 {
2923 // Failed for some reason - signal back to the client that this is the case, by updating its internal data back again, rather than have
2924 // it wait for a timeout on its rudp connection initialisation.
2925 SceNpMatching2SetRoomMemberDataInternalRequest reqParam;
2926 SceNpMatching2BinAttr binAttr;
2927
2928 memset(&reqParam, 0, sizeof(reqParam));
2929 memset(&binAttr, 0, sizeof(binAttr));
2930
2931 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID;
2932 binAttr.ptr = &oldMask;
2933 binAttr.size = sizeof(oldMask);
2934
2935 reqParam.roomId = manager->m_room;
2936 reqParam.memberId = pRoomMemberData->newRoomMemberDataInternal->memberId;
2937 reqParam.roomMemberBinAttrInternalNum = 1;
2938 reqParam.roomMemberBinAttrInternal = &binAttr;
2939
2940 int ret = sceNpMatching2SetRoomMemberDataInternal( manager->m_matchingContext, &reqParam, NULL, &manager->m_setRoomMemberInternalDataRequestId );
2941 }
2942 }
2943
2944 if( removedMask != 0 )
2945 {
2946 manager->RemoveRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, removedMask );
2947 }
2948
2949 break;
2950 }
2951 }
2952 else
2953 {
2954 // If, as a client, we receive an updated room member data this could be for two reason.
2955 // (1) Another client in the game has updated their own data as someone has joined/left the session
2956 // (2) The server has set someone's data back, due to a failed attempt to join a game
2957 // 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
2958 assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo );
2959 int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo);
2960 if( ( ret >= 0 ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4) ) )
2961 {
2962 gotEventData = true;
2963 SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(manager->cRoomMemberDataInternalUpdate);
2964 assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 );
2965 if( pRoomMemberData->newRoomMemberDataInternal->memberId == manager->m_localMemberId )
2966 {
2967 int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr));
2968 if( playerMask != manager->m_localPlayerJoinMask )
2969 {
2970 int playersToRemove = manager->m_localPlayerJoinMask & (~playerMask);
2971 manager->RemoveNetworkPlayers( playersToRemove );
2972 if( manager->m_listener )
2973 {
2974 for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ )
2975 {
2976 if( playersToRemove & ( 1 << i ) )
2977 {
2978 manager->m_listener->HandleAddLocalPlayerFailed(i);
2979 break;
2980 }
2981 }
2982 }
2983 }
2984 }
2985 }
2986
2987 }
2988 }
2989 break;
2990 case SCE_NP_MATCHING2_ROOM_EVENT_UpdatedSignalingOptParam:
2991 break;
2992 };
2993
2994 // If we didn't get the event data, then we need to clear it, or the system even queue will overflow
2995 if( !gotEventData )
2996 {
2997 sceNpMatching2ClearEventData(manager->m_matchingContext, eventKey);
2998 }
2999}
3000
3001// 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
3002// the connections being set up to layer sockets and Rudp on top.
3003void SQRNetworkManager_PS3::SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int errorCode, void *arg)
3004{
3005 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3006
3007 switch( event )
3008 {
3009 case SCE_NP_MATCHING2_SIGNALING_EVENT_Dead:
3010 if( manager->m_isHosting )
3011 {
3012 // Remove any players associated with this peer
3013 manager->RemoveRemotePlayersAndSync( peerMemberId, 15 );
3014 }
3015 else
3016 {
3017 // 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
3018 // 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
3019 if( manager->m_state != SNM_INT_STATE_LEAVING )
3020 {
3021 manager->DeleteServerContext();
3022 }
3023 }
3024 break;
3025 case SCE_NP_MATCHING2_SIGNALING_EVENT_Established:
3026 {
3027 if( manager->m_isHosting )
3028 {
3029 // 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
3030 // the actual creation of connections happens when the response for this request is processed.
3031
3032 SceNpMatching2GetRoomMemberDataInternalRequest reqParam;
3033 memset( &reqParam, 0, sizeof(reqParam));
3034 reqParam.roomId = roomId;
3035 reqParam.memberId = peerMemberId;
3036 SceNpMatching2AttributeId attrs[1] = {SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID};
3037 reqParam.attrId = attrs;
3038 reqParam.attrIdNum = 1;
3039
3040 sceNpMatching2GetRoomMemberDataInternal( manager->m_matchingContext, &reqParam, NULL, &manager->m_roomMemberDataRequestId);
3041 }
3042 else
3043 {
3044 // If we are the client, then we locally know what Rupd connections we need (from m_localPlayerJoinMask) and can kick this off.
3045 bool ret = manager->CreateRudpConnections( roomId, peerMemberId, manager->m_localPlayerJoinMask, manager->m_localMemberId);
3046 if( ret == false )
3047 {
3048 manager->DeleteServerContext();
3049 }
3050 }
3051 }
3052 break;
3053 }
3054}
3055
3056
3057// Implementation of SceNpBasicEventHandler
3058int SQRNetworkManager_PS3::BasicEventCallback(int event, int retCode, uint32_t reqId, void *arg)
3059{
3060 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3061 // 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
3062 sys_event_port_send(manager->m_basicEventPort, 0, 0, 0);
3063 return 0;
3064}
3065
3066// Implementation of SceNpManagerCallback
3067void SQRNetworkManager_PS3::ManagerCallback(int event, int result, void *arg)
3068{
3069 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3070 switch( event )
3071 {
3072 case SCE_NP_MANAGER_STATUS_GETTING_TICKET:
3073 break;
3074 case SCE_NP_MANAGER_STATUS_GETTING_PROFILE:
3075 break;
3076 case SCE_NP_MANAGER_STATUS_LOGGING_IN:
3077 break;
3078 case SCE_NP_MANAGER_STATUS_ONLINE:
3079 manager->InitialiseAfterOnline();
3080 break;
3081 case SCE_NP_MANAGER_EVENT_GOT_TICKET:
3082 {
3083 // Let the profile Manager deal with any tickets
3084 SonyRemoteStorage_PS3* pRemote = (SonyRemoteStorage_PS3*)app.getRemoteStorage();
3085 if(pRemote->isWaitingForTicket())
3086 {
3087 pRemote->npauthhandler(event, result, arg);
3088 }
3089 else
3090 {
3091 ProfileManager.HandleNetworkTicket(result,arg);
3092 }
3093 }
3094 break;
3095 case SCE_NP_MANAGER_STATUS_OFFLINE:
3096 break;
3097 }
3098 manager->UpdateOnlineStatus(event);
3099}
3100
3101// Implementation of CellSysutilCallback
3102void SQRNetworkManager_PS3::SysUtilCallback(uint64_t status, uint64_t param, void *userdata)
3103{
3104 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)userdata;
3105 struct CellNetCtlNetStartDialogResult netstart_result;
3106 int ret = 0;
3107 netstart_result.size = sizeof(netstart_result);
3108 switch(status)
3109 {
3110 case CELL_SYSUTIL_NET_CTL_NETSTART_FINISHED:
3111 ret = cellNetCtlNetStartDialogUnloadAsync(&netstart_result);
3112 if(ret < 0)
3113 {
3114 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED);
3115 if( s_SignInCompleteCallbackFn )
3116 {
3117 if( s_signInCompleteCallbackIfFailed )
3118 {
3119 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0);
3120 }
3121 s_SignInCompleteCallbackFn = NULL;
3122 }
3123 return;
3124 }
3125
3126 if( netstart_result.result != 0 )
3127 {
3128 // Failed, or user may have decided not to sign in - maybe need to differentiate here
3129 manager->SetState(SNM_INT_STATE_INITIALISE_FAILED);
3130 if( s_SignInCompleteCallbackFn )
3131 {
3132 if( s_signInCompleteCallbackIfFailed )
3133 {
3134 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0);
3135 }
3136 s_SignInCompleteCallbackFn = NULL;
3137 }
3138 }
3139
3140 break;
3141 case CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED:
3142 break;
3143 case CELL_SYSUTIL_NP_INVITATION_SELECTED:
3144 manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA);
3145 break;
3146 default:
3147 break;
3148 }
3149}
3150
3151// 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
3152// Rudp connection. We create one context/connection per local player on the non-hosting consoles.
3153void SQRNetworkManager_PS3::RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg)
3154{
3155 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3156 switch(event_id)
3157 {
3158 case CELL_RUDP_CONTEXT_EVENT_CLOSED:
3159 app.DebugPrintf(CMinecraftApp::USER_RR,"RUDP closed - event error 0x%x\n",error_code);
3160 if( !manager->m_isHosting )
3161 {
3162 if( manager->m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS )
3163 {
3164 manager->LeaveRoom(true);
3165 }
3166 }
3167 break;
3168 case CELL_RUDP_CONTEXT_EVENT_ESTABLISHED:
3169 {
3170 SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id);
3171 if( player )
3172 {
3173 // Flag connection stage as being completed for this player
3174 manager->NetworkPlayerConnectionComplete(player);
3175 }
3176 else
3177 {
3178 assert(false);
3179 }
3180 }
3181 break;
3182 case CELL_RUDP_CONTEXT_EVENT_ERROR:
3183 break;
3184 case CELL_RUDP_CONTEXT_EVENT_WRITABLE:
3185 {
3186 SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id);
3187 // This event signifies that room has opened up in the write buffer, so attempt to send something
3188 if( player )
3189 {
3190 player->SendMoreInternal();
3191 }
3192 }
3193 break;
3194 case CELL_RUDP_CONTEXT_EVENT_READABLE:
3195 if( manager->m_listener )
3196 {
3197 unsigned int dataSize = cellRudpGetSizeReadable(ctx_id);
3198 // 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
3199 if( manager->m_isHosting )
3200 {
3201 SQRNetworkPlayer *playerFrom = manager->GetPlayerFromRudpCtx( ctx_id );
3202 if( playerFrom && !playerFrom->HasSmallIdConfirmed() )
3203 {
3204 if( dataSize >= sizeof(SQRNetworkPlayer::InitSendData) )
3205 {
3206 SQRNetworkPlayer::InitSendData ISD;
3207 unsigned int bytesRead = cellRudpRead( ctx_id, &ISD, sizeof(SQRNetworkPlayer::InitSendData), 0, NULL );
3208 if( bytesRead == sizeof(SQRNetworkPlayer::InitSendData) )
3209 {
3210 manager->NetworkPlayerInitialDataReceived(playerFrom, &ISD);
3211 dataSize -= sizeof(SQRNetworkPlayer::InitSendData);
3212 }
3213 else
3214 {
3215 assert(false);
3216 }
3217 }
3218 else
3219 {
3220 assert(false);
3221 }
3222 }
3223 }
3224
3225 if( dataSize > 0 )
3226 {
3227 unsigned char *data = new unsigned char [ dataSize ];
3228 unsigned int bytesRead = cellRudpRead( ctx_id, data, dataSize, 0, NULL );
3229 if( bytesRead > 0 )
3230 {
3231 SQRNetworkPlayer *playerFrom, *playerTo;
3232 if( manager->m_isHosting )
3233 {
3234 // Data always going from a remote player, to the host
3235 playerFrom = manager->GetPlayerFromRudpCtx( ctx_id );
3236 playerTo = manager->m_aRoomSlotPlayers[0];
3237 }
3238 else
3239 {
3240 // Data always going from host player, to a local player
3241 playerFrom = manager->m_aRoomSlotPlayers[0];
3242 playerTo = manager->GetPlayerFromRudpCtx( ctx_id );
3243 }
3244 if( ( playerFrom != NULL ) && ( playerTo != NULL ) )
3245 {
3246 manager->m_listener->HandleDataReceived( playerFrom, playerTo, data, bytesRead );
3247 }
3248 }
3249 delete [] data;
3250 }
3251 }
3252 break;
3253 case CELL_RUDP_CONTEXT_EVENT_FLUSHED:
3254 break;
3255 }
3256}
3257
3258// Implementation of CellRudpEventHandler
3259#ifdef __PS3__
3260int SQRNetworkManager_PS3::RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct sockaddr const *addr, socklen_t addrlen, void *arg)
3261#else
3262int SQRNetworkManager_PS3::RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg)
3263#endif
3264{
3265 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3266
3267 if( event_id == CELL_RUDP_EVENT_SOCKET_RELEASED )
3268 {
3269 assert( soc == manager->m_soc );
3270 socketclose(soc);
3271 manager->m_soc = -1;
3272 }
3273 return 0;
3274}
3275
3276void SQRNetworkManager_PS3::NetCtlCallback(int prev_state, int new_state, int event, int error_code, void *arg)
3277{
3278 SQRNetworkManager_PS3 *manager = (SQRNetworkManager_PS3 *)arg;
3279 // 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
3280 // determine what has just happened
3281 if( event == CELL_NET_CTL_EVENT_LINK_DISCONNECTED )
3282 {
3283 manager->m_bLinkDisconnected = true;
3284 manager->m_listener->HandleDisconnect(false);
3285 }
3286 else if( event == CELL_NET_CTL_EVENT_ESTABLISH )
3287 {
3288 manager->m_bLinkDisconnected = false;
3289 }
3290}
3291
3292// Called when the context has been created, and we are intending to create a room.
3293void SQRNetworkManager_PS3::ServerContextValid_CreateRoom()
3294{
3295 // First find a world
3296 assert( m_state == SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT );
3297
3298 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD);
3299
3300 SceNpMatching2GetWorldInfoListRequest reqParam;
3301
3302 // Request parameters
3303 memset(&reqParam, 0, sizeof(reqParam));
3304 reqParam.serverId = m_serverId;
3305
3306 int ret = -1;
3307 if( !ForceErrorPoint(SNM_FORCE_ERROR_GET_WORLD_INFO_LIST) )
3308 {
3309 ret = sceNpMatching2GetWorldInfoList( m_matchingContext, &reqParam, NULL, &m_getWorldRequestId);
3310 }
3311 if (ret < 0)
3312 {
3313 SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED);
3314 return;
3315 }
3316}
3317
3318// Called when the context has been created, and we are intending to join a pre-existing room.
3319void SQRNetworkManager_PS3::ServerContextValid_JoinRoom()
3320{
3321 assert( m_state == SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT );
3322
3323 SetState(SNM_INT_STATE_JOINING_JOIN_ROOM);
3324
3325 // Join the room, passing the local player mask as initial binary data so that the host knows what local players are here
3326 SceNpMatching2JoinRoomRequest reqParam;
3327 SceNpMatching2BinAttr binAttr;
3328 memset(&reqParam, 0, sizeof(reqParam));
3329 memset(&binAttr, 0, sizeof(binAttr));
3330 binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID;
3331 binAttr.ptr = &m_localPlayerJoinMask;
3332 binAttr.size = sizeof(m_localPlayerJoinMask);
3333
3334 reqParam.roomId = m_roomToJoin;
3335 reqParam.roomMemberBinAttrInternalNum = 1;
3336 reqParam.roomMemberBinAttrInternal = &binAttr;
3337
3338 int ret = sceNpMatching2JoinRoom( m_matchingContext, &reqParam, NULL, &m_joinRoomRequestId );
3339 if ( (ret < 0) || ForceErrorPoint(SNM_FORCE_ERROR_JOIN_ROOM) )
3340 {
3341 if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH)
3342 {
3343 app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch );
3344 }
3345 SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED);
3346 }
3347}
3348
3349const SceNpCommunicationId* SQRNetworkManager_PS3::GetSceNpCommsId()
3350{
3351 return &s_npCommunicationId;
3352}
3353
3354const SceNpCommunicationSignature* SQRNetworkManager_PS3::GetSceNpCommsSig()
3355{
3356 return &s_npCommunicationSignature;
3357}
3358
3359int SQRNetworkManager_PS3::GetOldMask(SceNpMatching2RoomMemberId memberId)
3360{
3361 int oldMask = 0;
3362 for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ )
3363 {
3364 if( m_roomSyncData.players[i].m_roomMemberId == memberId )
3365 {
3366 oldMask |= (1 << m_roomSyncData.players[i].m_localIdx);
3367 }
3368 }
3369 return oldMask;
3370}
3371
3372int SQRNetworkManager_PS3::GetAddedMask(int newMask, int oldMask)
3373{
3374 return newMask & ~oldMask;
3375}
3376
3377int SQRNetworkManager_PS3::GetRemovedMask(int newMask, int oldMask)
3378{
3379 return oldMask & ~newMask;
3380}
3381
3382
3383void SQRNetworkManager_PS3::GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam )
3384{
3385 static SceNpMatching2GetRoomDataExternalListRequest reqParam;
3386 static SceNpMatching2RoomId aRoomId[1];
3387 static SceNpMatching2AttributeId attr[1];
3388
3389 // 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).
3390 // NULL parameters therefore basically represents an attempt to retry the last sceNpMatching2GetRoomDataExternalList
3391 if( extData != NULL )
3392 {
3393 aRoomId[0] = roomId;
3394 attr[0] = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID;
3395
3396 memset(&reqParam, 0, sizeof(reqParam));
3397 reqParam.roomId = aRoomId;
3398 reqParam.roomIdNum = 1;
3399 reqParam.attrIdNum = 1;
3400 reqParam.attrId = attr;
3401
3402 m_FriendSessionUpdatedFn = FriendSessionUpdatedFn;
3403 m_pParamFriendSessionUpdated = pParam;
3404 m_pExtDataToUpdate = extData;
3405 }
3406
3407 // Check there's a valid matching context and possibly recreate here
3408 if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) )
3409 {
3410 // No matching context, and failed to try and make one. We're really broken here.
3411 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated);
3412 return;
3413 }
3414
3415 // 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.
3416 if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT )
3417 {
3418 app.DebugPrintf("Having to recreate matching context, setting state to SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT\n");
3419 return;
3420 }
3421
3422 int ret = sceNpMatching2GetRoomDataExternalList( m_matchingContext, &reqParam, NULL, &m_roomDataExternalListRequestId );
3423
3424 // 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
3425 // to destroy the context first.
3426 if( ( ret == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment
3427 ( ret == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error
3428 {
3429 sceNpMatching2DestroyContext(m_matchingContext);
3430 m_matchingContextValid = false;
3431 if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) )
3432 {
3433 // No matching context, and failed to try and make one. We're really broken here.
3434 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated);
3435 return;
3436 };
3437 // 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.
3438 if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT )
3439 {
3440 return;
3441 }
3442 }
3443
3444 if( ret != 0 )
3445 {
3446 m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated);
3447 }
3448}
3449
3450
3451#ifdef _CONTENT_PACKAGE
3452bool SQRNetworkManager_PS3::ForceErrorPoint(eSQRForceError error)
3453{
3454 return false;
3455}
3456#else
3457bool SQRNetworkManager_PS3::aForceError[SNM_FORCE_ERROR_COUNT] =
3458{
3459 false, // SNM_FORCE_ERROR_NP2_INIT
3460 false, // SNM_FORCE_ERROR_NET_INITIALIZE_NETWORK
3461 false, // SNM_FORCE_ERROR_NET_CTL_INIT
3462 false, // SNM_FORCE_ERROR_RUDP_INIT
3463 false, // SNM_FORCE_ERROR_NET_START_DIALOG
3464 false, // SNM_FORCE_ERROR_MATCHING2_INIT
3465 false, // SNM_FORCE_ERROR_REGISTER_NP_CALLBACK
3466 false, // SNM_FORCE_ERROR_GET_NPID
3467 false, // SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT
3468 false, // SNM_FORCE_ERROR_REGISTER_CALLBACKS
3469 false, // SNM_FORCE_ERROR_CONTEXT_START_ASYNC
3470 false, // SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA
3471 false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY_COUNT
3472 false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY
3473 false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST
3474 false, // SNM_FORCE_ERROR_LEAVE_ROOM
3475 false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL
3476 false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2
3477 false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT
3478 false, // SNM_FORCE_ERROR_CREATE_JOIN_ROOM
3479 false, // SNM_FORCE_ERROR_GET_SERVER_INFO
3480 false, // SNM_FORCE_ERROR_DELETE_SERVER_CONTEXT
3481 false, // SNM_FORCE_ERROR_SETSOCKOPT_0
3482 false, // SNM_FORCE_ERROR_SETSOCKOPT_1
3483 false, // SNM_FORCE_ERROR_SETSOCKOPT_2
3484 false, // SNM_FORCE_ERROR_SOCK_BIND
3485 false, // SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT
3486 false, // SNM_FORCE_ERROR_RUDP_BIND
3487 false, // SNM_FORCE_ERROR_RUDP_INIT2
3488 false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA
3489 false, // SNM_FORCE_ERROR_GET_SERVER_INFO_DATA
3490 false, // SNM_FORCE_ERROR_GET_WORLD_INFO_DATA
3491 false, // SNM_FORCE_ERROR_GET_CREATE_JOIN_ROOM_DATA
3492 false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST_DATA
3493 false, // SNM_FORCE_ERROR_GET_JOIN_ROOM_DATA
3494 false, // SNM_FORCE_ERROR_GET_ROOM_MEMBER_DATA_INTERNAL
3495 false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA2
3496 false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK
3497 false, // SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK
3498 false, // SNM_FORCE_ERROR_UPDATED_ROOM_DATA
3499 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1
3500 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2
3501 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3
3502 false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4
3503 false, // SNM_FORCE_ERROR_GET_WORLD_INFO_LIST
3504 false, // SNM_FORCE_ERROR_JOIN_ROOM
3505};
3506
3507bool SQRNetworkManager_PS3::ForceErrorPoint(eSQRForceError err)
3508{
3509 return aForceError[err];
3510}
3511#endif
3512
3513void SQRNetworkManager_PS3::AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed/*=false*/)
3514{
3515 s_SignInCompleteCallbackFn = SignInCompleteCallbackFn;
3516 s_signInCompleteCallbackIfFailed = callIfFailed;
3517 s_SignInCompleteParam = pParam;
3518
3519 CellNetCtlNetStartDialogParam param;
3520 param.cid = 0;
3521 param.size = sizeof(param);
3522 param.type = CELL_NET_CTL_NETSTART_TYPE_NP;
3523 int ret = cellNetCtlNetStartDialogLoadAsync(¶m);
3524
3525 if( ret < 0 )
3526 {
3527 if(s_SignInCompleteCallbackFn) // MGH - added after crash on PS4
3528 {
3529 if( s_signInCompleteCallbackIfFailed )
3530 {
3531 s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0);
3532 }
3533 s_SignInCompleteCallbackFn = NULL;
3534 }
3535 }
3536}
3537
3538int SQRNetworkManager_PS3::SetRichPresence(const void *data, unsigned int options)
3539{
3540 const SceNpBasicPresenceDetails2 *newPresenceInfo = (const SceNpBasicPresenceDetails2 *)data;
3541
3542 s_lastPresenceInfo.struct_size = sizeof( SceNpBasicPresenceDetails2);
3543 memcpy( s_lastPresenceInfo.status, newPresenceInfo->status, SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX);
3544
3545 s_presenceStatusDirty = true;
3546 SendLastPresenceInfo();
3547
3548 // 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
3549 return 0;
3550}
3551
3552void SQRNetworkManager_PS3::UpdateRichPresenceCustomData(void *data, unsigned int dataBytes)
3553{
3554 assert(dataBytes <= SCE_NP_BASIC_MAX_PRESENCE_SIZE );
3555 memcpy(s_lastPresenceInfo.data, data, dataBytes);
3556 s_lastPresenceInfo.size = dataBytes;
3557
3558 s_presenceDataDirty = true;
3559 SendLastPresenceInfo();
3560}
3561
3562void SQRNetworkManager_PS3::TickRichPresence()
3563{
3564 if( s_resendPresenceCountdown )
3565 {
3566 s_resendPresenceCountdown--;
3567 if( s_resendPresenceCountdown == 0 )
3568 {
3569 SendLastPresenceInfo();
3570 }
3571 }
3572}
3573
3574void SQRNetworkManager_PS3::SendLastPresenceInfo()
3575{
3576 // Don't attempt to send if we are already waiting to resend
3577 if( s_resendPresenceCountdown ) return;
3578
3579 unsigned int options = 0;
3580 if( s_presenceStatusDirty ) options |= SCE_NP_BASIC_PRESENCE_OPTIONS_SET_STATUS;
3581 if( s_presenceDataDirty ) options |= SCE_NP_BASIC_PRESENCE_OPTIONS_SET_DATA;
3582
3583 int err = sceNpBasicSetPresenceDetails2( &s_lastPresenceInfo, options);
3584
3585 if( err == 0 )
3586 {
3587 s_presenceStatusDirty = false;
3588 s_presenceDataDirty = false;
3589 }
3590 else
3591 {
3592 s_resendPresenceCountdown = (20 * 65); // Bit over a minute before attempting to resend, we should get a new token every minute
3593 }
3594}
3595
3596void SQRNetworkManager_PS3::SetPresenceDataStartHostingGame()
3597{
3598 if( m_offlineGame )
3599 {
3600 SQRNetworkManager_PS3::UpdateRichPresenceCustomData(&c_presenceSyncInfoNULL, sizeof(SQRNetworkManager_PS3::PresenceSyncInfo) );
3601 }
3602 else
3603 {
3604 SQRNetworkManager_PS3::PresenceSyncInfo presenceInfo;
3605 CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData( &presenceInfo, m_joinExtData, m_room, m_serverId );
3606 SQRNetworkManager_PS3::UpdateRichPresenceCustomData(&presenceInfo, sizeof(SQRNetworkManager_PS3::PresenceSyncInfo) );
3607 }
3608}
3609
3610int SQRNetworkManager_PS3::GetJoiningReadyPercentage()
3611{
3612 if ( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) )
3613 {
3614 int completed = ( m_totalServerCount - m_serverCount ) - 1;
3615 int pc = ( completed * 100 ) / m_totalServerCount;
3616 if( pc < 0 ) pc = 0;
3617 if( pc > 100 ) pc = 100;
3618 return pc;
3619 }
3620 else
3621 {
3622 return 100;
3623 }
3624}