1 /*---------------------------------------------------------------------------*
2 Project: RevolutionDWC Demos
3 File: ./match_serverclient/src/main.c
4
5 Copyright 2005-2008 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 *---------------------------------------------------------------------------*/
14 /**
15 *
16 *
17 * Brief comment: This is a sample of server-client matchmaking.
18 *
19 * Performs server-client matchmaking over the internet.
20 * When matchmaking completes, communication is used to exchange positions between oneself and other parties.
21 * One's own position can be moved by pressing Up/Down/Left/Right on the +Control Pad.
22 *
23 * Version: 1.4.12 Revised the way in which Cancel is handled when it is passed to a callback function
24 * Version: 1.4.10: Removed login processing timeouts.
25 */
26 #include "../../common/include/common.h"
27
28 //---------------------------------------------------------
29 #define USE_RECV_TIMEOUT // Use DWC_SetRecvTimeoutTime
30
31 #ifdef USE_RECV_TIMEOUT
32 #define RECV_TIMEOUT_TIME (1000*30)
33 #define KEEPALIVE_INTERVAL (RECV_TIMEOUT_TIME/5)
34 #endif
35 //#define USE_RELIABLE // Perform reliable communications
36
37 // Constant Declarations
38 //---------------------------------------------------------
39
40 /// Enumerator that indicates the connection type
41 typedef enum{
42 SCCONNECT_NULL,
43 SCCONNECT_SERVER,
44 SCCONNECT_CLIENT,
45 SCCONNECT_GROUPID
46 }SCCONNECT;
47
48 /// Enumerator that indicates status
49 typedef enum{
50 STATE_INIT,
51 STATE_START,
52 STATE_UPDATE_FRIENDS,
53 STATE_ONLINE,
54 STATE_SETTING,
55 STATE_MATCHING,
56 STATE_SERVER,
57 STATE_CLIENT,
58 STATE_COMMUNICATING,
59 STATE_ERROR
60 }MATCHINGTESTSTATE;
61
62 /// Enumerator that indicates the present suspension state
63 typedef enum{
64 SUSPEND_NONE, // not suspended
65 SUSPEND_YES, // waiting in a suspended state
66 SUSPEND_NO // waiting in an unsuspended state
67 }SUSPEND_STATE;
68
69 #define MAX_PLAYERS 4 // Number of connected players including yourself
70 #define SIZE_SEND_BUFFER 512 // Size of send buffer
71 #define SIZE_RECV_BUFFER 4 * 1024 // Size of receive buffer
72 #define TIMEOUT (60 * 300) // Timeout duration (frames)
73 #define USER_TYPE_MALE 1 // Type used to attempt matchmaking
74 #define USER_TYPE_FEMALE 2 // Type used to attempt matchmaking
75 #define USER_MAX_MALE 2 // Maximum player count for the attempt type (male)
76 #define USER_MAX_FEMALE 2 // Maximum player count for the attempt type (female)
77 #define APP_CONNECTION_KEEPALIVE_TIME 30000
78
79 // Variable Declarations
80 //---------------------------------------------------------
81 static SCCONNECT s_connect; // Connection type
82 static MATCHINGTESTSTATE s_state; // Status
83 static SUSPEND_STATE suspend_state; // Suspension state
84 static int s_counter; // Timeout counter
85 static int s_currentPlayerNum; // The number of people currently connected.
86 static int s_serverIndex; // Index of the server to connect to.
87 static u32 s_groupID; // Group ID
88 static u8 force_stop = 0;
89 static BOOL useAttemptCallback = FALSE; // Flag used to attempt matchmaking
90 static u8 male = USER_TYPE_MALE; // Parameters used to attempt matchmaking (male: 1, female: 2)
91 static DWCTopologyType s_topologyType = DWC_TOPOLOGY_TYPE_HYBRID; // Network topology to use
92
93
94 // Position data structure exchanged through communications.
95 static struct Position
96 {
97 s32 xpos;
98 s32 ypos;
99 }
100 s_position[MAX_PLAYERS]; // Position data saved locally.
101
102 static u8 s_send_buf[ SIZE_SEND_BUFFER ]; // Send buffer
103 static u8 s_recv_buf[ MAX_PLAYERS-1 ][ SIZE_RECV_BUFFER ];// Receive buffer
104
105 /// Structure that contains the player information
106 static struct tagPlayerInfo
107 {
108
109 /// User data
110 DWCUserData userdata;
111
112 /// Friend data
113 DWCFriendData friendlist[FRIEND_LIST_LEN];
114
115 }
116 s_playerinfo ATTRIBUTE_ALIGN(32);
117
118 // Application-specific data sample to pass to the communication callback
119 typedef struct tagCommunicationData
120 {
121 s32 count;
122 }
123 CommunicationData;
124 CommunicationData s_commData;
125
126
127 // Function Prototype
128 //---------------------------------------------------------
129 static BOOL LoadFromNAND( void );
130 static void DispFriendList( void );
131 static void loginCallback(DWCError error, int profileID, void * param);
132 static void newClientCallback(int index, void* param );
133 static void matchedSCCallback(DWCError error, BOOL cancel, BOOL self, BOOL isServer, int index, void* param );
134 static void closeCallback(DWCError error, BOOL isLocal, BOOL isServer, u8 aid, int index, void* param);
135 static void sendCallback( int size, u8 aid, void* param );
136 static void recvCallback( u8 aid, u8* buffer, int size, void* param );
137 static void suspendCallback(DWCSuspendResult result, BOOL suspend, void* data);
138 static BOOL connectAttemptCallback( u8* data, void* param );
139
140
141 /**
142 * Load data from NAND
143 *
144 * Loads the user data and the friend roster from NAND.
145 *
146 * Return value: TRUE: There is user data in NAND flash memory.
147 * Return value: FALSE: There is no user data in NAND flash memory.
148 */
LoadFromNAND(void)149 BOOL LoadFromNAND( void )
150 {
151
152 // Load from NAND
153 //
154 DWCDemoPrintf("Loading from NAND...\n");
155
156 DWCDemoLoadNAND( "playerinfo",
157 0,
158 (u8*)&s_playerinfo,
159 sizeof( s_playerinfo ) );
160
161 // Check if user data is valid
162 //
163 if ( DWC_CheckUserData( &s_playerinfo.userdata ) )
164 {
165
166 // It was valid, so output the user data and return TRUE
167 DWC_ReportUserData( &s_playerinfo.userdata );
168 return TRUE;
169
170 }
171
172 // If valid user data had not been saved
173 //
174 // This demo cannot do anything if there are no friends
175 //
176 DWCDemoPrintf( "No valid userdata found.\n" );
177
178 return FALSE;
179 }
180
181 /**
182 * Display the friend roster.
183 */
DispFriendList(void)184 void DispFriendList( void )
185 {
186 int i;
187
188 DWCDemoPrintf( "\nFriend list\n");
189
190 for ( i = 0; i < FRIEND_LIST_LEN; i++ )
191 {
192
193 if ( !DWC_IsValidFriendData( &s_playerinfo.friendlist[i] ) )
194 continue;
195
196 DWCDemoPrintf( "Index: %d (%s) => ", i, DWCDemoGetFriendStatusString(&s_playerinfo.friendlist[i]) );
197 DWC_ReportFriendData( &s_playerinfo.userdata, &s_playerinfo.friendlist[i] );
198 }
199
200 DWCDemoPrintf( "\n");
201
202 }
203
204 /**
205 * This is the callback invoked after data transmission.
206 */
sendCallback(int size,u8 aid,void * param)207 static void sendCallback( int size, u8 aid, void* param )
208 {
209 CommunicationData* commData = (CommunicationData*)param;
210 commData->count++; // Add if data was sent
211 DWCDemoPrintf( "Send Callback aid:%d, size:%d mycount:%d\n", aid, size, commData->count );
212 }
213
214 /**
215 * This is the callback invoked at data reception.
216 */
recvCallback(u8 aid,u8 * buffer,int size,void * param)217 static void recvCallback( u8 aid, u8* buffer, int size, void* param )
218 {
219 //
220 // Save the received data.
221 //
222 // Eight bytes of data were sent in; storing a 4-byte X coordinate and a 4-byte Y coordinate.
223 //
224 if ( size == 8 )
225 {
226 CommunicationData* commData = (CommunicationData*)param;
227 commData->count--; // Subtract if data was received
228 s_position[ aid ].xpos = (s32)DWCi_LEtoHl(((u32*)buffer)[0]);
229 s_position[ aid ].ypos = (s32)DWCi_LEtoHl(((u32*)buffer)[1]);
230
231 DWCDemoPrintf( "[aid:%d] x:% 8d y:% 8d mycount:%d\n",
232 aid,
233 s_position[ aid ].xpos,
234 s_position[ aid ].ypos,
235 commData->count);
236 }
237 else
238 {
239
240 DWCDemoPrintf( "invalid recv data size\n" );
241
242 }
243 }
244
245 /**
246 * The callback function invoked at disconnection.
247 */
closeCallback(DWCError error,BOOL isLocal,BOOL isServer,u8 aid,int index,void * param)248 static void closeCallback(DWCError error, BOOL isLocal, BOOL isServer, u8 aid, int index, void* param)
249 {
250 (void)isLocal;
251 (void)isServer;
252 (void)param;
253
254 // reduce the number
255 --s_currentPlayerNum;
256
257 DWCDemoPrintf( "Closed Callback err:%d, aid:%d, idx:%d\n",
258 error, aid, index );
259 }
260
261 // Friend roster synchronization completion callback
updateServersCallback(DWCError error,BOOL isChanged,void * param)262 static void updateServersCallback(DWCError error, BOOL isChanged, void* param)
263 {
264 (void)param;
265 if (error == DWC_ERROR_NONE)
266 {
267 // Successful synchronous processing of friend roster.
268 // The friend roster must be saved if it has been modified
269 if (isChanged)
270 {
271 // Save to NAND
272 DWCDemoSaveNAND("playerinfo", 0, (u8*)&s_playerinfo, sizeof(s_playerinfo));
273 }
274 s_state = STATE_ONLINE;
275
276 }
277 else
278 {
279 DWCDemoPrintf("Update friend data Failed\n");
280 s_state = STATE_ERROR;
281 }
282 }
283
284 // Friend roster delete callback
deleteFriendCallback(int deletedIndex,int srcIndex,void * param)285 static void deleteFriendCallback(int deletedIndex, int srcIndex, void* param)
286 {
287 (void)param;
288 DWC_Printf(DWC_REPORTFLAG_TEST, "friend[%d] was deleted (equal friend[%d]).\n", deletedIndex, srcIndex);
289 // Save to NAND
290 DWCDemoSaveNAND("playerinfo", 0, (u8*)&s_playerinfo, sizeof(s_playerinfo));
291 }
292
293 /**
294 * The callback function called during login
295 */
loginCallback(DWCError error,int profileID,void * param)296 static void loginCallback(DWCError error, int profileID, void * param)
297 {
298 (void)param;
299
300 DWCDemoPrintf( "Login callback : %d\n", error );
301
302 if ( error == DWC_ERROR_NONE )
303 {
304
305 // When the login is successful
306 //
307 DWCDemoPrintf( "Login Success. ProfileID=%d\n", profileID );
308
309 // Specify the callback function.
310 DWC_SetConnectionClosedCallback( closeCallback, NULL );
311 DWC_SetUserSendCallback( sendCallback, &s_commData );
312 DWC_SetUserRecvCallback( recvCallback, &s_commData );
313
314 // Synchronize the local friend roster with the server friend roster
315 if (!DWC_UpdateServersAsync(
316 NULL,
317 updateServersCallback, NULL,
318 NULL, NULL,
319 deleteFriendCallback, NULL))
320 {
321 // Failed to start synchronization
322 DWCDemoPrintf( "DWC_UpdateServersAsync() call Failed\n" );
323 s_state = STATE_ERROR;
324 return;
325 }
326 s_state = STATE_UPDATE_FRIENDS;
327
328 }
329 else
330 {
331
332 // When the login failed
333 //
334 DWCDemoPrintf( "Login Failed\n" );
335 s_state = STATE_ERROR;
336 }
337 }
338
339 /**
340 * The callback function invoked whenever a client connection is started.
341 */
newClientCallback(int index,void * param)342 static void newClientCallback(int index, void* param )
343 {
344 (void)param;
345
346 DWCDemoPrintf( "New Client is connecting(idx %d)...\n", index);
347 }
348
349 /**
350 * The callback function invoked whenever a client connection is completed.
351 */
matchedSCCallback(DWCError error,BOOL cancel,BOOL self,BOOL isServer,int index,void * param)352 static void matchedSCCallback(DWCError error, BOOL cancel, BOOL self, BOOL isServer, int index, void* param )
353 {
354 (void)param;
355
356 DWCDemoPrintf( "Match err:%d, cancel:%d, self:%d isServer:%d index:%d \n", error, cancel, self, isServer, index );
357
358 if (cancel == TRUE)
359 {
360 if (self == TRUE && isServer == FALSE)
361 {
362 s_state = STATE_ONLINE;
363 }
364 DWCDemoPrintf( "canceled\n" );
365 return;
366 }
367
368 if ( error == DWC_ERROR_NONE )
369 {
370
371 s_currentPlayerNum++;
372
373 DWCDemoPrintf( "%d players connected.\n", s_currentPlayerNum );
374
375 /* if(s_currentPlayerNum == MAX_PLAYERS)*/
376 {
377
378 u8* pAidList;
379 int num = DWC_GetAIDList(&pAidList);
380 int i, j;
381 for (i = 0, j = 0; i < num; i++)
382 {
383 if (pAidList[i] == DWC_GetMyAID())
384 {
385 j++;
386 continue;
387 }
388
389 // Set a receive buffer for AIDs other than the local host's
390 DWC_SetRecvBuffer( pAidList[i], &s_recv_buf[i-j], SIZE_RECV_BUFFER );
391 #ifdef USE_RECV_TIMEOUT
392 DWC_SetRecvTimeoutTime(pAidList[i], RECV_TIMEOUT_TIME);
393 #endif
394 }
395
396 s_state = STATE_COMMUNICATING;
397 }
398 // Use this timing to get the group ID
399 s_groupID = DWC_GetGroupID();
400 }
401 else if (error == DWC_ERROR_SC_CONNECT_BLOCK)
402 {
403 // When local host was fumbled because of a difference in connection speed
404 int errorCode;
405 DWCErrorType errorType;
406
407 DWC_GetLastErrorEx(&errorCode, &errorType);
408 DWCDemoPrintf("DWC_GetLastErrorEx ErrorCode:%d ErrorType:%d\n", errorCode, (int)errorType);
409 DWCDemoPrintf( "DWC_ERROR_SC_CONNECT_BLOCK\n" );
410 DWC_ClearError();
411 s_state = STATE_ONLINE;
412 }
413 else if ( error == DWC_ERROR_NOT_FRIEND_SERVER )
414 {
415 DWCDemoPrintf( "Server not found\n" );
416
417 s_state = STATE_ERROR;
418 }
419 else
420 {
421 DWCDemoPrintf( "Match Error\n" );
422
423 s_state = STATE_ERROR;
424 }
425
426 }
427
428 // Callback to suspend matchmaking
stopSCCallback(void * param)429 static void stopSCCallback(void* param)
430 {
431 (void)param;
432 DWCDemoPrintf( "stopSCCallback called.\n");
433 }
434
435 // Callback invoked during an NN check
connectAttemptCallback(u8 * data,void * param)436 static BOOL connectAttemptCallback( u8* data, void* param )
437 {
438 // Check with the condition that there are no more than 2 males and 2 females
439 int numAid;
440 u8* aidList;
441 u8 userData[DWC_CONNECTION_USERDATA_LEN];
442 int i;
443 int maleCount = 0; // Number of males
444 int femaleCount = 0; // Number of females
445
446 (void)param;
447
448 numAid = DWC_GetAIDList(&aidList);
449 for (i = 0; i < numAid; i++)
450 {
451 DWC_GetConnectionUserData(aidList[i], userData);
452 if (userData[0] == USER_TYPE_MALE)
453 maleCount++;
454 else if (userData[0] == USER_TYPE_FEMALE)
455 femaleCount++;
456 }
457
458 if (data[0] == USER_TYPE_MALE && maleCount < USER_MAX_MALE)
459 return TRUE;
460 else if (data[0] == USER_TYPE_FEMALE && femaleCount < USER_MAX_FEMALE)
461 return TRUE;
462 else
463 return FALSE;
464 }
465
466 // Callback invoked when suspending is complete
suspendCallback(DWCSuspendResult result,BOOL suspend,void * data)467 static void suspendCallback(DWCSuspendResult result, BOOL suspend, void* data)
468 {
469 (void)data;
470 if (result == DWC_SUSPEND_SUCCESS)
471 {
472 DWCDemoPrintf("<SAMPLE> suspend successfly. suspend is (%s).\n", suspend ? "TRUE" : "FALSE");
473 }
474 else
475 {
476 DWCDemoPrintf("<SAMPLE> suspend error! result is (%d). suspend is (%s).\n", result, suspend ? "TRUE" : "FALSE");
477 }
478 suspend_state = SUSPEND_NONE;
479 }
480
481 #ifdef USE_RECV_TIMEOUT
userRecvTimeoutCallback(u8 aid,void * param)482 static void userRecvTimeoutCallback(u8 aid, void* param)
483 {
484 CommunicationData* commData = (CommunicationData*)param;
485 DWCDemoPrintf("aid (%d) is timeout. mycount is %d\n", aid, commData->count);
486 }
487 #endif
488
489 /**
490 * Main
491 */
DWCDemoMain()492 void DWCDemoMain()
493 {
494 int i;
495 u32 padlast; // The previous pressed state
496 u32 pad = 0; // The current pressed state
497 u32 padtrig; // Keys whose state changed from the last time
498 u32 padpressed; // Newly-pressed keys
499 u32 padreleased; // Newly-released keys
500 u8 userData[DWC_CONNECTION_USERDATA_LEN];
501
502 MATCHINGTESTSTATE lastState; // State in the previous cycle.
503 BOOL firstTime = TRUE; // When the state has changed.
504
505 // Initializes the position data.
506 //------------------------------
507 for ( i=0; i<MAX_PLAYERS; i++ )
508 {
509
510 s_position[ i ].xpos = 0;
511 s_position[ i ].ypos = 0;
512
513 }
514
515 // Get user data and the Friend Roster.
516 //------------------------------
517 if ( !LoadFromNAND() )
518 {
519
520 // Quit
521 return;
522
523 }
524
525 // Perform friend match initialization
526 //------------------------------
527 DWC_InitFriendsMatch( NULL,
528 &s_playerinfo.userdata,
529 GAME_PRODUCTID,
530 GAME_NAME,
531 GAME_SECRET_KEY,
532 0,
533 0,
534 s_playerinfo.friendlist,
535 FRIEND_LIST_LEN );
536
537 // Start login processing
538 //------------------------------
539 DWC_LoginAsync( (u16*)L"name",
540 NULL,
541 loginCallback,
542 NULL );
543
544 // Initialize the status.
545 s_connect = SCCONNECT_NULL;
546 s_state = STATE_START;
547 lastState = STATE_INIT;
548 s_counter = 0;
549 s_currentPlayerNum = 1;
550 s_serverIndex = 0;
551 //s_maxPlayerNum = 2;
552
553 #if 0
554 DWC_SetLoginLatencyFlag(TRUE);
555 #endif
556
557 #ifdef USE_RECV_TIMEOUT
558 DWC_SetUserRecvTimeoutCallback(userRecvTimeoutCallback, &s_commData);
559 #endif
560 DWC_SetConnectionKeepAliveTime(APP_CONNECTION_KEEPALIVE_TIME);
561
562 // Main loop
563 while (1)
564 {
565 // Key input
566 // Save the current state
567 padlast = pad;
568 pad = DWCDemoPadRead();
569 padtrig = padlast ^ pad;
570 padpressed = padtrig & pad;
571 padreleased = padtrig & ~pad;
572
573 if ( s_state >= STATE_MATCHING )
574 {
575 if ( force_stop )
576 {
577 if ( padpressed & DWCDEMO_KEY_Z )
578 {
579 DWCDemoPrintf("force stop OFF.\n");
580 force_stop = 0;
581 }
582 else
583 continue;
584 }
585 else
586 {
587 if ( padpressed & DWCDEMO_KEY_Z )
588 {
589 DWCDemoPrintf("force stop ON.\n");
590 force_stop = 1;
591 }
592 }
593 }
594
595 DWC_ProcessFriendsMatch();
596
597 if (DWC_GetLastErrorEx(NULL, NULL) != DWC_ERROR_NONE)
598 goto error;
599
600 firstTime = lastState != s_state;
601 lastState = s_state;
602
603 switch ( s_state )
604 {
605
606 // Processing login
607 //
608 case STATE_START:
609
610 // Wait until loginCallback() is invoked.
611 break;
612
613 // updating the friend roster
614 case STATE_UPDATE_FRIENDS:
615 break;
616
617 // Choosing of the connection type in progress
618 //
619 case STATE_ONLINE:
620 {
621 if ( firstTime)
622 {
623 s_connect = SCCONNECT_NULL;
624 DWCDemoPrintf("\n");
625 DWCDemoPrintf("--------------------------------\n");
626 DWCDemoPrintf("- Server Client Match Demo -\n");
627 DWCDemoPrintf("--------------------------------\n");
628 DWCDemoPrintf("A: Set up server\n");
629 DWCDemoPrintf("Z: Set up server(male)\n");
630 DWCDemoPrintf("B: Connect to server\n");
631 DWCDemoPrintf("X: Connect to server from groupID\n");
632 DWCDemoPrintf(" groupID is %d\n", s_groupID);
633 //DWCDemoPrintf("U/D:Change debug latency level\n");
634 DWCDemoPrintf("LEFT: Start with attempt callback(male)\n");
635 DWCDemoPrintf("RIGHT: Start with attempt callback(female)\n");
636 DWCDemoPrintf("START: Set Hybrid mode\n");
637 DWCDemoPrintf("Y: Set Star mode\n");
638 DWCDemoPrintf("UP: Set Fullmesh mode\n");
639 DWCDemoPrintf("DOWN: exit demo\n");
640 DWCDemoPrintf("\n");
641
642 suspend_state = SUSPEND_NONE;
643 }
644
645 if ( padpressed & DWCDEMO_KEY_A )
646 {
647 useAttemptCallback = FALSE;
648 s_connect = SCCONNECT_SERVER;
649 }
650 if ( padpressed & DWCDEMO_KEY_Z )
651 {
652 DWCDemoPrintf("Z: Set up server(male)\n");
653 s_connect = SCCONNECT_SERVER;
654 useAttemptCallback = TRUE;
655 male = USER_TYPE_MALE;
656 }
657 if ( padpressed & DWCDEMO_KEY_B )
658 {
659 useAttemptCallback = FALSE;
660 s_connect = SCCONNECT_CLIENT;
661 }
662 if ( padpressed & DWCDEMO_KEY_X )
663 {
664 // Uses the previous state for group ID connection
665 s_connect = SCCONNECT_GROUPID;
666 }
667 if ( padpressed & DWCDEMO_KEY_START )
668 {
669 DWCDemoPrintf("DWC_TOPOLOGY_TYPE_HYBRID set.\n");
670 s_topologyType = DWC_TOPOLOGY_TYPE_HYBRID;
671 firstTime = TRUE;
672 break;
673 }
674 else if ( padpressed & DWCDEMO_KEY_Y )
675 {
676 DWCDemoPrintf("DWC_TOPOLOGY_TYPE_STAR set.\n");
677 s_topologyType = DWC_TOPOLOGY_TYPE_STAR;
678 firstTime = TRUE;
679 break;
680 }
681 else if ( padpressed & DWCDEMO_KEY_UP )
682 {
683 DWCDemoPrintf("DWC_TOPOLOGY_TYPE_FULLMESH set.\n");
684 s_topologyType = DWC_TOPOLOGY_TYPE_FULLMESH;
685 firstTime = TRUE;
686 break;
687 }
688 else if ( padpressed & DWCDEMO_KEY_LEFT )
689 {
690 DWCDemoPrintf("Start with attempt callback(male)\n");
691 useAttemptCallback = TRUE;
692 s_state = STATE_MATCHING;
693 firstTime = TRUE;
694 s_connect = SCCONNECT_CLIENT;
695 male = USER_TYPE_MALE;
696 }
697 else if ( padpressed & DWCDEMO_KEY_RIGHT )
698 {
699 DWCDemoPrintf("Start with attempt callback(female)\n");
700 useAttemptCallback = TRUE;
701 s_state = STATE_MATCHING;
702 firstTime = TRUE;
703 s_connect = SCCONNECT_CLIENT;
704 male = USER_TYPE_FEMALE;
705 }
706 else if ( padpressed & DWCDEMO_KEY_DOWN )
707 {
708 // Quit
709 goto exit;
710 }
711
712 if ( s_connect != SCCONNECT_NULL) s_state = STATE_SETTING;
713
714 break;
715 }
716
717 // Configuring
718 //
719 case STATE_SETTING:
720 {
721 switch ( s_connect )
722 {
723 case SCCONNECT_SERVER:
724
725 s_state = STATE_MATCHING;
726 break;
727
728 // Specify the Friend Roster index of the server to connect to.
729 case SCCONNECT_CLIENT:
730 if ( firstTime )
731 {
732 DispFriendList();
733
734 DWCDemoPrintf("Arrow: Select index\n");
735 DWCDemoPrintf("A: OK\n");
736 DWCDemoPrintf("B: Cancel\n");
737 }
738
739 if ( padpressed & DWCDEMO_KEY_UP ) s_serverIndex++;
740 if ( padpressed & DWCDEMO_KEY_DOWN ) s_serverIndex--;
741
742 if ( s_serverIndex == FRIEND_LIST_LEN) s_serverIndex = FRIEND_LIST_LEN-1;
743 if ( s_serverIndex < 0) s_serverIndex = 0;
744
745 if ( firstTime || padpressed & (DWCDEMO_KEY_A | DWCDEMO_KEY_UP | DWCDEMO_KEY_DOWN))
746 {
747 DWCDemoPrintf("Specify server's friend list index: %d(%u:%s)\n", s_serverIndex,
748 s_playerinfo.friendlist[s_serverIndex].gs_profile_id.id,
749 DWCDemoGetFriendStatusString(&s_playerinfo.friendlist[s_serverIndex]));
750
751 }
752 else if (padpressed & DWCDEMO_KEY_B)
753 {
754 s_state = STATE_ONLINE;
755 firstTime = TRUE;
756 }
757 break;
758
759 // Either select the current group ID or input a new one
760 case SCCONNECT_GROUPID:
761 {
762 if ( s_groupID )
763 {
764 // Display it if it exists
765 DWCDemoPrintf("A: %d\n", s_groupID);
766 s_state = STATE_MATCHING;
767 }
768 else
769 {
770 // Return to the menu for the moment
771 DWCDemoPrintf("GroupID is None.\n", s_groupID);
772 s_state = STATE_ONLINE;
773 s_connect = SCCONNECT_NULL;
774 }
775 }
776 break;
777 }
778
779 if ( padpressed & DWCDEMO_KEY_A)
780 {
781 s_state = STATE_MATCHING;
782 break;
783 }
784
785 break;
786 }
787
788 // Matchmaking in progress
789 //
790 case STATE_MATCHING:
791 {
792 if ( firstTime )
793 {
794 // Set values depending on the UserType
795 userData[0] = male;
796 // unused, but for testing
797 userData[1] = 5;
798 userData[2] = 120;
799 userData[3] = 254;
800 switch ( s_connect )
801 {
802 case SCCONNECT_SERVER:
803 DWCDemoPrintf("Waiting for clients...\n");
804
805 // Become a server.
806 DWC_SetupGameServer( s_topologyType, // Connection network structure
807 (u8)MAX_PLAYERS, // The number of people to matchmake.
808 matchedSCCallback, //
809 NULL, //
810 newClientCallback, //
811 NULL,
812 useAttemptCallback ? connectAttemptCallback : NULL,
813 userData,
814 NULL ); //
815
816 s_state = STATE_COMMUNICATING;
817 firstTime = TRUE;
818 break;
819
820 case SCCONNECT_CLIENT:
821 DWCDemoPrintf("Connecting to server...\n");
822
823 // Connect to the server.
824 DWC_ConnectToGameServerAsync( s_topologyType, // Connection network structure
825 s_serverIndex, // Friend Roster index for the connection target server.
826 matchedSCCallback, //
827 NULL, //
828 newClientCallback, //
829 NULL,
830 useAttemptCallback ? connectAttemptCallback : NULL,
831 userData,
832 NULL ); //
833
834 break;
835
836 case SCCONNECT_GROUPID:
837 DWCDemoPrintf("Connecting to server from groupID...\n");
838
839 // Connect to the server through a group ID
840 DWC_ConnectToGameServerByGroupID( s_topologyType, // Connection network structure
841 s_groupID,
842 matchedSCCallback,
843 NULL,
844 newClientCallback,
845 NULL,
846 useAttemptCallback ? connectAttemptCallback : NULL,
847 userData,
848 NULL);
849 break;
850 }
851 }
852 if (padpressed & DWCDEMO_KEY_B)
853 {
854 if (DWC_CancelMatch() == TRUE)
855 {
856 DWCDemoPrintf("DWC_CancelMatch() called.\n");
857 s_state = STATE_ONLINE;
858 firstTime = TRUE;
859 }
860 else
861 {
862 DWCDemoPrintf("cannot call DWC_CancelMatch() now.\n");
863 }
864 break;
865 }
866 break;
867 }
868
869 // Communicating
870 //
871 case STATE_COMMUNICATING:
872
873 {
874 struct Position sendPos;
875 u8 me;
876
877 if ( firstTime )
878 {
879 DWCDemoPrintf("Push Arrow to send position.\n");
880 DWCDemoPrintf("Push [B] to call DWC_CloseAllConnectionsHard().\n");
881 }
882
883
884 // Get one's own AID.
885 me = DWC_GetMyAID();
886
887 if ( pad & DWCDEMO_KEY_UP ) s_position[ me ].ypos--;
888 if ( pad & DWCDEMO_KEY_DOWN ) s_position[ me ].ypos++;
889 if ( pad & DWCDEMO_KEY_LEFT ) s_position[ me ].xpos--;
890 if ( pad & DWCDEMO_KEY_RIGHT ) s_position[ me ].xpos++;
891
892 // Send a new position to the communication partner when there is input.
893 #ifdef USE_RECV_TIMEOUT
894 {
895 // Forcibly send data at intervals shorter than the receive timeout interval.
896 static int lastKeepAliveSent = 0;
897 int now = (int)DWCi_Np_TicksToMilliSeconds(DWCi_Np_GetTick());
898 if (lastKeepAliveSent == 0 || (now - lastKeepAliveSent) > KEEPALIVE_INTERVAL)
899 {
900 pad |= DWCDEMO_KEY_UP;
901 lastKeepAliveSent = now;
902 }
903 }
904 #endif
905 if ( pad & ( DWCDEMO_KEY_UP | DWCDEMO_KEY_DOWN
906 | DWCDEMO_KEY_LEFT | DWCDEMO_KEY_RIGHT ) )
907 {
908
909 sendPos.xpos = (s32)DWCi_HtoLEl((u32)s_position[ me ].xpos);
910 sendPos.ypos = (s32)DWCi_HtoLEl((u32)s_position[ me ].ypos);
911
912 // Send one's own position to all other parties.
913 #ifdef USE_RELIABLE
914 DWC_SendReliableBitmap( DWC_GetAIDBitmap(),
915 (const void*)&sendPos,
916 sizeof( s32 ) * 2 );
917 #else
918 DWC_SendUnreliableBitmap( DWC_GetAIDBitmap(),
919 (const void*)&sendPos,
920 sizeof( s32 ) * 2 );
921 #endif
922
923 }
924
925 // Disconnect communications and return to ONLINE if the cancel button is pressed.
926 //
927 if ( pad & DWCDEMO_KEY_B )
928 {
929 DWC_CloseAllConnectionsHard();
930 s_state = STATE_ONLINE;
931 s_connect = SCCONNECT_NULL;
932 }
933
934 // Suspend matchmaking with Y
935 if ( pad & DWCDEMO_KEY_Y )
936 {
937 // Temporarily suspend
938 DWC_RequestSuspendMatchAsync(TRUE, suspendCallback, NULL);
939 suspend_state = SUSPEND_YES;
940 }
941 else if ( pad & DWCDEMO_KEY_X )
942 {
943 // Begin accepting again
944 DWC_RequestSuspendMatchAsync(FALSE, suspendCallback, NULL);
945 suspend_state = SUSPEND_NO;
946 }
947
948 }
949 break;
950
951 // Error occurred
952 //
953 case STATE_ERROR:
954
955 goto error;
956
957 }
958
959 // Display information on the bottom-most line
960 if (DWC_GetServerAID() != DWC_INVALID_AID)
961 DWCDemoPrintfToFixedRow("GetServerAID=%d,IsServerMyself=%s", DWC_GetServerAID(), DWC_IsServerMyself() ? "Y" : "N");
962 else
963 DWCDemoPrintfToFixedRow("Svr:unknown.");
964
965
966 #ifdef _WIN32
967 // Display information in the title bar
968 DWCViewInfoToTitle();
969 #endif
970 DWCDemoUpdate();
971
972 }
973
974 exit:
975
976 // Quit
977 DWC_ShutdownFriendsMatch();
978
979 return;
980
981 error:
982
983 // Error processing
984 {
985
986 int errorCode;
987 DWCErrorType errorType;
988
989 if ( DWC_GetLastErrorEx( &errorCode, &errorType ) != DWC_ERROR_NONE )
990 {
991
992 DWCDemoPrintf( "DWC_GetLastErrorEx ErrorCode:%d ErrorType:%d\n",
993 errorCode,
994 (int)errorType );
995
996 // Clear the errors.
997 DWC_ClearError();
998
999 }
1000
1001 }
1002
1003 // Quit
1004
1005 DWC_ShutdownFriendsMatch();
1006
1007 return;
1008
1009 }
1010