/************************************************************************************ Filename : Service_NetClient.cpp Content : Client for service interface Created : June 12, 2014 Authors : Michael Antonov, Kevin Jenkins, Chris Taylor Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); you may not use the Oculus VR Rift SDK except in compliance with the License, which is provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. You may obtain a copy of the License at http://www.oculusvr.com/licenses/LICENSE-3.2 Unless required by applicable law or agreed to in writing, the Oculus VR SDK distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ************************************************************************************/ #include "Service_NetClient.h" #include "../Net/OVR_MessageIDTypes.h" #if defined (OVR_OS_MAC) || defined(OVR_OS_LINUX) #define GetCurrentProcessId getpid #endif OVR_DEFINE_SINGLETON(OVR::Service::NetClient); namespace OVR { namespace Service { using namespace OVR::Net; //// NetClient NetClient::NetClient() : LatencyTesterAvailable(false), HMDCount(0), EdgeTriggeredHMDCount(false) { GetSession()->AddSessionListener(this); // Register RPC functions registerRPC(); Start(); // Must be at end of function PushDestroyCallbacks(); } NetClient::~NetClient() { } void NetClient::OnSystemDestroy() { onSystemDestroy(); } void NetClient::OnThreadDestroy() { onThreadDestroy(); } int NetClient::Run() { SetThreadName("NetClient"); while (!Terminated) { // Note: There is no watchdog here because the watchdog is part of the private code GetSession()->Poll(false); if (GetSession()->GetActiveSocketsCount() == 0) { Thread::MSleep(10); } } return 0; } void NetClient::OnReceive(ReceivePayload* pPayload, ListenerReceiveResult* lrrOut) { OVR_UNUSED(lrrOut); OVR_UNUSED(pPayload); } void NetClient::OnDisconnected(Connection* conn) { OVR_UNUSED(conn); OVR_DEBUG_LOG(("[NetClient] Disconnected")); EdgeTriggeredHMDCount = false; } void NetClient::OnConnected(Connection* conn) { OVR_UNUSED(conn); OVR_DEBUG_LOG(("[NetClient] Connected to a server running version %d.%d.%d (my version=%d.%d.%d)", conn->RemoteMajorVersion, conn->RemoteMinorVersion, conn->RemotePatchVersion, RPCVersion_Major, RPCVersion_Minor, RPCVersion_Patch)); EdgeTriggeredHMDCount = false; } bool NetClient::Connect(bool blocking) { // Set up bind parameters OVR::Net::BerkleyBindParameters bbp; bbp.Address = "::1"; // Bind to localhost only! bbp.blockingTimeout = 5000; OVR::Net::SockAddr sa; sa.Set("::1", VRServicePort, SOCK_STREAM); // Attempt to connect OVR::Net::SessionResult result = GetSession()->ConnectPTCP(&bbp, &sa, blocking); // Already connected counts as success too return result == Net::SessionResult_OK || result == Net::SessionResult_AlreadyConnected || result == Net::SessionResult_ConnectInProgress; } void NetClient::Disconnect() { GetSession()->Shutdown(); } bool NetClient::IsConnected(bool attemptReconnect, bool blockOnReconnect) { // If it was able to connect, if (GetSession()->GetConnectionCount() > 0) { return true; } else if (attemptReconnect) { // Attempt to connect here Connect(blockOnReconnect); // If it connected, if (GetSession()->GetConnectionCount() > 0) { return true; } } // No connections return false; } void NetClient::GetLocalProtocolVersion(int& major, int& minor, int& patch) { major = RPCVersion_Major; minor = RPCVersion_Minor; patch = RPCVersion_Patch; } bool NetClient::GetRemoteProtocolVersion(int& major, int& minor, int& patch) { Ptr conn = GetSession()->GetConnectionAtIndex(0); if (conn) { major = conn->RemoteMajorVersion; minor = conn->RemoteMinorVersion; patch = conn->RemotePatchVersion; return true; } return false; } //// NetClient API const char* NetClient::GetStringValue(VirtualHmdId hmd, const char* key, const char* default_val) { if (!IsConnected(true, true)) { return ""; } // If a null value is provided, if (!default_val) { default_val = ""; } ProfileGetValue1_Str = default_val; OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(default_val); if (!GetRPC1()->CallBlocking("GetStringValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return ""; } if (!returnData.Read(ProfileGetValue1_Str)) { OVR_ASSERT(false); } return ProfileGetValue1_Str.ToCStr(); } bool NetClient::GetBoolValue(VirtualHmdId hmd, const char* key, bool default_val) { if (!IsConnected(true, true)) { return default_val; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(default_val); if (!GetRPC1()->CallBlocking("GetBoolValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return default_val; } uint8_t out = 0; if (!returnData.Read(out)) { OVR_ASSERT(false); } return out != 0; } int NetClient::GetIntValue(VirtualHmdId hmd, const char* key, int default_val) { if (!IsConnected(true, true)) { return default_val; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(default_val); if (!GetRPC1()->CallBlocking("GetIntValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return default_val; } int32_t out = (int32_t)default_val; if (!returnData.Read(out)) { OVR_ASSERT(false); } return out; } double NetClient::GetNumberValue(VirtualHmdId hmd, const char* key, double default_val) { if (!IsConnected(true, true)) { return default_val; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(default_val); if (!GetRPC1()->CallBlocking("GetNumberValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return default_val; } double out = 0.; returnData.Read(out); return out; } int NetClient::GetNumberValues(VirtualHmdId hmd, const char* key, double* values, int num_vals) { if (!IsConnected(true, true)) { return 0; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); bsOut.Write(key); int32_t w = (int32_t)num_vals; bsOut.Write(w); if (!GetRPC1()->CallBlocking("GetNumberValues_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return 0; } int32_t out = 0; if (!returnData.Read(out)) { OVR_ASSERT(false); } OVR_ASSERT(out >= 0 && out <= num_vals); if (out < 0) { out = 0; } else if (out > num_vals) { out = num_vals; } for (int i = 0; i < out && i < num_vals; i++) { if (!returnData.Read(values[i])) { return i; } } return out; } bool NetClient::SetStringValue(VirtualHmdId hmd, const char* key, const char* val) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(val); if (!GetRPC1()->Signal("SetStringValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } bool NetClient::SetBoolValue(VirtualHmdId hmd, const char* key, bool val) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bsOut.Write(key); uint8_t b = val ? 1 : 0; bsOut.Write(b); if (!GetRPC1()->Signal("SetBoolValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } bool NetClient::SetIntValue(VirtualHmdId hmd, const char* key, int val) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bsOut.Write(key); int32_t w = (int32_t)val; bsOut.Write(w); if (!GetRPC1()->Signal("SetIntValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } bool NetClient::SetNumberValue(VirtualHmdId hmd, const char* key, double val) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bsOut.Write(key); bsOut.Write(val); if (!GetRPC1()->Signal("SetNumberValue_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } bool NetClient::SetNumberValues(VirtualHmdId hmd, const char* key, const double* vals, int num_vals) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bsOut.Write(key); int32_t w_count = (int32_t)num_vals; bsOut.Write(w_count); for (int i = 0; i < num_vals; i++) { bsOut.Write(vals[i]); } if (!GetRPC1()->Signal("SetNumberValues_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } int NetClient::Hmd_Detect() { if (!IsConnected(true, false)) { return 0; } // If using edge-triggered HMD counting, if (EdgeTriggeredHMDCount) { // Return the last update from the server return HMDCount; } // Otherwise: We need to ask the first time OVR::Net::BitStream bsOut, returnData; if (!GetRPC1()->CallBlocking("Hmd_Detect_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return 0; } int32_t out = 0; if (!returnData.Read(out)) { OVR_ASSERT(false); } HMDCount = out; EdgeTriggeredHMDCount = true; return out; } bool NetClient::Hmd_Create(int index, HMDNetworkInfo* netInfo) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut, returnData; int32_t w = (int32_t)index; bsOut.Write(w); // Need the Pid for driver mode pid_t pid = GetCurrentProcessId(); bsOut.Write(pid); if (!GetRPC1()->CallBlocking("Hmd_Create_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } return netInfo->Deserialize(&returnData); } bool NetClient::GetDriverMode(bool& driverInstalled, bool& compatMode, bool& hideDK1Mode) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(InvalidVirtualHmdId); if (!GetRPC1()->CallBlocking("GetDriverMode_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } int32_t w_driverInstalled = 0; int32_t w_compatMode = 0; int32_t w_hideDK1Mode = 0; returnData.Read(w_driverInstalled); returnData.Read(w_compatMode); if (!returnData.Read(w_hideDK1Mode)) { return false; } driverInstalled = w_driverInstalled != 0; compatMode = w_compatMode != 0; hideDK1Mode = w_hideDK1Mode != 0; return true; } bool NetClient::SetDriverMode(bool compatMode, bool hideDK1Mode) { if (!IsConnected(true, true)) { return false; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(InvalidVirtualHmdId); int32_t w_compatMode, w_hideDK1Mode; w_compatMode = compatMode ? 1 : 0; w_hideDK1Mode = hideDK1Mode ? 1 : 0; bsOut.Write(w_compatMode); bsOut.Write(w_hideDK1Mode); if (!GetRPC1()->CallBlocking("SetDriverMode_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } int32_t out = 0; if (!returnData.Read(out)) { OVR_ASSERT(false); return false; } return out != 0; } bool NetClient::Hmd_AttachToWindow(VirtualHmdId hmd, void* hWindow) { if (!IsConnected(false, false)) { return false; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); #ifdef OVR_OS_LINUX if (hWindow == NULL) { return false; } unsigned long hWinWord = *(unsigned long *)hWindow; #else UInt64 hWinWord = (UPInt)hWindow; #endif bsOut.Write(hWinWord); if (!GetRPC1()->CallBlocking("Hmd_AttachToWindow_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return false; } return true; } void NetClient::Hmd_Release(VirtualHmdId hmd) { if (!IsConnected(false, false)) { return; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); bool result = GetRPC1()->CallBlocking("Hmd_Release_1", &bsOut, GetSession()->GetConnectionAtIndex(0)); OVR_ASSERT_AND_UNUSED(result, result); } void NetClient::SetLastError(String str) { Hmd_GetLastError_Str = str; } // Last string is cached locally. const char* NetClient::Hmd_GetLastError(VirtualHmdId hmd) { if (hmd == InvalidVirtualHmdId || !IsConnected(false, false)) { return Hmd_GetLastError_Str.ToCStr(); } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); if (!GetRPC1()->CallBlocking("Hmd_GetLastError_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return Hmd_GetLastError_Str.ToCStr(); } if (!returnData.Read(Hmd_GetLastError_Str)) { OVR_ASSERT(false); } return Hmd_GetLastError_Str.ToCStr(); } // Fills in description about HMD; this is the same as filled in by ovrHmd_Create. // The actual descriptor is a par bool NetClient::Hmd_GetHmdInfo(VirtualHmdId hmd, HMDInfo* hmdInfo) { if (!IsConnected(false, false)) { return false; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); if (!GetRPC1()->CallBlocking("Hmd_GetHmdInfo_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } return NetSessionCommon::DeserializeHMDInfo(&returnData, hmdInfo); } //------------------------------------------------------------------------------------- unsigned int NetClient::Hmd_GetEnabledCaps(VirtualHmdId hmd) { if (!IsConnected(false, false)) { return 0; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); if (!GetRPC1()->CallBlocking("Hmd_GetEnabledCaps_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return 0; } uint32_t c = 0; if (!returnData.Read(c)) { OVR_ASSERT(false); } return c; } // Returns new caps after modification unsigned int NetClient::Hmd_SetEnabledCaps(VirtualHmdId hmd, unsigned int hmdCaps) { if (!IsConnected(false, false)) { return 0; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); uint32_t c = (uint32_t)hmdCaps; bsOut.Write(c); if (!GetRPC1()->CallBlocking("Hmd_SetEnabledCaps_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return 0; } c = 0; if (!returnData.Read(c)) { OVR_ASSERT(false); } return c; } //------------------------------------------------------------------------------------- // *** Tracking Setup bool NetClient::Hmd_ConfigureTracking(VirtualHmdId hmd, unsigned supportedCaps, unsigned requiredCaps) { if (!IsConnected(false, false)) { return false; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(hmd); uint32_t w_sc = supportedCaps; bsOut.Write(w_sc); uint32_t w_rc = requiredCaps; bsOut.Write(w_rc); if (!GetRPC1()->CallBlocking("Hmd_ConfigureTracking_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } uint8_t b; if (!returnData.Read(b)) { OVR_ASSERT(false); } return b != 0; } void NetClient::Hmd_ResetTracking(VirtualHmdId hmd) { if (!IsConnected(false, false)) { return; } OVR::Net::BitStream bsOut; bsOut.Write(hmd); if (!GetRPC1()->CallBlocking("Hmd_ResetTracking_1", &bsOut, GetSession()->GetConnectionAtIndex(0))) { return; } } bool NetClient::LatencyUtil_ProcessInputs(double startTestSeconds, unsigned char rgbColorOut[3]) { if (!IsConnected(false, false)) { return false; } if (!LatencyTesterAvailable) { return false; } OVR::Net::BitStream bsOut, returnData; bsOut.Write(startTestSeconds); if (!GetRPC1()->CallBlocking("LatencyUtil_ProcessInputs_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return false; } uint8_t u; returnData.Read(u); rgbColorOut[0] = u; returnData.Read(u); rgbColorOut[1] = u; if (!returnData.Read(u)) { return false; } rgbColorOut[2] = u; return true; } const char* NetClient::LatencyUtil_GetResultsString() { if (!IsConnected(false, false)) { return NULL; } OVR::Net::BitStream bsOut, returnData; if (!GetRPC1()->CallBlocking("LatencyUtil_GetResultsString_1", &bsOut, GetSession()->GetConnectionAtIndex(0), &returnData)) { return NULL; } if (!returnData.Read(LatencyUtil_GetResultsString_Str)) { OVR_ASSERT(false); } return LatencyUtil_GetResultsString_Str.ToCStr(); } bool NetClient::ShutdownServer() { if (!IsConnected(false, false)) { return false; } OVR::Net::BitStream bsOut; GetRPC1()->BroadcastSignal("Shutdown_1", &bsOut); return true; } //// Push Notifications: void NetClient::registerRPC() { #define RPC_REGISTER_SLOT(observerScope, functionName) \ observerScope.SetHandler(OVR::Net::Plugins::RPCSlot::FromMember(this)); pRPC->RegisterSlot(OVR_STRINGIZE(functionName), observerScope); // Register RPC functions: RPC_REGISTER_SLOT(InitialServerStateScope, InitialServerState_1); RPC_REGISTER_SLOT(LatencyTesterAvailableScope, LatencyTesterAvailable_1); RPC_REGISTER_SLOT(DefaultLogOutputScope, DefaultLogOutput_1); RPC_REGISTER_SLOT(HMDCountUpdateScope, HMDCountUpdate_1); } void NetClient::InitialServerState_1(BitStream* userData, ReceivePayload* pPayload) { LatencyTesterAvailable_1(userData, pPayload); } void NetClient::LatencyTesterAvailable_1(BitStream* userData, ReceivePayload* pPayload) { OVR_UNUSED(pPayload); uint8_t b = 0; if (!userData->Read(b)) { OVR_ASSERT(false); return; } LatencyTesterAvailable = (b != 0); } void NetClient::DefaultLogOutput_1(BitStream* userData, ReceivePayload* pPayload) { OVR_UNUSED(pPayload); String formattedText; LogMessageType messageType = Log_Text; // Will normally be overwritten below. userData->Read(messageType); if (userData->Read(formattedText)) { if (OVR::Log::GetGlobalLog()) { OVR::String logStr = "[From Service] "; logStr.AppendString(formattedText); OVR::Log::GetGlobalLog()->LogMessage(messageType, "%s", logStr.ToCStr()); } } } void NetClient::HMDCountUpdate_1(BitStream* userData, ReceivePayload* pPayload) { OVR_UNUSED(pPayload); int32_t hmdCount = 0; if (!userData->Read(hmdCount)) { OVR_ASSERT(false); return; } HMDCount = hmdCount; EdgeTriggeredHMDCount = true; } }} // namespace OVR::Service