1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     demo_FriendsMain.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  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   $Rev:$
14  *---------------------------------------------------------------------------*/
15 
16  /* =======================================================================
17     Note:
18     To exchange presence with friends in this demo, a friend relationship must have already been created in advance in the HOME Menu.
19     ======================================================================= */
20 
21 #include <nn.h>
22 #include <nn/ac.h>
23 #include <nn/hid.h>
24 #include <nn/friends.h>
25 #include <nn/applet.h>
26 
27 namespace {
28 
29 const char16* DESCRIPTION_MESSAGE[] = {L"MESSAGE 1", L"MESSAGE 2", L"MESSAGE 3"};
30 
31 static size_t STACK_SIZE = 4096;
32 static nn::os::Event s_TerminateEvent(false);
33 
34 // Displays error code related to the friends library.
PrintErrorCode(nn::Result result)35 void PrintErrorCode(nn::Result result)
36 {
37     u32 errorCode = nn::friends::ResultToErrorCode(result);
38     if(errorCode != 0)
39     {
40         NN_LOG("ERROR CODE %u\n", errorCode);
41     }
42     else
43     {
44         NN_LOG("ERROR CODE NOT DEFINED\n");
45     }
46 }
47 
48 // Updates friend presence data
PrintFriendPresence(nn::friends::FriendKey friendKey)49 void PrintFriendPresence(nn::friends::FriendKey friendKey)
50 {
51     nn::friends::FriendPresence presence;
52     nn::friends::GetFriendPresence(&presence, &friendKey, 1);
53 
54     if(presence.isValid)
55     {
56         // At launch time, all values for features other than the online flag are disabled.
57         NN_LOG("isOnline: %d\n", presence.isOnline);
58         NN_LOG("isInvitingMe: %d\n", presence.isInvitingMe);
59         NN_LOG("isValid: %d\n", presence.isValid);
60         NN_LOG("GameMode\n");
61         NN_LOG("  joinAvailabilityFlag: %08x\n", presence.gameMode.joinAvailabilityFlag);
62         NN_LOG("  joinGameId %u\n", presence.gameMode.joinGameId);
63         NN_LOG("  ownerPrincipalId %u\n", presence.gameMode.ownerPrincipalId);
64         NN_LOG("  joinGroupId %u\n", presence.gameMode.joinGroupId);
65         NN_LOG("\n");
66     }
67 }
68 
69 // Logs into NFS (Nintendo Friend Server)
Login()70 void Login()
71 {
72     nn::Result result;
73     nn::os::Event event(false);
74 
75     NN_LOG("START NFS LOGIN\n");
76 
77     result = nn::friends::Login(&event);
78     if(result.IsSuccess())
79     {
80         if (event.Wait(nn::fnd::TimeSpan::FromMinutes(2)))
81         {
82             result = nn::friends::GetLastResponseResult();
83 
84             if(result.IsSuccess())
85             {
86                 NN_LOG("NFS LOGIN SUCCESSFUL\n");
87             }
88             else
89             {
90                 PrintErrorCode( result );
91             }
92         }
93         else
94         {
95             // Since it has not been confirmed that the login process has failed, the request must be canceled
96             nn::friends::Logout();
97         }
98     }
99     else
100     {
101         PrintErrorCode( result );
102     }
103 
104     event.Finalize();
105 }
106 
107 // Handles the received EventNotification
HandleFriendsNotificationEvent(const nn::friends::EventNotification * pNotification)108 void HandleFriendsNotificationEvent(const nn::friends::EventNotification* pNotification)
109 {
110     switch(pNotification->type)
111     {
112         case nn::friends::NOTIFICATION_NONE:
113             NN_LOG("NOTIFICATION_NONE\n");
114             break;
115         case nn::friends::NOTIFICATION_ONLINE:
116             // NOTIFICATION_ONLINE is notified when connecting to a server to which a login request has been sent in the background even if one has not send a login request
117             //
118             NN_LOG("NOTIFICATION_ONLINE\n");
119             break;
120         case nn::friends::NOTIFICATION_OFFLINE:
121             // NOTIFICATION_OFFLINE may be notified even if one has not send a logout request, such as disconnection from the AP.
122             // In this case, one's own login request is automatically canceled.
123             NN_LOG("NOTIFICATION_OFFLINE\n");
124             break;
125         case nn::friends::NOTIFICATION_FRIEND_ONLINE:
126             NN_LOG("NOTIFICATION_FRIEND_ONLINE %u\n", pNotification->friendKey.principalId);
127             PrintFriendPresence(pNotification->friendKey);
128             break;
129         case nn::friends::NOTIFICATION_FRIEND_OFFLINE:
130             NN_LOG("NOTIFICATION_FRIEND_OFFLINE %u\n", pNotification->friendKey.principalId);
131             break;
132         case nn::friends::NOTIFICATION_BECOME_FRIEND:
133             NN_LOG("NOTIFICATION_BECOME_FRIEND %u\n", pNotification->friendKey.principalId);
134             break;
135         default:
136             break;
137     }
138 }
139 
140 // Thread for Event dispatch
EventDispatchThread()141 void EventDispatchThread()
142 {
143     NN_LOG( "Start EventDispatchThread.\n" );
144 
145     // Registers Event for friends
146     nn::os::Event friendsEvent(false);
147     nn::friends::AttachToEventNotification(&friendsEvent);
148 
149     nn::os::WaitObject* waitObjects[2];
150     waitObjects[0] = &friendsEvent;
151     waitObjects[1] = &s_TerminateEvent;
152 
153     const size_t NOTIFICATION_LIST_SIZE = 10;
154     nn::friends::EventNotification notificationList[ NOTIFICATION_LIST_SIZE ];
155 
156     bool processing = true;
157     while(processing)
158     {
159         s32 eventNumber = nn::os::WaitObject::WaitAny(waitObjects, 2);
160         switch(eventNumber)
161         {
162         case 0:  // Friend-related event
163             {
164                 size_t size = 0;
165 
166                 // Immediately after an event is signaled, another event may occur.
167                 // Here, acquisition continues until the maintained EventNotificaiton list is empty.
168                 while((size = nn::friends::GetEventNotification(notificationList, NOTIFICATION_LIST_SIZE)) != 0)
169                 {
170                     for(int i = 0; i < size; ++i)
171                     {
172                         HandleFriendsNotificationEvent( notificationList + i );
173                     }
174                 }
175             }
176             break;
177         case 1: // Completion notification
178             {
179                 NN_LOG("TERMINATE EVENT\n");
180                 processing = false;
181             }
182             break;
183         default:
184             break;
185         }
186     }
187 
188     friendsEvent.Finalize();
189     NN_LOG( "End EventDispatchThread.\n" );
190 }
191 
192 } // end of namespace
193 
nnMain(void)194 extern "C" void nnMain( void )
195 {
196     // Call only nn::applet::Enable to also allow execution from the HOME Menu
197     // HOME Menu transitions, POWER Button presses, and sleep are all unsupported
198     nn::applet::Enable();
199 
200     nn::Result result;
201 
202     /* =======================================================================
203        Pre-processing
204            Note: Network configuration process or other processes.
205             Unnecessary when using the presence feature in a state where the processing has already been performed or if the online feature is not used
206        ======================================================================== */
207 
208     // Connect to access point
209     NN_LOG("client: Initialize AC\n");
210     nn::fs::Initialize();
211     NN_UTIL_PANIC_IF_FAILED( nn::ac::Initialize() );
212     nn::ac::Config config;
213     nn::ac::CreateDefaultConfig( &config );
214 
215     NN_LOG("client: Connecting to AP...\n");
216     result = nn::ac::Connect( config );
217     if( result.IsSuccess() )
218     {
219         // Connection successful
220         NN_LOG("Established a connection to the access point.\n");
221     }
222     else
223     {
224         // Connection failure
225         NN_LOG("Failed to connect to the access point. You can NOT log into NFS.\n");
226     }
227 
228     /* =======================================================================
229        friends library initialization
230        ======================================================================== */
231     NN_UTIL_PANIC_IF_FAILED( nn::friends::Initialize() );
232 
233     // Start event dispatch thread
234     nn::os::Thread thread;
235     thread.StartUsingAutoStack( EventDispatchThread, STACK_SIZE );
236 
237     /* =======================================================================
238        Using the friends library offline
239        ======================================================================== */
240     // Output one's own data
241     {
242         NN_LOG("PrincipalId:%u\n", nn::friends::GetMyPrincipalId());
243 
244         nn::friends::Profile profile;
245         char16 screenName[nn::friends::SCREEN_NAME_SIZE];
246         nn::friends::MiiData mii;
247 
248         nn::friends::GetMyProfile(&profile);
249         NN_LOG("MY PROFILE\n");
250         NN_LOG("region:%u\n", profile.region);
251         NN_LOG("country:%u\n", profile.country);
252         NN_LOG("area:%u\n", profile.area);
253         NN_LOG("language:%u\n", profile.language);
254         NN_LOG("platform:%u\n", profile.platform);
255 
256         nn::friends::GetMyScreenName(screenName);
257         NN_LOG("screenname:%s\n", screenName);
258 
259         nn::friends::GetMyMii(&mii);
260         // The Mii library is required to use Mii.
261     }
262 
263     // Outputs the list of friends
264     // If one is offline, all friends are treated as being offline
265     {
266         NN_LOG("\nFRIEND INFO\n");
267 
268         size_t numOfFriend = 0;
269         nn::friends::FriendKey friendKeyList[ nn::friends::FRIEND_LIST_SIZE ];
270         bit32 attributeFlag[ nn::friends::FRIEND_LIST_SIZE ];
271         nn::friends::Profile profileList[ nn::friends::FRIEND_LIST_SIZE ];
272 
273         nn::friends::GetFriendKeyList( friendKeyList, &numOfFriend, 0, nn::friends::FRIEND_LIST_SIZE );
274         nn::friends::GetFriendAttributeFlags(attributeFlag, friendKeyList, numOfFriend );
275         nn::friends::GetFriendProfile(profileList, friendKeyList, numOfFriend );
276 
277         for(int i = 0; i < numOfFriend; ++i)
278         {
279             NN_LOG("%dth friend PrincipalId:%u LOCALFRIENDCODE:%llu\n",
280                     i,
281                     friendKeyList[i].principalId,
282                     friendKeyList[i].localFriendCode);
283 
284             // To determine whether a friend or not, friend relationship is established if the nn::friends::ATTRIBUTE_FLAG_ESTABLISHED flag is on
285             if(attributeFlag[i] & nn::friends::ATTRIBUTE_FLAG_ESTABLISHED > 0)
286             {
287                 NN_LOG("FRIEND RELATIONSHIP ESTABLISHED\n");
288                 NN_LOG("region:%u\n", profileList[i].region);
289                 NN_LOG("country:%u\n", profileList[i].country);
290                 NN_LOG("area:%u\n", profileList[i].area);
291                 NN_LOG("language:%u\n", profileList[i].language);
292                 NN_LOG("platform:%u\n", profileList[i].platform);
293             }
294             // Else if friend relationship is not established
295             else
296             {
297                 NN_LOG("FRIEND RELATIONSHIP HAS NOT BEEN ESTABLISHED\n");
298             }
299         }
300     }
301 
302     /* =======================================================================
303        Uses online features such as notification for login, logout, and presence
304        ======================================================================== */
305 
306     NN_LOG("--------------------------------\n");
307     NN_LOG("PRESS A : LOGIN\n");
308     NN_LOG("PRESS B : LOGOUT\n");
309     NN_LOG("PRESS X : UPDATE GAME MODE\n");
310     NN_LOG("PRESS START : EXIT PROGRAM\n");
311     NN_LOG("--------------------------------\n");
312 
313     {
314         // Prepare pad
315         // HID library initialization
316         NN_UTIL_PANIC_IF_FAILED( nn::hid::Initialize() );
317 
318         nn::hid::PadReader padReader;
319         nn::hid::PadStatus padStatus;
320 
321         int messageType = 0;
322 
323         while(true)
324         {
325             nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(30));
326 
327             padReader.ReadLatest(&padStatus);
328 
329             // Login
330             if(padStatus.trigger & nn::hid::BUTTON_A)
331             {
332                 Login();
333             }
334             // Logout
335             else if(padStatus.trigger & nn::hid::BUTTON_B)
336             {
337                 nn::friends::Logout();
338             }
339             // Updates game mode
340             else if(padStatus.trigger & nn::hid::BUTTON_X)
341             {
342                 NN_LOG("UPDATE GAMEMODE DESCRIPTION\n");
343 
344                 // Game mode can be updated even when offline status. In that case, notification is sent to friend when logging in.
345 
346                 // Even if UpdateGameModeDescription is called continuously when online status, no error occurs.
347                 // However, since the notification interval to friends is 10 seconds, notification is not sent immediately.
348 
349                 // The set game mode description string can be confirmed on the HOME Menu.
350                 messageType = (++messageType) % 3;
351                 nn::friends::UpdateGameModeDescription(DESCRIPTION_MESSAGE[messageType]);
352             }
353             // Quit
354             else if(padStatus.trigger & nn::hid::BUTTON_START)
355             {
356                 break;
357             }
358         }
359     }
360 
361     // Perform logout process if needed
362     if(nn::friends::HasLoggedIn())
363     {
364         nn::friends::Logout();
365     }
366 
367     /* =======================================================================
368        Releases resources and shuts down friends library
369        ======================================================================== */
370     s_TerminateEvent.Signal();
371     thread.Join();
372     s_TerminateEvent.Finalize();
373 
374     NN_UTIL_PANIC_IF_FAILED( nn::friends::Finalize() );
375 
376     NN_UTIL_PANIC_IF_FAILED( nn::ac::Finalize() );
377 
378     NN_LOG("Exit client\n");
379     while(true)
380     {
381         nn::os::Thread::Yield();
382     }
383 }
384