From b691c43c44ff180d10e7d4a9afc83b98551ff586 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Sun, 1 Mar 2026 12:16:08 +0800 Subject: Initial commit --- Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp | 490 +++ Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h | 34 + .../Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp | 54 + .../Orbis/Network/PsPlusUpsellWrapper_Orbis.h | 20 + .../Orbis/Network/SQRNetworkManager_Orbis.cpp | 4155 ++++++++++++++++++++ .../Orbis/Network/SQRNetworkManager_Orbis.h | 355 ++ .../Orbis/Network/SonyCommerce_Orbis.cpp | 1314 +++++++ .../Orbis/Network/SonyCommerce_Orbis.h | 182 + Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp | 285 ++ Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h | 26 + .../Orbis/Network/SonyRemoteStorage_Orbis.cpp | 371 ++ .../Orbis/Network/SonyRemoteStorage_Orbis.h | 42 + .../Orbis/Network/SonyVoiceChatParty_Orbis.cpp | 355 ++ .../Orbis/Network/SonyVoiceChatParty_Orbis.h | 111 + .../Orbis/Network/SonyVoiceChat_Orbis.cpp | 1105 ++++++ .../Orbis/Network/SonyVoiceChat_Orbis.h | 215 + 16 files changed, 9114 insertions(+) create mode 100644 Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp create mode 100644 Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h create mode 100644 Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h create mode 100644 Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp create mode 100644 Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h (limited to 'Minecraft.Client/Orbis/Network') diff --git a/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp new file mode 100644 index 00000000..d1c9cf15 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp @@ -0,0 +1,490 @@ +#include "stdafx.h" + +#include "Orbis_NPToolkit.h" +#include "Orbis\ps4__np_conf.h" +#include "Orbis/Network/SonyCommerce_Orbis.h" + +// #define NP_TITLE_ID "CUSA00265_00" +// #define NP_TITLE_SECRET_HEX "c37e30fa1f7fd29e3534834d62781143ae29aa7b51d02320e7aa0b45116ad600e4d309e8431bc37977d98b8db480e721876e7d736e11fd906778c0033bbb6370903477b1dc1e65106afc62007a5feee3158844d721b88c3f4bff2e56417b6910cedfdec78b130d2e0dd35a35a9e2ae31d5889f9398c1d62b52a3630bb03faa5b" +// #define CLIENT_ID_FOR_SAMPLE "c8c483e7-f0b4-420b-877b-307fcb4c3cdc" + +//#define _USE_STANDARD_ALLOC + +// Singleton +OrbisNPToolkit NPToolkit; +sce::Toolkit::NP::Utilities::Future< sce::Toolkit::NP::NpSessionInformation > OrbisNPToolkit::sm_createJoinFuture; +sce::Toolkit::NP::NpSessionInformation OrbisNPToolkit::m_currentSessionInfo; +sce::Toolkit::NP::Utilities::Future OrbisNPToolkit::m_messageData; + + +void OrbisNPToolkit::presenceCallback( const sce::Toolkit::NP::Event& event ) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::presenceSet: + app.DebugPrintf("presenceSet Successfully\n"); + break; + case sce::Toolkit::NP::Event::presenceSetFailed: + app.DebugPrintf("presenceSetFailed event received = 0x%x\n", event.returnCode); + SQRNetworkManager_Orbis::SetPresenceFailedCallback(); + break; + default: + break; + } +} + +void OrbisNPToolkit::coreCallback( const sce::Toolkit::NP::Event& event ) +{ + switch (event.event) + { + case sce::Toolkit::NP::Event::enetDown: + app.DebugPrintf("Online: Received core callback: Network down \n"); + ProfileManager.SetNetworkStatus(false); + break; + case sce::Toolkit::NP::Event::enetUp: + app.DebugPrintf("Online: Received core callback: Network up \n"); + ProfileManager.SetNetworkStatus(true); + break; + case sce::Toolkit::NP::Event::loggedIn: + app.DebugPrintf("Online: Received core callback: PSN sign in \n"); + assert(event.userInformation.state == SCE_NP_STATE_SIGNED_IN); + ProfileManager.SignedInPSNStateCallback(event.userInformation.userId, event.userInformation.state, &event.userInformation.npId); + break; + case sce::Toolkit::NP::Event::loggedOut: + app.DebugPrintf("Online: Received core callback: PSN sign out \n"); + assert(event.userInformation.state == SCE_NP_STATE_SIGNED_OUT); + ProfileManager.SignedInPSNStateCallback(event.userInformation.userId, event.userInformation.state, &event.userInformation.npId); + break; + default: + app.DebugPrintf("Online: Received core callback: event Num: %d \n", event.event); + break; + } +} + +void OrbisNPToolkit::sceNpToolkitCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.service) + { + case sce::Toolkit::NP::ServiceType::core: + coreCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::netInfo: +// Menu::NetInfo::sceNpToolkitCallback(event); +// break; + case sce::Toolkit::NP::ServiceType::sessions: + sessionsCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::tss: +// Menu::Tss::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::ranking: +// Menu::Ranking::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::tus: +// Menu::Tus::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::profile: +// Menu::Profile::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::friends: +// Menu::Friends::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::auth: +// Menu::Auth::sceNpToolkitCallback(event); +// break; + case sce::Toolkit::NP::ServiceType::trophy: + ProfileManager.trophySystemCallback(event); + break; + case sce::Toolkit::NP::ServiceType::messaging: + messagingCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::inGameMessage: +// Menu::Messaging::sceNpToolkitCallback(event); +// break; + + case sce::Toolkit::NP::ServiceType::commerce: + SonyCommerce_Orbis::commerce2Handler(event); + break; + case sce::Toolkit::NP::ServiceType::presence: + presenceCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::wordFilter: +// Menu::WordFilter::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::sns: +// Menu::Sns::sceNpToolkitCallback(event); +// break; + + case sce::Toolkit::NP::ServiceType::gameCustomData: + gameCustomDataCallback(event); + default: + break; + } +} + + + +void OrbisNPToolkit::sessionsCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::npSessionCreateResult: ///< An event generated when the %Np session creation process has been completed. + app.DebugPrintf("npSessionCreateResult"); + if(sm_createJoinFuture.hasResult()) + { + app.DebugPrintf("Session Created Successfully\n"); + m_currentSessionInfo = *sm_createJoinFuture.get(); + } + else + { + app.DebugPrintf("Session Creation Failed 0x%x\n",sm_createJoinFuture.getError()); + } + sm_createJoinFuture.reset(); + break; + case sce::Toolkit::NP::Event::npSessionJoinResult: ///< An event generated when the join %Np session process has been completed. + app.DebugPrintf("npSessionJoinResult"); + if(sm_createJoinFuture.hasResult()) + { + app.DebugPrintf("Session joined successfully\n"); + m_currentSessionInfo = *sm_createJoinFuture.get(); + } + else + { + app.DebugPrintf("Session join Failed 0x%x\n",sm_createJoinFuture.getError()); + } + sm_createJoinFuture.reset(); + break; + case sce::Toolkit::NP::Event::npSessionError: ///< An event generated when there was error performing the current %Np session process. + app.DebugPrintf("npSessionError"); + break; + case sce::Toolkit::NP::Event::npSessionLeaveResult: ///< An event generated when the user has left the current %Np session. + app.DebugPrintf("npSessionLeaveResult"); + break; + case sce::Toolkit::NP::Event::npSessionModified: ///< An event generated when the %Np session has been modified. + app.DebugPrintf("npSessionModified"); + break; + case sce::Toolkit::NP::Event::npSessionUpdateResult: ///< An event generated when the %Np session has been updated. + app.DebugPrintf("npSessionUpdateResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetInfoResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionGetInfoResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetInfoListResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionGetInfoListResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetSessionDataResult: ///< An event generated when the %Np session data has been retrieved. + app.DebugPrintf("npSessionGetSessionDataResult"); + break; + case sce::Toolkit::NP::Event::npSessionSearchResult: ///< An event generated when the %Np session search request has been completed. + app.DebugPrintf("npSessionSearchResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteNotification: ///< An event generated when the %Np session push notification is received. + app.DebugPrintf("npSessionInviteNotification"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetInfoResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionInviteGetInfoResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetInfoListResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionInviteGetInfoListResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetDataResult: ///< An event generated when the %Np session data has been retrieved. + app.DebugPrintf("npSessionInviteGetDataResult"); + break; + default: + assert(0); + break; + } + +} + +void OrbisNPToolkit::gameCustomDataCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + + case sce::Toolkit::NP::Event::gameCustomDataItemListResult: + app.DebugPrintf("gameCustomDataItemListResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataGameDataResult: + app.DebugPrintf("gameCustomDataGameDataResult"); + if(m_messageData.hasResult()) + { + SQRNetworkManager_Orbis::GetInviteDataAndProcess(m_messageData.get()); + } + else + { + app.DebugPrintf("gameCustomDataMessageResult error 0x%08x\n", m_messageData.getError()); + + UINT uiIDA[1] = { IDS_OK }; + + switch(m_messageData.getError()) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + app.ShowPatchAvailableError(); + break; + case SCE_NP_ERROR_AGE_RESTRICTION: + default: + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1); + break; + } + } + break; + case sce::Toolkit::NP::Event::gameCustomDataMessageResult: + app.DebugPrintf("gameCustomDataMessageResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataSetUseFlagResult: + app.DebugPrintf("gameCustomDataSetUseFlagResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataGameThumbnailResult: + app.DebugPrintf("gameCustomDataGameThumbnailResult"); + break; + case sce::Toolkit::NP::Event::messageError: + app.DebugPrintf("messageError : 0x%08x\n", event.returnCode); + assert(0); + break; + default: + assert(0); + break; + } +} + +void OrbisNPToolkit::messagingCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::messageSent: ///< An event generated when a message has been sent. + app.DebugPrintf("sce::Toolkit::NP::Event::messageSent\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + case sce::Toolkit::NP::Event::messageError: ///< An event generated when a message failed to be received or sent. + app.DebugPrintf("sce::Toolkit::NP::Event::messageError\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + case sce::Toolkit::NP::Event::messageDialogTerminated: ///< An event generated when a message dialog box is terminated. + app.DebugPrintf("sce::Toolkit::NP::Event::messageDialogTerminated\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + //case sce::Toolkit::NP::Event::messageRetrieved: ///< An event generated when a message attachment has been retrieved. + //case sce::Toolkit::NP::Event::messageInGameDataReceived: ///< An event generated when in-game data is received. + //case sce::Toolkit::NP::Event::messageInGameDataRetrievalDone: ///< An event generated when in-game data retrieval is complete. + + // case sce::Toolkit::NP::Event::messageAttachmentReceived: ///< An event generated when a message with a data attachment has been received. + // case sce::Toolkit::NP::Event::messageAttachmentOpened: ///< An event generated when a message with a data attachment has been opened (and the sysutil GUI is closed). + // case sce::Toolkit::NP::Event::messageInviteReceived: ///< An event generated when a message with an invite has been received. + // case sce::Toolkit::NP::Event::messageInviteAccepted: ///< An event generated when a message with an invite has been accepted via ToolkitNp (and the sysutil GUI is closed). + } +} + +static uint8_t hexCharToUint(char ch) +{ + uint8_t val = 0; + + if ( isdigit(ch) ){ + val = (ch - '0'); + } + else if ( isupper(ch) ){ + val = (ch - 'A' + 10); + } + else{ + val = (ch - 'a' + 10); + } + + return val; +} + +void hexStrToBin( + const char *pHexStr, + uint8_t *pBinBuf, + size_t binBufSize + ) +{ + uint8_t val = 0; + int hexStrLen = strlen(pHexStr); + + int binOffset = 0; + for (int i = 0; i < hexStrLen; i++) { + val |= hexCharToUint(*(pHexStr + i)); + if (i % 2 == 0) { + val <<= 4; + } + else { + if (pBinBuf != NULL && binOffset < binBufSize) { + memcpy(pBinBuf + binOffset, &val, 1); + val = 0; + } + binOffset++; + } + } + + if (val != 0 && pBinBuf != NULL && binOffset < binBufSize) { + memcpy(pBinBuf + binOffset, &val, 1); + } + + return; +} + +void OrbisNPToolkit::init() +{ +// MenuApp menuApp; + + sce::Toolkit::NP::NpTitleId nptTitleId; + nptTitleId.setTitleSecret(*SQRNetworkManager_Orbis::GetSceNpTitleId(), *SQRNetworkManager_Orbis::GetSceNpTitleSecret()); + sce::Toolkit::NP::CommunicationId commsIds(s_npCommunicationId, s_npCommunicationPassphrase, s_npCommunicationSignature); + sce::Toolkit::NP::Parameters params(sceNpToolkitCallback,nptTitleId); + + + int ret = sce::Toolkit::NP::Interface::init(params); + if (ret != SCE_OK) + { + app.DebugPrintf("Failed to initialize NP Toolkit Library : 0x%x\n", ret); + assert(0); + } + + + ret = sce::Toolkit::NP::Interface::registerNpCommsId(commsIds, sce::Toolkit::NP::matching); + if (ret < 0) + { + app.DebugPrintf("Failed to register TSS Comms ID : 0x%x\n", ret); + assert(0); + } + + // Get network status and inform library + sce::Toolkit::NP::Utilities::Future netStateFuture = sce::Toolkit::NP::Utilities::Future(); + sce::Toolkit::NP::NetInfo::Interface::getNetInfo(&netStateFuture); + + // Wait for the net state (< 5ms) + while (netStateFuture.isBusy()) + { + Sleep(1); + } + + if (netStateFuture.hasResult()) + { + sce::Toolkit::NP::NetStateBasic *netState = netStateFuture.get(); + ProfileManager.SetNetworkStatus(netState->connectionStatus == SCE_NET_CTL_STATE_IPOBTAINED); + } + else + { + // Error message means we're disconnected + ProfileManager.SetNetworkStatus(false); + } + +// // Register Client ID for Auth +// ret = sce::Toolkit::NP::Interface::registerClientId(CLIENT_ID_FOR_SAMPLE); +// if (ret < 0) +// { +// app.DebugPrintf("Failed to register Auth Client ID : 0x%x\n", ret); +// assert(0); +// } + +} + + + +void OrbisNPToolkit::createNPSession() +{ +#define CURRENT_SESSION_ATTR_NUMS 5 +#define SESSION_IMAGE_PATH "/app0/orbis/session_image.png" +#define SESSION_STATUS "Minecraft online game (this text needs defined and localised)" +#define SESSION_NAME "Minecraft(this text needs defined and localised)" + + static const int maxSlots = 8; + + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x ...\n",ret); + } + + sce::Toolkit::NP::CreateNpSessionRequest createSessionRequest; + memset(&createSessionRequest,0,sizeof(createSessionRequest)); + strncpy(createSessionRequest.sessionName,SESSION_NAME,strlen(SESSION_NAME)); + createSessionRequest.sessionTypeFlag = SCE_TOOLKIT_NP_CREATE_SESSION_TYPE_PUBLIC; + createSessionRequest.maxSlots = maxSlots; + strncpy(createSessionRequest.sessionImgPath,SESSION_IMAGE_PATH,strlen(SESSION_IMAGE_PATH)); + strncpy(createSessionRequest.sessionStatus,SESSION_STATUS,strlen(SESSION_STATUS)); + createSessionRequest.userInfo.userId = userId; + char test[3] = {'R','K','B'}; + createSessionRequest.sessionData= test; + createSessionRequest.sessionDataSize = 3; + ret = sce::Toolkit::NP::Sessions::Interface::create(&createSessionRequest,&sm_createJoinFuture); +} + + +void OrbisNPToolkit::joinNPSession() +{ + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x ...\n",ret); + } + + sce::Toolkit::NP::JoinNpSessionRequest joinSessionRequest; + memset(&joinSessionRequest,0,sizeof(joinSessionRequest)); + // still to sort this out + ORBIS_STUBBED; +} + +void OrbisNPToolkit::leaveNPSession() +{ + +} + + + +void OrbisNPToolkit::getMessageData(SceNpGameCustomDataEventParam* paramData) +{ + + sce::Toolkit::NP::GameCustomDataGameDataRequest req; + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) { + //Error handling + } + + req.itemId = paramData->itemId; + req.userInfo.userId = userId; + ret = sce::Toolkit::NP::GameCustomData::Interface::getGameData(&req, &m_messageData); + if( ret < 0 ) { + //Error handling + } else { + //Error handling + } + + +// +// +// sce::Toolkit::NP::GameDataRequest req; +// SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; +// int ret = sceUserServiceGetInitialUser(&userId); +// if( ret < 0 ) { +// //Error handling +// } +// +// req.itemId = ItemID ; +// req.userInfo.userId = userId; +// sce::Toolkit::NP::Utilities::Future actualMessage; +// ret = sce::Toolkit::NP::GameCustomData::Interface::getGameData(&req,&actualMessage,false); +// if( ret < 0 ) { +// //Error handling +// } else { +// //Error handling +// } +// +// +// +// +// +// +// +// +// app.DebugPrintf("Session Invitation \n"); +// sce::Toolkit::NP::ReceiveMessageRequest request; +// request.eventParamData = paramData; +// request.msgType = SCE_TOOLKIT_NP_MESSAGE_TYPE_CUSTOM_DATA; +// sceUserServiceGetInitialUser(&request.userInfo.userId); +// sce::Toolkit::NP::Messaging::Interface::retrieveMessageAttachmentFromEvent(&request,&m_messageData); +} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h new file mode 100644 index 00000000..1ea72c4f --- /dev/null +++ b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + + +class OrbisNPToolkit +{ +public: + void init(); + static void sceNpToolkitCallback( const sce::Toolkit::NP::Event& event); + static void coreCallback( const sce::Toolkit::NP::Event& event); + static void presenceCallback( const sce::Toolkit::NP::Event& event); + static void sessionsCallback( const sce::Toolkit::NP::Event& event); + static void gameCustomDataCallback( const sce::Toolkit::NP::Event& event); + static void messagingCallback( const sce::Toolkit::NP::Event& event); + + static void createNPSession(); + static void destroyNPSession(); + static void joinNPSession(); + static void leaveNPSession(); + static SceNpSessionId* getNPSessionID() { return &m_currentSessionInfo.npSessionId; } + + static void getMessageData(SceNpGameCustomDataEventParam* paramData); +private: + static sce::Toolkit::NP::Utilities::Future sm_createJoinFuture; + static sce::Toolkit::NP::NpSessionInformation m_currentSessionInfo; + static sce::Toolkit::NP::Utilities::Future m_messageData; + +}; + +// Singleton +extern OrbisNPToolkit NPToolkit; + diff --git a/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp new file mode 100644 index 00000000..0af16589 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "PsPlusUpsellWrapper_Orbis.h" + +PsPlusUpsellWrapper::PsPlusUpsellWrapper(int userIndex) + : m_userIndex(userIndex) +{ + m_bHasResponse = false; +} + +bool PsPlusUpsellWrapper::displayUpsell() +{ + app.DebugPrintf(" Bringing up system PsPlus upsell for Pad_%i.\n", m_userIndex); + + sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode = SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(m_userIndex); + + sceNpCommerceDialogOpen(¶m); + + return true; +} + +bool PsPlusUpsellWrapper::hasResponse() +{ + if (m_bHasResponse) return true; + + if (sceNpCommerceDialogUpdateStatus() == SCE_COMMON_DIALOG_STATUS_FINISHED) + { + app.DebugPrintf( + " Pad_%i %s an PsPlus upsell.\n", + m_userIndex, (m_result.authorized?"accepted":"rejected") + ); + + m_bHasResponse = true; + + sceNpCommerceDialogGetResult(&m_result); + + sceNpCommerceDialogTerminate(); + +#ifndef _CONTENT_PACKAGE + // 4J-JEV: If HasPlayStationPlus is miraculously true now, + // we didn't give it enough time to update before bringing up the upsell + assert( ProfileManager.HasPlayStationPlus(m_userIndex) == false ); +#endif + + ProfileManager.PsPlusUpdate(m_userIndex, &m_result); + } + + return false; +} diff --git a/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h new file mode 100644 index 00000000..64ad9a98 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h @@ -0,0 +1,20 @@ +#pragma once + + +// 4J-JEV: To help handle PsPlus upsells. +class PsPlusUpsellWrapper +{ +private: + bool m_bHasResponse; + + SceNpCommerceDialogResult m_result; + +public: + const int m_userIndex; + + PsPlusUpsellWrapper(int userIndex); + + bool displayUpsell(); + + bool hasResponse(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp new file mode 100644 index 00000000..5c02cb08 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp @@ -0,0 +1,4155 @@ +#include "stdafx.h" +#include "SQRNetworkManager_Orbis.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../ps4__np_conf.h" +#include "Orbis_NPToolkit.h" +#include "SonyVoiceChat_Orbis.h" +#include "Common\Network\Sony\SonyHttp.h" +#include "..\..\..\Minecraft.World\C4JThread.h" +// #include "..\PS3Extras\PS3Strings.h" + + +int (* SQRNetworkManager_Orbis::s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad) = NULL; +void * SQRNetworkManager_Orbis::s_SignInCompleteParam = NULL; +sce::Toolkit::NP::PresenceDetails SQRNetworkManager_Orbis::s_lastPresenceInfo; + +__int64 SQRNetworkManager_Orbis::s_lastPresenceTime = 0; +__int64 SQRNetworkManager_Orbis::s_resendPresenceTime = 0; + +bool SQRNetworkManager_Orbis::s_presenceStatusDirty = false; +bool SQRNetworkManager_Orbis::s_presenceDataDirty = false; +bool SQRNetworkManager_Orbis::s_signInCompleteCallbackIfFailed = false; +bool SQRNetworkManager_Orbis::s_signInCompleteCallbackFAIL = false; +int SQRNetworkManager_Orbis::s_SignInCompleteCallbackPad = -1; +bool SQRNetworkManager_Orbis::s_SignInCompleteCallbackPending = false; +long long SQRNetworkManager_Orbis::s_errorDialogClosed = -1; +long long SQRNetworkManager_Orbis::s_systemDialogClosed = -1; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::s_lastPresenceSyncInfo = { 0 }; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::c_presenceSyncInfoNULL = { 0 }; +//SceNpBasicAttachmentDataId SQRNetworkManager_Orbis::s_lastInviteIdToRetry = SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID; +long long SQRNetworkManager_Orbis::s_roomStartTime = 0; +bool SQRNetworkManager_Orbis::b_inviteRecvGUIRunning = false; +SQRNetworkManager_Orbis::PresenceSyncInfo* SQRNetworkManager_Orbis::m_gameBootInvite; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::m_gameBootInvite_data; +bool SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=false; +bool SQRNetworkManager_Orbis::s_errorDialogRunning=false; +bool SQRNetworkManager_Orbis::s_bInviteDialogRunning=false; + +static const int sc_UserEventHandle = 0; + +static const int sc_verbose = false; + +int g_numRUDPContextsBound = 0; +//unsigned int SQRNetworkManager_Orbis::RoomSyncData::playerCount = 0; + +// This maps internal to extern states, and needs to match element-by-element the eSQRNetworkManagerInternalState enumerated type +const SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::m_INTtoEXTStateMappings[SQRNetworkManager_Orbis::SNM_INT_STATE_COUNT] = +{ + SNM_STATE_INITIALISING, // SNM_INT_STATE_UNINITIALISED + SNM_STATE_INITIALISING, // SNM_INT_STATE_SIGNING_IN + SNM_STATE_INITIALISING, // SNM_INT_STATE_STARTING_CONTEXT + SNM_STATE_INITIALISE_FAILED, // SNM_INT_STATE_INITIALISE_FAILED + SNM_STATE_IDLE, // SNM_INT_STATE_IDLE + SNM_STATE_IDLE, // SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_FOUND + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_WAITING_TO_PLAY + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_FOUND + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS + SNM_STATE_ENDING, // SNM_INT_STATE_SERVER_DELETING_CONTEXT + SNM_STATE_STARTING, // SNM_INT_STATE_STARTING + SNM_STATE_PLAYING, // SNM_INT_STATE_PLAYING + SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING + SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING_FAILED + SNM_STATE_ENDING, // SNM_INT_STATE_ENDING +}; + +SQRNetworkManager_Orbis::SQRNetworkManager_Orbis(ISQRNetworkManagerListener *listener) +{ + m_state = SNM_INT_STATE_UNINITIALISED; + m_stateExternal = SNM_STATE_INITIALISING; + m_nextIdleReasonIsFull = false; + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + m_serverContextValid = false; + m_isHosting = false; + m_currentSmallId = 0; + memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); + m_listener = listener; + m_soc = -1; + m_resendExternalRoomDataCountdown = 0; + m_matching2initialised = false; + m_matchingContextValid = false; + m_inviteIndex = 0; + m_doBootInviteCheck = true; + m_isInSession = false; + m_offlineGame = false; + m_offlineSQR = false; + m_aServerId = NULL; + m_gameBootInvite = NULL; + m_onlineStatus = false; + m_bLinkDisconnected = false; + m_bRefreshingRestrictionsForInvite = false; + + InitializeCriticalSection(&m_csRoomSyncData); + InitializeCriticalSection(&m_csPlayerState); + InitializeCriticalSection(&m_csStateChangeQueue); + InitializeCriticalSection(&m_signallingEventListCS); + + int ret = sceKernelCreateEqueue(&m_basicEventQueue, "SQRNetworkManager_Orbis EQ"); + assert(ret == SCE_OK); + ret = sceKernelAddUserEvent(m_basicEventQueue, sc_UserEventHandle); + assert(ret == SCE_OK); + + m_basicEventThread = new C4JThread(&BasicEventThreadProc,this,"Basic Event Handler"); + m_basicEventThread->Run(); +} + +// First stage of initialisation. This initialises a few things that don't require the user to be signed in, and then kicks of the network start dialog utility. +// Initialisation continues in InitialiseAfterOnline once this completes. +void SQRNetworkManager_Orbis::Initialise() +{ + #define NP_IN_GAME_MESSAGE_POOL_SIZE ( 16 * 1024 ) + + int32_t ret = 0; + int32_t libCtxId = 0; + ret = sceNpInGameMessageInitialize(NP_IN_GAME_MESSAGE_POOL_SIZE, NULL); + assert (ret >= 0); + libCtxId = ret; + + assert( m_state == SNM_INT_STATE_UNINITIALISED ); + + + //Initialize libnetctl + ret = sceNetCtlInit(); + if( ( ret < 0 /*&& ret != CELL_NET_CTL_ERROR_NOT_TERMINATED*/ ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_CTL_INIT ) ) + { + app.DebugPrintf("sceNetCtlInit failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + int hid; + ret = sceNetCtlRegisterCallback(&NetCtlCallback,this,&hid); + + // Initialise RUDP + const int RUDP_POOL_SIZE = (500 * 1024); // TODO - find out what we need, this size is copied from library reference + uint8_t *rudp_pool = (uint8_t *)malloc(RUDP_POOL_SIZE); + ret = sceRudpInit(rudp_pool, RUDP_POOL_SIZE); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_RUDP_INIT ) ) + { + app.DebugPrintf("sceRudpInit failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + SetState(SNM_INT_STATE_SIGNING_IN); + + + SonyHttp::init(); + ret = sceNpSetNpTitleId(GetSceNpTitleId(), GetSceNpTitleSecret()); + if (ret < 0) + { + app.DebugPrintf("sceNpSetNpTitleId failed, ret=%x\n", ret); + assert(0); + } + + ret = sceRudpEnableInternalIOThread(RUDP_THREAD_STACK_SIZE, RUDP_THREAD_PRIORITY); + if(ret < 0) + { + app.DebugPrintf("sceRudpEnableInternalIOThread failed with error code 0x%08x\n", ret); + assert(0); + } + // Already online? the callback won't catch this, so carry on initialising now + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + InitialiseAfterOnline(); + } + else + { + // On Orbis, PSN sign in is only handled by the XMB + SetState(SNM_INT_STATE_IDLE); + } + SonyVoiceChat_Orbis::init(); + + +} + +void SQRNetworkManager_Orbis::Terminate() +{ + // If playing, attempt to nicely leave the room before shutting down so that our friends won't still think this game is in progress + if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || + ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || + ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || + ( m_state == SNM_INT_STATE_PLAYING ) ) + { + if( !m_offlineGame ) + { + LeaveRoom(true); + int count = 200; + do + { + Tick(); + Sleep(10); + count--; + } while( ( count > 0 ) && ( m_state != SNM_INT_STATE_IDLE ) ); + app.DebugPrintf(CMinecraftApp::USER_RR,"Attempted to leave room, %dms used\n",count * 10); + } + } + + int ret = sceRudpEnd(); + ret = sceNpMatching2Terminate(); + // Terminate event thread by sending it a non-zero value for data + sceKernelTriggerUserEvent(m_basicEventQueue, sc_UserEventHandle, (void*)1); + + + do + { + Sleep(10); + } while( m_basicEventThread->isRunning() ); +} + +// Second stage of initialisation, that requires NP Manager to be online & the player to be signed in. This kicks of the creation of a context +// for Np Matching 2. Initialisation is finally complete when we get a callback to ContextCallback. The SQRNetworkManager_Orbis is then finally moved +// into SNM_INT_STATE_IDLE at this stage. +void SQRNetworkManager_Orbis::InitialiseAfterOnline() +{ + SceNpId npId; + int option = 0; + + // We should only be doing this if we have come in from an initialisation stage (SQRNetworkManager_Orbis::Initialise) or we've had a network disconnect and are coming in from an offline state. + // Don't do anything otherwise - this mainly to catch a bit of a corner case in the initialisation phase where potentially we could register for the callback that would call this with sceNpManagerRegisterCallback. + // and could then really quickly go online so that the there becomes two paths (via the callback or SQRNetworkManager_Orbis::Initialise) by which this could be called + if( ( m_state != SNM_INT_STATE_SIGNING_IN ) && !(( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ) + { + // If we aren't going to continue on with this sign-in but are expecting a callback, then let the game know that we have completed the bit we are expecting to do. This + // will happen whilst in game, when we want to be able to sign into PSN, but don't expect the full matching stuff to get set up. + if( s_SignInCompleteCallbackFn ) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam,true,s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + s_SignInCompleteCallbackPad = -1; + } + return; + } + + // Initialize matching2 + SceNpMatching2InitializeParameter initParam; + memset(&initParam,0,sizeof(initParam)); + initParam.size = sizeof(initParam); + initParam.cpuAffinityMask = 1 << 4; + initParam.threadPriority = SCE_KERNEL_PRIO_FIFO_DEFAULT; // This seems to be completely ignored and always set to default anyway + initParam.threadStackSize = SCE_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT * 2; + initParam.poolSize = SCE_NP_MATCHING2_POOLSIZE_DEFAULT * 2; + int ret = sceNpMatching2Initialize(&initParam); + + if( ( ret < 0 && ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) || ForceErrorPoint( SNM_FORCE_ERROR_MATCHING2_INIT ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2Initialize failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + app.DebugPrintf("SQRNetworkManager::InitialiseAfterOnline - matching context is now valid\n"); + m_matching2initialised = true; + + // Get NP ID of the signed-in user + SceNpId npID; + int primaryPad = ProfileManager.GetPrimaryPad(); + if(primaryPad >=0 && ProfileManager.IsSignedInLive(primaryPad)) + { + ProfileManager.GetSceNpId(primaryPad, &npID); + } + else + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - Primary pad not signed in to live"); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + SceNpServiceLabel serviceLabel = 0; + SceNpMatching2CreateContextParam param; + memset(¶m, 0, sizeof(param)); + param.npId = &npID; + param.serviceLabel = serviceLabel; + param.size = sizeof(param); + + + ret = sceNpMatching2CreateContext(¶m, &m_matchingContext); + + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2CreateContext failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + m_matchingContextValid = true; + + bool bRet = RegisterCallbacks(); + if( ( !bRet ) || ForceErrorPoint( SNM_FORCE_ERROR_REGISTER_CALLBACKS ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - RegisterCallbacks failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + // State should be starting context until the callback that this has been created happens + SetState(SNM_INT_STATE_STARTING_CONTEXT); + + // Start the context + // Set time-out time to 10 seconds + ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CONTEXT_START_ASYNC ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2ContextStart failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + } +} + +void SQRNetworkManager_Orbis::RefreshChatAndContentRestrictionsReturned_HandleInvite(void *pParam) +{ + SQRNetworkManager_Orbis *netMan = (SQRNetworkManager_Orbis *)pParam; + + netMan->m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), netMan->m_gameBootInvite ); + netMan->m_gameBootInvite = NULL; + + netMan->m_bRefreshingRestrictionsForInvite = false; +} + +// General tick function to be called from main game loop - any internal tick functions should be called from here. +void SQRNetworkManager_Orbis::Tick() +{ + OnlineCheck(); + sceNetCtlCheckCallback(); + ServerContextTick(); + RoomCreateTick(); + FriendSearchTick(); + TickRichPresence(); + TickInviteGUI(); + TickNotify(); + tickErrorDialog(); + SignallingEventsTick(); // process the signalling events here now instead of in the matching2 callback + if( ( m_gameBootInvite ) && ( s_safeToRespondToGameBootInvite ) && !m_bRefreshingRestrictionsForInvite ) + { + m_bRefreshingRestrictionsForInvite = true; + ProfileManager.RefreshChatAndContentRestrictions(RefreshChatAndContentRestrictionsReturned_HandleInvite, this); + //m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), m_gameBootInvite ); + //m_gameBootInvite = NULL; + } + + ErrorHandlingTick(); + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + if( m_resendExternalRoomDataCountdown ) + { + if( m_state == SNM_INT_STATE_PLAYING ) + { + m_resendExternalRoomDataCountdown--; + if( m_resendExternalRoomDataCountdown == 0 ) + { + UpdateExternalRoomData(); + } + } + else + { + m_resendExternalRoomDataCountdown = 0; + } + } + +// ProfileManager.SetNetworkStatus(GetOnlineStatus()); + + // Client only - do the final transition to a starting & playing state once we have fully joined the room, And told the game about all the local players so they are also all valid + if( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) + { + if( m_localPlayerJoined == m_localPlayerCount ) + { + // Since we're now fully joined, we can update our presence info so that our friends could find us in this game. This data was set up + // at the point that we joined the game (either from search info, or an invitation). + UpdateRichPresenceCustomData(&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); + SetState( SNM_INT_STATE_STARTING); + SetState( SNM_INT_STATE_PLAYING ); + } + } + + if( m_state == SNM_INT_STATE_SERVER_DELETING_CONTEXT ) + { + // make sure we've removed all the remote players and killed the udp connections before we bail out + if(m_RudpCtxToPlayerMap.size() == 0) + ResetToIdle(); + } + + EnterCriticalSection(&m_csStateChangeQueue); + while(m_stateChangeQueue.size() > 0 ) + { + if( m_listener ) + { + m_listener->HandleStateChange(m_stateChangeQueue.front().m_oldState, m_stateChangeQueue.front().m_newState, m_stateChangeQueue.front().m_idleReasonIsSessionFull); + if( m_stateChangeQueue.front().m_newState == SNM_STATE_IDLE ) + { + m_isInSession = false; + } + } + m_stateExternal = m_stateChangeQueue.front().m_newState; + m_stateChangeQueue.pop(); + } + LeaveCriticalSection(&m_csStateChangeQueue); + + // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it frmo the tick instead + if(m_bCallPSNSignInCallback) + { + // TODO: This may be a sign in attempt for a different pad, need to fix this + app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ s_SignInCompleteCallbackFn false ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + + m_bCallPSNSignInCallback=false; + if( s_signInCompleteCallbackIfFailed && s_signInCompleteCallbackFAIL) + { + s_signInCompleteCallbackIfFailed=false; + s_signInCompleteCallbackFAIL=false; + if(s_SignInCompleteCallbackFn) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + } + s_SignInCompleteCallbackPad = -1; + } + else if(s_SignInCompleteCallbackFn) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + s_SignInCompleteCallbackPad = -1; + } + } + +} + + +void SQRNetworkManager_Orbis::tickErrorDialog() +{ + if(s_errorDialogRunning) + { + SceErrorDialogStatus s = sceErrorDialogUpdateStatus(); + switch (s) + { + case SCE_ERROR_DIALOG_STATUS_NONE: + assert(0); + break; + + case SCE_ERROR_DIALOG_STATUS_INITIALIZED: + case SCE_ERROR_DIALOG_STATUS_RUNNING: +// sceErrorDialogClose(); + break; + + case SCE_ERROR_DIALOG_STATUS_FINISHED: + sceErrorDialogTerminate(); + s_errorDialogRunning = false; + + // Start callback timer + s_SignInCompleteCallbackPending = true; + s_errorDialogClosed = System::currentTimeMillis(); + break; + } + } + + + // Note: I'll revisit this at some point, there must be a better way + // Long story short we need to wait for a bit for the system UI to appear, If it doesn't appear or it + // disappears we wait for a bit then check the sign in results. Not perfect but it is safe! + + // If we have a pending callback, try callback + if (s_SignInCompleteCallbackPending) + { + int sinceErrorClosed = System::currentTimeMillis() - s_errorDialogClosed; + + // If error dialog has been gone for a while, look for system dialogue + if (sinceErrorClosed > SYSTEM_UI_WAIT_TIME) + { + // Check if the system UI is currently displayed + SceSystemServiceStatus status = SceSystemServiceStatus(); + sceSystemServiceGetStatus(&status); + bool systemUiDisplayed = status.isInBackgroundExecution || status.isSystemUiOverlaid; + + if (systemUiDisplayed) + { + // Wait till the system goes away + } + else + { + // Start timer if we haven't already + if (s_systemDialogClosed <= 0) + { + s_systemDialogClosed = System::currentTimeMillis(); + } + else + { + int sinceSystemClosed = System::currentTimeMillis() - s_systemDialogClosed; + + // If the system dialog has been closed a while, we're ready to callback + if (sinceSystemClosed > SYSTEM_UI_WAIT_TIME) + { + // 4J-PB - don't call this here, let the SQRNetworkManager_Orbis::Tick call it + //CallSignInCompleteCallback(); + m_bCallPSNSignInCallback=true; + + // Reset + s_SignInCompleteCallbackPending = false; + s_errorDialogClosed = -1; + s_systemDialogClosed = -1; + } + } + } + } + } +} + +// Detect any states which reflect internal error states, do anything required, and transition away again +void SQRNetworkManager_Orbis::ErrorHandlingTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_INITIALISE_FAILED: + if( s_SignInCompleteCallbackFn ) + { + if( s_signInCompleteCallbackIfFailed ) + { + // 4J-PB - flag the failed sign in complete to be called + s_signInCompleteCallbackFAIL=true; + m_bCallPSNSignInCallback=true; + //s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); + } + //s_SignInCompleteCallbackFn = NULL; + //s_SignInCompleteCallbackPad = -1; + } + app.DebugPrintf("Network error: SNM_INT_STATE_INITIALISE_FAILED\n"); + if( m_isInSession && m_offlineGame) // m_offlineSQR ) // MGH - changed this to m_offlineGame, as m_offlineSQR can be true when running an online game but the init has failed because the servers are down + { + // This is a fix for an issue where a player attempts (and fails) to sign in, whilst in an offline game. This was setting the state to idle, which in turn + // sets the game to Not be in a session anymore (but the game wasn't generally aware of, and so keeps playing). Howoever, the game's connections use + // their tick to determine whether to empty their queues or not and so no communications (even though they don't actually use this network manager for local connections) + // were happening. + SetState(SNM_INT_STATE_PLAYING); + } + else + { + m_offlineSQR = true; + SetState(SNM_INT_STATE_IDLE); + } + break; + case SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED\n"); + ResetToIdle(); + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED\n"); + app.SetDisconnectReason(DisconnectPacket::eDisconnect_NetworkError); + DeleteServerContext(); + break; + case SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED\n"); + ResetToIdle(); + break; + case SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED\n"); + DeleteServerContext(); + break; + case SNM_INT_STATE_LEAVING_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_LEAVING_FAILED\n"); + if( !m_isHosting ) + { + RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); + } + DeleteServerContext(); + break; + } + +} + +// Start hosting a game, by creating a room & joining it. We explicity create a server context here (via GetServerContext) as Sony suggest that +// this means we have greater control of representing when players are actually "online". The creation of the room is carried out in a callback +// after that server context is made (ServerContextValidCallback_CreateRoom). +// hostIndex is the index of the user that is hosting the session, and localPlayerMask has bit 0 - 3 set to indicate the full set of local players joining the game. +// extData and extDataSize define the initial state of room data that is externally visible (eg by players searching for rooms, but not in it) +void SQRNetworkManager_Orbis::CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline) +{ + // hostIndex should always be in the mask + assert( ( ( 1 << hostIndex ) & localPlayerMask ) != 0 ); + + m_isHosting = true; + m_joinExtData = extData; + m_joinExtDataSize = extDataSize; + m_offlineGame = offline; + m_resendExternalRoomDataCountdown = 0; + m_isInSession= true; + + // Default value for room, which we can use for offlinae games + m_room = 0; + + // Initialise room data that will be synchronised. Slot 0 is always reserved for the host. We don't know the + // room member until the room is actually created so this will be set/updated at that point + memset( &m_roomSyncData, 0, sizeof(m_roomSyncData) ); + m_roomSyncData.setPlayerCount(1); + m_roomSyncData.players[0].m_smallId = m_currentSmallId++; + m_roomSyncData.players[0].m_localIdx = hostIndex; + + // Remove the host player that we've already added, then add any other local players specified in the mask + localPlayerMask &= ~( ( 1 << hostIndex ) & localPlayerMask ); + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( localPlayerMask & ( 1 << i ) ) + { + m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_smallId = m_currentSmallId++; + m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_localIdx = i; + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + } + } + m_localPlayerCount = m_roomSyncData.getPlayerCount(); + + // For offline games, we can jump straight to the state that says we've just created the room (or would have, for an online game) + if( m_offlineGame ) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); + } + else + { + // Kick off the sequence of events required for an online game, starting with getting the server context + m_isInSession = GetServerContext(); + } +} + +// Updates the externally visible data that was associated with the room when it was created with CreateAndJoinRoom. +void SQRNetworkManager_Orbis::UpdateExternalRoomData() +{ + if( m_offlineGame ) return; + if( m_isHosting ) + { + SceNpMatching2SetRoomDataExternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + SceNpMatching2BinAttr roomBinAttr; + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + roomBinAttr.ptr = m_joinExtData; + roomBinAttr.size = m_joinExtDataSize; + reqParam.roomBinAttrExternalNum = 1; + reqParam.roomBinAttrExternal = &roomBinAttr; + + int ret = sceNpMatching2SetRoomDataExternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SetRoomDataExternal returns 0x%x, number of players %d\n",ret,((char *)m_joinExtData)[174]); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA ) ) + { + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + m_resendExternalRoomDataCountdown = 60; + } + } +} + +// Determine if the friend room manager is busy. If it isn't busy, then other operations (searching for a friend, reading the found friend's room lists) may safely be performed +bool SQRNetworkManager_Orbis::FriendRoomManagerIsBusy() +{ + return (m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE); +} + +// Initiate a search for rooms that the signed in user's friends are in. This is an asynchronous operation, this function returns after it kicks off a search across all game servers +// for any of the player's friends. +bool SQRNetworkManager_Orbis::FriendRoomManagerSearch() +{ + if( m_state != SNM_INT_STATE_IDLE ) return false; + + // Don't start another search if we're already searching... + if( m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE ) + { + return false; + } + + // Free up any external data that we received from the previous search + for( int i = 0; i < m_aFriendSearchResults.size(); i++ ) + { + if(m_aFriendSearchResults[i].m_RoomExtDataReceived) + free(m_aFriendSearchResults[i].m_RoomExtDataReceived); + m_aFriendSearchResults[i].m_RoomExtDataReceived = NULL; + } + + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT; + m_friendCount = 0; + m_aFriendSearchResults.clear(); + + // Get friend list - doing this in another thread as it can lock up for a few seconds + m_getFriendCountThread = new C4JThread(&GetFriendsThreadProc,this,"GetFriendsThreadProc"); + m_getFriendCountThread->Run(); + + return true; +} + +bool SQRNetworkManager_Orbis::FriendRoomManagerSearch2() +{ + SceNpMatching2AttributeId attrId; + SceNpMatching2GetUserInfoListRequest reqParam; + + if( m_friendCount == 0 ) + { + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + return false; + } + + if( m_aFriendSearchResults.size() > 0 ) + { + // If we have some results, then we also want to make sure that we don't have any duplicate rooms here if more than one friend is playing in the same room. + unordered_set uniqueRooms; + for( unsigned int i = 0; i < m_aFriendSearchResults.size(); i++ ) + { + if(m_aFriendSearchResults[i].m_RoomFound) + { + uniqueRooms.insert( m_aFriendSearchResults[i].m_RoomId ); + } + } + + // Tidy the results up further based on this + for( unsigned int i = 0; i < m_aFriendSearchResults.size(); ) + { + if( uniqueRooms.find(m_aFriendSearchResults[i].m_RoomId) == uniqueRooms.end() ) + { + free(m_aFriendSearchResults[i].m_RoomExtDataReceived); + m_aFriendSearchResults[i] = m_aFriendSearchResults.back(); + m_aFriendSearchResults.pop_back(); + } + else + { + uniqueRooms.erase(m_aFriendSearchResults[i].m_RoomId); + i++; + } + } + } + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + return true; +} + +void SQRNetworkManager_Orbis::FriendSearchTick() +{ + // Move onto next state if we're done getting our friend count + if( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT ) + { + if( !m_getFriendCountThread->isRunning() ) + { + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_INFO; + delete m_getFriendCountThread; + m_getFriendCountThread = NULL; + FriendRoomManagerSearch2(); + } + } +} + +// The handler for basic events can't actually get the events themselves, this has to be done on another thread. Instead, we send a sys_event_t to a queue on This thread, +// which has a single data item used which we can use to determine whether to terminate this thread or get a basic event & handle that. +int SQRNetworkManager_Orbis::BasicEventThreadProc( void *lpParameter ) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; + + int ret = SCE_OK; + SceKernelEvent event; + int outEv; + + do + { + ret = sceKernelWaitEqueue(manager->m_basicEventQueue, &event, 1, &outEv, NULL); + + // If the sys_event_t we've sent here from the handler has a non-zero data1 element, this is to signify that we should terminate the thread + if( event.udata == 0 ) + { +// int iEvent; +// SceNpUserInfo from; +// uint8_t buffer[SCE_NP_BASIC_MAX_MESSAGE_SIZE]; +// size_t bufferSize = SCE_NP_BASIC_MAX_MESSAGE_SIZE; +// int ret = sceNpBasicGetEvent(&iEvent, &from, &buffer, &bufferSize); +// if( ret == 0 ) +// { +// if( iEvent == SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION ) +// { +// // 4J Stu - Don't do this here as it can be very disruptive to gameplay. Players can bring this up from LoadOrJoinMenu, PauseMenu and InGameInfoMenu +// //sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); +// } +// if( iEvent == SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT ) +// { +// SceNpBasicExtendedAttachmentData *result = (SceNpBasicExtendedAttachmentData *)buffer; +// if(result->userAction == SCE_NP_BASIC_MESSAGE_ACTION_ACCEPT ) +// { +// manager->GetInviteDataAndProcess(result->data.id); +// } +// } +// app.DebugPrintf("Incoming basic event of type %d\n",iEvent); +// } + } + + } while(event.udata == 0 ); + return 0; +} + +int SQRNetworkManager_Orbis::GetFriendsThreadProc( void* lpParameter ) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; + sce::Toolkit::NP::Utilities::Future friendList; + + int ret = 0; + manager->m_aFriendSearchResults.clear(); + manager->m_friendCount = 0; + if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + app.DebugPrintf("getFriendslist failed, not signed into Live! \n"); + return 0; + } + + sce::Toolkit::NP::FriendInfoRequest requestParam; + memset(&requestParam,0,sizeof(requestParam)); + requestParam.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_IN_CONTEXT; + requestParam.limit = 0; + requestParam.offset = 0; + requestParam.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + bool async = false; + ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, &requestParam, async); + if(ret != SCE_OK) + { + app.DebugPrintf("getFriendslist failed! 0x%08x\n", ret); + return 0; + } + + if (friendList.hasResult()) + { + sce::Toolkit::NP::FriendsList::const_iterator iter ; + int i = 1 ; + bool noFriends = true; + for (iter = friendList.get()->begin(); iter != friendList.get()->end() ; ++iter,i++) + { + manager->m_friendCount++; + noFriends = false; + app.DebugPrintf("------ Friend: %d -----\n",i); + app.DebugPrintf("Online Name: %s\n",iter->npid.handle.data); + app.DebugPrintf("------------------------\n"); + + + sce::Toolkit::NP::PresenceRequest presenceRequest; + sce::Toolkit::NP::Utilities::Future presenceInfo; + memset(&presenceRequest,0,sizeof(presenceRequest)); + strncpy(presenceRequest.onlineId.data,iter->npid.handle.data, strlen(iter->npid.handle.data)); + presenceRequest.presenceType = SCE_TOOLKIT_NP_PRESENCE_TYPE_INCONTEXT_INFO; + presenceRequest.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());; + + + ret = sce::Toolkit::NP::Presence::Interface::getPresence(&presenceRequest,&presenceInfo,false); + if( ret < 0 ) + { + app.DebugPrintf("getPresence() error. ret = 0x%x\n", ret); + } + else + { + app.DebugPrintf("\nPresence Data Retrieved:\n"); + app.DebugPrintf("Platform Type: %s\n", presenceInfo.get()->platformType.c_str()); + app.DebugPrintf("Online Status: %s\n", (presenceInfo.get()->onlineStatus == SCE_NP_GAME_PRESENCE_STATUS_OFFLINE)?"OFFLINE":"ONLINE"); + app.DebugPrintf("Presence Type (Refer to SCE_TOOLKIT_NP_PRESENCE_TYPE_* flags): %d\n", presenceInfo.get()->presenceType); + app.DebugPrintf("Title ID: %s\n", presenceInfo.get()->gameInfo.npTitleId.c_str()); + app.DebugPrintf("Title Name: %s\n", presenceInfo.get()->gameInfo.npTitleName.c_str()); + app.DebugPrintf("Title Status String: %s\n", presenceInfo.get()->gameInfo.gameStatus.c_str()); + + FriendSearchResult result; + memcpy(&result.m_NpId, &iter->npid, sizeof(SceNpId)); + result.m_RoomFound = false; + + // Only include the friend's game if its the same network id ( this also filters out generally Zeroed PresenceSyncInfo, which we do when we aren't in an active game session) +// if( presenceDetails.size == sizeof(PresenceSyncInfo) ) + { + PresenceSyncInfo *pso = (PresenceSyncInfo *)presenceInfo.get()->gameInfo.gameData; + if( pso->netVersion == MINECRAFT_NET_VERSION ) + { + if( !pso->inviteOnly ) + { + result.m_RoomFound = true; + result.m_RoomId = pso->m_RoomId; + result.m_ServerId = pso->m_ServerId; + + CPlatformNetworkManagerSony::MallocAndSetExtDataFromSQRPresenceInfo(&result.m_RoomExtDataReceived, pso); + manager->m_aFriendSearchResults.push_back(result); + } + } + } + + } + } + } + else if (friendList.hasError()) + { + app.DebugPrintf( "Error occurred while retrieving FriendsList, ret = 0x%x\n", friendList.getError()); + app.DebugPrintf("Check sign-in status\n"); + + } + + return 0; +} + +// Get count of rooms that friends are playing in. Only valid when FriendRoomManagerIsBusy() returns false +int SQRNetworkManager_Orbis::FriendRoomManagerGetCount() +{ + assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); + return m_aFriendSearchResults.size(); +} + +// Get details of a found session that a friend is playing in. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false +void SQRNetworkManager_Orbis::FriendRoomManagerGetRoomInfo(int idx, SQRNetworkManager_Orbis::SessionSearchResult *searchResult) +{ + assert( idx < m_aFriendSearchResults.size() ); + assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); + + searchResult->m_NpId = m_aFriendSearchResults[idx].m_NpId; + searchResult->m_sessionId.m_RoomId = m_aFriendSearchResults[idx].m_RoomId; + searchResult->m_sessionId.m_ServerId = m_aFriendSearchResults[idx].m_ServerId; + searchResult->m_extData = m_aFriendSearchResults[idx].m_RoomExtDataReceived; +} + +// Get overall state of the network manager. +SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::GetState() +{ + return m_stateExternal;; +} + +bool SQRNetworkManager_Orbis::IsHost() +{ + return m_isHosting; +} + +bool SQRNetworkManager_Orbis::IsReadyToPlayOrIdle() +{ + return (( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || ( m_state == SNM_INT_STATE_PLAYING ) || ( m_state == SNM_INT_STATE_IDLE ) ); +} + + +// Consider as "in session" from the moment that a game is created or joined, until the point where the game itself has been told via state change that we are now idle. The +// game code requires IsInSession to return true as soon as it has asked to do one of these things (even if the state system hasn't really caught up with this request yet), and +// it also requires that it is informed of the state changes leading up to not being in the session, before this should report false. +bool SQRNetworkManager_Orbis::IsInSession() +{ + return m_isInSession; +} + +// Get count of players currently in the session +int SQRNetworkManager_Orbis::GetPlayerCount() +{ + return m_roomSyncData.getPlayerCount(); +} + +// Get count of players who are in the session, but not local to this machine +int SQRNetworkManager_Orbis::GetOnlinePlayerCount() +{ + int onlineCount = 0; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + onlineCount++; + } + } + return onlineCount; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByIndex(int idx) +{ + if( idx < MAX_ONLINE_PLAYER_COUNT ) + { + return GetPlayerIfReady(m_aRoomSlotPlayers[idx]); + } + else + { + return NULL; + } +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerBySmallId(int idx) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_smallId == idx ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByXuid(PlayerUID xuid) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_UID == xuid ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetLocalPlayerByUserIndex(int idx) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && ( m_roomSyncData.players[i].m_localIdx == idx ) ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetHostPlayer() +{ + EnterCriticalSection(&m_csRoomSyncData); + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[0]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerIfReady(SQRNetworkPlayer *player) +{ + if( player == NULL ) return NULL; + + if( player->IsReady() ) return player; + + return NULL; +} + +// Update state internally +void SQRNetworkManager_Orbis::SetState(SQRNetworkManager_Orbis::eSQRNetworkManagerInternalState state) +{ + eSQRNetworkManagerState oldState = m_INTtoEXTStateMappings[m_state]; + eSQRNetworkManagerState newState = m_INTtoEXTStateMappings[state]; + bool setIdleReasonSessionFull = false; + if( ( state == SNM_INT_STATE_IDLE ) && m_nextIdleReasonIsFull ) + { + setIdleReasonSessionFull = true; + m_nextIdleReasonIsFull = false; + } + m_state = state; + // Queue any important (ie externally relevant) state changes - we will do a call back for these in our main tick. Don't do it directly here + // as we could be coming from any thread at this stage, with any stack size etc. and so we don't generally want to expect the game to be able to handle itself in such circumstances. + if( ( newState != oldState ) || setIdleReasonSessionFull ) + { + EnterCriticalSection(&m_csStateChangeQueue); + m_stateChangeQueue.push(StateChangeInfo(oldState,newState,setIdleReasonSessionFull)); + LeaveCriticalSection(&m_csStateChangeQueue); + } +} + +void SQRNetworkManager_Orbis::ResetToIdle() +{ + app.DebugPrintf("------------------ResetToIdle--------------------\n"); + // If we're the client, remove any networked players properly ( this will destory their rupd context etc.) + if( !m_isHosting ) + { + RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); + } + m_serverContextValid = false; + m_isHosting = false; + m_currentSmallId = 0; + EnterCriticalSection(&m_csRoomSyncData); + for(int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + delete m_aRoomSlotPlayers[i]; + } + memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); + memset( &m_roomSyncData,0,sizeof(m_roomSyncData)); + LeaveCriticalSection(&m_csRoomSyncData); + SetState(SNM_INT_STATE_IDLE); + assert(m_RudpCtxToPlayerMap.size() == 0); + SonyVoiceChat_Orbis::checkFinished(); +} + +// Join a room that was found with FriendRoomManagerSearch. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false +bool SQRNetworkManager_Orbis::JoinRoom(SQRNetworkManager_Orbis::SessionSearchResult *searchResult, int localPlayerMask) +{ + // Set up the presence info we would like to synchronise out when we have fully joined the game + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, searchResult->m_extData, searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId); + return JoinRoom(searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId, localPlayerMask, NULL); +} + +// Join room with a specified roomId. This is used when joining from an invite, as well as by the previous method +bool SQRNetworkManager_Orbis::JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_Orbis::PresenceSyncInfo *presence) +{ + // The presence info will be directly passed in if we are joining from an invite, otherwise it has already been set up. This is synchronised out when we have fully joined the game. + if( presence ) + { + memcpy( &s_lastPresenceSyncInfo, presence, sizeof(PresenceSyncInfo) ); + } + + m_isInSession = true; + + m_isHosting = false; + m_offlineGame = false; + m_roomToJoin = roomId; + m_localPlayerJoinMask = localPlayerMask; + m_localPlayerCount = 0; + m_localPlayerJoined = 0; + + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( localPlayerMask & ( 1 << i ) ) m_localPlayerCount++; + } + + return GetServerContext( serverId ); +} + +void SQRNetworkManager_Orbis::StartGame() +{ + assert( ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || (( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ); + + SetState( SNM_INT_STATE_STARTING); + SetState( SNM_INT_STATE_PLAYING); +} + +void SQRNetworkManager_Orbis::LeaveRoom(bool bActuallyLeaveRoom) +{ + if( m_offlineGame ) + { + if( m_state != SNM_INT_STATE_PLAYING ) return; + + SetState(SNM_INT_STATE_LEAVING); + SetState(SNM_INT_STATE_ENDING); + ResetToIdle(); + return; + } + + UpdateRichPresenceCustomData(& c_presenceSyncInfoNULL, sizeof(PresenceSyncInfo) ); + +// SonyVoiceChat::shutdown(); + + // Attempt to leave the room if we are in any of the states we could be in if we have successfully created it + if( bActuallyLeaveRoom ) + { + if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || + ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || + ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || + ( m_state == SNM_INT_STATE_PLAYING ) ) + { + SceNpMatching2LeaveRoomRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + + SetState(SNM_INT_STATE_LEAVING); + int ret = sceNpMatching2LeaveRoom( m_matchingContext, &reqParam, NULL, &m_leaveRoomRequestId ); + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_LEAVE_ROOM) ) + { + SetState(SNM_INT_STATE_LEAVING_FAILED); + } + } + else if ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ) + { + // Haven't created the room yet, but will have created the server context so need to recover from that + DeleteServerContext(); + } + else + { + SetState(SNM_INT_STATE_IDLE); + } + } + else + { + // We have created a room but have now had some kind of connection error which means that we've been dropped out of the room and it has been destroyed, so + // no need to leave it again since it doesn't exist anymore. Still need to destroy server context which may be valid + DeleteServerContext(); + } +} + +void SQRNetworkManager_Orbis::EndGame() +{ +} + +bool SQRNetworkManager_Orbis::SessionHasSpace(int spaceRequired) +{ + return( ( m_roomSyncData.getPlayerCount() + spaceRequired ) <= MAX_ONLINE_PLAYER_COUNT ); +} + +bool SQRNetworkManager_Orbis::AddLocalPlayerByUserIndex(int idx) +{ + if( m_isHosting ) + { + if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; + + // Host's players are always at the start of the sync data, so we just need to find the first entry that isn't us to determine what we want to insert before + int insertAtIdx = m_roomSyncData.getPlayerCount(); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + insertAtIdx = i; + break; + } + else + { + // Don't add the same local index twice + if( m_roomSyncData.players[i].m_localIdx == idx ) + { + return false; + } + } + } + + // Make room for a new entry... + for( int i = m_roomSyncData.getPlayerCount(); i > insertAtIdx; i-- ) + { + m_roomSyncData.players[i] = m_roomSyncData.players[i-1]; + } + m_roomSyncData.players[insertAtIdx].m_localIdx = idx; + m_roomSyncData.players[insertAtIdx].m_roomMemberId = m_localMemberId; + m_roomSyncData.players[insertAtIdx].m_smallId = m_currentSmallId++; + + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + + // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. + // This will also create the required new SQRNetworkPlayer and do all the callbacks that requires etc. + MapRoomSlotPlayers(); + + // Sync this back out to our networked clients... + SyncRoomData(); + + UpdateRemotePlay(); + + // no connections being made because we're all on the host, so add this player to the existing connections + SonyVoiceChat_Orbis::connectPlayerToAll(idx); + return true; + } + else + { + // Don't attempt to join if our client's view of the players indicates that there aren't any free slots + if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; + + // Add the requested player to the mask of local players currently in the game, and update this data - this + // will also then resync with the server which can respond appropriately + int mask = 1 << idx; + if( m_localPlayerJoinMask & mask ) return false; + + m_localPlayerJoinMask |= mask; + + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_room; + reqParam.memberId = m_localMemberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); + + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL) ) + { + return false; + } + + UpdateRemotePlay(); + + // Create the client's end of the rudp connections... note that m_roomSyncData.players[0].m_roomMemberId is always be the host's room member id. + bool rudpOk = CreateRudpConnections(m_room, m_roomSyncData.players[0].m_roomMemberId, mask, m_localMemberId ); + + if( rudpOk ) + { + bool ret = CreateVoiceRudpConnections( m_room, m_roomSyncData.players[0].m_roomMemberId, mask); + assert(ret); + return true; + } + else + { + m_localPlayerJoinMask &= (~mask); + return false; + } + } +} + +bool SQRNetworkManager_Orbis::RemoveLocalPlayerByUserIndex(int idx) +{ + if( m_isHosting ) + { + EnterCriticalSection(&m_csRoomSyncData); + + int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && + ( m_roomSyncData.players[i].m_localIdx == idx ) ) + { + // Shuffle all remaining entries up... + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); + for( int j = i; j < m_roomSyncData.getPlayerCount(); j++ ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j+1]; + } + + // Zero last element that isn't part of the currently sized array anymore + memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); + + // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. + // This will also delete the SQRNetworkPlayer and do all the callbacks that requires etc. + MapRoomSlotPlayers(roomSlotPlayerCount); + m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; + + // Sync this back out to our networked clients... + SyncRoomData(); + + SonyVoiceChat_Orbis::disconnectLocalPlayer(idx); + + LeaveCriticalSection(&m_csRoomSyncData); + return true; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + + UpdateRemotePlay(); + + return false; + } + else + { + // Remove the requested player from the mask of local players currently in the game, and update this data - this + // will also then resync with the server which can respond appropriately + int mask = 1 << idx; + if( ( m_localPlayerJoinMask & mask ) == 0 ) return false; + + m_localPlayerJoinMask &= ~mask; + + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_room; + reqParam.memberId = m_localMemberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); + + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2) ) + { + return false; + } + + RemoveNetworkPlayers( mask ); + + UpdateRemotePlay(); + + return true; + } +} + +// Update remote play on multiplayer status +void SQRNetworkManager_Orbis::UpdateRemotePlay() +{ + int localPlayerCount = 0; + for(int i = 0; i < XUSER_MAX_COUNT; i++) + { + if(GetLocalPlayerByUserIndex(i) != NULL) localPlayerCount++; + } + InputManager.SetLocalMultiplayer(localPlayerCount > 1); +} + +extern uint8_t *mallocAndCreateUTF8ArrayFromString(int iID); + +// Bring up a Gui to send an invite so a player that the user can select. This invite will contain the room Id so that +void SQRNetworkManager_Orbis::SendInviteGUI() +{ + if(s_bInviteDialogRunning) + { + app.DebugPrintf("SendInviteGUI - Invite dialog is already running so ignoring request\n"); + return; + } + + s_bInviteDialogRunning = true; + + //Set invitation information - this is now exactly the same as the presence information that we synchronise out. + + // If we joined a game, we'll have already set s_lastPresenceSyncInfo up (whether we came in from an invite, or joining a game we discovered). If we were hosting, + // then we'll need to set this up now from the external dasta. + if( m_isHosting ) + { + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, m_joinExtData, m_room, m_serverId); + } + + sce::Toolkit::NP::MessageData messData; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + +//#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.png" +#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.jpg" + + char *subject = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_SUBJECT_MAX_18_CHARS); + char *body = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_BODY); + + messData.body.assign(body); + messData.dialogFlag = SCE_TOOLKIT_NP_DIALOG_TYPE_USER_EDITABLE; + messData.npIdsCount = 2; // TODO: Set this to the number of available slots + messData.npIds = NULL; + messData.userInfo.userId = userId; + + // Set expire to maximum + messData.expireMinutes = 0; + + // Now setting session ID + messData.npSessionId = *OrbisNPToolkit::getNPSessionID(); + + // Attachment + sce::Toolkit::NP::MessageAttachment attachment = sce::Toolkit::NP::MessageAttachment(); + attachment.setAttachmentData((SceChar8*)&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); // This step is important, allocates space on heap for the data + messData.attachment = attachment.getAttachmentData(); + messData.attachmentSize = attachment.getAttachmentSize(); + + messData.dataDescription.assign(body); + messData.dataName.assign(subject); + messData.iconPath.assign(SESSION_IMAGE_PATH); + + + int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_CUSTOM_DATA); +// int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_INVITE); + + free(subject); + free(body); + + if(ret < SCE_TOOLKIT_NP_SUCCESS ) + { + s_bInviteDialogRunning = false; + app.DebugPrintf("Send Message failed 0x%x ...\n",ret); + assert(0); + return; + } +} + + +void SQRNetworkManager_Orbis::RecvInviteGUI() +{ + int ret = sceGameCustomDataDialogInitialize(); + if(ret != SCE_OK) + { + app.DebugPrintf("sceGameCustomDataDialogInitialize() failed. ret = 0x%x\n", ret); + } + else + { + + SceGameCustomDataDialogParam dialogParam; + SceGameCustomDataDialogDataParam dataParam; + + sceGameCustomDataDialogParamInit( &dialogParam ); + memset( &dataParam, 0x00, sizeof( SceGameCustomDataDialogDataParam ) ); + dialogParam.mode = SCE_GAME_CUSTOM_DATA_DIALOG_MODE_RECV; + dialogParam.dataParam = &dataParam; + dialogParam.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + ret = sceGameCustomDataDialogOpen( &dialogParam ); + + if( SCE_OK != ret ) + { + app.DebugPrintf("sceGameCustomDataDialogOpen() failed. ret = 0x%x\n", ret); + } + else + { + b_inviteRecvGUIRunning = true; + } + } +} + + +void SQRNetworkManager_Orbis::TickInviteGUI() +{ + + if(b_inviteRecvGUIRunning) + { + SceCommonDialogStatus status = sceGameCustomDataDialogUpdateStatus(); + + if( SCE_COMMON_DIALOG_STATUS_FINISHED == status ) + { + SceGameCustomDataDialogOnlineIdList sentOnlineIdList; + memset( &sentOnlineIdList, 0x0, sizeof(SceGameCustomDataDialogOnlineIdList)); + SceGameCustomDataDialogResult dialogResult; + memset( &dialogResult, 0x0, sizeof(SceGameCustomDataDialogResult) ); + dialogResult.sentOnlineIds = &sentOnlineIdList; + + int32_t ret = sceGameCustomDataDialogGetResult( &dialogResult ); + + if( SCE_OK != ret ) + { + app.DebugPrintf( "***** sceGameCustomDataDialogGetResult error:0x%x\n", ret); + } + sceGameCustomDataDialogClose(); + sceGameCustomDataDialogTerminate(); + b_inviteRecvGUIRunning = false; + } + } +} + +// Get the data for an invite into a statically allocated array of invites, and pass a pointer of this back up to the game. Elements in the array are used in a circular fashion, to save any issues with handling freeing of this invite data as the +// qnet equivalent of this seems to just assume that the data persists forever. +void SQRNetworkManager_Orbis::GetInviteDataAndProcess(sce::Toolkit::NP::MessageAttachment* pInvite) +{ + + app.DebugPrintf("GameCustomData attachment size : %d\n", pInvite->getAttachmentSize()); + if(pInvite->getAttachmentSize() == sizeof(m_gameBootInvite_data)) + { + memcpy(&m_gameBootInvite_data, pInvite->getAttachmentData(), sizeof(m_gameBootInvite_data)); + m_gameBootInvite = &m_gameBootInvite_data; + } +// InvitationInfoRequest requestInfo; +// sce::Toolkit::NP::Utilities::Future< NpSessionInvitationInfo > inviteInfo; +// +// requestInfo.invitationId = pInvite->invitationId; +// requestInfo.userInfo.npId = pInvite->onlineId; +// int err = sce::Toolkit::NP::Sessions::Interface::getInvitationInfo(&requestInfo, &inviteInfo, false); +// if(err == SCE_OK) +// { +// if(inviteInfo.hasResult()) +// { +// inviteInfo.get()-> +// } +// else if(inviteInfo.hasError()) +// { +// app.DebugPrintf("inviteInfo.getError() 0x%08x", inviteInfo.getError()); +// } +// } +// else +// { +// app.DebugPrintf("getInvitationInfo error 0x%08x", err); +// } +// +// +// +// INVITE_INFO *invite = &m_inviteReceived[m_inviteIndex]; +// m_inviteIndex = ( m_inviteIndex + 1 ) % MAX_SIMULTANEOUS_INVITES; +// size_t dataSize = sizeof(INVITE_INFO); +// int ret = sceNpBasicRecvMessageAttachmentLoad(id, invite, &dataSize); +// +// // If we fail ( which we might do if we aren't online at this point) then zero the invite information and we'll try and get it later after (possibly) signing in +// if( ret != 0 ) +// { +// memset(invite, 0, sizeof( INVITE_INFO ) ); +// s_lastInviteIdToRetry = id; +// } +// +// m_gameBootInvite = invite; +} + +// bool SQRNetworkManager_Orbis::UpdateInviteData(SQRNetworkManager_Orbis::PresenceSyncInfo *invite) +// { +// // size_t dataSize = sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo); +// // int ret = sceNpBasicRecvMessageAttachmentLoad(s_lastInviteIdToRetry, invite, &dataSize); +// // return (ret == 0); +// } + +// This method is a helper used in MapRoomSlotPlayers - tries to find a player that matches: +// (1) the playerType +// (2) if playerType is remote, memberId +// (3) localPlayerIdx +// The reason we don't care about memberid when the player isn't remote is that it doesn't matter (since we know the player is either on this machine, or it is the host and there's only one of those), +// and there's a period when starting up the host game where it doesn't accurately know the memberId for its own local players +void SQRNetworkManager_Orbis::FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId) +{ + for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) + { + if( ((*it)->m_type == playerType ) && ( (*it)->m_localPlayerIdx == localPlayerIdx ) ) + { + if( ( playerType != SQRNetworkPlayer::SNP_TYPE_REMOTE ) || ( (*it)->m_roomMemberId == memberId ) ) + { + SQRNetworkPlayer *player = *it; + m_vecTempPlayers.erase(it); + m_aRoomSlotPlayers[ slot ] = player; + return; + } + } + } + // Create the player - non-network players can be considered complete as soon as we create them as we aren't waiting on their network connections becoming complete, so can flag them as such and notify via callback + PlayerUID *pUID = NULL; + PlayerUID localUID; + if( ( playerType == SQRNetworkPlayer::SNP_TYPE_LOCAL ) || + (m_isHosting && ( playerType == SQRNetworkPlayer::SNP_TYPE_HOST )) ) + { + // Local players can establish their UID at this point + ProfileManager.GetXUID(localPlayerIdx,&localUID,true); + pUID = &localUID; + } + SQRNetworkPlayer *player = new SQRNetworkPlayer(this, (SQRNetworkPlayer::eSQRNetworkPlayerType)playerType, m_isHosting, memberId, localPlayerIdx, 0, pUID ); + // For offline games, set name directly from gamertag as the PlayerUID will be full of zeroes. + if( m_offlineGame ) + { + player->SetName(ProfileManager.GetGamertag(localPlayerIdx)); + } + NonNetworkPlayerComplete( player, smallId); + m_aRoomSlotPlayers[ slot ] = player; + HandlePlayerJoined( player ); +} + +// For data sending on the local machine, used to send between host and localplayers on the host +void SQRNetworkManager_Orbis::LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize) +{ + assert(m_isHosting); + if(m_listener) + { + m_listener->HandleDataReceived( playerFrom, playerTo, (unsigned char *)data, dataSize ); + } +} + +int SQRNetworkManager_Orbis::GetSessionIndex(SQRNetworkPlayer *player) +{ + int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + for( int i = 0; i < roomSlotPlayerCount; i++ ) + { + if( m_aRoomSlotPlayers[i] == player ) return i; + } + return 0; +} + +// Updates m_aRoomSlotPlayers, based on what is in m_roomSyncData. This needs to be updated when room members join & leave, and when any SQRNetworkPlayer is created externally that this should be mapping to +void SQRNetworkManager_Orbis::MapRoomSlotPlayers(int roomSlotPlayerCount/*=-1*/) +{ + EnterCriticalSection(&m_csRoomSyncData); + + // If we pass an explicit roomSlotPlayerCount, it is because we are removing a player, and this is the count of slots that there were *before* the removal. + bool zeroLastSlot = false; + if( roomSlotPlayerCount == -1 ) + { + roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + } + else + { + zeroLastSlot = true; + } + + if( m_isHosting ) + { + for( int i = 0; i < roomSlotPlayerCount; i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + // On host, remote players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. + // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer + if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_REMOTE ) + { + m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); + m_aRoomSlotPlayers[i] = NULL; + } + } + } + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData + } + else + { + if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) + { + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_LOCAL, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData + } + else + { + m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); + // If we're the host, then we allocated the small id so can flag now if we've got a player to flag... + if( m_aRoomSlotPlayers[i] ) + { + NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); + } + } + } + } + + if( zeroLastSlot ) + { + if( roomSlotPlayerCount ) + { + m_aRoomSlotPlayers[ roomSlotPlayerCount - 1 ] = 0; + } + } + + // Also update the externally visible room data for the current slots + if (m_listener ) + { + m_listener->HandleResyncPlayerRequest(m_aRoomSlotPlayers); + } + } + else + { + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + // On clients, local players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. + // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer + if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_LOCAL ) + { + m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); + m_aRoomSlotPlayers[i] = NULL; + } + } + } + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data + } + else + { + if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) + { + // This player is local to this machine - don't bother setting UID from sync data, as it will already have been set accurately when we (locally) made this player + m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); + // If we've got the room sync data back from the server, then we've got our smallId. Set flag for this. + if( m_aRoomSlotPlayers[i] ) + { + NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); + } + } + else + { + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_REMOTE, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data + } + } + } + } + // Clear up any non-network players that are no longer required - this would be a good point to notify of players leaving when we support that + // FindOrCreateNonNetworkPlayer will have pulled any players that we Do need out of m_vecTempPlayers, so the ones that are remaining are no longer in the game + for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) + { + if( m_listener ) + { + m_listener->HandlePlayerLeaving(*it); + } + delete (*it); + } + m_vecTempPlayers.clear(); + + LeaveCriticalSection(&m_csRoomSyncData); +} + +// On host, update the room sync data with UIDs that are in the players +void SQRNetworkManager_Orbis::UpdateRoomSyncUIDsFromPlayers() +{ + EnterCriticalSection(&m_csRoomSyncData); + if( m_isHosting ) + { + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); + } + } + } + + LeaveCriticalSection(&m_csRoomSyncData); +} + +// On the client, move UIDs from the room sync data out to the players. +void SQRNetworkManager_Orbis::UpdatePlayersFromRoomSyncUIDs() +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); + } + else + { + // Don't sync local players as we already set those up with their UID in the first place... + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); + } + } + } + } + LeaveCriticalSection(&m_csRoomSyncData); +} + +// Host only - add remote players to our internal storage of player slots, and synchronise this with other room members. +bool SQRNetworkManager_Orbis::AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull/*==NULL*/ ) +{ + assert( m_isHosting ); + + EnterCriticalSection(&m_csRoomSyncData); + + // Establish whether we have enough room to add the players + int addCount = 0; + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + addCount++; + } + } + + if( ( m_roomSyncData.getPlayerCount() + addCount ) > MAX_ONLINE_PLAYER_COUNT ) + { + if( isFull ) + { + *isFull = true; + } + LeaveCriticalSection(&m_csRoomSyncData); + return false; + } + + // We want to keep all players from a particular machine together, so search through the room sync data to see if we can find + // any pre-existing players from this machine. + int firstIdx = -1; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId == memberId ) + { + firstIdx = i; + break; + } + } + + // We'll just be inserting at the end unless we've got a pre-existing player to insert after. Even then there might be no following + // players. + int insertIdx = m_roomSyncData.getPlayerCount(); + if( firstIdx > -1 ) + { + for( int i = firstIdx; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != memberId ) + { + insertIdx = i; + break; + } + } + } + + // Add all remote players determined from the player mask to our own slots of active players + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + // Shift any following players along... + for( int j = m_roomSyncData.getPlayerCount(); j > insertIdx; j-- ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j-1]; + } + PlayerSyncData *player = &m_roomSyncData.players[ insertIdx ]; + player->m_smallId = m_currentSmallId++; + player->m_roomMemberId = memberId; + player->m_localIdx = i; + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + insertIdx++; + } + } + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + // And then synchronise this out to all other machines + SyncRoomData(); + + LeaveCriticalSection(&m_csRoomSyncData); + + return true; +} + +// Host only - remove all remote players belonging to the supplied memberId, and in the supplied mask, and synchronise this with other room members +void SQRNetworkManager_Orbis::RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask ) +{ + assert( m_isHosting ); + EnterCriticalSection(&m_csRoomSyncData); + + // Remove any applicable players, keeping remaining players in order + for( int i = 0; i < m_roomSyncData.getPlayerCount(); ) + { + if( ( m_roomSyncData.players[ i ].m_roomMemberId == memberId ) && ( ( 1 << m_roomSyncData.players[ i ].m_localIdx ) & mask ) ) + { + SQRNetworkPlayer *player = GetPlayerFromRoomMemberAndLocalIdx( memberId, m_roomSyncData.players[ i ].m_localIdx ); + if( player ) + { + // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) + int ctx = player->m_rudpCtx; + int err = sceRudpTerminate( ctx ); + assert(err == SCE_OK); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + g_numRUDPContextsBound--; + if( m_listener ) + { + m_listener->HandlePlayerLeaving(player); + } + // Delete the player itself and the mapping from context to player map as this context is no longer valid + delete player; + m_RudpCtxToPlayerMap.erase(ctx); + + removePlayerFromVoiceChat(player); + } + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); + // Shuffled entries up into the space that we have just created + for( int j = i ; j < m_roomSyncData.getPlayerCount(); j++ ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j + 1]; + m_aRoomSlotPlayers[j] = m_aRoomSlotPlayers[j + 1]; + } + // Zero last element, that isn't part of the currently sized array anymore + memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); + m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; + } + else + { + i++; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + + // And then synchronise this out to all other machines + SyncRoomData(); + +// if(GetOnlinePlayerCount() == 0) +// SonyVoiceChat::shutdown(); +} + +// Client only - remove all network players matching the supplied mask +void SQRNetworkManager_Orbis::RemoveNetworkPlayers( int mask ) +{ + assert( !m_isHosting ); + + for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); ) + { + SQRNetworkPlayer *player = it->second; + if( (player->m_roomMemberId == m_localMemberId ) && ( ( 1 << player->m_localPlayerIdx ) & mask ) ) + { + // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) + int ctx = it->first; + int err = sceRudpTerminate( ctx ); + g_numRUDPContextsBound--; + assert(err == SCE_OK); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + if( m_listener ) + { + m_listener->HandlePlayerLeaving(player); + } + // Delete any reference to this player from the player mappings + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_aRoomSlotPlayers[i] == player ) + { + m_aRoomSlotPlayers[i] = NULL; + } + } + // And delete the reference from the ctx->player map + it = m_RudpCtxToPlayerMap.erase(it); + + removePlayerFromVoiceChat(player); + + // Delete the player itself and the mapping from context to player map as this context is no longer valid + delete player; + } + else + { + it++; + } + } + +} + +// Host only - update the memberId of the local players, and synchronise with other room members +void SQRNetworkManager_Orbis::SetLocalPlayersAndSync() +{ + assert( m_isHosting ); + for( int i = 0; i < m_localPlayerCount; i++ ) + { + m_roomSyncData.players[i].m_roomMemberId = m_localMemberId; + } + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + // And then synchronise this out to all other machines + SyncRoomData(); + +} + +// Host only - sync the room data with other machines +void SQRNetworkManager_Orbis::SyncRoomData() +{ + if( m_offlineGame ) return; + + UpdateRoomSyncUIDsFromPlayers(); + + SceNpMatching2SetRoomDataInternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + SceNpMatching2BinAttr roomBinAttr; + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; + roomBinAttr.ptr = &m_roomSyncData; + roomBinAttr.size = sizeof( m_roomSyncData ); + reqParam.roomBinAttrInternalNum = 1; + reqParam.roomBinAttrInternal = &roomBinAttr; + sceNpMatching2SetRoomDataInternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); +} + +// Check if the matching context is valid, and if not attempt to create one. If to do this requires starting an asynchronous process, then sets the internal state to the state passed in +// before doing this. +// Returns true on success. +bool SQRNetworkManager_Orbis::GetMatchingContext(eSQRNetworkManagerInternalState asyncState) +{ + if( m_matchingContextValid ) return true; + + int ret = 0; + if( !m_matching2initialised) + { + + SceNpMatching2InitializeParameter param; + memset(¶m, 0, sizeof(param)); + param.size = sizeof(param); + ret = sceNpMatching2Initialize(¶m); + } + if( (ret < 0) && (ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2Init2 failed with code 0x%08x\n", ret); + return false; + } + m_matching2initialised = true; + + // Get NP ID of the signed-in user + SceNpId npId; + /*ret = */ProfileManager.GetSceNpId(ProfileManager.GetPrimaryPad(), &npId); + + + // Create context + SceNpServiceLabel serviceLabel = 0; + SceNpMatching2CreateContextParam param; + memset(¶m, 0, sizeof(param)); + param.npId = &npId; + param.serviceLabel = serviceLabel; + param.size = sizeof(param); + ret = sceNpMatching2CreateContext(¶m, &m_matchingContext); + if( ret < 0 ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2CreateContext failed with code 0x%08x\n", ret); + return false; + } + if( ret < 0 ) return false; + + if( !RegisterCallbacks() ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - RegisterCallbacks failed\n"); + return false; + } + + // Set internal state & kick off async process that will actually start the context. + SetState(asyncState); + ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); + if( ret < 0 ) + { + // Put state back so that the caller isn't expecting a callback from sceNpMatching2ContextStartAsync completing to happen + SetState(SNM_INT_STATE_IDLE); + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2ContextStartAsync failed with code 0x%08x\n", ret); + return false; + } + + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - matching context is now valid\n"); + m_matchingContextValid = true; + return true; +} + +// Starts the process of obtaining a server context. This is an asynchronous operation, at the end of which (if successful), we'll be creating +// a room. General procedure followed here is as suggested by Sony - we get a list of servers, then pick a random one, and see if it is available. +// If not we just cycle round trying other random ones until we either find an available one or fail. +bool SQRNetworkManager_Orbis::GetServerContext() +{ + assert(m_state == SNM_INT_STATE_IDLE); + assert(m_serverContextValid == false); + + // Check that the matching context is valid & recreate if necessary + if( !GetMatchingContext(SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT) ) return false; + // If this caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes + // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) + if( m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) return true; + + return GetServerContext2(); +} + +// Code split out from previous method, so we can also call from creating matching context if required +bool SQRNetworkManager_Orbis::GetServerContext2() +{ + + m_aServerId = (SceNpMatching2ServerId *)realloc( m_aServerId, sizeof(SceNpMatching2ServerId) * 1 ); + int err = sceNpMatching2GetServerId(m_matchingContext, m_aServerId); + if(err != SCE_OK) + { + m_serverCount = 0; + assert(0); + } + m_serverCount = 1; + + SetState(SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER); + return SelectRandomServer(); +} + +// Overloaded method for (as before) obtaining a server context. This version is so that can also get a server context for a specific server rather than a random one, +// using mainly the same code by making a single element list. This is used when joining an existing room. +bool SQRNetworkManager_Orbis::GetServerContext(SceNpMatching2ServerId serverId) +{ + assert(m_state == SNM_INT_STATE_IDLE); + assert(m_serverContextValid == false); + + // Check that the matching context is valid & recreate if necessary + if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) + { + app.DebugPrintf("SQRNetworkManager::GetServerContext - Failed due to no matching context\n"); + return false; + } + + // 4J Stu - If this state is set, then we have successfully created a new context but it won't have started yet + // Therefore the sceNpMatching2GetServerIdListLocal call will fail. If we just skip this check everything should be good. +// if( m_state != SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) +// { +// // Get list of server IDs of servers allocated to the application. We don't actually need to do this, but it is as good a way as any to try a matching2 service and check that +// // the context *really* is valid. +// int serverCount = sceNpMatching2GetServerIdListLocal( m_matchingContext, NULL, 0 ); +// // If an error is returned here, we need to destroy and recerate our server - if this goes ok we should come back through this path again +// if( ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment +// ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error +// { +// sceNpMatching2DestroyContext(m_matchingContext); +// m_matchingContextValid = false; +// if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) return false; +// } +// } + m_serverCount = 1; + m_totalServerCount = m_serverCount; + m_aServerId = (SceNpMatching2ServerId *)realloc(m_aServerId, sizeof(SceNpMatching2ServerId) * m_serverCount ); + m_aServerId[0] = serverId; + + // If one of the previous GetMatchingContext calls caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes + // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) + if( m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) return true; + + SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + return SelectRandomServer(); +} + +// Tick to update the search for a server which is available, for the creation of a server context. +void SQRNetworkManager_Orbis::ServerContextTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER: + case SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER: + break; + case SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR: + case SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR: + // Attempt to keep searching if a single server failed + SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER:SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + if(!SelectRandomServer()) + { + SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED:SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); + } + break; + case SNM_INT_STATE_HOSTING_SERVER_FOUND: + m_serverContextValid = true; + ServerContextValid_CreateRoom(); + break; + + case SNM_INT_STATE_JOINING_SERVER_FOUND: + m_serverContextValid = true; + ServerContextValid_JoinRoom(); + break; + default: + break; + } +} + +// Tick the process of creating a room. +void SQRNetworkManager_Orbis::RoomCreateTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD: + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND: + { + SceNpMatching2CreateJoinRoomRequest reqParam; + SceNpMatching2SignalingOptParam optSignalingParam; + SceNpMatching2BinAttr roomBinAttrExt; + SceNpMatching2BinAttr roomBinAttr; + memset(&reqParam, 0, sizeof(reqParam)); + memset(&optSignalingParam, 0, sizeof( optSignalingParam) ); + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + memset(&roomBinAttrExt, 0, sizeof(roomBinAttrExt)); + + reqParam.worldId = m_worldId; + reqParam.flagAttr = SCE_NP_MATCHING2_ROOM_FLAG_ATTR_NAT_TYPE_RESTRICTION; + reqParam.sigOptParam = &optSignalingParam; + reqParam.maxSlot = MAX_ONLINE_PLAYER_COUNT; + reqParam.roomBinAttrInternalNum = 1; + reqParam.roomBinAttrInternal = &roomBinAttr; + reqParam.roomBinAttrExternalNum = 1; + reqParam.roomBinAttrExternal = &roomBinAttrExt; + + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; + roomBinAttr.ptr = &m_roomSyncData; + roomBinAttr.size = sizeof( m_roomSyncData ); + + roomBinAttrExt.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + roomBinAttrExt.ptr = m_joinExtData; + roomBinAttrExt.size = m_joinExtDataSize; + + optSignalingParam.type = SCE_NP_MATCHING2_SIGNALING_TYPE_MESH; + optSignalingParam.hubMemberId = 0; // Room owner is the hub of the star + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM); + app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room start\n"); + s_roomStartTime = System::currentTimeMillis(); + int ret = sceNpMatching2CreateJoinRoom( m_matchingContext, &reqParam, NULL, &m_createRoomRequestId ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_JOIN_ROOM) ) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + } + } + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM: + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS: + SetState(SNM_INT_STATE_HOSTING_WAITING_TO_PLAY); + + // Now we know the local member id we can update our local players + SetLocalPlayersAndSync(); + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: + break; + default: + break; + } +} + +// For a player using the network to communicate, flag as having its connection complete. This wraps the player's own functionality, so that we can determine if this +// call is transitioning us from not ready to ready, and call a registered callback. +void SQRNetworkManager_Orbis::NetworkPlayerConnectionComplete(SQRNetworkPlayer *player) +{ + EnterCriticalSection(&m_csPlayerState); + bool wasReady = player->IsReady(); + bool wasClientReady = player->HasConnectionAndSmallId(); + player->ConnectionComplete(); + bool isReady = player->IsReady(); + bool isClientReady = player->HasConnectionAndSmallId(); + if( !m_isHosting ) + { + // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end + if( ( !wasClientReady ) && ( isClientReady ) ) + { + player->ConfirmReady(); + isReady = true; + } + } + LeaveCriticalSection(&m_csPlayerState); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// For a player using the network to communicate, set its small id, thereby flagging it as having one allocated +void SQRNetworkManager_Orbis::NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId) +{ + EnterCriticalSection(&m_csPlayerState); + bool wasReady = player->IsReady(); + bool wasClientReady = player->HasConnectionAndSmallId(); + player->SmallIdAllocated(smallId); + bool isReady = player->IsReady(); + bool isClientReady = player->HasConnectionAndSmallId(); + + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Small ID allocated\n"); + if( !m_isHosting ) + { + // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end + if( ( !wasClientReady ) && ( isClientReady ) ) + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Confirm ready\n"); + player->ConfirmReady(); + isReady = true; + } + } + LeaveCriticalSection(&m_csPlayerState); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// On host, for a player using the network to communicate, confirm that its small id has now been received back +void SQRNetworkManager_Orbis::NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player, void *data) +{ + EnterCriticalSection(&m_csPlayerState); + SQRNetworkPlayer::InitSendData *ISD = (SQRNetworkPlayer::InitSendData *)data; + bool wasReady = player->IsReady(); + player->InitialDataReceived(ISD); + bool isReady = player->IsReady(); + LeaveCriticalSection(&m_csPlayerState); + // Sync room data back out as we've updated a player's UID here + SyncRoomData(); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// For non-network players, flag that it is complete/ready, and assign its small id. We don't want to call any callbacks for these, as that can be explicitly done when local players are added. +// Also, we dynamically destroy & recreate local players quite a lot when remapping player slots which would create a lot of messages we don't want. +void SQRNetworkManager_Orbis::NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId) +{ + player->ConnectionComplete(); + player->SmallIdAllocated(smallId); +} + +void SQRNetworkManager_Orbis::HandlePlayerJoined(SQRNetworkPlayer *player) +{ + if( m_listener ) + { + m_listener->HandlePlayerJoined( player ); + } + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> HandlePlayerJoined\n"); + + // On client, keep a count of how many local players we have told the game about. We can only transition to telling the game that we are playing once the room is set up And all the local players are valid to use. + if( !m_isHosting ) + { + if( player->IsLocal() ) + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LocalPlayerJoined++\n"); + + m_localPlayerJoined++; + } + } +} + +// Selects a random server from the current list, removes that server so it won't be searched for again, and then kick off an attempt to find out if that particular server is available. +bool SQRNetworkManager_Orbis::SelectRandomServer() +{ + assert( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ); + + if( m_serverCount == 0 ) + { + SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED : SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); + app.DebugPrintf("SQRNetworkManager::SelectRandomServer - Server count is 0\n"); + return false; + } + + // not really selecting a random server, as we've already been allocated one, but calling this to match PS3 + int serverIdx; + serverIdx = 0; + m_serverCount--; + m_aServerId[serverIdx] = m_aServerId[m_serverCount]; + + // This server is available + SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_FOUND : SNM_INT_STATE_JOINING_SERVER_FOUND); + m_serverId = m_aServerId[serverIdx]; + + return true; +} + +// Delete the current server context. Should be called when finished with the current host or client game session. +void SQRNetworkManager_Orbis::DeleteServerContext() +{ + // No server context on PS4, so we just set the state, and then we'll check all the UDP connections have shutdown before setting to idle + if( m_serverContextValid ) + { + m_serverContextValid = false; + SetState(SNM_INT_STATE_SERVER_DELETING_CONTEXT); + } +} + +// Creates a set of Rudp connections by the "active open" method. This requires that both ends of the connection call cellRudpInitiate to fully create a connection. We +// create one connection per local play on any remote machine. +// +// peerMemberId is the room member Id of the remote end of the connection +// playersMemberId is the room member Id that the players belong to +// ie for the host (when matching incoming connections), these will be the same thing... and for the client, peerMemberId will be the host, whereas playersMemberId will be itself + + +std::string getIPAddressString(SceNetInAddr add) +{ + char str[32]; + unsigned char *vals = (unsigned char*)&add.s_addr; + sprintf(str, "%d.%d.%d.%d", (int)vals[0], (int)vals[1], (int)vals[2], (int)vals[3]); + return std::string(str); +} + +bool SQRNetworkManager_Orbis::CreateSocket() +{ + // First get details of the UDPP2P connection that has been established + int connStatus; + SceNetSockaddrIn sinp2pLocal, sinp2pPeer; + SceNpMatching2SignalingNetInfo netInfo; + + // Local end first... + memset(&sinp2pLocal, 0, sizeof(sinp2pLocal)); + memset(&netInfo, 0 , sizeof(netInfo)); + netInfo.size = sizeof(netInfo); + int ret = sceNpMatching2SignalingGetLocalNetInfo(&netInfo); + if( ret < 0 ) return false; + sinp2pLocal.sin_len = sizeof(sinp2pLocal); + sinp2pLocal.sin_family = AF_INET; + sinp2pLocal.sin_port = sceNetHtons(SCE_NP_PORT); + sinp2pLocal.sin_addr = netInfo.localAddr; + + + // Set vport for both ends of connection + sinp2pLocal.sin_vport = sceNetHtons(1); + + // Create socket & bind + ret = sceNetSocket("rupdSocket", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM_P2P, 0); + assert(ret >= 0); + m_soc = ret; + int optval = 1; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USECRYPTO, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_0) ) return false; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USESIGNATURE, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_1) ) return false; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_NBIO, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_2) ) return false; + + ret = sceNetBind(m_soc, (SceNetSockaddr *)&sinp2pLocal, sizeof(sinp2pLocal)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SOCK_BIND) ) return false; + return true; + +} + + +bool SQRNetworkManager_Orbis::CreateVoiceRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask) +{ + SceNetSockaddrIn sinp2pPeer; + SceNpMatching2SignalingNetInfo netInfo; + int connStatus; + + memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); + sinp2pPeer.sin_len = sizeof(sinp2pPeer); + sinp2pPeer.sin_family = AF_INET; + int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); + sinp2pPeer.sin_vport = sceNetHtons(1); + + + ret = 0; + // Create socket & bind, if we don't already have one + if( m_soc == -1 ) + { + if(CreateSocket() == false) + return false; + } + + // create this connection if we don't have it already + SQRVoiceConnection* pConnection = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); + if(pConnection == NULL) + { + + // Create an Rudp context for the voice connection, this will happen regardless of whether the peer is client or host + int rudpCtx; + ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); + if(ret < 0){ app.DebugPrintf("sceRudpCreateContext failed : 0x%08x\n", ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); + + // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. + ret = sceRudpBind( rudpCtx, m_soc , 5, SCE_RUDP_MUXMODE_P2P ); + if(ret < 0){ app.DebugPrintf("sceRudpBind %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; + g_numRUDPContextsBound++; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); + + ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); + if(ret < 0){ app.DebugPrintf("sceRudpInitiate %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); + + app.DebugPrintf("-----------------------------\n"); + app.DebugPrintf("Voice rudp context created %d connected to %s\n", rudpCtx, getIPAddressString(sinp2pPeer.sin_addr).c_str()); + app.DebugPrintf("-----------------------------\n"); + + pConnection = SonyVoiceChat_Orbis::addRemoteConnection(rudpCtx, peerMemberId); + } + + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + bool bMaskVal = ( playerMask & ( 1 << i ) ); + + if(bMaskVal || GetLocalPlayerByUserIndex(i)) + SonyVoiceChat_Orbis::connectPlayer(pConnection, i); + } + return true; +} + + + +bool SQRNetworkManager_Orbis::CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersMemberId) +{ + // First get details of the UDPP2P connection that has been established + int connStatus; + SceNetSockaddrIn sinp2pPeer; + + // get the peer + memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); + sinp2pPeer.sin_len = sizeof(sinp2pPeer); + sinp2pPeer.sin_family = AF_INET; + + int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SignalingGetConnectionStatus returned 0x%x, connStatus %d peer add:%s peer port:0x%x\n",ret, connStatus,getIPAddressString(sinp2pPeer.sin_addr).c_str(),sinp2pPeer.sin_port); + + // Set vport + sinp2pPeer.sin_vport = sceNetHtons(1); + + // Create socket & bind, if we don't already have one + if( m_soc == -1 ) + { + if(CreateSocket() == false) + return false; + } + + // Create an Rudp context for each local player that is required. These can be used as individual virtual connections between room members (ie consoles), which are multiplexed + // over the socket we have just made + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( ( playerMask & ( 1 << i ) ) == 0 ) continue; + + int rudpCtx; + + // Socket for the local network node created, now can create an Rupd context. + ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); + + if( m_isHosting ) + { + m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_REMOTE, true, playersMemberId, i, rudpCtx, NULL ); + } + else + { + // Local players can establish their UID at this point + PlayerUID localUID; + ProfileManager.GetXUID(i,&localUID,true); + + m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_LOCAL, false, m_localMemberId, i, rudpCtx, &localUID ); + } + + // If we've created a player, then we want to try and patch up any connections that we should have to it + MapRoomSlotPlayers(); + + // TODO - set any non-default options for the context. By default, the context is set to have delivery critical and order critical both on + + // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. + ret = sceRudpBind( rudpCtx, m_soc , 1 + i, SCE_RUDP_MUXMODE_P2P ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; + g_numRUDPContextsBound++; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); + + + ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); + } + return true; +} + + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRudpCtx(int rudpCtx) +{ + AUTO_VAR(it,m_RudpCtxToPlayerMap.find(rudpCtx)); + if( it != m_RudpCtxToPlayerMap.end() ) + { + return it->second; + } + return NULL; +} + + + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx) +{ + for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); it++ ) + { + if( (it->second->m_roomMemberId == roomMember ) && ( it->second->m_localPlayerIdx == localIdx ) ) + { + return it->second; + } + } + return NULL; +} + + +// This is called as part of the general initialisation of the network manager, to register any callbacks that the sony libraries require. +// Returns true if all were registered successfully. +bool SQRNetworkManager_Orbis::RegisterCallbacks() +{ + // Register RUDP event handler + int ret = sceRudpSetEventHandler(RudpEventCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - cellRudpSetEventHandler failed with code 0x%08x\n", ret); + return false; + } + + // Register the context callback function + ret = sceNpMatching2RegisterContextCallback(ContextCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterContextCallback failed with code 0x%08x\n", ret); + return false; + } + + // Register the default request callback & parameters + SceNpMatching2RequestOptParam optParam; + + memset(&optParam, 0, sizeof(optParam)); + optParam.cbFunc = DefaultRequestCallback; + optParam.cbFuncArg = this; + optParam.timeout = (30 * 1000 * 1000); + optParam.appReqId = 0; + + ret = sceNpMatching2SetDefaultRequestOptParam(m_matchingContext, &optParam); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2SetDefaultRequestOptParam failed with code 0x%08x\n", ret); + return false; + } + + // Register signalling callback + ret = sceNpMatching2RegisterSignalingCallback(m_matchingContext, SignallingCallback, this); + if (ret < 0) + { + return false; + } + + // Register room event callback + ret = sceNpMatching2RegisterRoomEventCallback(m_matchingContext, RoomEventCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterRoomEventCallback failed with code 0x%08x\n", ret); + return false; + } + + return true; +} + +extern bool g_bBootedFromInvite; + + +// This is an implementation of SceNpMatching2ContextCallback. Used to determine whether the matching 2 context is valid or not. +void SQRNetworkManager_Orbis::ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg) +{ + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback id:%d, event:%d, eventCause:%d, errorCode:0x%08x\n", id, event, eventCause, errorCode); + + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if (id != manager->m_matchingContext) + { + return; + } + + switch( event ) + { + case SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED: + if(errorCode < 0) + { + if(manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT || + manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT || + manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) + { + // matching context failed to start (this can happen when you block the IP addresses of the matching servers on your router + // agent-0101.ww.sp-int.matching.playstation.net (198.107.157.191) + // static-resource.sp-int.community.playstation.net (203.105.77.140) + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - Error\n"); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + break; + } + } + + // Some special cases to detect when this event is coming in, in case we had to start the matching context because there wasn't a valid context when we went to get a server context. These two + // responses here complete what should then happen to get the server context in each case (for hosting or joining a game) + if( manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + manager->SetState( SNM_INT_STATE_IDLE ); + manager->GetExtDataForRoom(0, NULL, NULL, NULL); + break; + } + + if( manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) + { + manager->GetServerContext2(); + break; + } + if( manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) + { + manager->SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + manager->SelectRandomServer(); + break; + } + if ( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) + { + manager->ServerContextValid_CreateRoom(); + break; + } + // Normal handling of context starting, from standard initialisation procedure + assert( manager->m_state == SNM_INT_STATE_STARTING_CONTEXT ); + if (errorCode < 0) + { + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED failed with error 0x%08x\n", errorCode); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + } + else + { + manager->m_offlineSQR = false; + manager->SetState(SNM_INT_STATE_IDLE); + + // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it from the tick instead + m_bCallPSNSignInCallback=true; + app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ m_bCallPSNSignInCallback true ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + + // if(s_SignInCompleteCallbackFn) + // { + // s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, 0); + // s_SignInCompleteCallbackFn = NULL; + // } + + + + // Check to see if we were booted from an invite. Only do this once, the first time we have all our networking stuff set up on boot-up + if( manager->m_doBootInviteCheck ) + { +// ORBIS_STUBBED; +// unsigned int type, attributes; +// CellGameContentSize gameSize;` +// char dirName[CELL_GAME_DIRNAME_SIZE]; +// +// if( g_bBootedFromInvite ) +// { +// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); +// manager->m_doBootInviteCheck = false; +// } + } + } + break; + case SCE_NP_MATCHING2_CONTEXT_EVENT_STOPPED: + if( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) + { + sceNpMatching2ContextStart(manager->m_matchingContext, (10*1000*1000)); + break; + } + assert(false); + break; + case SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER: + + app.DebugPrintf("SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER\n"); + app.DebugPrintf("eventCause=%u, errorCode=0x%08x\n", eventCause, errorCode); + + sceNpMatching2DestroyContext(manager->m_matchingContext); + manager->m_matchingContextValid = false; + manager->m_offlineSQR = true; + + if(manager->m_state == SNM_INT_STATE_STARTING_CONTEXT) + { + // MGH - to fix potential issue with a bad state assert on loading a level + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER failed\n"); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + } + switch(errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_ERROR_LATEST_PATCH_PKG_EXIST failed\n"); + // 4J-PB - need to fail the init, so the external state will be set to idle, and we can continue offline + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + break; + } + + break; + } +} + +// This is an implementation of SceNpMatching2RequestCallback. This callback is used by default for any matching 2 request functions. +void SQRNetworkManager_Orbis::DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg) +{ + int ret; + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if( id != manager->m_matchingContext ) return; + + // MGH - added this here for the NAT failure, should only happen for the join request, but without being able to test we can't be sure + if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) + { + app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); + } + + switch( event ) + { + // This is the response to sceNpMatching2GetWorldInfoList, which is called as part of the process to create a room (which needs a world to be created in). We aren't anticipating + // using worlds in a meaningful way so just getting the first world we find on the server here, and then advancing the state so that the tick can get on with the rest of the process. + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST: + if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) + { + // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here + break; + } + assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD ); + if( errorCode == 0 ) + { + if( data != 0 ) + { + // Currently just using first world - this may well be all that we need anyway + SceNpMatching2GetWorldInfoListResponse *pWorldList = (SceNpMatching2GetWorldInfoListResponse *)data; + if( pWorldList->worldNum >= 1 ) + { + manager->m_worldId = pWorldList->world[0].worldId; + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND); + break; + } + } + } + // We get this error when starting a new game after a disconnect occurred in a previous game with at least one remote player. Fix by stopping/starting the matching context. + // We stop the context here, which is picked up in the callback, and started again. Then the start event is picked up and reattempts the sceNpMatching2GetWorldInfoList. + if( errorCode == SCE_NET_ERROR_RESOLVER_ENODNS ) + { + sceNpMatching2ContextStop(manager->m_matchingContext); + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT); + break; + } + app.DebugPrintf("SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST failed, errorCode 0x%x, data %d\n", errorCode, data); + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + break; + // This is the response to sceNpMatching2CreateJoinRoom, which if successful means that we are just about ready to move to an online state as host of a game. The final + // transition actually occurs in the create room tick, on detecting that the state has transitioned to SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS here. + case SCE_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM: + if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) + { + // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here + break; + } + app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room complete, time taken %d, error 0x%x\n",System::currentTimeMillis()-s_roomStartTime, errorCode); + assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ); + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2CreateJoinRoomResponse *roomData = (SceNpMatching2CreateJoinRoomResponse *)data; + manager->m_localMemberId = roomData->memberList.me->memberId; + + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); + manager->m_room = roomData->roomDataInternal->roomId; + break; + } + } + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + break; + // This is the response to sceNpMatching2JoinRoom, which is called as the final stage of the process started when calling the JoinRoom method. If this is successful, then + // the state can change to SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS. We can transition out of that state once we have told the application that all the local players + // have joined. + case SCE_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM: + assert( manager->m_state == SNM_INT_STATE_JOINING_JOIN_ROOM); + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2JoinRoomResponse *roomData = (SceNpMatching2JoinRoomResponse *)data; + + manager->m_localMemberId = roomData->memberList.me->memberId; + manager->m_room = roomData->roomDataInternal->roomId; +// SonyVoiceChat::init(manager); + // Copy over initial room sync data + for( int i = 0; i < roomData->roomDataInternal->roomBinAttrInternalNum; i++ ) + { + if( roomData->roomDataInternal->roomBinAttrInternal[i].data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) + { + assert( roomData->roomDataInternal->roomBinAttrInternal[i].data.size == sizeof( manager->m_roomSyncData ) ); + memcpy( &manager->m_roomSyncData, roomData->roomDataInternal[i].roomBinAttrInternal[0].data.ptr, sizeof( manager->m_roomSyncData ) ); + +// manager->UpdatePlayersFromRoomSyncUIDs(); + // Update mapping from the room slot players to SQRNetworkPlayer instances + manager->MapRoomSlotPlayers(); + break; + } + } + manager->SetState(SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS); + break; + } + } + manager->SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); + if(errorCode == SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL) // MGH - added to fix "host has exited" error when 2 players go after the final slot + { + Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), DisconnectPacket::eDisconnect_ServerFull); + } + break; + // This is the response to sceNpMatching2GetRoomMemberDataInternal.This only happens on the host, as a response to an incoming connection being established, when we + // kick off the request for room member internal data so that we can determine what local players that remote machine is intending to bring into the game. At this point we can + // activate the host end of each of the Rupd connection that this machine requires. We can also update our player slot data (which gets syncronised back out to other room members) at this point. + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_MEMBER_DATA_INTERNAL: + if( errorCode == 0 ) + { + + if( data != 0 ) + { + SceNpMatching2GetRoomMemberDataInternalResponse *pRoomMemberData = (SceNpMatching2GetRoomMemberDataInternalResponse *)data; + assert( pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternalNum == 1 ); + + if( manager->m_isHosting ) + { + int playerMask = *((int *)(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr)); + + bool isFull = false; + bool success1 = manager->AddRemotePlayersAndSync( pRoomMemberData->roomMemberDataInternal->memberId, playerMask, &isFull ); + bool success2; + if( success1 ) + { + success2 = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, playerMask, pRoomMemberData->roomMemberDataInternal->memberId); + if( success2 ) + { + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, 0); + assert(ret == true); + break; + } + } + // Something has gone wrong adding these players to the room - kick out the player + SceNpMatching2KickoutRoomMemberRequest reqParam; + SceNpMatching2PresenceOptionData optParam; + memset(&reqParam,0,sizeof(reqParam)); + reqParam.roomId = manager->m_room; + reqParam.target = pRoomMemberData->roomMemberDataInternal->memberId; + // Set flag to indicate whether we were kicked for being out of room or not + reqParam.optData.data[0] = isFull ? 1 : 0; + reqParam.optData.len = 1; + int ret = sceNpMatching2KickoutRoomMember(manager->m_matchingContext, &reqParam, NULL, &manager->m_kickRequestId); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2KickoutRoomMember returns error 0x%x\n",ret); + } + else + { + if(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr == NULL) + { + // the host doesn't send out data, so this must be the host we're connecting to + + // If we are the client, then we locally know what Rupd connections we need (from m_localPlayerJoinMask) and can kick this off. + manager->m_hostMemberId = pRoomMemberData->roomMemberDataInternal->memberId; + bool ret = manager->CreateRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask, manager->m_localMemberId); + if( ret == false ) + { + manager->DeleteServerContext(); + } + else + { + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); + assert(ret == true); + } + } + else + { + // client <-> client + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); + assert(ret == true); + } + } + + } + } + break; + case SCE_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM: + // This is the response to sceNpMatching2LeaveRoom - from the Sony docs, this doesn't ever fail so no need to do error checking here +// SonyVoiceChat::signalDisconnected(); + assert(manager->m_state == SNM_INT_STATE_LEAVING ); + manager->DeleteServerContext(); + break; + // This is the response to SceNpMatching2GetRoomDataExternalListRequest, which happens when we request the full details of a room we are interested in joining + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_EXTERNAL_LIST: + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2GetRoomDataExternalListResponse *pExternalData = (SceNpMatching2GetRoomDataExternalListResponse *)data; + SceNpMatching2RoomDataExternal *pRoomExtData = pExternalData->roomDataExternal; + if( pExternalData->roomDataExternalNum == 1 ) + { + if(pRoomExtData->roomBinAttrExternalNum == 1 ) + { + memcpy(manager->m_pExtDataToUpdate, pRoomExtData->roomBinAttrExternal[0].ptr,pRoomExtData->roomBinAttrExternal[0].size); + manager->m_FriendSessionUpdatedFn(true, manager->m_pParamFriendSessionUpdated); + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + break; + case SCE_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL: + if( ( errorCode != 0 ) || manager->ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK) ) + { + app.DebugPrintf(CMinecraftApp::USER_RR,"Error updating external data 0x%x (from SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal event in callback)\n",errorCode); + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + manager->m_resendExternalRoomDataCountdown = 60; + } + break; + }; +} + +void SQRNetworkManager_Orbis::RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, const void *data, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + + bool gotEventData = false; + switch( event ) + { + case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_KICKEDOUT: + { +// SonyVoiceChat::signalRoomKickedOut(); + // We've been kicked out. This server has rejected our attempt to join, most likely because there wasn't enough space in the server to have us. There's a flag set + // so we can determine which thing has happened +// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo ); +// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataUpdateInfo, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo); +// app.DebugPrintf(CMinecraftApp::USER_RR,"SCE_NP_MATCHING2_ROOM_EVENT_Kickedout, sceNpMatching2GetEventData returning 0x%x\n",ret); + + bool bIsFull = false; + if( ( data ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) + { + gotEventData = true; + SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)(data); + if( pUpdateInfo->optData.len == 1 ) + { + if( pUpdateInfo->optData.data[0] == 1 ) + { + bIsFull = true; + } + } + } + app.DebugPrintf(CMinecraftApp::USER_RR,"IsFull determined to be %d\n",bIsFull); + if( bIsFull ) + { + manager->m_nextIdleReasonIsFull = true; + } + manager->ResetToIdle(); + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_DESTROYED: +// SonyVoiceChat::signalRoomDestroyed(); + + { + SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)data; + app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed\n"); + if( pUpdateInfo ) + { + app.DebugPrintf("Further info: Error 0x%x, cause %d\n",pUpdateInfo->errorCode,pUpdateInfo->eventCause); + } + // If we're hosting, then handle this a bit like a disconnect, in that we will shift the game into an offline game - but don't need to actually leave the room + // since that has been destroyed and so isn't there to be left anymore. Don't do this if we are disconnected though, as we've already handled this. + if( ( manager->m_isHosting ) && !manager->m_bLinkDisconnected ) + { + // MGH - we're not receiving an SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD after this so we have to remove all the remote players + while(manager->m_RudpCtxToPlayerMap.size()) + { + SQRNetworkPlayer* pRemotePlayer = manager->m_RudpCtxToPlayerMap.begin()->second; + manager->RemoveRemotePlayersAndSync( pRemotePlayer->m_roomMemberId, 15 ); + } + + if(pUpdateInfo && (pUpdateInfo->eventCause==SCE_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT)) + { + manager->m_listener->HandleDisconnect(true,true); + } + else + { + manager->m_listener->HandleDisconnect(true); + } + } + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_OWNER_CHANGED: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_DATA_INTERNAL: + // We are using the room internal data to synchronise the player data stored in m_roomSyncData from the host to clients. + // The host is the thing creating the internal room data, so it doesn't need to update itself. + if( !manager->m_isHosting ) + { +// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataInternal, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo); + if( ( data) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) + { + gotEventData = true; + SceNpMatching2RoomDataInternalUpdateInfo *pRoomData = (SceNpMatching2RoomDataInternalUpdateInfo *)(data); + for(int i = 0; i < pRoomData->newRoomBinAttrInternalNum; i++) + { + if( pRoomData->newRoomBinAttrInternal[i]->data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) + { + assert( pRoomData->newRoomBinAttrInternal[i]->data.size == sizeof( manager->m_roomSyncData ) ); + memcpy( &manager->m_roomSyncData, pRoomData->newRoomBinAttrInternal[i]->data.ptr, sizeof( manager->m_roomSyncData ) ); + +// manager->UpdatePlayersFromRoomSyncUIDs(); + // Update mapping from the room slot players to SQRNetworkPlayer instances + manager->MapRoomSlotPlayers(); +#if 0 + { + printf("New player sync data arrived\n"); + for(int i = 0; i < manager->m_roomSyncData.getPlayerCount(); i++ ) + { + printf("%d: small %d, machine %d, local %d\n",i, manager->m_roomSyncData.players[i].m_smallId, manager->m_roomSyncData.players[i].m_roomMemberId, manager->m_roomSyncData.players[i].m_localIdx); + } + } +#endif + break; + } + } + break; + } + // TODO - handle error here? What could we do? + } + + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL: + app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_ROOM_MEMBER_DATA_INTERNAL\n"); + + if( /*( errorCode == 0 ) && */(!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1) ) ) + { + // We'll get this sync'd round all the connected clients, but we only care about it on the host where we can use it to work out if any RUDP connections need to be made or released + if( manager->m_isHosting ) + { +// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); + if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2) ) ) + { + gotEventData = true; + SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); + assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); + + int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); + int oldMask = manager->GetOldMask( pRoomMemberData->newRoomMemberDataInternal->memberId ); + int addedMask = manager->GetAddedMask(playerMask, oldMask ); + int removedMask = manager->GetRemovedMask(playerMask, oldMask ); + + if( addedMask != 0 ) + { + bool success = manager->AddRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask ); + if( success ) + { + success = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask, pRoomMemberData->newRoomMemberDataInternal->memberId); + } + if( ( !success ) || (manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3) ) ) + { + // Failed for some reason - signal back to the client that this is the case, by updating its internal data back again, rather than have + // it wait for a timeout on its rudp connection initialisation. + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &oldMask; + binAttr.size = sizeof(oldMask); + + reqParam.roomId = manager->m_room; + reqParam.memberId = pRoomMemberData->newRoomMemberDataInternal->memberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( manager->m_matchingContext, &reqParam, NULL, &manager->m_setRoomMemberInternalDataRequestId ); + } + else + { + success = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, 0); + assert(success); + } + + } + + if( removedMask != 0 ) + { + manager->RemoveRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, removedMask ); + } + + break; + } + } + else + { + // If, as a client, we receive an updated room member data this could be for two reason. + // (1) Another client in the game has updated their own data as someone has joined/left the session + // (2) The server has set someone's data back, due to a failed attempt to join a game + // We're only interested in scenario (2), when the data that has been updated is our own, in which case we know to abandon creating rudp connections etc. for a new player +// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); + if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4) ) ) + { + gotEventData = true; + SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); + assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); + if( pRoomMemberData->newRoomMemberDataInternal->memberId == manager->m_localMemberId ) + { + int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); + if( playerMask != manager->m_localPlayerJoinMask ) + { + int playersToRemove = manager->m_localPlayerJoinMask & (~playerMask); + manager->RemoveNetworkPlayers( playersToRemove ); + if( manager->m_listener ) + { + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playersToRemove & ( 1 << i ) ) + { + manager->m_listener->HandleAddLocalPlayerFailed(i); + break; + } + } + } + } + } + } + + } + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_SIGNALING_OPT_PARAM: + break; + }; + +// // If we didn't get the event data, then we need to clear it, or the system even queue will overflow +// if( !gotEventData ) +// { +// sceNpMatching2ClearEventData(manager->m_matchingContext, eventKey); +// } +} + + + +// This is an implementation of SceNpMatching2SignalingCallback. We configure our too automatically create a star network of connections with the host at the hub, and can respond here to +// the connections being set up to layer sockets and Rudp on top. +void SQRNetworkManager_Orbis::SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code, void *arg) +{ + // MGH - changed this to queue up the signalling events from the callback and process them later on the server thread + + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + EnterCriticalSection(&manager->m_signallingEventListCS); + SignallingEvent ev; + ev.ctxId = ctxId; + ev.roomId = roomId; + ev.peerMemberId = peerMemberId; + ev.event = event; + ev.error_code = error_code; + manager->m_signallingEventList.push_back(ev); + LeaveCriticalSection(&manager->m_signallingEventListCS); +} + + + + +void SQRNetworkManager_Orbis::ProcessSignallingEvent(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code) +{ + switch( event ) + { + case SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD: + { + if( m_isHosting ) + { + // Remove any players associated with this peer + RemoveRemotePlayersAndSync( peerMemberId, 15 ); + } + else if(peerMemberId == m_hostMemberId) + { + // Host has left the game... so its all over for this client too. Finish everything up now, including deleting the server context which belongs to this gaming session + // This also might be a response to a request to leave the game from our end too so don't need to do anything in that case + if( m_state != SNM_INT_STATE_LEAVING ) + { + DeleteServerContext(); + ResetToIdle(); + } + } + else + { + // we've lost connection to another client (voice only) so kill the voice connection + // no players left on the remote machine once we remove this one + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); + if(pVoice) + SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); + } + } + break; + + case SCE_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED: + { + // MGH - changed this to always get the data now, as we need to know if the connecting peer is the host or not + // If we're the host, then we need to get the data associated with the connecting peer to know what connections we should be trying to match. So + // the actual creation of connections happens when the response for this request is processed. + + SceNpMatching2GetRoomMemberDataInternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam)); + reqParam.roomId = roomId; + reqParam.memberId = peerMemberId; + SceNpMatching2AttributeId attrs[1] = {SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID}; + reqParam.attrId = attrs; + reqParam.attrIdNum = 1; + + sceNpMatching2GetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_roomMemberDataRequestId); + } + break; + } +} + +void SQRNetworkManager_Orbis::SignallingEventsTick() +{ + EnterCriticalSection(&m_signallingEventListCS); + for(int i=0;iSetState(SNM_INT_STATE_INITIALISE_FAILED); +// if( s_SignInCompleteCallbackFn ) +// { +// if( s_signInCompleteCallbackIfFailed ) +// { +// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); +// } +// s_SignInCompleteCallbackFn = NULL; +// } +// return; +// } +// +// if( netstart_result.result != 0 ) +// { +// // Failed, or user may have decided not to sign in - maybe need to differentiate here +// manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); +// if( s_SignInCompleteCallbackFn ) +// { +// if( s_signInCompleteCallbackIfFailed ) +// { +// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); +// } +// s_SignInCompleteCallbackFn = NULL; +// } +// } +// +// break; +// case CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED: +// break; +// case CELL_SYSUTIL_NP_INVITATION_SELECTED: +// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); +// break; +// default: +// break; +// } +} + +// Implementation of CellRudpContextEventHandler. This is associate with an Rudp context every time one is created, and can be used to determine the status of each +// Rudp connection. We create one context/connection per local player on the non-hosting consoles. +void SQRNetworkManager_Orbis::RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + switch(event_id) + { + case SCE_RUDP_CONTEXT_EVENT_CLOSED: + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_CLOSED\n"); + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + if(pVoice) + { + pVoice->m_bConnected = false; + } + else + { + app.DebugPrintf(CMinecraftApp::USER_RR,"RUDP closed - event error 0x%x\n",error_code); + if( !manager->m_isHosting ) + { + if( manager->m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) + { + manager->LeaveRoom(true); + } + } + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_ESTABLISHED: + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ESTABLISHED\n"); + + SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); + if( player ) + { + // Flag connection stage as being completed for this player + manager->NetworkPlayerConnectionComplete(player); + } + else + { + SonyVoiceChat_Orbis::setConnected(ctx_id); + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_ERROR: + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ERROR\n"); + break; + case SCE_RUDP_CONTEXT_EVENT_WRITABLE: + { + SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); + // This event signifies that room has opened up in the write buffer, so attempt to send something + if( player ) + { + player->SendMoreInternal(); + } + else + { + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + assert(pVoice); + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_READABLE: + if( manager->m_listener ) + { + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + if(pVoice) + { + pVoice->readRemoteData(); + } + else + { + unsigned int dataSize = sceRudpGetSizeReadable(ctx_id); + // If we're the host, and this player hasn't yet had its small id confirmed, then the first byte sent to us should be this id + if( manager->m_isHosting ) + { + SQRNetworkPlayer *playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); + if( playerFrom && !playerFrom->HasSmallIdConfirmed() ) + { + if( dataSize >= sizeof(SQRNetworkPlayer::InitSendData) ) + { + SQRNetworkPlayer::InitSendData ISD; + int bytesRead = sceRudpRead( ctx_id, &ISD, sizeof(SQRNetworkPlayer::InitSendData), 0, NULL ); + if( bytesRead == sizeof(SQRNetworkPlayer::InitSendData) ) + { + manager->NetworkPlayerInitialDataReceived(playerFrom, &ISD); + dataSize -= sizeof(SQRNetworkPlayer::InitSendData); + } + else + { + assert(false); + } + } + else + { + assert(false); + } + } + } + + if( dataSize > 0 ) + { + unsigned char *data = new unsigned char [ dataSize ]; + int bytesRead = sceRudpRead( ctx_id, data, dataSize, 0, NULL ); + if( bytesRead > 0 ) + { + SQRNetworkPlayer *playerFrom, *playerTo; + if( manager->m_isHosting ) + { + // Data always going from a remote player, to the host + playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); + playerTo = manager->m_aRoomSlotPlayers[0]; + } + else + { + // Data always going from host player, to a local player + playerFrom = manager->m_aRoomSlotPlayers[0]; + playerTo = manager->GetPlayerFromRudpCtx( ctx_id ); + } + if( ( playerFrom != NULL ) && ( playerTo != NULL ) ) + { + manager->m_listener->HandleDataReceived( playerFrom, playerTo, data, bytesRead ); + } + } + delete [] data; + } + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_FLUSHED: + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_FLUSHED\n"); + break; + } +} + +// Implementation of CellRudpEventHandler +int SQRNetworkManager_Orbis::RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if( event_id == SCE_RUDP_EVENT_SOCKET_RELEASED ) + { + assert( soc == manager->m_soc ); + sceNetSocketClose(soc); + manager->m_soc = -1; + } + return 0; +} + +void SQRNetworkManager_Orbis::NetCtlCallback(int eventType, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + // Oddly, the disconnect event comes in with a new state of "CELL_NET_CTL_STATE_Connecting"... looks like the event is more important than the state to + // determine what has just happened + if( eventType == SCE_NET_CTL_EVENT_TYPE_DISCONNECTED)// CELL_NET_CTL_EVENT_LINK_DISCONNECTED ) + { + manager->m_bLinkDisconnected = true; + manager->m_listener->HandleDisconnect(false); + } + else //if( event == CELL_NET_CTL_EVENT_ESTABLISH ) + { + manager->m_bLinkDisconnected = false; + } + +} + +// Called when the context has been created, and we are intending to create a room. +void SQRNetworkManager_Orbis::ServerContextValid_CreateRoom() +{ + // First find a world + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD); + + SceNpMatching2GetWorldInfoListRequest reqParam; + + // Request parameters + memset(&reqParam, 0, sizeof(reqParam)); + reqParam.serverId = m_serverId; + + int ret = -1; + if( !ForceErrorPoint(SNM_FORCE_ERROR_GET_WORLD_INFO_LIST) ) + { + ret = sceNpMatching2GetWorldInfoList( m_matchingContext, &reqParam, NULL, &m_getWorldRequestId); + } + if (ret < 0) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + return; + } +} + +// Called when the context has been created, and we are intending to join a pre-existing room. +void SQRNetworkManager_Orbis::ServerContextValid_JoinRoom() +{ +// assert( m_state == SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT ); + + SetState(SNM_INT_STATE_JOINING_JOIN_ROOM); + + // Join the room, passing the local player mask as initial binary data so that the host knows what local players are here + SceNpMatching2JoinRoomRequest reqParam; + SceNpMatching2BinAttr binAttr; + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_roomToJoin; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2JoinRoom( m_matchingContext, &reqParam, NULL, &m_joinRoomRequestId ); + if ( (ret < 0) || ForceErrorPoint(SNM_FORCE_ERROR_JOIN_ROOM) ) + { + if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) + { + app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); + } + SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); + } +} + +const SceNpCommunicationId* SQRNetworkManager_Orbis::GetSceNpCommsId() +{ + return &s_npCommunicationId; +} + +const SceNpCommunicationSignature* SQRNetworkManager_Orbis::GetSceNpCommsSig() +{ + return &s_npCommunicationSignature; +} + +const SceNpTitleId* SQRNetworkManager_Orbis::GetSceNpTitleId() +{ + return &s_npTitleId; +} + +const SceNpTitleSecret* SQRNetworkManager_Orbis::GetSceNpTitleSecret() +{ + return &s_npTitleSecret; +} + +int SQRNetworkManager_Orbis::GetOldMask(SceNpMatching2RoomMemberId memberId) +{ + int oldMask = 0; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId == memberId ) + { + oldMask |= (1 << m_roomSyncData.players[i].m_localIdx); + } + } + return oldMask; +} + +int SQRNetworkManager_Orbis::GetAddedMask(int newMask, int oldMask) +{ + return newMask & ~oldMask; +} + +int SQRNetworkManager_Orbis::GetRemovedMask(int newMask, int oldMask) +{ + return oldMask & ~newMask; +} + + +void SQRNetworkManager_Orbis::GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) +{ + static SceNpMatching2GetRoomDataExternalListRequest reqParam; + static SceNpMatching2RoomId aRoomId[1]; + static SceNpMatching2AttributeId attr[1]; + + // All parameters will be NULL if this is being called a second time, after creating a new matching context via one of the paths below (using GetMatchingContext). + // NULL parameters therefore basically represents an attempt to retry the last sceNpMatching2GetRoomDataExternalList + if( extData != NULL ) + { + aRoomId[0] = roomId; + attr[0] = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + + memset(&reqParam, 0, sizeof(reqParam)); + reqParam.roomId = aRoomId; + reqParam.roomIdNum = 1; + reqParam.attrIdNum = 1; + reqParam.attrId = attr; + + m_FriendSessionUpdatedFn = FriendSessionUpdatedFn; + m_pParamFriendSessionUpdated = pParam; + m_pExtDataToUpdate = extData; + } + + // Check there's a valid matching context and possibly recreate here + if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) + { + // No matching context, and failed to try and make one. We're really broken here. + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + return; + } + + // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. + if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + app.DebugPrintf("Having to recreate matching context, setting state to SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT\n"); + return; + } + + int ret = sceNpMatching2GetRoomDataExternalList( m_matchingContext, &reqParam, NULL, &m_roomDataExternalListRequestId ); + + // If we hadn't properly detected that a matching context was unvailable, we might still get an error indicating that it is from the previous call. Handle similarly, but we need + // to destroy the context first. + if( ret == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) // Also checking for this as a means of simulating the previous error + { + sceNpMatching2DestroyContext(m_matchingContext); + m_matchingContextValid = false; + if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) + { + // No matching context, and failed to try and make one. We're really broken here. + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + return; + }; + // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. + if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + return; + } + } + + if( ret != 0 ) + { + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + } +} + + +#ifdef _CONTENT_PACKAGE +bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError error) +{ + return false; +} +#else +bool SQRNetworkManager_Orbis::aForceError[SNM_FORCE_ERROR_COUNT] = +{ + false, // SNM_FORCE_ERROR_NP2_INIT + false, // SNM_FORCE_ERROR_NET_INITIALIZE_NETWORK + false, // SNM_FORCE_ERROR_NET_CTL_INIT + false, // SNM_FORCE_ERROR_RUDP_INIT + false, // SNM_FORCE_ERROR_NET_START_DIALOG + false, // SNM_FORCE_ERROR_MATCHING2_INIT + false, // SNM_FORCE_ERROR_REGISTER_NP_CALLBACK + false, // SNM_FORCE_ERROR_GET_NPID + false, // SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT + false, // SNM_FORCE_ERROR_REGISTER_CALLBACKS + false, // SNM_FORCE_ERROR_CONTEXT_START_ASYNC + false, // SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY_COUNT + false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY + false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST + false, // SNM_FORCE_ERROR_LEAVE_ROOM + false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL + false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2 + false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT + false, // SNM_FORCE_ERROR_CREATE_JOIN_ROOM + false, // SNM_FORCE_ERROR_GET_SERVER_INFO + false, // SNM_FORCE_ERROR_DELETE_SERVER_CONTEXT + false, // SNM_FORCE_ERROR_SETSOCKOPT_0 + false, // SNM_FORCE_ERROR_SETSOCKOPT_1 + false, // SNM_FORCE_ERROR_SETSOCKOPT_2 + false, // SNM_FORCE_ERROR_SOCK_BIND + false, // SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT + false, // SNM_FORCE_ERROR_RUDP_BIND + false, // SNM_FORCE_ERROR_RUDP_INIT2 + false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA + false, // SNM_FORCE_ERROR_GET_SERVER_INFO_DATA + false, // SNM_FORCE_ERROR_GET_WORLD_INFO_DATA + false, // SNM_FORCE_ERROR_GET_CREATE_JOIN_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST_DATA + false, // SNM_FORCE_ERROR_GET_JOIN_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_ROOM_MEMBER_DATA_INTERNAL + false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA2 + false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK + false, // SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK + false, // SNM_FORCE_ERROR_UPDATED_ROOM_DATA + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4 + false, // SNM_FORCE_ERROR_GET_WORLD_INFO_LIST + false, // SNM_FORCE_ERROR_JOIN_ROOM +}; + +bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError err) +{ + return aForceError[err]; +} +#endif + + +int ErrorPSNDisconnectedDialogReturned(void *pParam, int iPad, const C4JStorage::EMessageResult iResult) +{ + //SQRNetworkManager_Orbis::CallSignInCompleteCallback(); + SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=true; + + return 0; +} + +void SQRNetworkManager_Orbis::AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed/*=false*/, int iPad/*=-1*/) +{ + s_SignInCompleteCallbackFn = SignInCompleteCallbackFn; + s_signInCompleteCallbackIfFailed = callIfFailed; + s_SignInCompleteParam = pParam; + s_SignInCompleteCallbackPad = iPad; + + // If pad isn't set, get primary pad + iPad = iPad == -1 ? ProfileManager.GetPrimaryPad() : iPad; + + if(ProfileManager.isSignedInPSN(iPad) == false) + { + bool bOpenedDialog = false; + int NPError = ProfileManager.getNPAvailability(iPad); + if(NPError == SCE_NP_ERROR_SIGNED_OUT || NPError == SCE_NP_ERROR_NOT_SIGNED_UP) + { + int32_t ret=sceErrorDialogInitialize(); + if ( ret==SCE_OK ) + { + SceErrorDialogParam dialogParameter; + sceErrorDialogParamInitialize( &dialogParameter ); + dialogParameter.errorCode = SCE_NP_ERROR_SIGNED_OUT; // for force display. + dialogParameter.userId = ProfileManager.getUserID(iPad); + ret = sceErrorDialogOpen( &dialogParameter ); + if( ret < 0 ) + { + app.DebugPrintf("sceErrorDialogOpen failed : 0x%08x\n",ret); + assert(0); + } + else + { + bOpenedDialog = true; + app.DebugPrintf("sceErrorDialogOpen s_errorDialogRunning\n"); + s_errorDialogRunning = true; + } + } + } + + if(!bOpenedDialog) + { + // This shouldn't happen generally + assert(0); + + if(s_SignInCompleteCallbackFn) // MGH - added after crash on PS4 + { + if( s_signInCompleteCallbackIfFailed ) + { + m_bCallPSNSignInCallback=true; + s_signInCompleteCallbackFAIL=true; + + //s_signInCompleteCallbackIfFailed=false; + //s_SignInCompleteCallbackFn(s_SignInCompleteParam, false, iPad); + } + //s_SignInCompleteCallbackFn = NULL; + } + } + } + else if(ProfileManager.isConnectedToPSN(iPad) == false) + { + // we're signed into PSN, but we don't have a net connection, throw up a lan error + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA,1,iPad,ErrorPSNDisconnectedDialogReturned,pParam, app.GetStringTable()); + + } +} + +int SQRNetworkManager_Orbis::SetRichPresence(const void *data) +{ + const sce::Toolkit::NP::PresenceDetails *newPresenceInfo = (const sce::Toolkit::NP::PresenceDetails *)data; + + s_lastPresenceInfo.status = newPresenceInfo->status; + s_lastPresenceInfo.userInfo = newPresenceInfo->userInfo; + + s_presenceStatusDirty = true; + SendLastPresenceInfo(); + + // Return as if no error happened no matter what, as we'll be resending ourselves if we need to and don't want the calling system to retry + return 0; +} + +void SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(void *data, unsigned int dataBytes) +{ + assert(dataBytes <= SCE_TOOLKIT_NP_IN_GAME_PRESENCE_DATA_SIZE_MAX ); + memcpy(s_lastPresenceInfo.data, data, dataBytes); + s_lastPresenceInfo.size = dataBytes; + + s_presenceDataDirty = true; + SendLastPresenceInfo(); +} + +void SQRNetworkManager_Orbis::TickRichPresence() +{ + if( s_resendPresenceTime ) + { + if( s_resendPresenceTime < System::currentTimeMillis() ) + { + s_resendPresenceTime = 0; + SendLastPresenceInfo(); + } + } +} + +void SQRNetworkManager_Orbis::SendLastPresenceInfo() +{ + // Don't attempt to send if we are already waiting to resend + if( s_resendPresenceTime ) return; + + // If we are trying to send at a rate faster than we should be, then use the resend mechanism to send at an appropriate time + if( ( System::currentTimeMillis() - s_lastPresenceTime ) < MIN_PRESENCE_RESEND_TIME ) + { + s_resendPresenceTime = s_lastPresenceTime + MIN_PRESENCE_RESEND_TIME; + return; + } + + // On PS4 we can't set the status and the data at the same time + unsigned int options = 0; + if( s_presenceDataDirty ) + { + // Prioritise data over status as it is critical to discovering the network game + s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_DATA; + } + else if( s_presenceStatusDirty ) + { + s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_STATUS; + } + else + { + return; // nothing to be done. + } + + int err = -1; + // check if we're connected to the PSN first + if(ProfileManager.IsSignedInLive(ProfileManager.getQuadrant(s_lastPresenceInfo.userInfo.userId))) + { + err = sce::Toolkit::NP::Presence::Interface::setPresence(&s_lastPresenceInfo); + app.DebugPrintf("Updating presence type %d @ %dms\n",s_lastPresenceInfo.presenceType,(System::currentTimeMillis()%1000000)); + s_lastPresenceTime = System::currentTimeMillis(); + } + + if( err == SCE_TOOLKIT_NP_SUCCESS ) + { + // Successfully sent something + if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) + { + s_presenceDataDirty = false; + } + else + { + s_presenceStatusDirty = false; + } + } + + // If there's still work to be done, use resend mechanism to do it + if( s_presenceDataDirty || s_presenceStatusDirty ) + { + s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; + } +} + +void SQRNetworkManager_Orbis::SetPresenceFailedCallback() +{ + // Check the last presence type to be sent + if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) + { + s_presenceDataDirty = true; + } + else + { + s_presenceStatusDirty = true; + } + s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; +} + + + +void SQRNetworkManager_Orbis::SetPresenceDataStartHostingGame() +{ + if( m_offlineGame ) + { + SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&c_presenceSyncInfoNULL, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); + } + else + { + SQRNetworkManager_Orbis::PresenceSyncInfo presenceInfo; + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData( &presenceInfo, m_joinExtData, m_room, m_serverId ); + SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&presenceInfo, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); +// OrbisNPToolkit::createNPSession(); + } +} + +int SQRNetworkManager_Orbis::GetJoiningReadyPercentage() +{ + if ( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ) + { + int completed = ( m_totalServerCount - m_serverCount ) - 1; + int pc = ( completed * 100 ) / m_totalServerCount; + if( pc < 0 ) pc = 0; + if( pc > 100 ) pc = 100; + return pc; + } + else + { + return 100; + } +} + +void SQRNetworkManager_Orbis::removePlayerFromVoiceChat( SQRNetworkPlayer* pPlayer ) +{ + if(pPlayer->IsLocal()) + { + + SonyVoiceChat_Orbis::disconnectLocalPlayer(pPlayer->GetLocalPlayerIndex()); + } + else + { + int numRemotePlayersLeft = 0; + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + if( m_aRoomSlotPlayers[i] != pPlayer ) + { + if(m_aRoomSlotPlayers[i]->m_roomMemberId == pPlayer->m_roomMemberId) + numRemotePlayersLeft++; + } + } + } + if(numRemotePlayersLeft == 0) + { + // no players left on the remote machine once we remove this one + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(pPlayer->m_roomMemberId); + //assert(pVoice); + if(pVoice) + SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); + } + } +} + +// While we're in online gameplay notify Sony accordingly (every 5 seconds) +void SQRNetworkManager_Orbis::TickNotify() +{ + if (g_NetworkManager.IsInSession() && !g_NetworkManager.IsLocalGame()) + { + long long currentTime = System::currentTimeMillis(); + + // Note: interval at which to notify Sony of realtime play, according to docs an interval greater than 1 sec is bad + // but in testing NP debug was happy with 5 seconds, to be on the safe side call it 0.75 seconds + int notifyInterval = 750; + + if (currentTime - m_lastNotifyTime > notifyInterval) + { + m_lastNotifyTime = currentTime; + for(int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (ProfileManager.IsSignedInLive(i)) + { + NotifyRealtimePlusFeature(i); + } + } + } + } +} + +// Notify plus feature for given quadrant +void SQRNetworkManager_Orbis::NotifyRealtimePlusFeature(int iQuadrant) +{ + SceNpNotifyPlusFeatureParameter param = SceNpNotifyPlusFeatureParameter(); + param.userId = ProfileManager.getUserID(iQuadrant); + param.size = sizeof(SceNpNotifyPlusFeatureParameter); + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + ZeroMemory(param.padding, sizeof(char) * 4); + ZeroMemory(param.reserved, sizeof(uint8_t) * 32); + + int err = sceNpNotifyPlusFeature(¶m); + if (err != SCE_OK) + { + app.DebugPrintf("SQRNetworkManager_Orbis::NotifyRealtimePlusFeature: sceNpNotifyPlusFeature failed (0x%x)\n", err); + assert(0); + } +} + +// void SQRNetworkManager_Orbis::CallSignInCompleteCallback() +// { +// // If there's a callback +// if( s_SignInCompleteCallbackFn) +// { +// app.DebugPrintf("============ Calling CallSignInCompleteCallback and s_SignInCompleteCallbackFn is OK\n"); +// bool isSignedIn = ProfileManager.IsSignedInLive(s_SignInCompleteCallbackPad); +// +// s_SignInCompleteCallbackFn(s_SignInCompleteParam, isSignedIn, s_SignInCompleteCallbackPad); +// s_SignInCompleteCallbackFn = NULL; +// s_SignInCompleteCallbackPad = -1; +// } +// else +// { +// app.DebugPrintf("============ Calling CallSignInCompleteCallback but s_SignInCompleteCallbackFn is NULL\n"); +// } +//} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h new file mode 100644 index 00000000..5b19f63e --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h @@ -0,0 +1,355 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +// #include "SonyVoiceChat_Orbis.h" + +#include "..\..\Common\Network\Sony\SQRNetworkManager.h" + +class SQRNetworkPlayer; +class ISQRNetworkManagerListener; +class SonyVoiceChat_Orbis; +class SQRVoiceConnection; +class C4JThread; + +// This is the lowest level manager for providing network functionality on Sony platforms. This manages various network activities including the players within a gaming session. +// The game shouldn't directly use this class, it is here to provide functionality required by PlatformNetworkManagerSony. + +class SQRNetworkManager_Orbis : public SQRNetworkManager +{ + friend class SonyVoiceChat_Orbis; + friend class SQRNetworkPlayer; + + static const eSQRNetworkManagerState m_INTtoEXTStateMappings[SNM_INT_STATE_COUNT]; + +public: + SQRNetworkManager_Orbis(ISQRNetworkManagerListener *listener); + + // General + void Tick(); + void Initialise(); + void Terminate(); + eSQRNetworkManagerState GetState(); + bool IsHost(); + bool IsReadyToPlayOrIdle(); + bool IsInSession(); + + // Session management + void CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline); + void UpdateExternalRoomData(); + bool FriendRoomManagerIsBusy(); + bool FriendRoomManagerSearch(); + bool FriendRoomManagerSearch2(); + int FriendRoomManagerGetCount(); + void FriendRoomManagerGetRoomInfo(int idx, SessionSearchResult *searchResult); + bool JoinRoom(SessionSearchResult *searchResult, int localPlayerMask); + bool JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_Orbis::PresenceSyncInfo *presence); + void StartGame(); + void LeaveRoom(bool bActuallyLeaveRoom); + void EndGame(); + bool SessionHasSpace(int spaceRequired); + bool AddLocalPlayerByUserIndex(int idx); + bool RemoveLocalPlayerByUserIndex(int idx); + void SendInviteGUI(); + static void RecvInviteGUI(); + void TickInviteGUI(); + + // Remote play + void UpdateRemotePlay(); + + + +// void GetInviteDataAndProcess(SceNpBasicAttachmentDataId id); +// static bool UpdateInviteData(SQRNetworkManager_Orbis::PresenceSyncInfo *invite); + void GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ); + + // Player retrieval + int GetPlayerCount(); + int GetOnlinePlayerCount(); + SQRNetworkPlayer *GetPlayerByIndex(int idx); + SQRNetworkPlayer *GetPlayerBySmallId(int idx); + SQRNetworkPlayer *GetPlayerByXuid(PlayerUID xuid); + SQRNetworkPlayer *GetLocalPlayerByUserIndex(int idx); + SQRNetworkPlayer *GetHostPlayer(); + + void removePlayerFromVoiceChat(SQRNetworkPlayer* pPlayer); + // Communication parameter storage + static const SceNpCommunicationId* GetSceNpCommsId(); + static const SceNpCommunicationSignature* GetSceNpCommsSig(); + static const SceNpTitleId* GetSceNpTitleId(); + static const SceNpTitleSecret* GetSceNpTitleSecret(); + + static void GetInviteDataAndProcess(sce::Toolkit::NP::MessageAttachment* pInvite); +private: + void InitialiseAfterOnline(); + void ErrorHandlingTick(); + void UpdateOnlineStatus(int status) { m_onlineStatus = status; } + int GetOnlineStatus() { return m_onlineStatus; } + + ISQRNetworkManagerListener *m_listener; + SQRNetworkPlayer *GetPlayerIfReady(SQRNetworkPlayer *player); + + // Internal state + void SetState(eSQRNetworkManagerInternalState state); + void ResetToIdle(); + eSQRNetworkManagerInternalState m_state; + eSQRNetworkManagerState m_stateExternal; + bool m_nextIdleReasonIsFull; + bool m_isHosting; + SceNpMatching2RoomMemberId m_localMemberId; + SceNpMatching2RoomMemberId m_hostMemberId; // if we're not the host + int m_localPlayerCount; + int m_localPlayerJoined; // Client only, keep a count of how many local players we have confirmed as joined to the application + SceNpMatching2RoomId m_room; + unsigned char m_currentSmallId; + int m_soc; + bool m_offlineGame; + bool m_offlineSQR; + int m_resendExternalRoomDataCountdown; + bool m_matching2initialised; + PresenceSyncInfo m_inviteReceived[MAX_SIMULTANEOUS_INVITES]; + int m_inviteIndex; + static PresenceSyncInfo *m_gameBootInvite; + static PresenceSyncInfo m_gameBootInvite_data; + bool m_doBootInviteCheck; + bool m_isInSession; +// static SceNpBasicAttachmentDataId s_lastInviteIdToRetry; + int m_onlineStatus; + bool m_bLinkDisconnected; + + +private: + + CRITICAL_SECTION m_csRoomSyncData; + RoomSyncData m_roomSyncData; + void *m_joinExtData; + int m_joinExtDataSize; + + std::vector m_vecTempPlayers; + SQRNetworkPlayer *m_aRoomSlotPlayers[MAX_ONLINE_PLAYER_COUNT]; // Maps from the players in m_roomSyncData, to SQRNetworkPlayers + void FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId); + + void MapRoomSlotPlayers(int roomSlotPlayerCount =-1); + void UpdateRoomSyncUIDsFromPlayers(); + void UpdatePlayersFromRoomSyncUIDs(); + void LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize); + int GetSessionIndex(SQRNetworkPlayer *player); + + bool AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull = NULL ); + void RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask ); + void RemoveNetworkPlayers( int mask ); + void SetLocalPlayersAndSync(); + void SyncRoomData(); + SceNpMatching2RequestId m_setRoomDataRequestId; + SceNpMatching2RequestId m_setRoomIntDataRequestId; + SceNpMatching2RequestId m_roomExtDataRequestId; + + // Server context management + bool GetMatchingContext(eSQRNetworkManagerInternalState asyncState); + bool GetServerContext(); + bool GetServerContext2(); + bool GetServerContext(SceNpMatching2ServerId serverId); + void DeleteServerContext(); + bool SelectRandomServer(); + void ServerContextTick(); + int m_totalServerCount; + int m_serverCount; + SceNpMatching2ServerId *m_aServerId; + SceNpMatching2ServerId m_serverId; + bool m_serverContextValid; + SceNpMatching2RequestId m_serverSearchRequestId; + SceNpMatching2RequestId m_serverContextRequestId; + + // Room creation management + SceNpMatching2RequestId m_getWorldRequestId; + SceNpMatching2RequestId m_createRoomRequestId; + SceNpMatching2WorldId m_worldId; + void RoomCreateTick(); + + // Room joining management + SceNpMatching2RoomId m_roomToJoin; + int m_localPlayerJoinMask; + SceNpMatching2RequestId m_joinRoomRequestId; + SceNpMatching2RequestId m_kickRequestId; + + // Room leaving management + SceNpMatching2RequestId m_leaveRoomRequestId; + + // Adding extra network players management + SceNpMatching2RequestId m_setRoomMemberInternalDataRequestId; + + // Player state management + void NetworkPlayerConnectionComplete(SQRNetworkPlayer *player); + void NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId); + void NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player,void *data); + void NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId); + void HandlePlayerJoined(SQRNetworkPlayer *player); + CRITICAL_SECTION m_csPlayerState; + + // State and thread for managing basic event type messages + C4JThread *m_basicEventThread; + SceKernelEqueue m_basicEventQueue; + static int BasicEventThreadProc( void *lpParameter); + + // State and storage for managing search for friends' games + eSQRNetworkManagerFriendSearchState m_friendSearchState; + SceNpMatching2ContextId m_matchingContext; + bool m_matchingContextValid; + SceNpMatching2RequestId m_friendSearchRequestId; + unsigned int m_friendCount; + C4JThread *m_getFriendCountThread; + static int GetFriendsThreadProc( void* lpParameter ); + void FriendSearchTick(); + SceNpMatching2RequestId m_roomDataExternalListRequestId; + void (* m_FriendSessionUpdatedFn)(bool success, void *pParam); + void *m_pParamFriendSessionUpdated; + void *m_pExtDataToUpdate; + + // Results from searching for rooms that friends are playing in - 5 matched arrays to store their NpIds, rooms, servers, whether a room was found, and whether the external data had been received for the room. Also a count of how many elements are used in this array. + class FriendSearchResult + { + public: + SceNpId m_NpId; + SceNpMatching2RoomId m_RoomId; + SceNpMatching2ServerId m_ServerId; + bool m_RoomFound; + void *m_RoomExtDataReceived; + }; + std::vector m_aFriendSearchResults; + + // Rudp management and local players + std::unordered_map m_RudpCtxToPlayerMap; + + std::unordered_map m_NetAddrToVoiceConnectionMap; + + bool CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersPeerMemberId); + bool CreateVoiceRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask); + bool CreateSocket(); + SQRNetworkPlayer *GetPlayerFromRudpCtx(int rudpCtx); + SQRVoiceConnection* GetVoiceConnectionFromRudpCtx(int rudpCtx); + + SQRNetworkPlayer *GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx); + SceNpMatching2RequestId m_roomMemberDataRequestId; + + // Callbacks (for matching) + bool RegisterCallbacks(); + static void ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg); + + static void DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg); + static void RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, const void *data, void *arg); + + + // MGH - changed this to queue up the signalling events from the callback and process them later on the server thread + class SignallingEvent + { + public: + SceNpMatching2ContextId ctxId; + SceNpMatching2RoomId roomId; + SceNpMatching2RoomMemberId peerMemberId; + SceNpMatching2Event event; + int error_code; + }; + std::vector m_signallingEventList; + CRITICAL_SECTION m_signallingEventListCS; + + static void SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code, void *arg); + void ProcessSignallingEvent(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code); + void SignallingEventsTick(); + + // Callback for NpBasic + static int BasicEventCallback(int event, int retCode, uint32_t reqId, void *arg); + + // Callback for NpManager + static void ManagerCallback(int event, int result, void *arg); + + // Callback for sys util + static void SysUtilCallback(uint64_t status, uint64_t param, void *userdata); + + // Callbacks for rudp + static void RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg); + static int RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg); + + // Callback for netctl + static void NetCtlCallback(int eventType, void *arg); + + // Methods to be called when the server context has been created + void ServerContextValid_CreateRoom(); + void ServerContextValid_JoinRoom(); + + // Mask utilities + int GetOldMask(SceNpMatching2RoomMemberId memberId); + int GetAddedMask(int newMask, int oldMask); + int GetRemovedMask(int newMask, int oldMask); + +#ifndef _CONTENT_PACKAGE + static bool aForceError[SNM_FORCE_ERROR_COUNT]; +#endif + bool ForceErrorPoint(eSQRForceError err); + +public: + static void AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed = false, int iPad = -1); + //static void CallSignInCompleteCallback(); + static int (*s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad); + static bool s_signInCompleteCallbackIfFailed; + static bool s_signInCompleteCallbackFAIL; + + static void *s_SignInCompleteParam; + static bool s_SignInCompleteCallbackPending; + static long long s_errorDialogClosed; + static long long s_systemDialogClosed; + static int s_SignInCompleteCallbackPad; + + // Time to wait for system UI before we check result + #define SYSTEM_UI_WAIT_TIME 1000 + + static int SetRichPresence(const void *data); + void SetPresenceDataStartHostingGame(); + int GetJoiningReadyPercentage(); + static void SetPresenceFailedCallback(); + + // 4J-PB - so we can stop the crash when Iggy's LoadMovie is called from the ContextCallback + static bool m_bCallPSNSignInCallback; + +private: + static void UpdateRichPresenceCustomData(void *data, unsigned int dataBytes); + static void TickRichPresence(); + static void SendLastPresenceInfo(); + + void OnlineCheck(); + void tickErrorDialog(); + + static sce::Toolkit::NP::PresenceDetails s_lastPresenceInfo; + + static const int MIN_PRESENCE_RESEND_TIME = 30 * 1000; // Minimum presence send rate - doesn't seem possible to find out what this actually should be + static __int64 s_lastPresenceTime; + static __int64 s_resendPresenceTime; + + static bool s_presenceStatusDirty; + static bool s_presenceDataDirty; + + static PresenceSyncInfo s_lastPresenceSyncInfo; + static PresenceSyncInfo c_presenceSyncInfoNULL; + static bool b_inviteRecvGUIRunning; + + // Debug + static long long s_roomStartTime; + + // Error dialog + static bool s_errorDialogRunning; + + // NP Notify + void TickNotify(); + void NotifyRealtimePlusFeature(int iQuadrant); + long long m_lastNotifyTime; + + static void RefreshChatAndContentRestrictionsReturned_HandleInvite(void *pParam); + bool m_bRefreshingRestrictionsForInvite; + +public: + static bool s_bInviteDialogRunning; +}; + diff --git a/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp new file mode 100644 index 00000000..34ab67e4 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp @@ -0,0 +1,1314 @@ +#include "stdafx.h" + +#include "SonyCommerce_Orbis.h" +#include "PS3\PS3Extras\ShutdownManager.h" +#include + + +bool SonyCommerce_Orbis::m_bCommerceInitialised = false; +// SceNpCommerce2SessionInfo SonyCommerce_Orbis::m_sessionInfo; +SonyCommerce_Orbis::State SonyCommerce_Orbis::m_state = e_state_noSession; +int SonyCommerce_Orbis::m_errorCode = 0; +LPVOID SonyCommerce_Orbis::m_callbackParam = NULL; + +void* SonyCommerce_Orbis::m_receiveBuffer = NULL; +SonyCommerce_Orbis::Event SonyCommerce_Orbis::m_event; +std::queue SonyCommerce_Orbis::m_messageQueue; +std::vector* SonyCommerce_Orbis::m_pProductInfoList = NULL; +SonyCommerce_Orbis::ProductInfoDetailed* SonyCommerce_Orbis::m_pProductInfoDetailed = NULL; +SonyCommerce_Orbis::ProductInfo* SonyCommerce_Orbis::m_pProductInfo = NULL; + +SonyCommerce_Orbis::CategoryInfo* SonyCommerce_Orbis::m_pCategoryInfo = NULL; +const char* SonyCommerce_Orbis::m_pProductID = NULL; +char* SonyCommerce_Orbis::m_pCategoryID = NULL; +SonyCommerce_Orbis::CheckoutInputParams SonyCommerce_Orbis::m_checkoutInputParams; +SonyCommerce_Orbis::DownloadListInputParams SonyCommerce_Orbis::m_downloadInputParams; + +SonyCommerce_Orbis::CallbackFunc SonyCommerce_Orbis::m_callbackFunc = NULL; +// sys_memory_container_t SonyCommerce_Orbis::m_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; +bool SonyCommerce_Orbis::m_bUpgradingTrial = false; + +SonyCommerce_Orbis::CallbackFunc SonyCommerce_Orbis::m_trialUpgradeCallbackFunc; +LPVOID SonyCommerce_Orbis::m_trialUpgradeCallbackParam; + +CRITICAL_SECTION SonyCommerce_Orbis::m_queueLock; + +uint32_t SonyCommerce_Orbis::m_contextId=0; ///< The npcommerce2 context ID +bool SonyCommerce_Orbis::m_contextCreated=false; ///< npcommerce2 context ID created? +SonyCommerce_Orbis::Phase SonyCommerce_Orbis::m_currentPhase = e_phase_stopped; ///< Current commerce2 util +// char SonyCommerce_Orbis::m_commercebuffer[SCE_NP_COMMERCE2_RECV_BUF_SIZE]; + +C4JThread* SonyCommerce_Orbis::m_tickThread = NULL; +bool SonyCommerce_Orbis::m_bLicenseChecked=false; // Check the trial/full license for the game + + + + +sce::Toolkit::NP::Utilities::Future > g_productList; +sce::Toolkit::NP::Utilities::Future g_categoryInfo; +sce::Toolkit::NP::Utilities::Future g_detailedProductInfo; + + +SonyCommerce_Orbis::ProductInfoDetailed s_trialUpgradeProductInfoDetailed; +void SonyCommerce_Orbis::Delete() +{ + m_pProductInfoList=NULL; + m_pProductInfoDetailed=NULL; + m_pProductInfo=NULL; + m_pCategoryInfo = NULL; + m_pProductID = NULL; + m_pCategoryID = NULL; +} + +void SonyCommerce_Orbis::Init() +{ + assert(m_state == e_state_noSession); + if(!m_bCommerceInitialised) + { + m_bCommerceInitialised = true; + m_pCategoryID=(char *)malloc(sizeof(char) * 100); + InitializeCriticalSection(&m_queueLock); + } +} + + + +void SonyCommerce_Orbis::CheckForTrialUpgradeKey_Callback(LPVOID param, bool bFullVersion) +{ + ProfileManager.SetFullVersion(bFullVersion); + if(ProfileManager.IsFullVersion()) + { + StorageManager.SetSaveDisabled(false); + ConsoleUIController::handleUnlockFullVersionCallback(); + // licence has been checked, so we're ok to install the trophies now +// ProfileManager.InitialiseTrophies( SQRNetworkManager_Orbis::GetSceNpCommsId(), +// SQRNetworkManager_Orbis::GetSceNpCommsSig()); +// + } + m_bLicenseChecked=true; +} + +bool SonyCommerce_Orbis::LicenseChecked() +{ + return m_bLicenseChecked; +} + +void SonyCommerce_Orbis::CheckForTrialUpgradeKey() +{ + StorageManager.CheckForTrialUpgradeKey(CheckForTrialUpgradeKey_Callback, NULL); +} + +int SonyCommerce_Orbis::Shutdown() +{ + int ret=0; + m_bCommerceInitialised = false; + if (m_contextCreated) + { + m_contextId = 0; + m_contextCreated = false; + } + + delete m_pCategoryID; + DeleteCriticalSection(&m_queueLock); + + // clear any possible callback function + m_callbackFunc = NULL; + + return ret; +} + + + +int SonyCommerce_Orbis::TickLoop(void* lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eCommerceThread); + while( (m_currentPhase != e_phase_stopped) && ShutdownManager::ShouldRun(ShutdownManager::eCommerceThread) ) + { + processEvent(); + processMessage(); + Sleep(16); // sleep for a frame +// ((SonyCommerce_Orbis*)app.GetCommerce())->Test(); + } + + Shutdown(); + ShutdownManager::HasFinished(ShutdownManager::eCommerceThread); + + return 0; +} + +void SonyCommerce_Orbis::copyProductList(std::vector* pProductList, std::vector* pNPProductList) +{ + ProductInfo tempInfo; + std::vector tempProductVec; + // Reserve some space + int numProducts = pNPProductList->size(); + tempProductVec.reserve(numProducts); + for(int i=0;iat(i); + + // reset tempInfo + memset(&tempInfo, 0x0, sizeof(tempInfo)); + strncpy(tempInfo.productId, npInfo.productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + strncpy(tempInfo.productName, npInfo.productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + strncpy(tempInfo.shortDescription, npInfo.shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strcpy(tempInfo.longDescription,"Missing long description"); + strncpy(tempInfo.spName, npInfo.spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + strncpy(tempInfo.imageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + tempInfo.releaseDate = npInfo.releaseDate; + tempInfo.purchasabilityFlag = npInfo.purchasabilityFlag; + // Take out the price. Nicely formatted + // but also keep the price as a value in case it's 0 - we need to show "free" for that + tempInfo.ui32Price = -1;// not available here + strncpy(tempInfo.price, npInfo.price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + tempProductVec.push_back(tempInfo); + } + pNPProductList->clear(); // clear the vector now we're done, this doesn't happen automatically for the next query + + // Set our result + *pProductList = tempProductVec; +} + +int SonyCommerce_Orbis::getProductList(std::vector* productList, char *categoryId) +{ + int ret; + sce::Toolkit::NP::ProductListInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + params.serviceLabel = 0; + app.DebugPrintf("Getting Product List ...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::getProductList(&g_productList, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::getProductList() error. ret = 0x%x\n", ret); + return ret; + } + + if (g_productList.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyProductList(productList, g_productList.get()); + m_event = e_event_commerceGotProductList; + } + + return ret; +} + + + +void SonyCommerce_Orbis::copyCategoryInfo(CategoryInfo *pInfo, sce::Toolkit::NP::CategoryInfo *pNPInfo) +{ + strcpy(pInfo->current.categoryId, pNPInfo->current.categoryId); + strcpy(pInfo->current.categoryName, pNPInfo->current.categoryName); + strcpy(pInfo->current.categoryDescription, pNPInfo->current.categoryDescription); + strcpy(pInfo->current.imageUrl, pNPInfo->current.imageUrl); + pInfo->countOfProducts = pNPInfo->countOfProducts; + pInfo->countOfSubCategories = pNPInfo->countOfSubCategories; + if(pInfo->countOfSubCategories > 0) + { + std::list::iterator iter = pNPInfo->subCategories.begin(); + std::list::iterator iterEnd = pNPInfo->subCategories.end(); + + while(iter != iterEnd) + { + // For each sub category, obtain information + CategoryInfoSub tempSubCatInfo; + strcpy(tempSubCatInfo.categoryId, iter->categoryId); + strcpy(tempSubCatInfo.categoryName, iter->categoryName); + strcpy(tempSubCatInfo.categoryDescription, iter->categoryDescription); + strcpy(tempSubCatInfo.imageUrl, iter->imageUrl); + // Add to the list + pInfo->subCategories.push_back(tempSubCatInfo); + iter++; + } + } +} + +int SonyCommerce_Orbis::getCategoryInfo(CategoryInfo *pInfo, char *categoryId) +{ + + + int ret; + sce::Toolkit::NP::CategoryInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, "");//categoryId); + params.serviceLabel = 0; + + app.DebugPrintf("Getting Category Information...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::getCategoryInfo(&g_categoryInfo, params, true); + if (ret < 0) + { + // error + app.DebugPrintf("Commerce::Interface::getCategoryInfo error: 0x%x\n", ret); + return ret; + } + else if (g_categoryInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyCategoryInfo(pInfo, g_categoryInfo.get()); + m_event = e_event_commerceGotCategoryInfo; + } + + return ret; +} + +void SonyCommerce_Orbis::copyDetailedProductInfo(ProductInfoDetailed *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo) +{ + // populate our temp struct + // pInfo->ratingDescriptors = npInfo.ratingSystemId; + strncpy(pInfo->productId, pNPInfo->productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + strncpy(pInfo->productName, pNPInfo->productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + strncpy(pInfo->shortDescription, pNPInfo->shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strncpy(pInfo->longDescription, pNPInfo->longDescription, SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN); + strncpy(pInfo->legalDescription, pNPInfo->legalDescription, SCE_NP_COMMERCE2_PRODUCT_LEGAL_DESCRIPTION_LEN); + strncpy(pInfo->spName, pNPInfo->spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + strncpy(pInfo->imageUrl, pNPInfo->imageUrl, SCE_NP_COMMERCE2_URL_LEN); + pInfo->releaseDate = pNPInfo->releaseDate; + strncpy(pInfo->ratingSystemId, pNPInfo->ratingSystemId, SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN); + strncpy(pInfo->ratingImageUrl, pNPInfo->imageUrl, SCE_NP_COMMERCE2_URL_LEN); + strncpy(pInfo->skuId, pNPInfo->skuId, SCE_NP_COMMERCE2_SKU_ID_LEN); + pInfo->purchasabilityFlag = pNPInfo->purchasabilityFlag; + pInfo->ui32Price= pNPInfo->intPrice; + strncpy(pInfo->price, pNPInfo->price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + +} +int SonyCommerce_Orbis::getDetailedProductInfo(ProductInfoDetailed *pInfo, const char *productId, char *categoryId) +{ + int ret; + sce::Toolkit::NP::DetailedProductInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + strcpy(params.productId, productId); + + + app.DebugPrintf("Getting Detailed Product Information ... \n"); + ret = sce::Toolkit::NP::Commerce::Interface::getDetailedProductInfo(&g_detailedProductInfo, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::getDetailedProductInfo() error. ret = 0x%x\n", ret); + return ret; + } + + if (g_detailedProductInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyDetailedProductInfo(pInfo, g_detailedProductInfo.get()); + m_event = e_event_commerceGotDetailedProductInfo; + } + return ret; +} + +void SonyCommerce_Orbis::copyAddDetailedProductInfo(ProductInfo *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo) +{ + + // populate our temp struct + // pInfo->ratingDescriptors = npInfo.ratingSystemId; + // strncpy(pInfo->productId, npInfo.productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + // strncpy(pInfo->productName, npInfo.productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + // strncpy(pInfo->shortDescription, npInfo.shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strncpy(pInfo->longDescription, pNPInfo->longDescription, SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN); + // strncpy(pInfo->legalDescription, npInfo.legalDescription, SCE_NP_COMMERCE2_PRODUCT_LEGAL_DESCRIPTION_LEN); + // strncpy(pInfo->spName, npInfo.spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + // strncpy(pInfo->imageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + // pInfo->releaseDate = npInfo.releaseDate; + // strncpy(pInfo->ratingSystemId, npInfo.ratingSystemId, SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN); + // strncpy(pInfo->ratingImageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + strncpy(pInfo->skuId, pNPInfo->skuId, SCE_NP_COMMERCE2_SKU_ID_LEN); + pInfo->purchasabilityFlag = pNPInfo->purchasabilityFlag; + pInfo->ui32Price= pNPInfo->intPrice; + strncpy(pInfo->price, pNPInfo->price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + +} + +int SonyCommerce_Orbis::addDetailedProductInfo(ProductInfo *pInfo, const char *productId, char *categoryId) +{ + int ret; + sce::Toolkit::NP::DetailedProductInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + strcpy(params.productId, productId); + + + app.DebugPrintf("Getting Detailed Product Information ... \n"); + ret = sce::Toolkit::NP::Commerce::Interface::getDetailedProductInfo(&g_detailedProductInfo, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::addDetailedProductInfo() error. ret = 0x%x\n", ret); + } + + if (g_detailedProductInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyAddDetailedProductInfo(pInfo, g_detailedProductInfo.get()); + m_event = e_event_commerceAddedDetailedProductInfo; + } + return ret; +} + + +int SonyCommerce_Orbis::checkout(CheckoutInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::CheckoutInputParams npParams; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Checkout...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::checkout(npParams, false); + if (ret < 0) + { + app.DebugPrintf("Sample menu checkout() error. ret = 0x%x\n", ret); + } + + + return ret; +} + + +int SonyCommerce_Orbis::downloadList(DownloadListInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::DownloadListInputParams npParams; + //memset(&npParams,0,sizeof(sce::Toolkit::NP::DownloadListInputParams)); + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + npParams.skuIds.clear(); + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Store Download List...\n"); + ret = sce::Toolkit::NP::Commerce::Interface::displayDownloadList(npParams, true); + if (ret < 0) + { + app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); + } + + return ret; +} + +int SonyCommerce_Orbis::checkout_game(CheckoutInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::CheckoutInputParams npParams; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Checkout...\n"); + sce::Toolkit::NP::ProductBrowseParams Myparams; + + Myparams.serviceLabel = 0; + Myparams.userInfo.userId = userId; + strncpy(Myparams.productId, "MINECRAFTPS40000", strlen("MINECRAFTPS40000")); + + ret = sce::Toolkit::NP::Commerce::Interface::productBrowse(Myparams, false); + if (ret < 0) { + // Error handling + } + + + //ret = sce::Toolkit::NP::Commerce::Interface::checkout(npParams, false); + if (ret < 0) + { + app.DebugPrintf("Sample menu checkout() error. ret = 0x%x\n", ret); + } + + + return ret; +} + +int SonyCommerce_Orbis::downloadList_game(DownloadListInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::DownloadListInputParams npParams; + //memset(&npParams,0,sizeof(sce::Toolkit::NP::DownloadListInputParams)); + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + npParams.skuIds.clear(); + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Store Download List...\n"); +// ret = sce::Toolkit::NP::Commerce::Interface::displayDownloadList(npParams, true); +// if (ret < 0) +// { +// app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); +// } + + sce::Toolkit::NP::ProductBrowseParams Myparams; + + Myparams.serviceLabel = 0; + Myparams.userInfo.userId = userId; + strncpy(Myparams.productId, "MINECRAFTPS40000", strlen("MINECRAFTPS40000")); + + ret = sce::Toolkit::NP::Commerce::Interface::productBrowse(Myparams, false); + if (ret < 0) { + // Error handling + app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); + } + + + + return ret; +} + +void SonyCommerce_Orbis::UpgradeTrialCallback2(LPVOID lpParam,int err) +{ + SonyCommerce* pCommerce = (SonyCommerce*)lpParam; + app.DebugPrintf(4,"SonyCommerce_UpgradeTrialCallback2 : err : 0x%08x\n", err); + pCommerce->CheckForTrialUpgradeKey(); + if(err != SCE_OK) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PRO_UNLOCKGAME_TITLE, IDS_NO_DLCOFFERS, uiIDA,1,ProfileManager.GetPrimaryPad()); + } + m_trialUpgradeCallbackFunc(m_trialUpgradeCallbackParam, m_errorCode); +} + +void SonyCommerce_Orbis::UpgradeTrialCallback1(LPVOID lpParam,int err) +{ + SonyCommerce* pCommerce = (SonyCommerce*)lpParam; + app.DebugPrintf(4,"SonyCommerce_UpgradeTrialCallback1 : err : 0x%08x\n", err); + if(err == SCE_OK) + { + char* skuID = s_trialUpgradeProductInfoDetailed.skuId; + if(s_trialUpgradeProductInfoDetailed.purchasabilityFlag == SCE_TOOLKIT_NP_COMMERCE_NOT_PURCHASED) + { + app.DebugPrintf(4,"UpgradeTrialCallback1 - Checkout\n"); + pCommerce->Checkout_Game(UpgradeTrialCallback2, pCommerce, skuID); + + + + } + else + { + app.DebugPrintf(4,"UpgradeTrialCallback1 - DownloadAlreadyPurchased\n"); + pCommerce->DownloadAlreadyPurchased_Game(UpgradeTrialCallback2, pCommerce, skuID); + } + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PRO_UNLOCKGAME_TITLE, IDS_NO_DLCOFFERS, uiIDA,1,ProfileManager.GetPrimaryPad()); + m_trialUpgradeCallbackFunc(m_trialUpgradeCallbackParam, m_errorCode); + } +} + + + +// global func, so we can call from the profile lib +void SonyCommerce_UpgradeTrial() +{ + // we're now calling the app function here, which manages pending requests + app.UpgradeTrial(); +} + +void SonyCommerce_Orbis::UpgradeTrial(CallbackFunc cb, LPVOID lpParam) +{ + m_trialUpgradeCallbackFunc = cb; + m_trialUpgradeCallbackParam = lpParam; + +// static char szTrialUpgradeSkuID[64]; +// sprintf(szTrialUpgradeSkuID, "%s-TRIALUPGRADE0001", app.GetCommerceCategory());//, szSKUSuffix); + GetDetailedProductInfo(UpgradeTrialCallback1, this, &s_trialUpgradeProductInfoDetailed, app.GetUpgradeKey(), app.GetCommerceCategory()); +} + + +int SonyCommerce_Orbis::createContext() +{ +// SceNpId npId; +// int ret = sceNpManagerGetNpId(&npId); +// if(ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpManagerGetNpId problem\n"); +// return ret; +// } +// +// if (m_contextCreated) { +// ret = sceNpCommerce2DestroyCtx(m_contextId); +// if (ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpCommerce2DestroyCtx problem\n"); +// return ret; +// } +// } +// +// // Create commerce2 context +// ret = sceNpCommerce2CreateCtx(SCE_NP_COMMERCE2_VERSION, &npId, commerce2Handler, NULL, &m_contextId); +// if (ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpCommerce2CreateCtx problem\n"); +// return ret; +// } + + m_contextCreated = true; + + return SCE_OK; +} + +int SonyCommerce_Orbis::createSession() +{ + // From the Sony docs + // + // sce::Toolkit::NP::Commerce::Interface::CreateSession + // + // This function is provided to maintain compatibility with the PlayStation®Vita and PlayStation®3 platforms. Because commerce on the PlayStation®4 is not session based, SCE_TOOLKIT_NP_SUCCESS is always returned. + + int ret = sce::Toolkit::NP::Commerce::Interface::createSession(); + + if (ret < 0) + { + return ret; + } + m_currentPhase = e_phase_creatingSessionPhase; + + return ret; +} + + +void SonyCommerce_Orbis::commerce2Handler( const sce::Toolkit::NP::Event& event) +{ +// Event reply; +// reply.service = Toolkit::NP::commerce; +// + if(m_bCommerceInitialised==false) + { + // 4J-PB - this happens when we've signed out of the PSn, and we were in the process of retrieving DLC product info + // Ignore this event + app.DebugPrintf("@@@@@@@@ IGNORING COMMERCE EVENT AFTER COMMERCE SHUTDOWN @@@@@@@@@\n"); + return; + } + EnterCriticalSection(&m_queueLock); + + switch (event.event) + { + case sce::Toolkit::NP::Event::UserEvent::commerceError: + { + m_messageQueue.push(e_message_commerceEnd); + m_errorCode = event.returnCode; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceSessionCreated: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionCreated; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceSessionAborted: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionAborted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceCheckoutStarted: + { + m_currentPhase = e_phase_checkoutPhase; + m_event = e_event_commerceCheckoutStarted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceGotCategoryInfo: + { + copyCategoryInfo(m_pCategoryInfo, g_categoryInfo.get()); + m_pCategoryInfo = NULL; + m_event = e_event_commerceGotCategoryInfo; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceGotProductList: + { + copyProductList(m_pProductInfoList, g_productList.get()); + m_pProductInfoDetailed = NULL; + m_event = e_event_commerceGotProductList; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceGotDetailedProductInfo: + { + if(m_pProductInfoDetailed) + { + copyDetailedProductInfo(m_pProductInfoDetailed, g_detailedProductInfo.get()); + m_pProductInfoDetailed = NULL; + } + else + { + copyAddDetailedProductInfo(m_pProductInfo, g_detailedProductInfo.get()); + m_pProductInfo = NULL; + } + m_event = e_event_commerceGotDetailedProductInfo; + break; + } + + + +// case SCE_NP_COMMERCE2_EVENT_DO_CHECKOUT_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceCheckoutSuccess; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_CHECKOUT_BACK: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceCheckoutAborted; +// break; +// } + case sce::Toolkit::NP::Event::UserEvent::commerceCheckoutFinished: + { + m_event = e_event_commerceCheckoutFinished; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceDownloadListStarted: + { + m_currentPhase = e_phase_downloadListPhase; + m_event = e_event_commerceDownloadListStarted; + break; + } +// case SCE_NP_COMMERCE2_EVENT_DO_DL_LIST_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceDownloadListSuccess; +// break; +// } + case sce::Toolkit::NP::Event::UserEvent::commerceDownloadListFinished: + { + m_event = e_event_commerceDownloadListFinished; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseStarted: + { + m_currentPhase = e_phase_productBrowsePhase; + m_event = e_event_commerceProductBrowseStarted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseSuccess: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceProductBrowseSuccess; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseAborted: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceProductBrowseAborted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseFinished: + { + m_event = e_event_commerceProductBrowseFinished; + break; + } + +// case SCE_NP_COMMERCE2_EVENT_DO_PROD_BROWSE_OPENED: +// break; +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_STARTED: +// { +// m_currentPhase = e_phase_voucherRedeemPhase; +// m_event = e_event_commerceVoucherInputStarted; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceVoucherInputSuccess; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_BACK: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceVoucherInputAborted; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_FINISHED: +// { +// m_event = e_event_commerceVoucherInputFinished; +// break; +// } + default: + break; + }; + + LeaveCriticalSection(&m_queueLock); +} + + + +void SonyCommerce_Orbis::processMessage() +{ + EnterCriticalSection(&m_queueLock); + int ret; + if(m_messageQueue.empty()) + { + LeaveCriticalSection(&m_queueLock); + return; + } + Message msg = m_messageQueue.front(); + m_messageQueue.pop(); + + switch (msg) + { + + case e_message_commerceCreateSession: + ret = createSession(); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + + case e_message_commerceGetCategoryInfo: + { + ret = getCategoryInfo(m_pCategoryInfo, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + app.DebugPrintf(4,"ERROR - e_event_commerceGotCategoryInfo - %s\n",m_pCategoryID); + m_errorCode = ret; + } + break; + } + + case e_message_commerceGetProductList: + { + ret = getProductList(m_pProductInfoList, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + } + break; + } + + case e_message_commerceGetDetailedProductInfo: + { + ret = getDetailedProductInfo(m_pProductInfoDetailed, m_pProductID, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + case e_message_commerceAddDetailedProductInfo: + { + ret = addDetailedProductInfo(m_pProductInfo, m_pProductID, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + +// +// case e_message_commerceStoreProductBrowse: +// { +// ret = productBrowse(*(ProductBrowseParams *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// _TOOLKIT_NP_DEL (ProductBrowseParams *)msg.inputArgs; +// break; +// } +// +// case e_message_commerceUpgradeTrial: +// { +// ret = upgradeTrial(); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// break; +// } +// +// case e_message_commerceRedeemVoucher: +// { +// ret = voucherCodeInput(*(VoucherInputParams *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// _TOOLKIT_NP_DEL (VoucherInputParams *)msg.inputArgs; +// break; +// } +// +// case e_message_commerceGetEntitlementList: +// { +// Job > tmpJob(static_cast > *>(msg.output)); +// +// int state = 0; +// int ret = sceNpManagerGetStatus(&state); +// +// // We don't want to process this if we are offline +// if (ret < 0 || state != SCE_NP_MANAGER_STATUS_ONLINE) { +// m_event = e_event_commerceError; +// reply.returnCode = SCE_TOOLKIT_NP_OFFLINE; +// tmpJob.setError(SCE_TOOLKIT_NP_OFFLINE); +// } else { +// getEntitlementList(&tmpJob); +// } +// break; +// } +// +// case e_message_commerceConsumeEntitlement: +// { +// int state = 0; +// int ret = sceNpManagerGetStatus(&state); +// +// // We don't want to process this if we are offline +// if (ret < 0 || state != SCE_NP_MANAGER_STATUS_ONLINE) { +// m_event = e_event_commerceError; +// reply.returnCode = SCE_TOOLKIT_NP_OFFLINE; +// } else { +// +// ret = consumeEntitlement(*(EntitlementToConsume *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } else { +// m_event = e_event_commerceConsumedEntitlement; +// } +// } +// _TOOLKIT_NP_DEL (EntitlementToConsume *)msg.inputArgs; +// +// break; +// } +// + case e_message_commerceCheckout: + { + ret = checkout(m_checkoutInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceDownloadList: + { + ret = downloadList(m_downloadInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceCheckout_Game: + { + ret = checkout_game(m_checkoutInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceDownloadList_Game: + { + ret = downloadList_game(m_downloadInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceEnd: + app.DebugPrintf("XXX - e_message_commerceEnd!\n"); + ret = commerceEnd(); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + // 4J-PB - we don't seem to handle the error code here + else if(m_errorCode!=0) + { + m_event = e_event_commerceError; + } + break; + + default: + break; + } + + LeaveCriticalSection(&m_queueLock); +} + + +void SonyCommerce_Orbis::processEvent() +{ + int ret = 0; + + switch (m_event) + { + case e_event_none: + break; + case e_event_commerceSessionCreated: + app.DebugPrintf(4,"Commerce Session Created.\n"); + runCallback(); + break; + case e_event_commerceSessionAborted: + app.DebugPrintf(4,"Commerce Session aborted.\n"); + runCallback(); + break; + case e_event_commerceGotProductList: + app.DebugPrintf(4,"Got product list.\n"); + runCallback(); + break; + case e_event_commerceGotCategoryInfo: + app.DebugPrintf(4,"Got category info\n"); + runCallback(); + break; + case e_event_commerceGotDetailedProductInfo: + app.DebugPrintf(4,"Got detailed product info.\n"); + runCallback(); + break; + case e_event_commerceAddedDetailedProductInfo: + app.DebugPrintf(4,"Added detailed product info.\n"); + runCallback(); + break; + case e_event_commerceProductBrowseStarted: + break; + case e_event_commerceProductBrowseSuccess: + break; + case e_event_commerceProductBrowseAborted: + break; + case e_event_commerceProductBrowseFinished: + app.DebugPrintf(4,"e_event_commerceProductBrowseFinished succeeded: 0x%x\n", m_errorCode); + + if(m_callbackFunc!=NULL) + { + runCallback(); + } + + //assert(0); +// ret = sys_memory_container_destroy(s_memContainer); +// if (ret < 0) { +// printf("Failed to destroy memory container"); +// } +// s_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; + break; + case e_event_commerceVoucherInputStarted: + break; + case e_event_commerceVoucherInputSuccess: + break; + case e_event_commerceVoucherInputAborted: + break; + case e_event_commerceVoucherInputFinished: + assert(0); +// ret = sys_memory_container_destroy(s_memContainer); +// if (ret < 0) { +// printf("Failed to destroy memory container"); +// } +// s_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; + break; + case e_event_commerceGotEntitlementList: + break; + case e_event_commerceConsumedEntitlement: + break; + case e_event_commerceCheckoutStarted: + app.DebugPrintf(4,"Checkout Started\n"); + break; + case e_event_commerceCheckoutSuccess: + app.DebugPrintf(4,"Checkout succeeded: 0x%x\n", m_errorCode); + // clear the DLC installed and check again + app.ClearDLCInstalled(); + ui.HandleDLCInstalled(0); + break; + case e_event_commerceCheckoutAborted: + app.DebugPrintf(4,"Checkout aborted: 0x%x\n", m_errorCode); + break; + case e_event_commerceCheckoutFinished: + app.DebugPrintf(4,"Checkout Finished: 0x%x\n", m_errorCode); + if (ret < 0) { + app.DebugPrintf(4,"Failed to destroy memory container"); + } + + // 4J-PB - if there's been an error - like dlc already purchased, the runcallback has already happened, and will crash this time + if(m_callbackFunc!=NULL) + { + runCallback(); + } + break; + case e_event_commerceDownloadListStarted: + app.DebugPrintf(4,"Download List Started\n"); + break; + case e_event_commerceDownloadListSuccess: + app.DebugPrintf(4,"Download succeeded: 0x%x\n", m_errorCode); + break; + case e_event_commerceDownloadListFinished: + app.DebugPrintf(4,"Download Finished: 0x%x\n", m_errorCode); + if (ret < 0) { + app.DebugPrintf(4,"Failed to destroy memory container"); + } + + // 4J-PB - if there's been an error - like dlc already purchased, the runcallback has already happened, and will crash this time + if(m_callbackFunc!=NULL) + { + runCallback(); + } + break; + case e_event_commerceError: + app.DebugPrintf(4,"Commerce Error 0x%x\n", m_errorCode); + runCallback(); + break; + default: + break; + } + m_event = e_event_none; +} + + +int SonyCommerce_Orbis::commerceEnd() +{ + int ret = 0; + +// if (m_currentPhase == e_phase_voucherRedeemPhase) +// ret = sceNpCommerce2DoProductCodeFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_productBrowsePhase) +// ret = sceNpCommerce2DoProductBrowseFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_creatingSessionPhase) +// ret = sceNpCommerce2CreateSessionFinish(m_contextId, &m_sessionInfo); +// else if (m_currentPhase == e_phase_checkoutPhase) +// ret = sceNpCommerce2DoCheckoutFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_downloadListPhase) +// ret = sceNpCommerce2DoDlListFinishAsync(m_contextId); + + m_currentPhase = e_phase_idle; + + return ret; +} + +void SonyCommerce_Orbis::CreateSession( CallbackFunc cb, LPVOID lpParam ) +{ + // 4J-PB - reset any previous error code + // I had this happen when I was offline on Vita, and accepted the PSN sign-in + // the m_errorCode was picked up in the message queue after the commerce init call + if(m_errorCode!=0) + { + app.DebugPrintf("m_errorCode was set!\n"); + m_errorCode=0; + } + Init(); + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + + // We don't need to create a session on PS4, from the Sony docs - +// This function is provided to maintain compatibility with the PlayStation®Vita and PlayStation®3 +// platforms. Because commerce on the PlayStation®4 is not session based, SCE_TOOLKIT_NP_SUCCESS is always returned. + + + +// m_messageQueue.push(e_message_commerceCreateSession); + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionCreated; + + if(m_tickThread == NULL) + m_tickThread = new C4JThread(TickLoop, NULL, "SonyCommerce_Orbis tick"); + if(m_tickThread->isRunning() == false) + { + m_currentPhase = e_phase_idle; + m_tickThread->Run(); + } + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::CloseSession() +{ + assert(m_currentPhase == e_phase_idle); + m_currentPhase = e_phase_stopped; + // 4J-PB - don't call shutdown here - the SonyCommerce_Orbis::TickLoop thread is still running and could crash accessing the critical section that Shutdown destroys + //Shutdown(); +} + +void SonyCommerce_Orbis::GetProductList( CallbackFunc cb, LPVOID lpParam, std::vector* productList, const char *categoryId) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfoList = productList; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetProductList); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::GetDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfoDetailed* productInfo, const char *productId, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfoDetailed = productInfo; + m_pProductID = productId; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetDetailedProductInfo); + LeaveCriticalSection(&m_queueLock); +} + +// 4J-PB - fill out the long description and the price for the product +void SonyCommerce_Orbis::AddDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfo* productInfo, const char *productId, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfo = productInfo; + m_pProductID = productId; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceAddDetailedProductInfo); + LeaveCriticalSection(&m_queueLock); +} +void SonyCommerce_Orbis::GetCategoryInfo( CallbackFunc cb, LPVOID lpParam, CategoryInfo *info, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pCategoryInfo = info; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetCategoryInfo); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::Checkout( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_checkoutInputParams.skuIds.clear(); + m_checkoutInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceCheckout); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::DownloadAlreadyPurchased( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_downloadInputParams.skuIds.clear(); + m_downloadInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceDownloadList); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::Checkout_Game( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_checkoutInputParams.skuIds.clear(); + m_checkoutInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceCheckout_Game); + LeaveCriticalSection(&m_queueLock); +} +void SonyCommerce_Orbis::DownloadAlreadyPurchased_Game( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_downloadInputParams.skuIds.clear(); + m_downloadInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceDownloadList_Game); + LeaveCriticalSection(&m_queueLock); +} + + +/* +bool g_bDoCommerceCreateSession = false; +bool g_bDoCommerceGetProductList = false; +bool g_bDoCommerceGetCategoryInfo = false; +bool g_bDoCommerceGetProductInfoDetailed = false; +bool g_bDoCommerceCheckout = false; +bool g_bDoCommerceCloseSession = false; +const char* g_category = "EP4433-CUSA00265_00"; +const char* g_skuID = "SKINPACK00000001-E001"; +std::vector g_productInfo; +SonyCommerce::CategoryInfo g_categoryInfo2; +SonyCommerce::ProductInfoDetailed g_productInfoDetailed; + +void testCallback(LPVOID lpParam, int error_code) +{ + app.DebugPrintf("Callback hit, error 0x%08x\n", error_code); +} + +void SonyCommerce_Orbis::Test() +{ + int err = SCE_OK; + if(g_bDoCommerceCreateSession) + { + CreateSession(testCallback, this); + g_bDoCommerceCreateSession = false; + } + if(g_bDoCommerceGetProductList) + { + GetProductList(testCallback, this, &g_productInfo, g_category); + g_bDoCommerceGetProductList = false; + } + + if(g_bDoCommerceGetCategoryInfo) + { + GetCategoryInfo(testCallback, this, &g_categoryInfo2, g_category); + g_bDoCommerceGetCategoryInfo = false; + } + + if(g_bDoCommerceGetProductInfoDetailed) + { + GetDetailedProductInfo(testCallback, this, &g_productInfoDetailed, g_productInfo[0].productId, g_category); + g_bDoCommerceGetProductInfoDetailed = false; + } + + if(g_bDoCommerceCheckout) + { + //Checkout(testCallback, this, g_skuID);//g_productInfoDetailed.skuId); + Checkout(testCallback, this, g_productInfoDetailed.skuId); + g_bDoCommerceCheckout = false; + } + if(g_bDoCommerceCloseSession) + { + CloseSession(); + g_bDoCommerceCloseSession = false; + } + +} +*/ \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h new file mode 100644 index 00000000..a2a42d57 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h @@ -0,0 +1,182 @@ +#pragma once + +#include "Common\Network\Sony\SonyCommerce.h" +class OrbisNPToolkit; + +class SonyCommerce_Orbis : public SonyCommerce +{ + friend class OrbisNPToolkit; + enum State + { + e_state_noSession, + e_state_creatingSession, + e_state_createSessionDone, + e_state_idle, + + + }; + /// This enum is used to verify the current utility that is running + enum Phase + { + e_phase_stopped = 0, + e_phase_idle, + e_phase_voucherRedeemPhase, + e_phase_productBrowsePhase, + e_phase_creatingSessionPhase, + e_phase_checkoutPhase, + e_phase_downloadListPhase + }; + + enum Message + { + e_message_commerceNone, + e_message_commerceCreateSession, ///< Create a commerce session + e_message_commerceGetCategoryInfo, ///< Information about a category in the Store + e_message_commerceGetProductList, ///< Get a list of products available in the Store + e_message_commerceGetDetailedProductInfo, ///< Get a list of products available in the Store, with additional details + e_message_commerceAddDetailedProductInfo, ///< Add additional details to a ProdcutInfo already retrieved + e_message_commerceStoreProductBrowse, ///< Launches the Store to a specified product + e_message_commerceUpgradeTrial, ///< Upgrade a trial to full game + e_message_commerceRedeemVoucher, ///< Redeem a voucher code + e_message_commerceGetEntitlementList, ///< Get a list of entitlements associated with the current PSN user. + e_message_commerceConsumeEntitlement, ///< Consume an amount from a consumable entitlement. + e_message_commerceCheckout, ///< Launch the Store checkout + e_message_commerceDownloadList, ///< Launch the download list + e_message_commerceCheckout_Game, ///< Launch the Store checkout + e_message_commerceDownloadList_Game, ///< Launch the download list + e_message_commerceEnd ///< End commerce2 processing + }; + + enum Event + { + e_event_none, + e_event_commerceSessionCreated, ///< An event generated when a commerce session has successfully been created. + e_event_commerceSessionAborted, ///< An event generated when the creation of commerce session has been aborted. + e_event_commerceGotCategoryInfo, ///< An event generated when some category information has been retrieved from the store. + e_event_commerceGotProductList, ///< An event generated when a list of products that are available has been retrieved from the store. + e_event_commerceGotDetailedProductInfo, ///< An event generated when some detailed product information has been retrieved from the store. + e_event_commerceAddedDetailedProductInfo, ///< An event generated when some detailed product information has been retrieved from the store. + e_event_commerceProductBrowseStarted, ///< An event generated when product overlay has started. + e_event_commerceProductBrowseSuccess, ///< An event generated when a product browse was completed successfully, and the user purchased the product. + e_event_commerceProductBrowseAborted, ///< An event generated when a product browse was aborted by the user (the user pressed back). + e_event_commerceProductBrowseFinished, ///< An event generated when a product browse has finished and it is now safe to free memory. + e_event_commerceVoucherInputStarted, ///< An event generated when a voucher code input overlay was started. + e_event_commerceVoucherInputSuccess, ///< An event generated when a voucher code input completed successfully. + e_event_commerceVoucherInputAborted, ///< An event generated when a voucher code input was aborted by the user (user pressed back). + e_event_commerceVoucherInputFinished, ///< An event generated when a voucher code input has finished. It is now safe to free memory. + e_event_commerceGotEntitlementList, ///< An event generated when a the list of entitlements has been received for the current user. + e_event_commerceConsumedEntitlement, ///< An event generated when the has successfully consumed an entitlement. + e_event_commerceCheckoutStarted, ///< An event generated when a store checkout overlay has started. + e_event_commerceCheckoutSuccess, ///< An event generated when user has successfully purchased from the checkout. + e_event_commerceCheckoutAborted, ///< An event generated when the checkout was aborted by the user (user pressed back). + e_event_commerceCheckoutFinished, ///< An event generated when a store checkout overlay has finished. + e_event_commerceDownloadListStarted, ///< An event generated when a download list overlay has started. + e_event_commerceDownloadListSuccess, ///< An event generated when the user has ended the download list. + e_event_commerceDownloadListFinished, ///< An event generated when a download list overlay has finished. + e_event_commerceError ///< An event generated when a commerce error has occurred. + }; + + static bool m_bLicenseChecked; + static bool m_bCommerceInitialised; +// static SceNpCommerce2SessionInfo m_sessionInfo; + static State m_state; + static int m_errorCode; + static LPVOID m_callbackParam; + static Event m_event; + static Message m_message; + // static uint32_t m_requestID; + static void* m_receiveBuffer; + static std::vector *m_pProductInfoList; + static ProductInfoDetailed *m_pProductInfoDetailed; + static ProductInfo *m_pProductInfo; + static CategoryInfo* m_pCategoryInfo; + static char* m_pCategoryID; + static const char* m_pProductID; + static std::queue m_messageQueue; + static CallbackFunc m_callbackFunc; + static CheckoutInputParams m_checkoutInputParams; + static DownloadListInputParams m_downloadInputParams; +// static sys_memory_container_t m_memContainer; + static bool m_bUpgradingTrial; + static C4JThread* m_tickThread; + static CallbackFunc m_trialUpgradeCallbackFunc; + static LPVOID m_trialUpgradeCallbackParam; + static CRITICAL_SECTION m_queueLock; + + static void runCallback() + { + assert(m_callbackFunc); + CallbackFunc func = m_callbackFunc; + m_callbackFunc = NULL; + if(func) + func(m_callbackParam, m_errorCode); + m_errorCode = SCE_OK; + } + static void setCallback(CallbackFunc cb,LPVOID lpParam) + { + assert(m_callbackFunc == NULL); + m_callbackFunc = cb; + m_callbackParam = lpParam; + } + + + static uint32_t m_contextId; ///< The npcommerce2 context ID + static bool m_contextCreated; ///< npcommerce2 context ID created? + static Phase m_currentPhase; ///< Current commerce2 util +// static char m_commercebuffer[SCE_NP_COMMERCE2_RECV_BUF_SIZE]; + + + + static void commerce2Handler( const sce::Toolkit::NP::Event& event); + static void processMessage(); + static void processEvent(); + + static int createContext(); + static int createSession(); + static void setError(int err) { m_errorCode = err; } + static int getCategoryInfo(CategoryInfo *info, char *categoryId); + static int getProductList(std::vector* productList, char *categoryId); + static int getDetailedProductInfo(ProductInfoDetailed *info, const char *productId, char *categoryId); + static int addDetailedProductInfo(ProductInfo *info, const char *productId, char *categoryId); + static int checkout(CheckoutInputParams ¶ms); + static int downloadList(DownloadListInputParams ¶ms); + static int checkout_game(CheckoutInputParams ¶ms); + static int downloadList_game(DownloadListInputParams ¶ms); + static void UpgradeTrialCallback1(LPVOID lpParam,int err); + static void UpgradeTrialCallback2(LPVOID lpParam,int err); + static void Delete(); + static void copyCategoryInfo(CategoryInfo *pInfo, sce::Toolkit::NP::CategoryInfo *pNPInfo); + static void copyProductList(std::vector* pProductList, std::vector* pNPProductList); + static void copyDetailedProductInfo(ProductInfoDetailed *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo); + static void copyAddDetailedProductInfo(ProductInfo *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo); + + + static int commerceEnd(); + // static int upgradeTrial(); + + static int TickLoop(void* lpParam); + //void Test(); + + static void Init(); + static int Shutdown(); + + static void CheckForTrialUpgradeKey_Callback(LPVOID param, bool bFullVersion); + +public: + + virtual void CreateSession(CallbackFunc cb, LPVOID lpParam); + virtual void CloseSession(); + + virtual void GetCategoryInfo(CallbackFunc cb, LPVOID lpParam, CategoryInfo *info, const char *categoryId); + virtual void GetProductList(CallbackFunc cb, LPVOID lpParam, std::vector* productList, const char *categoryId); + virtual void GetDetailedProductInfo(CallbackFunc cb, LPVOID lpParam, ProductInfoDetailed* productInfoDetailed, const char *productId, const char *categoryId); + virtual void AddDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfo* productInfo, const char *productId, const char *categoryId ); + virtual void Checkout(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void DownloadAlreadyPurchased(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void Checkout_Game(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void DownloadAlreadyPurchased_Game(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void UpgradeTrial(CallbackFunc cb, LPVOID lpParam); + virtual void CheckForTrialUpgradeKey(); + virtual bool LicenseChecked(); + +}; diff --git a/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp new file mode 100644 index 00000000..7f7c62d6 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp @@ -0,0 +1,285 @@ +#include "stdafx.h" +#include "SonyHttp_Orbis.h" + +static const int sc_SSLHeapSize = (304 * 1024U); +static const int sc_HTTPHeapSize = (48 * 1024); +static const int sc_NetHeapSize = (16 * 1024); + +#define TEST_USER_AGENT "SimpleSample/1.00" + + +int SonyHttp_Orbis::libnetMemId = 0; +int SonyHttp_Orbis::libsslCtxId = 0; +int SonyHttp_Orbis::libhttpCtxId = 0; +bool SonyHttp_Orbis:: bInitialised = false; + + + + + +bool SonyHttp_Orbis::init() +{ + int ret = sceNetPoolCreate("simple", sc_NetHeapSize, 0); + assert(ret >= 0); + libnetMemId = ret; + + ret = sceSslInit(sc_SSLHeapSize); + assert(ret >= 0); + libsslCtxId = ret; + + ret = sceHttpInit(libnetMemId, libsslCtxId, sc_HTTPHeapSize); + assert(ret >= 0); + libhttpCtxId = ret; + + bInitialised = true; + return true; +} + +void SonyHttp_Orbis::shutdown() +{ + int ret = sceHttpTerm(libhttpCtxId); + assert(ret == SCE_OK); + + ret = sceSslTerm(libsslCtxId); + assert(ret == SCE_OK); + + /* libnet */ + ret = sceNetPoolDestroy(libnetMemId); + assert(ret == SCE_OK); + +} +void SonyHttp_Orbis::printSslError(SceInt32 sslErr, SceUInt32 sslErrDetail) +{ + switch (sslErr) + { + case (SCE_HTTPS_ERROR_CERT): /* Verify error */ + /* Internal error at verifying certificate*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_INTERNAL){ + app.DebugPrintf("ssl verify error: unexpcted error\n"); + } + /* Error of server certificate or CA certificate */ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_INVALID_CERT){ + app.DebugPrintf("ssl verify error: invalid server cert or CA cert\n"); + } + /* Server hostname and server certificate are mismatched*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_CN_CHECK){ + app.DebugPrintf("ssl verify error: invalid server hostname\n"); + } + /* Server certificate or CA certificate is expired.*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_NOT_AFTER_CHECK){ + app.DebugPrintf("ssl verify error: server cert or CA cert had expired\n"); + } + /* Server certificate or CA certificate is before validated.*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK){ + app.DebugPrintf("ssl verify error: server cert or CA cert isn't validated yet.\n"); + } + /* Unknown CA error */ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_UNKNOWN_CA){ + app.DebugPrintf("ssl verify error: unknown CA\n"); + } + break; + case (SCE_HTTPS_ERROR_HANDSHAKE): /* fail to ssl-handshake */ + app.DebugPrintf("ssl error: handshake error\n"); + break; + case (SCE_HTTPS_ERROR_IO): /* Error of Socket IO */ + app.DebugPrintf("ssl error: io error\n"); + break; + case (SCE_HTTP_ERROR_OUT_OF_MEMORY): /* Out of memory*/ + app.DebugPrintf("ssl error: out of memory\n"); + break; + case (SCE_HTTPS_ERROR_INTERNAL): /* Unexpected Internal Error*/ + app.DebugPrintf("ssl error: unexpcted error\n"); + break; + default: + break; + } + return; +} + + +void SonyHttp_Orbis::printSslCertInfo(int libsslCtxId,SceSslCert *sslCert) +{ + SceInt32 ret; + SceUChar8 *sboData = NULL ; + SceSize sboLen, counter; + + ret = sceSslGetSerialNumber(libsslCtxId, sslCert, NULL, &sboLen); + if (ret < 0){ + app.DebugPrintf("sceSslGetSerialNumber() returns 0x%x\n", ret); + } + else { + sboData = (SceUChar8*)malloc(sboLen); + if ( sboData != NULL ) { + ret = sceSslGetSerialNumber(libsslCtxId, sslCert, sboData, &sboLen); + if (ret < 0){ + app.DebugPrintf ("sceSslGetSerialNumber() returns 0x%x\n", ret); + } + else { + app.DebugPrintf("Serial number="); + for (counter = 0; counter < sboLen; counter++){ + app.DebugPrintf("%02X", sboData[counter]); + } + app.DebugPrintf("\n"); + } + free(sboData); + } + } +} + + +bool SonyHttp_Orbis::getDataFromURL( const char* szURL, void** ppOutData, int* pDataSize) +{ + if(!bInitialised) + return false; + return http_get(szURL, ppOutData, pDataSize); +} + + +SceInt32 SonyHttp_Orbis::sslCallback(int libsslCtxId,unsigned int verifyErr,SceSslCert * const sslCert[],int certNum,void *userArg) +{ + SceInt32 i; + (void)userArg; + + app.DebugPrintf("Ssl callback:\n"); + app.DebugPrintf("\tbase tmpl[%x]\n", (*(SceInt32*)(userArg)) ); + + if (verifyErr != 0){ + printSslError((SceInt32)SCE_HTTPS_ERROR_CERT, verifyErr); + } + for (i = 0; i < certNum; i++){ + printSslCertInfo(libsslCtxId,sslCert[i]); + } + if (verifyErr == 0){ + return SCE_OK; + } else { + return -1; + } +} + +bool SonyHttp_Orbis::http_get_close(bool bOK, SceInt32 tmplId, SceInt32 connId, SceInt32 reqId) +{ + SceInt32 ret; + if (reqId > 0) + { + ret = sceHttpDeleteRequest(reqId); + assert(ret >= 0); + } + if (connId > 0) + { + ret = sceHttpDeleteConnection(connId); + assert(ret >= 0); + } + if (tmplId > 0) + { + ret = sceHttpDeleteTemplate(tmplId); + assert(ret >= 0); + } + assert(bOK); + return bOK; +} + +bool SonyHttp_Orbis::http_get(const char *targetUrl, void** ppOutData, int* pDataSize) +{ + SceInt32 ret, tmplId=0, connId=0, reqId=0, statusCode; + SceULong64 contentLength=0; + SceBool finFlag=SCE_FALSE; + SceUChar8* recvBuf; + + ret = sceHttpCreateTemplate(libhttpCtxId, TEST_USER_AGENT, SCE_HTTP_VERSION_1_1, SCE_TRUE); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateTemplate() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + tmplId = ret; + + /* Perform http_get without server verification */ + ret = sceHttpsDisableOption(tmplId,SCE_HTTPS_FLAG_SERVER_VERIFY); + if (ret < 0) + { + app.DebugPrintf("sceHttpsDisableOption() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + /* Register SSL callback */ + ret = sceHttpsSetSslCallback(tmplId, sslCallback, (void*)&tmplId); + if (ret < 0) + { + app.DebugPrintf("sceHttpsSetSslCallback() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + ret = sceHttpCreateConnectionWithURL(tmplId, targetUrl, SCE_TRUE); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateConnectionWithURL() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + connId = ret; + + ret = sceHttpCreateRequestWithURL(connId, SCE_HTTP_METHOD_GET, targetUrl, 0); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateRequestWithURL() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + reqId = ret; + + ret = sceHttpSendRequest(reqId, NULL, 0); + if (ret < 0) + { + app.DebugPrintf("sceHttpSendRequest() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + ret = sceHttpGetStatusCode(reqId, &statusCode); + if (ret < 0) + { + app.DebugPrintf("sceHttpGetStatusCode() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + app.DebugPrintf("response code = %d\n", statusCode); + + if(statusCode == 200) + { + int contentLengthType; + ret = sceHttpGetResponseContentLength(reqId, &contentLengthType, &contentLength); + if(ret < 0) + { + app.DebugPrintf("sceHttpGetContentLength() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + else + { + if (contentLengthType == SCE_HTTP_CONTENTLEN_EXIST) + { + app.DebugPrintf("Content-Length = %lu\n", contentLength); + } + } + recvBuf = new SceUChar8[contentLength+1]; + int bufferLeft = contentLength+1; + SceUChar8* pCurrBuffPos = recvBuf; + int totalBytesRead = 0; + while(finFlag != SCE_TRUE) + { + ret = sceHttpReadData(reqId, pCurrBuffPos, bufferLeft); + if (ret < 0) + { + app.DebugPrintf("\n sceHttpReadData() failed 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + else if (ret == 0) + { + finFlag = SCE_TRUE; + } + app.DebugPrintf("\n sceHttpReadData() read %d bytes\n", ret); + pCurrBuffPos += ret; + totalBytesRead += ret; + bufferLeft -= ret; + } + } + + *ppOutData = recvBuf; + *pDataSize = contentLength; + return http_get_close(true, tmplId, connId, reqId); +} diff --git a/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h new file mode 100644 index 00000000..591a62c5 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +class SonyHttp_Orbis +{ + static SceInt32 sslCallback(int libsslCtxId,unsigned int verifyErr,SceSslCert * const sslCert[],int certNum,void *userArg); + static bool http_get(const char *targetUrl, void** ppOutData, int* pDataSize); + static bool http_get_close(bool bOK, SceInt32 tmplId, SceInt32 connId, SceInt32 reqId); + + static void printSslError(SceInt32 sslErr, SceUInt32 sslErrDetail); + static void printSslCertInfo(int libsslCtxId,SceSslCert *sslCert); + + static int libnetMemId; + static int libsslCtxId; + static int libhttpCtxId; + + static bool bInitialised; + +public: + bool init(); + void shutdown(); + bool getDataFromURL(const char* szURL, void** ppOutData, int* pDataSize); + + static int getHTTPContextID() { return libhttpCtxId; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp new file mode 100644 index 00000000..e248f602 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp @@ -0,0 +1,371 @@ +#include "stdafx.h" + +#include "SonyRemoteStorage_Orbis.h" +#include "SonyHttp_Orbis.h" +#include +#include +#include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + + + + +#define AUTH_SCOPE "psn:s2s" +#define CLIENT_ID "969e9d21-527c-4c22-b539-f8e479f690bc" +static SceRemoteStorageData s_getDataOutput; + + +void SonyRemoteStorage_Orbis::staticInternalCallback(const SceRemoteStorageEvent event, int32_t retCode, void * userData) +{ + ((SonyRemoteStorage_Orbis*)userData)->internalCallback(event, retCode); +} +void SonyRemoteStorage_Orbis::internalCallback(const SceRemoteStorageEvent event, int32_t retCode) +{ + m_lastErrorCode = retCode; + + switch(event) + { + case ERROR_OCCURRED: + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred with retCode: 0x%x \n", retCode); + m_status = e_error; +// shutdown(); // removed, as the remote storage lib now tries to reconnect if an error has occurred + runCallback(); + break; + + case GET_DATA_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Get Data success \n"); + m_status = e_getDataSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Get Data was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + if(StorageManager.SaveGameDirUnMount(m_mountPoint) == false) + { + app.DebugPrintf("Failed to unmount %s\n", m_mountPoint); + } + + m_mountPoint[0] = 0; + runCallback(); + break; + + case GET_DATA_PROGRESS: + app.DebugPrintf("SonyRemoteStorage_Orbis: Get data progress: %i%%\n", retCode); + m_status = e_getDataInProgress; + m_dataProgress = retCode; + m_startTime = System::currentTimeMillis(); + break; + + case GET_STATUS_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Get Status success \n"); + app.DebugPrintf("SonyRemoteStorage_Orbis: Remaining Syncs for this user: %llu\n", outputGetStatus->remainingSyncs); + app.DebugPrintf("SonyRemoteStorage_Orbis: Number of files on the cloud: %d\n", outputGetStatus->numFiles); + for(int i = 0; i < outputGetStatus->numFiles; i++) + { + app.DebugPrintf("\n*** File %d information: ***\n", (i + 1)); + app.DebugPrintf("File name: %s \n", outputGetStatus->data[i].fileName); + app.DebugPrintf("File description: %s \n", outputGetStatus->data[i].fileDescription); + app.DebugPrintf("MD5 Checksum: %s \n", outputGetStatus->data[i].md5Checksum); + app.DebugPrintf("Size of the file: %u bytes \n", outputGetStatus->data[i].fileSize); + app.DebugPrintf("Timestamp: %s \n", outputGetStatus->data[i].timeStamp); + app.DebugPrintf("Visibility: \"%s\" \n", (outputGetStatus->data[i].visibility == 0)?"Private":((outputGetStatus->data[i].visibility == 1)?"Public read only":"Public read and write")); + } + m_status = e_getStatusSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Get Status was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + runCallback(); + break; + + case PSN_SIGN_IN_REQUIRED: + app.DebugPrintf("SonyRemoteStorage_Orbis: User's PSN sign-in through web browser is required \n"); + m_status = e_signInRequired; + runCallback(); + break; + + case SET_DATA_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Set Data success \n"); + m_status = e_setDataSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Set Data was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + runCallback(); + break; + + case SET_DATA_PROGRESS: + app.DebugPrintf("SonyRemoteStorage_Orbis: Set data progress: %i%%\n", retCode); + m_status = e_setDataInProgress; + m_dataProgress = retCode; + + break; + + case USER_ACCOUNT_LINKED: + app.DebugPrintf("SonyRemoteStorage_Orbis: User's account has been linked with PSN \n"); + m_bInitialised = true; + m_status = e_accountLinked; + runCallback(); + break; + + case WEB_BROWSER_RESULT: + app.DebugPrintf("SonyRemoteStorage_Orbis: This function is not used on PS Vita, as the account will be linked, it is not needed to open a browser to link it \n"); + assert(0); + break; + + default: + app.DebugPrintf("SonyRemoteStorage_Orbis: This should never happen \n"); + assert(0); + break; + + } +} + +bool SonyRemoteStorage_Orbis::init(CallbackFunc cb, LPVOID lpParam) +{ + + int ret = 0; + int reqId = 0; + SceNpId npId; + SceUserServiceUserId userId; + SceRemoteStorageInitParams params; + + m_callbackFunc = cb; + m_callbackParam = lpParam; + + if(m_bInitialised) + { + runCallback(); + return true; + } + + ret = sceUserServiceGetInitialUser(&userId); + if (ret < 0) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x\n", ret); + return false; + } + + ret = sceNpGetNpId(userId, &npId); + if (ret < 0) { + app.DebugPrintf("Couldn't retrieve NP ID 0x%x\n", ret); + return false; + } + +// ret = sceNpAuthCreateRequest(); +// if (ret < 0) { +// app.DebugPrintf("Couldn't create auth request 0x%x\n", ret); +// return false; +// } +// +// reqId = ret; +// +// SceNpClientId clientId; +// memset(&clientId, 0x0, sizeof(clientId)); + +// SceNpAuthorizationCode authCode; +// memset(&authCode, 0x0, sizeof(authCode)); + +// SceNpAuthGetAuthorizationCodeParameter authParams; +// memset(&authParams, 0x0, sizeof(authParams)); +// +// authParams.size = sizeof(authParams); +// authParams.pOnlineId = &npId.handle; +// authParams.pScope = AUTH_SCOPE; + +// memcpy(clientId.id, CLIENT_ID, strlen(CLIENT_ID)); +// authParams.pClientId = &clientId; + +// ret = sceNpAuthGetAuthorizationCode(reqId, &authParams, &authCode, NULL); +// if (ret < 0) { +// app.DebugPrintf("Failed to get auth code 0x%x\n", ret); +// } + +// ret = sceNpAuthDeleteRequest(reqId); +// if (ret < 0) { +// app.DebugPrintf("Couldn't delete auth request 0x%x\n", ret); +// } + + params.callback = staticInternalCallback; + params.userData = this; +// memcpy(params.authCode, authCode.code, SCE_NP_AUTHORIZATION_CODE_MAX_LEN); + params.userId = userId; + params.environment = DEVELOPMENT; + params.httpContextId = SonyHttp_Orbis::getHTTPContextID(); + strcpy(params.clientId, CLIENT_ID); + + params.thread.threadAffinity = SCE_KERNEL_CPUMASK_USER_ALL; + params.thread.threadPriority = SCE_KERNEL_PRIO_FIFO_DEFAULT; + + params.timeout.connectMs = 30 * 1000; //30 seconds is the default + params.timeout.resolveMs = 30 * 1000; //30 seconds is the default + params.timeout.receiveMs = 120 * 1000; //120 seconds is the default + params.timeout.sendMs = 120 * 1000; //120 seconds is the default + + params.pool.memPoolSize = 7 * 1024 * 1024; + if(m_memPoolBuffer == NULL) + m_memPoolBuffer = malloc(params.pool.memPoolSize); + + params.pool.memPoolBuffer = m_memPoolBuffer; + + ret = sceRemoteStorageInit(params); + if (ret >= 0) + { + app.DebugPrintf("Session will be created \n"); + } + else if(ret == SCE_REMOTE_STORAGE_ERROR_ALREADY_INITIALISED) + { + app.DebugPrintf("Already initialised: 0x%x \n", ret); + runCallback(); + } + else + { + app.DebugPrintf("Error creating session: 0x%x \n", ret); + return false; + } + return true; +} + + + +bool SonyRemoteStorage_Orbis::getRemoteFileInfo(SceRemoteStorageStatus* pInfo, CallbackFunc cb, LPVOID lpParam) +{ + m_callbackFunc = cb; + m_callbackParam = lpParam; + outputGetStatus = pInfo; + + SceRemoteStorageStatusReqParams params; + reqId = sceRemoteStorageGetStatus(params, outputGetStatus); + m_status = e_getStatusInProgress; + + if(reqId >= 0) + { + app.DebugPrintf("Get Status request sent \n"); + return true; + } + else + { + app.DebugPrintf("Error sending Get Status request: 0x%x \n", reqId); + return false; + } +} + +void SonyRemoteStorage_Orbis::abort() +{ + SceRemoteStorageAbortReqParams params; + params.requestId = reqId; + int ret = sceRemoteStorageAbort(params); + + if(ret >= 0) + { + app.DebugPrintf("Abort request done \n"); + } + else + { + app.DebugPrintf("Error in Abort request: 0x%x \n", ret); + } +} + + +bool SonyRemoteStorage_Orbis::setData( PSAVE_INFO info, CallbackFunc cb, LPVOID lpParam ) +{ + assert(0); + return false; + +// m_callbackFunc = cb; +// m_callbackParam = lpParam; +// +// strcpy(m_saveFilename, savePath); +// strcpy(m_saveFileDesc, saveDesc); +// m_status = e_setDataInProgress; +// +// +// SceRemoteStorageSetDataReqParams params; +// params.visibility = PUBLIC_READ_WRITE; +// strcpy(params.pathLocation, m_saveFilename); +// sprintf(params.fileName, "/%s/GAMEDATA", m_saveFilename); +// // strcpy(params.fileName, "/test/small.txt"); +// strcpy(params.fileDescription, m_saveFileDesc); +// strcpy(params.ps3DataFilename, "GAMEDATA"); +// +// params.ps3FileType = CELL_SAVEDATA_FILETYPE_NORMALFILE; +// memcpy(params.secureFileId, m_secureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE); +// +// +// +// reqId = sceRemoteStorageSetData(params); +// +// app.DebugPrintf("\n*******************************\n"); +// if(reqId >= 0) +// { +// app.DebugPrintf("Set Data request sent \n"); +// return true; +// } +// else +// { +// app.DebugPrintf("Error sending Set Data request: 0x%x \n", reqId); +// return false; +// } +} + +bool SonyRemoteStorage_Orbis::getData( const char* remotePath, const char* localPath, CallbackFunc cb, LPVOID lpParam ) +{ + m_callbackFunc = cb; + m_callbackParam = lpParam; + + if(StorageManager.SaveGameDirMountExisting(m_mountPoint) == false) + { + app.DebugPrintf("Error mounting save dir \n"); + m_mountPoint[0] = 0; + return false; + } + + SceRemoteStorageGetDataReqParams params; + sprintf(params.pathLocation, "%s/GAMEDATA", m_mountPoint); + // strcpy(params.fileName, "/test/small.txt"); + strcpy(params.fileName, remotePath); + SceRemoteStorageData s_getDataOutput; + reqId = sceRemoteStorageGetData(params, &s_getDataOutput); + + app.DebugPrintf("\n*******************************\n"); + if(reqId >= 0) + { + app.DebugPrintf("Get Data request sent \n"); + return true; + } + else + { + app.DebugPrintf("Error sending Get Data request: 0x%x \n", reqId); + return false; + } +} + +void SonyRemoteStorage_Orbis::runCallback() +{ + assert(m_callbackFunc); + if(m_callbackFunc) + { + m_callbackFunc(m_callbackParam, m_status, m_lastErrorCode); + } + m_lastErrorCode = SCE_OK; +} diff --git a/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h new file mode 100644 index 00000000..0b36c41d --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h @@ -0,0 +1,42 @@ +#pragma once + + +#include "Common\Network\Sony\SonyRemoteStorage.h" + +class SonyRemoteStorage_Orbis : public SonyRemoteStorage +{ +public: + + + virtual bool init(CallbackFunc cb, LPVOID lpParam); + virtual bool setData(PSAVE_INFO info, CallbackFunc cb, LPVOID lpParam); + + virtual bool getRemoteFileInfo(SceRemoteStorageStatus* pInfo, CallbackFunc cb, LPVOID lpParam); + virtual bool getData(const char* remotePath, const char* localPath, CallbackFunc cb, LPVOID lpParam); + + virtual void abort(); + virtual bool setDataInternal(){ assert(0); } + +private: + int reqId; + void * psnTicket; + size_t psnTicketSize; + bool m_waitingForTicket; + bool initialized; + SceRemoteStorageStatus* outputGetStatus; + SceRemoteStorageData outputGetData; + + int32_t m_lastErrorCode; + int m_getDataProgress; + int m_setDataProgress; + char m_saveFilename[SCE_REMOTE_STORAGE_DATA_NAME_MAX_LEN]; + char m_saveFileDesc[SCE_REMOTE_STORAGE_DATA_DESCRIPTION_MAX_LEN]; + char m_remoteFilename[SCE_REMOTE_STORAGE_DATA_NAME_MAX_LEN]; + char m_mountPoint[SCE_SAVE_DATA_MOUNT_POINT_DATA_MAXSIZE]; + + static void staticInternalCallback(const SceRemoteStorageEvent event, int32_t retCode, void * userData); + void internalCallback(const SceRemoteStorageEvent event, int32_t retCode); + + void runCallback(); +}; + diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp new file mode 100644 index 00000000..4777e598 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp @@ -0,0 +1,355 @@ +#include "stdafx.h" + +#include "SonyVoiceChat_Orbis.h" + + + +bool m_bIsPartyAPIInitialized; +bool m_bIsPartyBinaryMessageAPIReady; + +SceNpPartyState m_stPartyState; + + SonyVoiceChatParty_Orbis::PartyInfo SonyVoiceChatParty_Orbis::m_partyInfo; + +void SonyVoiceChatParty_Orbis::init() +{ +// m_bIsRunning = false; +// m_pRenderer = NULL; +// m_pPadContext = NULL; +// m_pGraphicsContext = NULL; +// m_iPartyMemberCount = 0; +// m_iPartyLeader = 0; + int ret = SCE_OK; + SceNpPartyInitializeParam stPartyInit; + SceUserServiceLoginUserIdList userIdList; + + + // Run PartyInitializeParamInitialize inline + sceNpPartyInitializeParamInitialize( &stPartyInit ); + + // Initialize the Party API + ret = sceNpPartyInitialize( &stPartyInit ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyInitialize failed result:0x%x\n", ret ); + return; + } + + // Register handlers for party room events + SceNpPartyEventHandlers stPartyEventHandler; + memset( &stPartyEventHandler, 0, sizeof( SceNpPartyEventHandlers ) ); + stPartyEventHandler.roomEventHandler = partyRoomEventHandler; + stPartyEventHandler.voiceEventHandler = partyVoiceEventHandler; + stPartyEventHandler.binaryMessageEventHandler = partyBinaryMessageEventHandler; + ret = sceNpPartyRegisterHandler( &stPartyEventHandler, NULL ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyRegisterHandler failed result:0x%x\n", ret ); + return; + } + + + // Get current party state + ret = sceNpPartyGetState( &m_stPartyState ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyGetState failed result:0x%x\n", ret ); + return; + } + + m_bIsPartyAPIInitialized = true; +} + +void SonyVoiceChatParty_Orbis::shutdown() +{ +} + +void SonyVoiceChatParty_Orbis::setEnabled( bool bEnabled ) +{ +} + + + + +void SonyVoiceChatParty_Orbis::tick() +{ + sceNpPartyCheckCallback(); +} + + + +bool SonyVoiceChatParty_Orbis::hasMicConnected(const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + { + // in the party, might not have a mic though, still need to check for this + return true; + } + return false; +} + +void SonyVoiceChatParty_Orbis::mute( bool bMute ) +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetVoiceMuting(bMute); +// assert(err == CELL_OK); +// } +} + +void SonyVoiceChatParty_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */ +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetPlayerVoiceMuting(member_id, bMute); +// assert(err == CELL_OK); +// } +} + +void SonyVoiceChatParty_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */ +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetVoiceMuting(bMute); +// assert(err == CELL_OK); +// } +} + + +bool SonyVoiceChatParty_Orbis::isMuted() +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetVoiceMuting(&bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + +bool SonyVoiceChatParty_Orbis::isMutedPlayer( const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + return pInfo->m_voiceMuted; + assert(0 && "Didn't find playerUID"); // + return false; +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetPlayerVoiceMuting(member_id, &bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + +bool SonyVoiceChatParty_Orbis::isMutedLocalPlayer() +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetVoiceMuting(&bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + + +bool SonyVoiceChatParty_Orbis::isTalking( const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + { + DWORD currTime = GetTickCount(); + DWORD timeElapsed = currTime - pInfo->m_lastTimeTalking; + return (timeElapsed < 1000); + } + assert(0 && "Didn't find playerUID"); // + return false; +} + + + + +void SonyVoiceChatParty_Orbis::partyRoomEventHandler(SceNpPartyRoomEventType eventType, const void* data, void* userdata) +{ + + switch(eventType) + { + case SCE_NP_PARTY_ROOM_EVENT_JOINED: + { + // local player joined party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_JOINED\n"); + SceNpPartyJoinedInfo* pPartyJoinedInfo = (SceNpPartyJoinedInfo*) data; + + m_partyInfo.clear(); + app.DebugPrintf("Local user joined party.\n"); + for(int i=0; imemberNum;i++) + { + m_partyInfo.addMember(pPartyJoinedInfo->memberIds[i], pPartyJoinedInfo->members[i], false, false); + app.DebugPrintf("Member room ID : %d - Member PSN ID : %s\n", pPartyJoinedInfo->memberIds[i], pPartyJoinedInfo->members[i].handle.data); + } + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_MEMBER_JOINED: + { + // remote player joined party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_MEMBER_JOINED\n"); + SceNpPartyMemberInfo *pNewPartyMemberInfo = (SceNpPartyMemberInfo*) data; + app.DebugPrintf("A remote player[%s] has a joined the party.\n",pNewPartyMemberInfo->npId.handle.data); + + m_partyInfo.addMember(pNewPartyMemberInfo->memberId, pNewPartyMemberInfo->npId, pNewPartyMemberInfo->memberFlags & SCE_NP_PARTY_MEMBER_FLAG_IS_LOCAL, pNewPartyMemberInfo->memberFlags & SCE_NP_PARTY_MEMBER_FLAG_IS_PARTY_LEADER); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_MEMBER_LEFT: + { + // remote player left party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_MEMBER_LEFT\n"); + SceNpPartyMemberInfo *pLeavingPartyMemberInfo = (SceNpPartyMemberInfo*) data; + app.DebugPrintf("A remote player[%s] has left the party.\n", pLeavingPartyMemberInfo->npId.handle.data); + m_partyInfo.removeMember(pLeavingPartyMemberInfo->memberId); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_LEFT: + { + // local player left party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_LEFT\n"); + SceNpPartyRoomLeftInfo *pLeftInfo = (SceNpPartyRoomLeftInfo*)data; + printf("Local party member[%s] id:%d has left the party. Reason[%d]\n", pLeftInfo->npId.handle.data, pLeftInfo->memberId, pLeftInfo->reason); + m_partyInfo.removeMember(pLeftInfo->memberId); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_CREATE_RESPONSE: + { + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_CREATE_RESPONSE\n"); + SceNpPartyCreateResponseInfo *pCreateResponseInfo = (SceNpPartyCreateResponseInfo*) data; + + switch( pCreateResponseInfo->status ) + { + case SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_OK: + app.DebugPrintf( "Local party member userId[0x%x] response for creating a party. Status = SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_OK\n" , pCreateResponseInfo->userId ); + break; + case SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_CANCELLED: + app.DebugPrintf( "Local party member userId[0x%x] response for creating a party. Status = SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_CANCELLED\n" , pCreateResponseInfo->userId ); + break; + default: + app.DebugPrintf( "Warning: Unknown SceNpPartyCreateResponseStatus [%d]. Ignore.\n", pCreateResponseInfo->status ); + break; + } + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_SHOW_INVITATION_RESPONSE: + { + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_SHOW_INVITATION_RESPONSE\n"); + SceNpPartyShowInvitationResponseInfo *pShowInvitationResponseInfo = (SceNpPartyShowInvitationResponseInfo*) data; + + switch( pShowInvitationResponseInfo->status ) + { + case SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_OK: + app.DebugPrintf( "Local party member userId[0x%x] response for sending an invitation/suggestion. Status = SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_OK\n" , pShowInvitationResponseInfo->userId ); + break; + case SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_CANCELLED: + app.DebugPrintf( "Local party member userId[0x%x] response for sending an invitation/suggestion. Status = SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_CANCELLED\n" , pShowInvitationResponseInfo->userId ); + break; + default: + app.DebugPrintf( "Warning: Unknown SceNpPartyShowInvitationResponseStatus [%d]. Ignore.\n", pShowInvitationResponseInfo->status ); + break; + } + } + break; + + default: + { + app.DebugPrintf( "Warning: Un-handled party event[%d]. Ignore.\n", eventType ); + } + break; + + } + +} + + + +void SonyVoiceChatParty_Orbis::partyVoiceEventHandler( const SceNpPartyMemberVoiceInfo* memberVoiceInfo, void* userdata ) +{ + + switch(memberVoiceInfo->memberVoiceState) + { + case SCE_NP_PARTY_MEMBER_VOICE_STATE_DISCONNECTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_DISCONNECTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceConnected = false; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_CONNECTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_CONNECTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceConnected = true; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_TALKING: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_TALKING\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + { + pInfo->m_voiceMuted = false; + pInfo->m_lastTimeTalking = GetTickCount(); + } + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_MUTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_MUTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceMuted = true; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_UNKNOWN: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_UNKNOWN\n"); + } + break; + default: + { + app.DebugPrintf("Warning: Un-handled voice event. Ignore.\n"); + } + break; + } +} + + +void SonyVoiceChatParty_Orbis::partyBinaryMessageEventHandler( SceNpPartyBinaryMessageEvent event, + const void *data, + void * userdata + ) +{ + switch(event) + { + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_READY: + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_DATA: + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_COMPATIBILITY: + default: +// app.DebugPrintf("partyBinaryMessageEventHandler not supported"); +// assert(0); + break; + } +} diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h new file mode 100644 index 00000000..84f19c29 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +#include + +class SonyVoiceChatParty_Orbis +{ +public: + + static void init(); + static void shutdown(); + static void tick(); + static void setEnabled(bool bEnabled); + static bool hasMicConnected(const PlayerUID& memberUID); + static bool isTalking( const PlayerUID& memberUID); + static void mute(bool bMute); //Turn chat audio on or off + static void mutePlayer(const SceNpMatching2RoomMemberId member_id, bool bMute); //Turn chat audio from a specified player on or off; + static void muteLocalPlayer(bool bMute); //Turn microphone input on or off; + + static bool isMuted(); + static bool isMutedPlayer(const PlayerUID& memberUID); + static bool isMutedLocalPlayer(); //Turn microphone input on or off; + + +private: + //NP Party Event Handlers + static void partyRoomEventHandler(SceNpPartyRoomEventType eventType,const void* data, void* userdata); + static void partyVoiceEventHandler(const SceNpPartyMemberVoiceInfo* memberVoiceInfo,void* userdata); + static void partyBinaryMessageEventHandler(SceNpPartyBinaryMessageEvent event, const void *data, void * userdata); + + class MemberInfo + { + public: + SceNpId m_NpId; + SceNpPartyRoomMemberId m_roomID; + bool m_voiceConnected; + bool m_voiceMuted; + DWORD m_lastTimeTalking; + bool m_localUser; + bool m_partyLeader; + MemberInfo() + { + m_voiceConnected = false; + m_voiceMuted = false; + m_lastTimeTalking = 0; + m_localUser = false; + m_partyLeader = false; + } + }; + class PartyInfo + { + public: + int m_numMembers; + MemberInfo m_members[SCE_NP_PARTY_MEMBER_NUM_MAX]; + bool m_privateParty; + + MemberInfo* getMember(SceNpPartyRoomMemberId roomID) + { + for(int i=0;i= 0); + + m_numMembers--; + if(m_numMembers > 0) + m_members[index] = m_members[m_numMembers]; + } + void clear() { m_numMembers = 0;} + }; + static PartyInfo m_partyInfo; +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp new file mode 100644 index 00000000..d869d38b --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp @@ -0,0 +1,1105 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include + +#include "SonyVoiceChat_Orbis.h" + +std::vector SonyVoiceChat_Orbis::m_remoteConnections; +bool SonyVoiceChat_Orbis::m_bVoiceStarted = false; +int SonyVoiceChat_Orbis::m_numLocalDevicesConnected = 0; +SQRLocalVoiceDevice SonyVoiceChat_Orbis::m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT]; +uint32_t SonyVoiceChat_Orbis::m_voiceOutPort; +bool SonyVoiceChat_Orbis::m_forceSendPacket = false; // force a packet across the network, even if there's no data, so we can update flags +RingBuffer SonyVoiceChat_Orbis::m_recordRingBuffer(sc_ringBufferSize); +VoicePacket::Flags SonyVoiceChat_Orbis::m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; +bool SonyVoiceChat_Orbis::m_bInitialised = false; +CRITICAL_SECTION SonyVoiceChat_Orbis::m_csRemoteConnections; + +// sample related variables +SceVoiceStartParam startParam; +int32_t playSize = 0; + +static const int sc_thresholdValue = 100; + +static const bool sc_verbose = false; + +// #define _USE_PCM_AUDIO_ +//#define USE_PCM_MIC_DATA + + + +int g_loadedPCMVoiceDataSizes[4]; +int g_loadedPCMVoiceDataPos[4]; +char* g_loadedPCMVoiceData[4]; + +static void CreatePort(uint32_t *portId, const SceVoicePortParam *pArg) +{ + C4JThread::PushAffinityAllCores(); // PS4 only + + int err = sceVoiceCreatePort( portId, pArg ); + assert(err == SCE_OK); + assert(*portId != SCE_VOICE_INVALID_PORT_ID); + C4JThread::PopAffinity(); // PS4 only +} + +static void DeletePort(uint32_t& port) +{ + int32_t result; + if (port != SCE_VOICE_INVALID_PORT_ID) + { + result = sceVoiceDeletePort( port ); + if (result != SCE_OK) + { + app.DebugPrintf("sceVoiceDeletePort failed %0x\n", result); + assert(0); + } + port = SCE_VOICE_INVALID_PORT_ID; + } +} + + +void LoadPCMVoiceData() +{ + for(int i=0;i<4;i++) + { + char filename[64]; + sprintf(filename, "voice%d.pcm", i+1); + HANDLE file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + DWORD dwHigh=0; + g_loadedPCMVoiceDataSizes[i] = GetFileSize(file,&dwHigh); + + if(g_loadedPCMVoiceDataSizes[i]!=0) + { + g_loadedPCMVoiceData[i] = new char[g_loadedPCMVoiceDataSizes[i]]; + DWORD bytesRead; + BOOL bSuccess = ReadFile(file, g_loadedPCMVoiceData[i], g_loadedPCMVoiceDataSizes[i], &bytesRead, NULL); + assert(bSuccess); + } + g_loadedPCMVoiceDataPos[i] = 0; + } +} + + +void SonyVoiceChat_Orbis::init() +{ + int returnCode = SCE_OK; + SceUserServiceUserId initialUserId; + + if (returnCode < 0) + { + app.DebugPrintf("Error: sceSysmoduleLoadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode); + assert(0); + } + + + SceVoiceInitParam params; + SceVoicePortParam portArgs; + memset( ¶ms, 0, sizeof(params) ); + params.appType = SCE_VOICE_APPTYPE_GAME; + params.onEvent = 0; + returnCode = sceVoiceInit( ¶ms , SCE_VOICE_VERSION_100); + if (returnCode < 0) + { + app.DebugPrintf("Error: sceVoiceInit(), ret 0x%08x\n", returnCode); + assert(0); + } + +#ifdef _USE_PCM_AUDIO_ + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.format.sampleRate = SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.bufSize = 4096; +#else + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_VOICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.voice.bitrate = VOICE_ENCODED_FORMAT; +#endif + CreatePort( &m_voiceOutPort, &portArgs ); + + start(); + m_bInitialised = true; + InitializeCriticalSection(&m_csRemoteConnections); +} + + +void SonyVoiceChat_Orbis::start() +{ + if( m_bVoiceStarted == false) + { + startParam.container = malloc(SCE_VOICE_MEMORY_CONTAINER_SIZE); + startParam.memSize = SCE_VOICE_MEMORY_CONTAINER_SIZE; + + int err; + + C4JThread::PushAffinityAllCores(); // PS4 only + err = sceVoiceStart(&startParam); + assert(err == SCE_OK); + C4JThread::PopAffinity(); // PS4 only + + m_bVoiceStarted = true; + } +} + +void SonyVoiceChat_Orbis::checkFinished() +{ + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=0;im_bFlaggedForShutdown); + } +// assert(m_numLocalDevicesConnected == 0); + + LeaveCriticalSection(&m_csRemoteConnections); +} + + +void SonyVoiceChat_Orbis::shutdown() +{ + m_bInitialised = false; + int32_t result; + result = sceVoiceStop(); + assert(result == SCE_OK); + result = sceVoiceEnd(); + assert(result == SCE_OK); + free(startParam.container); + + int returnCode = sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE); + if (returnCode < 0) + { + app.DebugPrintf("Error: sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode); + assert(0); + } +} + +void SonyVoiceChat_Orbis::setEnabled( bool bEnabled ) +{ +} + + + +// Internal send function. This attempts to send as many elements in the queue as possible until the write function tells us that we can't send any more. This way, +// we are guaranteed that if there *is* anything more in the queue left to send, we'll get a CELL_RUDP_CONTEXT_EVENT_WRITABLE event when whatever we've managed to +// send here is complete, and can continue on. +void SQRVoiceConnection::SendMoreInternal() +{ + EnterCriticalSection(&m_csQueue); + bool keepSending; + do + { + keepSending = false; + if( m_sendQueue.size() > 0) + { + // Attempt to send the full data in the first element in our queue + unsigned char *data= m_sendQueue.front().current; + int dataSize = m_sendQueue.front().end - m_sendQueue.front().current; + int ret = sceRudpWrite( m_rudpCtx, data, dataSize, 0);//CELL_RUDP_MSG_LATENCY_CRITICAL ); + int wouldBlockFlag = SCE_RUDP_ERROR_WOULDBLOCK; + + if( ret == dataSize ) + { + // Fully sent, remove from queue - will loop in the while loop to see if there's anything else in the queue we could send + delete [] m_sendQueue.front().start; + m_sendQueue.pop(); + if( m_sendQueue.size() ) + { + keepSending = true; + } + } + else if( ( ret >= 0 ) || ( ret == wouldBlockFlag ) ) + { + + + // Things left to send - adjust this element in the queue + int remainingBytes; + if( ret >= 0 ) + { + // Only ret bytes sent so far + remainingBytes = dataSize - ret; + assert(remainingBytes > 0 ); + } + else + { + // Is CELL_RUDP_ERROR_WOULDBLOCK, nothing has yet been sent + remainingBytes = dataSize; + } + m_sendQueue.front().current = m_sendQueue.front().end - remainingBytes; + } + } + } while (keepSending); + LeaveCriticalSection(&m_csQueue); +} + +void SQRVoiceConnection::SendInternal(const void *data, unsigned int dataSize) +{ + EnterCriticalSection(&m_csQueue); + + QueuedSendBlock sendBlock; + + unsigned char *dataCurrent = (unsigned char *)data; + unsigned int dataRemaining = dataSize; + + while( dataRemaining ) + { + int dataSize = dataRemaining; + if( dataSize > SNP_MAX_PAYLOAD ) dataSize = SNP_MAX_PAYLOAD; + sendBlock.start = new unsigned char [dataSize]; + sendBlock.end = sendBlock.start + dataSize; + sendBlock.current = sendBlock.start; + memcpy( sendBlock.start, dataCurrent, dataSize); + m_sendQueue.push(sendBlock); + dataRemaining -= dataSize; + dataCurrent += dataSize; + } + +// app.DebugPrintf("voice sent %d bytes\n", dataSize); + + // Now try and send as much as we can + SendMoreInternal(); + + LeaveCriticalSection(&m_csQueue); +} + +void SQRVoiceConnection::readRemoteData() +{ + unsigned int dataSize = sceRudpGetSizeReadable(m_rudpCtx); + if( dataSize > 0 ) + { + VoicePacket packet; + int bytesRead = sceRudpRead( m_rudpCtx, &packet, dataSize, 0, NULL ); + unsigned int writeSize; + if( bytesRead > 0 ) + { +// app.DebugPrintf("voice received %d bytes\n", bytesRead); + writeSize = bytesRead; + packet.verifyData(bytesRead, 19); + addPacket(packet); +// m_playRingBuffer.Write((char*)data, writeSize); + + } + } + +} + + + +SQRVoiceConnection::SQRVoiceConnection( int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId ) + : m_rudpCtx(rudpCtx) + , m_remoteRoomMemberId(remoteRoomMemberId) + , m_bConnected(false) + , m_headsetConnectionMask(0) + , m_playRingBuffer(sc_ringBufferSize) +{ + InitializeCriticalSection(&m_csQueue); + InitializeCriticalSection(&m_csPacketQueue); + + SceVoiceInitParam params; + SceVoicePortParam portArgs; +#ifdef _USE_PCM_AUDIO_ + portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 100; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.bufSize = 4096; +#else + portArgs.portType = SCE_VOICE_PORTTYPE_IN_VOICE; + portArgs.bMute = false; + portArgs.threshold = sc_thresholdValue; // compensate network jitter + portArgs.volume = 1.0f; + portArgs.voice.bitrate = VOICE_ENCODED_FORMAT; +#endif + CreatePort( &m_voiceInPort, &portArgs ); + m_nextExpectedFrameIndex = 0; + m_bFlaggedForShutdown = false; +} + +SQRVoiceConnection::~SQRVoiceConnection() +{ + DeleteCriticalSection(&m_csQueue); + DeleteCriticalSection(&m_csPacketQueue); + extern int g_numRUDPContextsBound; + int err = sceRudpTerminate( m_rudpCtx ); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + + app.DebugPrintf("-----------------------------\n"); + if(err<0) + app.DebugPrintf("Voice rudp context failed to delete!!! %d\n", m_rudpCtx); + else + { + g_numRUDPContextsBound--; + app.DebugPrintf("Voice rudp context deleted %d\n", m_rudpCtx); + } + app.DebugPrintf("-----------------------------\n"); + + DeletePort(m_voiceInPort); + +} + +bool SQRVoiceConnection::getNextPacket( VoicePacket& packet ) +{ + EnterCriticalSection(&m_csPacketQueue); + bool retVal = false; + if(m_receivedVoicePackets.size() > 0) + { + retVal = true; + packet = m_receivedVoicePackets.front(); + m_receivedVoicePackets.pop(); + } + LeaveCriticalSection(&m_csPacketQueue); + return retVal; +} + +void SQRVoiceConnection::addPacket( VoicePacket& packet ) +{ + EnterCriticalSection(&m_csPacketQueue); + m_receivedVoicePackets.push(packet); + LeaveCriticalSection(&m_csPacketQueue); +} + +int g_frameNum = 0; +bool g_bRecording = false; + + +uint32_t frameSendIndex = 0; +uint32_t lastReadFrameCnt = 0; + + +void PrintAllOutputVoiceStates( std::vector& connections) +{ + for(int rIdx=0;rIdxm_voiceInPort, &portInfo ); + static SceVoicePortState lastPortState = SCE_VOICE_PORTSTATE_IDLE; + if(portInfo.state != lastPortState) + { + lastPortState = portInfo.state; + switch(portInfo.state) + { + case SCE_VOICE_PORTSTATE_IDLE: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_IDLE\n"); + break; + case SCE_VOICE_PORTSTATE_BUFFERING: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_BUFFERING\n"); + break; + case SCE_VOICE_PORTSTATE_RUNNING: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_RUNNING\n"); + break; + case SCE_VOICE_PORTSTATE_READY: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_READY\n"); + break; + case SCE_VOICE_PORTSTATE_NULL: + default: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_NULL\n"); + break; + } + } + } + +} + + +void SonyVoiceChat_Orbis::sendPCMMicData() +{ + int32_t result; + uint32_t outputPortBytes; + VoicePacket packetToSend; + uint32_t readSize; + SceVoiceBasePortInfo portInfo; + memset( &portInfo, 0, sizeof(portInfo) ); + uint16_t frameGap = 0; + + DWORD tick = GetTickCount(); + static DWORD lastTick = 0; + int numFrames = ceilf((tick - lastTick)/16.0f); + lastTick = tick; + readSize = 512 * numFrames; + + if(g_loadedPCMVoiceDataPos[0] + readSize < g_loadedPCMVoiceDataSizes[0]) + { + for(int i=0;i (g_loadedPCMVoiceDataSizes[0] + 8192)) + g_loadedPCMVoiceDataPos[0] = 0; + + +} + +void SonyVoiceChat_Orbis::sendAllVoiceData() +{ + int32_t result; + uint32_t outputPortBytes; + VoicePacket packetToSend; + uint32_t readSize; + SceVoiceBasePortInfo portInfo; + memset( &portInfo, 0, sizeof(portInfo) ); + uint16_t frameGap = 0; + + VoicePacket::Flags lastPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + + for(int i=0; isizeof(packetToSend.m_data))?sizeof(packetToSend.m_data):outputPortBytes; + if( outputPortBytes || flagsChanged || m_forceSendPacket) + { + frameSendIndex += lastReadFrameCnt; + if(outputPortBytes) + { + readSize = outputPortBytes; + result = sceVoiceReadFromOPort(m_voiceOutPort, packetToSend.m_data, &readSize ); + if (result != SCE_OK) + { + app.DebugPrintf("sceVoiceReadFromOPort failed %0x\n", result); + assert(0); + return; + } + lastReadFrameCnt = readSize/portInfo.frameSize; + assert(readSize%portInfo.frameSize == 0); + + packetToSend.m_numFrames = lastReadFrameCnt; + packetToSend.m_frameSendIndex = frameSendIndex; + packetToSend.setChecksum(readSize); + } + else + { + readSize = 0; + packetToSend.m_numFrames = 0; + packetToSend.m_frameSendIndex = frameSendIndex; + packetToSend.setChecksum(readSize); + + } + + + int packetSize = packetToSend.getPacketSize(readSize); + + EnterCriticalSection(&m_csRemoteConnections); + + // send this packet out to all our remote connections + for(int rIdx=0;rIdxm_bConnected) + m_remoteConnections[rIdx]->SendInternal(&packetToSend, packetSize); + } + + LeaveCriticalSection(&m_csRemoteConnections); + } + m_forceSendPacket = false; +} + +bool g_bPlaying = false; + +void SonyVoiceChat_Orbis::playAllReceivedData() +{ + EnterCriticalSection(&m_csRemoteConnections); + // write all the incoming data from the network to each of the input voices + for(int rIdx=0;rIdxgetNextPacket(packet)) // MGH - changed to a while loop, so all the packets are sent to the voice port, and it can handle delayed packets due to the size of it's internal buffer + { + int frameGap; + if (pVoice->m_nextExpectedFrameIndex == packet.m_frameSendIndex) + { + // no voice frame drop, continuous frames + frameGap = 0; + app.DebugPrintf(sc_verbose, "index@%d gets expected frame\n",pVoice->m_nextExpectedFrameIndex); + pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames; + } + else if (pVoice->m_nextExpectedFrameIndex < packet.m_frameSendIndex) + { + // has voice frame drop, dropped forwarding frames + frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex; + app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n",pVoice->m_nextExpectedFrameIndex, frameGap); + pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames; + } + else if (pVoice->m_nextExpectedFrameIndex > packet.m_frameSendIndex) + { + // has voice frame drop, dropped preceding frames, no reset on pVoice->m_nextExpectedFrameIndex + frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex; + app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n", pVoice->m_nextExpectedFrameIndex, frameGap); + } + + SceVoiceBasePortInfo portInfo; + int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo ); + if (result != SCE_OK) + { + app.DebugPrintf(sc_verbose, "sceVoiceGetPortInfo LoopbackVoiceInPort failed %x\n", result); + assert(0); + LeaveCriticalSection(&m_csRemoteConnections); + return; + } + uint32_t writeSize = packet.m_numFrames * portInfo.frameSize; + int inputPortBytes = portInfo.numByte; + inputPortBytes = (inputPortBytes>writeSize)?writeSize:inputPortBytes; + writeSize = inputPortBytes; + result = sceVoiceWriteToIPort(pVoice->m_voiceInPort, packet.m_data, &writeSize, frameGap); + if (result != SCE_OK) + { + app.DebugPrintf(sc_verbose, "sceVoiceWriteToIPort failed %0x\n", result); + assert(0); + LeaveCriticalSection(&m_csRemoteConnections); + return; + } + if (writeSize != inputPortBytes) + { + // libvoice internal voice in port buffer fulls + app.DebugPrintf(sc_verbose, "internal voice in port buffer fulls. \n"); + } + packet.m_numFrames = 0; + + // copy the flags + for(int flagIndex=0;flagIndexm_remotePlayerFlags[flagIndex] = packet.m_localPlayerFlags[flagIndex]; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + +} + +void SonyVoiceChat_Orbis::tick() +{ + if(m_bInitialised) + { +// DWORD tick = GetTickCount(); +// static DWORD lastTick = 0; +// app.DebugPrintf("Time since last voice tick : %d ms\n", tick - lastTick); +// lastTick = tick; + g_frameNum++; +#ifdef USE_PCM_MIC_DATA + sendPCMMicData(); +#endif + sendAllVoiceData(); + playAllReceivedData(); + + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=m_remoteConnections.size()-1;i>=0;i--) + { + if(m_remoteConnections[i]->m_bFlaggedForShutdown) + { + delete m_remoteConnections[i]; + m_remoteConnections.erase(m_remoteConnections.begin() + i); + } + } + + LeaveCriticalSection(&m_csRemoteConnections); + + // MGH - added to hopefully fix a lockup with shutdowns happening on different threads, when more than 1 player leaves the game + for(int i=0;iIsLocal()) + { + return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected; + } + else + { + EnterCriticalSection(&m_csRemoteConnections); + for(int i=0;im_remoteRoomMemberId == pNetPlayer->m_roomMemberId) + { + bool bMicConnected = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected; + LeaveCriticalSection(&m_csRemoteConnections); + return bMicConnected; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + } + // if we get here we've not found the player, panic!! + assert(0); + return false; +} + +void SonyVoiceChat_Orbis::mute( bool bMute ) +{ +} + +void SonyVoiceChat_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */ +{ +} + +void SonyVoiceChat_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */ +{ +} + +bool SonyVoiceChat_Orbis::isMuted() +{ +} + +bool SonyVoiceChat_Orbis::isMutedPlayer( const PlayerUID& memberUID) +{ + return false; +} + +bool SonyVoiceChat_Orbis::isMutedLocalPlayer() +{ + return false; +} + + +bool SonyVoiceChat_Orbis::isTalking(SQRNetworkPlayer* pNetPlayer) +{ + if(pNetPlayer->IsLocal()) + { + return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking; + } + else + { + EnterCriticalSection(&m_csRemoteConnections); + for(int i=0;im_remoteRoomMemberId == pNetPlayer->m_roomMemberId) + { + bool bTalking = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking; + LeaveCriticalSection(&m_csRemoteConnections); + return bTalking; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + } + // if we get here we've not found the player, panic!! + assert(0); + return false; +} + + +void SQRLocalVoiceDevice::init(SceUserServiceUserId localUserID, bool bChatRestricted) +{ + SceVoiceInitParam params; + SceVoicePortParam portArgs; + + int returnCode = 0; + m_bChatRestricted = bChatRestricted; + +#ifdef USE_PCM_MIC_DATA + portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 100; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.bufSize = 4096; + CreatePort( &m_microphonePort, &portArgs ); +#else + portArgs.portType = SCE_VOICE_PORTTYPE_IN_DEVICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.device.userId = localUserID; + portArgs.device.type = SCE_AUDIO_IN_TYPE_VOICE; + portArgs.device.index = 0; + CreatePort( &m_microphonePort, &portArgs ); + +#endif + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_DEVICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.device.userId = localUserID; + portArgs.device.type = SCE_AUDIO_OUT_PORT_TYPE_VOICE; + portArgs.device.index = 0; + CreatePort( &m_headsetPort, &portArgs ); + + m_localUserID = localUserID; + +} + + + +void SQRLocalVoiceDevice::shutdownWhenFlagged() +{ + + assert(isValid()); + m_localUserID = SCE_USER_SERVICE_USER_ID_INVALID; + DeletePort(m_microphonePort); + DeletePort(m_headsetPort); + m_bFlaggedForShutdown = false; +} + + + +SQRVoiceConnection* SonyVoiceChat_Orbis::addRemoteConnection( int RudpCxt, SceNpMatching2RoomMemberId peerMemberId) +{ + EnterCriticalSection(&m_csRemoteConnections); + SQRVoiceConnection* pConn = new SQRVoiceConnection(RudpCxt, peerMemberId); + m_remoteConnections.push_back(pConn); + m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags + LeaveCriticalSection(&m_csRemoteConnections); + + return pConn; +} + +void SonyVoiceChat_Orbis::connectPorts(uint32_t inPort, uint32_t outPort) +{ + int returnCode = sceVoiceConnectIPortToOPort(inPort, outPort); + if (returnCode != SCE_OK ) + { + app.DebugPrintf("sceVoiceConnectIPortToOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort); + assert(0); + } +} +void SonyVoiceChat_Orbis::disconnectPorts(uint32_t inPort, uint32_t outPort) +{ + int returnCode = sceVoiceDisconnectIPortFromOPort(inPort, outPort); + if (returnCode != SCE_OK ) + { + app.DebugPrintf("sceVoiceDisconnectIPortFromOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort); + assert(0); + } +} + + +void SonyVoiceChat_Orbis::makeLocalConnections() +{ + // connect all mics to other devices headsets, for local chat + for(int i=0;iisValid()) + { + for(int j=0;jisValid()) + { + if(pConnectFrom->m_localConnections[j] == false) + { + if(pConnectTo->m_bChatRestricted == false && pConnectFrom->m_bChatRestricted == false) + { + connectPorts(pConnectFrom->m_microphonePort, pConnectTo->m_headsetPort); + pConnectFrom->m_localConnections[j] = true; + } + } + } + } + } + } +} + +void SonyVoiceChat_Orbis::breakLocalConnections(int playerIdx) +{ + // break any connections with devices that are no longer valid + for(int i=0;im_localConnections[j] == true) + { + SQRLocalVoiceDevice* pConnectedTo = &m_localVoiceDevices[j]; + if(i==playerIdx || j==playerIdx) + { + if(pConnectedTo->m_bChatRestricted == false && pConnectedFrom->m_bChatRestricted == false) + { + disconnectPorts(pConnectedFrom->m_microphonePort, pConnectedTo->m_headsetPort); + pConnectedFrom->m_localConnections[j] = false; + } + } + } + } + } +} + + +void SonyVoiceChat_Orbis::initLocalPlayer(int playerIndex) +{ + if(m_localVoiceDevices[playerIndex].isValid() == false) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + + // create all device ports required + m_localVoiceDevices[playerIndex].init(ProfileManager.getUserID(playerIndex), chatRestricted); + m_numLocalDevicesConnected++; + if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false) + { + connectPorts(m_localVoiceDevices[playerIndex].m_microphonePort, m_voiceOutPort); + } + m_forceSendPacket = true; // new local device, so we'll force a packet through for the flags + + } + makeLocalConnections(); +} + +void SonyVoiceChat_Orbis::connectPlayer(SQRVoiceConnection* pConnection, int playerIndex) +{ + if((pConnection->m_headsetConnectionMask & (1 << playerIndex)) == 0) + { + initLocalPlayer(playerIndex); // added this as we can get a client->client connection coming in first, and the network player hasn't been created yet (so this hasn't been initialised) + if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false) + { + connectPorts(pConnection->m_voiceInPort, m_localVoiceDevices[playerIndex].m_headsetPort); + } + pConnection->m_headsetConnectionMask |= (1 << playerIndex); + app.DebugPrintf("Connecting player %d to rudp context %d\n", playerIndex, pConnection->m_rudpCtx); + m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags + } +} + +SQRVoiceConnection* SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx( int RudpCtx ) +{ + for(int i=0;im_rudpCtx == RudpCtx) + return m_remoteConnections[i]; + } + return NULL; +} + +void SonyVoiceChat_Orbis::connectPlayerToAll( int playerIndex ) +{ + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=0;im_remoteRoomMemberId == roomMemberID) + { + return m_remoteConnections[i]; + } + } + + return NULL; +} + +void SonyVoiceChat_Orbis::disconnectLocalPlayer( int localIdx ) +{ + EnterCriticalSection(&m_csRemoteConnections); + + if(m_localVoiceDevices[localIdx].m_bChatRestricted == false) + { + disconnectPorts(m_localVoiceDevices[localIdx].m_microphonePort, m_voiceOutPort); + + for(int i=0;im_voiceInPort, m_localVoiceDevices[localIdx].m_headsetPort); + m_remoteConnections[i]->m_headsetConnectionMask &= (~(1 << localIdx)); + app.DebugPrintf("disconnecting player %d from rudp context %d\n", localIdx, m_remoteConnections[i]->m_rudpCtx); + } + } + LeaveCriticalSection(&m_csRemoteConnections); + + breakLocalConnections(localIdx); + m_localVoiceDevices[localIdx].flagForShutdown(); + m_numLocalDevicesConnected--; + + if(m_numLocalDevicesConnected == 0) // no more local players, kill all the remote connections + { + for(int i=0;i=0); + if(voiceIdx>=0) + { + m_remoteConnections[voiceIdx]->m_bFlaggedForShutdown = true; + } + + LeaveCriticalSection(&m_csRemoteConnections); + +} + +void SonyVoiceChat_Orbis::setConnected( int RudpCtx ) +{ + SQRVoiceConnection* pVoice = GetVoiceConnectionFromRudpCtx(RudpCtx); + if(pVoice) + { + pVoice->m_bConnected = true; + m_forceSendPacket = true; + } + else + { + assert(false); + } +} + + + + +RingBuffer::RingBuffer( int sizeBytes ) +{ + buffer = new char[sizeBytes]; + buf_size = sizeBytes; + buf_full = buf_free = 0; +} + + +int RingBuffer::Write( char* data, int len_ ) +{ + if (len_ <= 0) return len_; + unsigned int len = (unsigned int)len_; + unsigned int data_size = buf_size - (buf_free - buf_full); + if (len > data_size) + len = data_size; + data_size = buf_size - (buf_free % buf_size); + if (data_size > len) + data_size = len; + memcpy(buffer + (buf_free % buf_size), data, data_size); + if (data_size != len) + memcpy(buffer, data + data_size, len - data_size); + buf_free += len; + return len; +} + +int RingBuffer::Read( char* data, int max_bytes_ ) +{ + if (max_bytes_ <= 0) return max_bytes_; + unsigned int max_bytes = (unsigned int)max_bytes_; + unsigned int result = buf_free - buf_full; + if (result > max_bytes) + result = max_bytes; + unsigned int chunk = buf_size - (buf_full % buf_size); + if (chunk > result) + chunk = result; + memcpy(data, buffer + (buf_full % buf_size), chunk); + if (chunk != result) + memcpy(data + chunk, buffer, result - chunk); + buf_full += result; + return result; +} diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h new file mode 100644 index 00000000..950ca09a --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h @@ -0,0 +1,215 @@ +#pragma once + +#include +#include +#include +#include +#include "Common/Network/Sony/SQRNetworkPlayer.h" + +static const int sc_maxVoiceDataSize = 2048; + + +class VoicePacket +{ + static const int MAX_LOCAL_PLAYER_COUNT = 4; + +public: + struct Flags + { + bool m_bTalking : 1; + bool m_bHasMicConnected : 1; + }; + + Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + uint32_t m_frameSendIndex; + uint32_t m_numFrames; + uint32_t m_checkSum; + uint32_t m_playerIndexFlags; + char m_data[sc_maxVoiceDataSize]; + + static int getPacketSize(int dataSize) { return (uint64_t)&((VoicePacket*)0)->m_data[dataSize];} + void setChecksum(int dataSize) + { + m_checkSum = 0; + for(int i=0;i, +// the reader thread changes pointer +class RingBuffer +{ +public: + RingBuffer(int sizeBytes); + ~RingBuffer() { delete buffer; } + void Reset(void) { buf_full = buf_free = 0; } + unsigned int DataSize(void) { return (buf_free - buf_full); } + int Write(char* data, int len_); + int Read(char* data, int max_bytes_); + int getDataSize() { return buf_free - buf_full; } + void ResetByWriter(void) { buf_free = buf_full; } + void ResetByReader(void) { buf_full = buf_free; } + +private: + char* buffer; + unsigned int buf_size; + unsigned int buf_full; + unsigned int buf_free; +}; + + +static const int sc_ringBufferSize = 16384; + +class SQRLocalVoiceDevice +{ +public: + uint32_t m_headsetPort; + uint32_t m_microphonePort; + uint8_t m_localConnections[4]; // connection between this devices mic and other local player's headsets + bool m_bChatRestricted; + bool m_bFlaggedForShutdown; + SceUserServiceUserId m_localUserID; + +public: + SQRLocalVoiceDevice() + : m_headsetPort(SCE_VOICE_INVALID_PORT_ID) + , m_microphonePort(SCE_VOICE_INVALID_PORT_ID) + , m_localUserID(SCE_USER_SERVICE_USER_ID_INVALID) + { + for(int i=0;i<4;i++) + m_localConnections[i] = 0; + } + + void init(SceUserServiceUserId localUserID, bool bChatRestricted); + void flagForShutdown() { m_bFlaggedForShutdown = true;} + void shutdownWhenFlagged(); + bool isValid() { return m_localUserID != SCE_USER_SERVICE_USER_ID_INVALID; } +// void setBitRate() +// { +// int err = sceVoiceSetBitRate(uint32_t portId, +// SceVoiceBitRate bitrate +// ); +// } +}; + +#define VOICE_ENCODED_FORMAT SCE_VOICE_BITRATE_7300 + + + +class SQRVoiceConnection +{ + static const int MAX_LOCAL_PLAYER_COUNT = 4; + static const int SNP_MAX_PAYLOAD = 1346; // This is the default RUDP payload size - if we want to change this we'll need to use cellRudpSetOption to set something else & adjust segment size + class QueuedSendBlock + { + public: + unsigned char *start; + unsigned char *end; + unsigned char *current; + }; + + std::queue m_sendQueue; + CRITICAL_SECTION m_csQueue; + +public: + int m_rudpCtx; + bool m_bConnected; + uint32_t m_voiceInPort; // 1 input port per connection, incoming UDP packets are written to each of these, and then they're connected out to all headsets + int m_headsetConnectionMask; // 1 bit per player, if the headset connection has been made + RingBuffer m_playRingBuffer; + SceNpMatching2RoomMemberId m_remoteRoomMemberId; // Assigned by Matching2 lib, we can use to indicate which machine this player belongs to (note - 16 bits) + std::queue m_receivedVoicePackets; + CRITICAL_SECTION m_csPacketQueue; + VoicePacket::Flags m_remotePlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + uint32_t m_nextExpectedFrameIndex; + bool m_bFlaggedForShutdown; + SQRVoiceConnection(int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId); + ~SQRVoiceConnection(); + + void SendInternal(const void *data, unsigned int dataSize); + void SendMoreInternal(); + void readRemoteData(); + bool getNextPacket(VoicePacket& packet); + void addPacket(VoicePacket& packet); + + +}; + +class SonyVoiceChat_Orbis +{ +public: + + static void init(); + static void start(); + static void shutdown(); + static void tick(); + static void checkFinished(); + static void setEnabled(bool bEnabled); + static bool hasMicConnected(SQRNetworkPlayer* pNetPlayer); + static bool isTalking(SQRNetworkPlayer* pNetPlayer); + static void mute(bool bMute); //Turn chat audio on or off + static void mutePlayer(const SceNpMatching2RoomMemberId member_id, bool bMute); //Turn chat audio from a specified player on or off; + static void muteLocalPlayer(bool bMute); //Turn microphone input on or off; + + static bool isMuted(); + static bool isMutedPlayer(const PlayerUID& memberUID); + static bool isMutedLocalPlayer(); //Turn microphone input on or off; + + static void initLocalPlayer(int playerIndex); + + static SQRVoiceConnection* addRemoteConnection(int RudpCxt, SceNpMatching2RoomMemberId peerMemberId); + static void connectPlayer(SQRVoiceConnection* pConnection, int playerIndex); + static void connectPlayerToAll(int playerIndex); + static void disconnectLocalPlayer(int localIdx); + static void disconnectRemoteConnection( SQRVoiceConnection* pVoice ); + + static void VoiceEventCallback( SceVoiceEventData* pEvent ); + + static std::vector m_remoteConnections; + static void connectPorts(uint32_t inPort, uint32_t outPort); + static void disconnectPorts(uint32_t inPort, uint32_t outPort); + static void makeLocalConnections(); + static void breakLocalConnections(int playerIdx); + + static void sendAllVoiceData(); + static void playAllReceivedData(); + + static void sendPCMMicData(); + static SQRVoiceConnection* getVoiceConnectionFromRoomMemberID(SceNpMatching2RoomMemberId roomMemberID); + + static SQRVoiceConnection* GetVoiceConnectionFromRudpCtx(int RudpCtx); + static void setConnected(int RudpCtx); + +private: + static const int MAX_LOCAL_PLAYER_COUNT = 4; + + static bool m_bVoiceStarted; + static int m_numLocalDevicesConnected; + static SQRLocalVoiceDevice m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT]; + + static uint32_t m_voiceOutPort; // single output port that all local devices are mixed to, and then sent out to all other remote machines + + static RingBuffer m_recordRingBuffer; + static RingBuffer m_playRingBuffer; + static VoicePacket::Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + static bool m_forceSendPacket; // force a packet across the network, even if there's no data, so we can update flags + static bool m_bInitialised; + static CRITICAL_SECTION m_csRemoteConnections; + + +}; \ No newline at end of file -- cgit v1.2.3