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