/*---------------------------------------------------------------------------* Project: Horizon File: demo_FriendsMain.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev:$ *---------------------------------------------------------------------------*/ /* ======================================================================= Note: To exchange presence with friends in this demo, a friend relationship must have already been created in advance in the HOME Menu. ======================================================================= */ #include #include #include #include #include namespace { const char16* DESCRIPTION_MESSAGE[] = {L"MESSAGE 1", L"MESSAGE 2", L"MESSAGE 3"}; static size_t STACK_SIZE = 4096; static nn::os::Event s_TerminateEvent(false); // Displays error code related to the friends library. void PrintErrorCode(nn::Result result) { u32 errorCode = nn::friends::ResultToErrorCode(result); if(errorCode != 0) { NN_LOG("ERROR CODE %u\n", errorCode); } else { NN_LOG("ERROR CODE NOT DEFINED\n"); } } // Updates friend presence data void PrintFriendPresence(nn::friends::FriendKey friendKey) { nn::friends::FriendPresence presence; nn::friends::GetFriendPresence(&presence, &friendKey, 1); if(presence.isValid) { // At launch time, all values for features other than the online flag are disabled. NN_LOG("isOnline: %d\n", presence.isOnline); NN_LOG("isInvitingMe: %d\n", presence.isInvitingMe); NN_LOG("isValid: %d\n", presence.isValid); NN_LOG("GameMode\n"); NN_LOG(" joinAvailabilityFlag: %08x\n", presence.gameMode.joinAvailabilityFlag); NN_LOG(" joinGameId %u\n", presence.gameMode.joinGameId); NN_LOG(" ownerPrincipalId %u\n", presence.gameMode.ownerPrincipalId); NN_LOG(" joinGroupId %u\n", presence.gameMode.joinGroupId); NN_LOG("\n"); } } // Logs into NFS (Nintendo Friend Server) void Login() { nn::Result result; nn::os::Event event(false); NN_LOG("START NFS LOGIN\n"); result = nn::friends::Login(&event); if(result.IsSuccess()) { if (event.Wait(nn::fnd::TimeSpan::FromMinutes(2))) { result = nn::friends::GetLastResponseResult(); if(result.IsSuccess()) { NN_LOG("NFS LOGIN SUCCESSFUL\n"); } else { PrintErrorCode( result ); } } else { // Since it has not been confirmed that the login process has failed, the request must be canceled nn::friends::Logout(); } } else { PrintErrorCode( result ); } event.Finalize(); } // Handles the received EventNotification void HandleFriendsNotificationEvent(const nn::friends::EventNotification* pNotification) { switch(pNotification->type) { case nn::friends::NOTIFICATION_NONE: NN_LOG("NOTIFICATION_NONE\n"); break; case nn::friends::NOTIFICATION_ONLINE: // 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 // NN_LOG("NOTIFICATION_ONLINE\n"); break; case nn::friends::NOTIFICATION_OFFLINE: // NOTIFICATION_OFFLINE may be notified even if one has not send a logout request, such as disconnection from the AP. // In this case, one's own login request is automatically canceled. NN_LOG("NOTIFICATION_OFFLINE\n"); break; case nn::friends::NOTIFICATION_FRIEND_ONLINE: NN_LOG("NOTIFICATION_FRIEND_ONLINE %u\n", pNotification->friendKey.principalId); PrintFriendPresence(pNotification->friendKey); break; case nn::friends::NOTIFICATION_FRIEND_OFFLINE: NN_LOG("NOTIFICATION_FRIEND_OFFLINE %u\n", pNotification->friendKey.principalId); break; case nn::friends::NOTIFICATION_BECOME_FRIEND: NN_LOG("NOTIFICATION_BECOME_FRIEND %u\n", pNotification->friendKey.principalId); break; default: break; } } // Thread for Event dispatch void EventDispatchThread() { NN_LOG( "Start EventDispatchThread.\n" ); // Registers Event for friends nn::os::Event friendsEvent(false); nn::friends::AttachToEventNotification(&friendsEvent); nn::os::WaitObject* waitObjects[2]; waitObjects[0] = &friendsEvent; waitObjects[1] = &s_TerminateEvent; const size_t NOTIFICATION_LIST_SIZE = 10; nn::friends::EventNotification notificationList[ NOTIFICATION_LIST_SIZE ]; bool processing = true; while(processing) { s32 eventNumber = nn::os::WaitObject::WaitAny(waitObjects, 2); switch(eventNumber) { case 0: // Friend-related event { size_t size = 0; // Immediately after an event is signaled, another event may occur. // Here, acquisition continues until the maintained EventNotificaiton list is empty. while((size = nn::friends::GetEventNotification(notificationList, NOTIFICATION_LIST_SIZE)) != 0) { for(int i = 0; i < size; ++i) { HandleFriendsNotificationEvent( notificationList + i ); } } } break; case 1: // Completion notification { NN_LOG("TERMINATE EVENT\n"); processing = false; } break; default: break; } } friendsEvent.Finalize(); NN_LOG( "End EventDispatchThread.\n" ); } } // end of namespace extern "C" void nnMain( void ) { // Call only nn::applet::Enable to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); nn::Result result; /* ======================================================================= Pre-processing Note: Network configuration process or other processes.  Unnecessary when using the presence feature in a state where the processing has already been performed or if the online feature is not used ======================================================================== */ // Connect to access point NN_LOG("client: Initialize AC\n"); nn::fs::Initialize(); NN_UTIL_PANIC_IF_FAILED( nn::ac::Initialize() ); nn::ac::Config config; nn::ac::CreateDefaultConfig( &config ); NN_LOG("client: Connecting to AP...\n"); result = nn::ac::Connect( config ); if( result.IsSuccess() ) { // Connection successful NN_LOG("Established a connection to the access point.\n"); } else { // Connection failure NN_LOG("Failed to connect to the access point. You can NOT log into NFS.\n"); } /* ======================================================================= friends library initialization ======================================================================== */ NN_UTIL_PANIC_IF_FAILED( nn::friends::Initialize() ); // Start event dispatch thread nn::os::Thread thread; thread.StartUsingAutoStack( EventDispatchThread, STACK_SIZE ); /* ======================================================================= Using the friends library offline ======================================================================== */ // Output one's own data { NN_LOG("PrincipalId:%u\n", nn::friends::GetMyPrincipalId()); nn::friends::Profile profile; char16 screenName[nn::friends::SCREEN_NAME_SIZE]; nn::friends::MiiData mii; nn::friends::GetMyProfile(&profile); NN_LOG("MY PROFILE\n"); NN_LOG("region:%u\n", profile.region); NN_LOG("country:%u\n", profile.country); NN_LOG("area:%u\n", profile.area); NN_LOG("language:%u\n", profile.language); NN_LOG("platform:%u\n", profile.platform); nn::friends::GetMyScreenName(screenName); NN_LOG("screenname:%s\n", screenName); nn::friends::GetMyMii(&mii); // The Mii library is required to use Mii. } // Outputs the list of friends // If one is offline, all friends are treated as being offline { NN_LOG("\nFRIEND INFO\n"); size_t numOfFriend = 0; nn::friends::FriendKey friendKeyList[ nn::friends::FRIEND_LIST_SIZE ]; bit32 attributeFlag[ nn::friends::FRIEND_LIST_SIZE ]; nn::friends::Profile profileList[ nn::friends::FRIEND_LIST_SIZE ]; nn::friends::GetFriendKeyList( friendKeyList, &numOfFriend, 0, nn::friends::FRIEND_LIST_SIZE ); nn::friends::GetFriendAttributeFlags(attributeFlag, friendKeyList, numOfFriend ); nn::friends::GetFriendProfile(profileList, friendKeyList, numOfFriend ); for(int i = 0; i < numOfFriend; ++i) { NN_LOG("%dth friend PrincipalId:%u LOCALFRIENDCODE:%llu\n", i, friendKeyList[i].principalId, friendKeyList[i].localFriendCode); // To determine whether a friend or not, friend relationship is established if the nn::friends::ATTRIBUTE_FLAG_ESTABLISHED flag is on if(attributeFlag[i] & nn::friends::ATTRIBUTE_FLAG_ESTABLISHED > 0) { NN_LOG("FRIEND RELATIONSHIP ESTABLISHED\n"); NN_LOG("region:%u\n", profileList[i].region); NN_LOG("country:%u\n", profileList[i].country); NN_LOG("area:%u\n", profileList[i].area); NN_LOG("language:%u\n", profileList[i].language); NN_LOG("platform:%u\n", profileList[i].platform); } // Else if friend relationship is not established else { NN_LOG("FRIEND RELATIONSHIP HAS NOT BEEN ESTABLISHED\n"); } } } /* ======================================================================= Uses online features such as notification for login, logout, and presence ======================================================================== */ NN_LOG("--------------------------------\n"); NN_LOG("PRESS A : LOGIN\n"); NN_LOG("PRESS B : LOGOUT\n"); NN_LOG("PRESS X : UPDATE GAME MODE\n"); NN_LOG("PRESS START : EXIT PROGRAM\n"); NN_LOG("--------------------------------\n"); { // Prepare pad // HID library initialization NN_UTIL_PANIC_IF_FAILED( nn::hid::Initialize() ); nn::hid::PadReader padReader; nn::hid::PadStatus padStatus; int messageType = 0; while(true) { nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(30)); padReader.ReadLatest(&padStatus); // Login if(padStatus.trigger & nn::hid::BUTTON_A) { Login(); } // Logout else if(padStatus.trigger & nn::hid::BUTTON_B) { nn::friends::Logout(); } // Updates game mode else if(padStatus.trigger & nn::hid::BUTTON_X) { NN_LOG("UPDATE GAMEMODE DESCRIPTION\n"); // Game mode can be updated even when offline status. In that case, notification is sent to friend when logging in. // Even if UpdateGameModeDescription is called continuously when online status, no error occurs. // However, since the notification interval to friends is 10 seconds, notification is not sent immediately. // The set game mode description string can be confirmed on the HOME Menu. messageType = (++messageType) % 3; nn::friends::UpdateGameModeDescription(DESCRIPTION_MESSAGE[messageType]); } // Quit else if(padStatus.trigger & nn::hid::BUTTON_START) { break; } } } // Perform logout process if needed if(nn::friends::HasLoggedIn()) { nn::friends::Logout(); } /* ======================================================================= Releases resources and shuts down friends library ======================================================================== */ s_TerminateEvent.Signal(); thread.Join(); s_TerminateEvent.Finalize(); NN_UTIL_PANIC_IF_FAILED( nn::friends::Finalize() ); NN_UTIL_PANIC_IF_FAILED( nn::ac::Finalize() ); NN_LOG("Exit client\n"); while(true) { nn::os::Thread::Yield(); } }