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