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