/// <summary> /// Called when new data has arrived from a peer via the specified Link. /// </summary> /// <param name="link"></param> /// <param name="contract"></param> /// <param name="data"></param> /// <returns></returns> unsafe virtual protected VscResult ReceiveCallback(VscLink link, VscContract contract, VscUpdateData data) { { VscResult retRes; var node = VsConnectNode.GetInstanceFromLink(link); retRes = ShouldProcessCallback(node, link); if (!retRes.IsError()) { VscSchema schema = vsc_Api_V3_t.Contract_GetSchema(contract); var numSchemaFields = vsc_Api_V3_t.Schema_GetNumFields(schema); var localRole = vsc_Api_V3_t.Contract_GetLocalRole(contract); Check(localRole == VscRole.SENDER); Check(localRole == VscRole.SENDER); Check(schema != IntPtr.Zero); Check((IntPtr)vsc_Api_V3_t.UpdateData_GetSchema(data) == (IntPtr)schema); for (int fieldIndex = 0; fieldIndex < numSchemaFields; ++fieldIndex) { var field = vsc_Api_V3_t.Schema_GetField(schema, fieldIndex); var arraySize = vsc_Api_V3_t.Field_GetNumElements(field); Check(arraySize > 0); var vsVar = ToObject(vsc_Api_V3_t.Field_GetAppData(field)) as VSVarBase; if (vsVar != null && vsVar.Direction == VsVarDirection.Input) { double *fieldValue = (double *)vsc_Api_V3_t.UpdateData_GetFieldValue(data, fieldIndex); Check(fieldValue != null); if (vsVar.VariableType == typeof(double)) { ((VsConnectVar <double>)vsVar).Value = *fieldValue; } if (vsVar.VariableType == typeof(double[])) { double[] values = new double[arraySize]; for (int index = 0; index < arraySize; index++) { values[index] = fieldValue[index]; } ((VsConnectVar <double[]>)vsVar).Value = values; } } } retRes = VscResult.OK; } return(retRes); } }
/// <summary> /// Callback that informs us when a Link is connected. /// </summary> /// <param name="connectingLink"></param> /// <returns></returns> virtual protected VscResult LinkConnectCallback(VscLink connectingLink) { VscResult retRes = VscResult.OK; Log($"VS Connect Link connecting: {connectingLink.ptr.ToInt64():x}.\n"); VsConnectNode node = VsConnectNode.GetInstanceFromLink(connectingLink); retRes = ShouldProcessCallback(node, connectingLink); return(retRes); }
void ContractCanceledCallback(VscLink link, VscContract contract) { var tsMode = vsc_Api_V3_t.Contract_GetTsMode(contract); var localRole = vsc_Api_V3_t.Contract_GetLocalRole(contract); if (tsMode != VscTimeSyncMode.NONE && localRole == VscRole.RECEIVER) { // Our Incoming TS Contract is being canceled. Ensure // time dilation is set back to its original value: //auto unode = UVsConnectNode::GetInstanceFromLink(link); //auto uVsConnect = Cast<UVsConnect>(unode->GetOuter()); //auto worldSettings = uVsConnect->mWorld->GetWorldSettings(); //if ( worldSettings ) uVsConnect->RestoreOriginalTimeDilation(worldSettings); } }
static public VsConnectNode GetInstanceFromLink(VscLink link) { VsConnectNode retNode = null; var vscNode = vsc_Api_V3_t.Link_GetNode(link); if (vscNode != IntPtr.Zero) { var nodeAppData = vsc_Api_V3_t.Node_GetAppData(vscNode); retNode = VsUtility.ToObject(nodeAppData) as VsConnectNode; } return(retNode); }
virtual protected VscResult ShouldProcessCallback(VsConnectNode node, VscLink link) { VscResult retRes; if (node == null || link == IntPtr.Zero) { retRes = VscResult.ERROR_INVALID_PARAM; } else if (node.ShuttingDown) { retRes = VscResult.ERROR_UNAVAILABLE; } else { retRes = VscResult.OK; } return(retRes); }
/// <summary> /// Callback that informs us when a Link becomes disconnected. /// </summary> /// <param name="disconnectedLink"></param> virtual protected void LinkDisconnectCallback(VscLink link) { Log($"VS Connect Link disconnected: {link.ptr.ToInt64():x}.\n"); var contract = vsc_Api_V3_t.Link_GetTsContract(link); if (contract != IntPtr.Zero) { var localRole = vsc_Api_V3_t.Contract_GetLocalRole(contract); if (localRole == VscRole.RECEIVER) { // Our Incoming TS Contract is being canceled. Ensure // time dilation is set back to its original value: //var unode = UVsConnectNode::GetInstanceFromLink(link); //var uVsConnect = Cast<UVsConnect>(unode->GetOuter()); //var worldSettings = uVsConnect->mWorld->GetWorldSettings(); //if (worldSettings) uVsConnect->RestoreOriginalTimeDilation(worldSettings); } } }
/// <summary> /// Callback used by VS Connect to report log messages to this application. /// </summary> /// <param name="logLevel"></param> /// <param name="vscNode"></param> /// <param name="vscLink"></param> /// <param name="vscContract"></param> /// <param name="format"></param> /// <param name="argptr"></param> /// <returns></returns> protected unsafe int _VscLogCallback(VscLogLevel logLevel , VscNode vscNode , VscLink vscLink , VscContract vscContract , string format , IntPtr argptr) { string logMessage = $"VS Connect "; logMessage += logLevel == VscLogLevel.ERROR ? "ERROR" : (logLevel == VscLogLevel.WARNING ? "WARNING" : ""); logMessage += string.Format("N:{0:x} L:{1:x} C:{2:x}", vscNode.ptr.ToInt64(), vscLink.ptr.ToInt64(), vscContract.ptr.ToInt64()); int BUFF_SIZE = 1024; byte[] tempBuff = new byte[BUFF_SIZE]; int charsOutput2 = vsprintf(tempBuff, format, argptr); //check(charsOutput2 > 0 ); logMessage += System.Text.Encoding.UTF8.GetString(tempBuff, 0, charsOutput2); if (VscLogLevel.ERROR == logLevel) { LogError(logMessage); } else if (VscLogLevel.WARNING == logLevel) { LogWarning(logMessage); } else { Log(logMessage); } return(logMessage.Length); }
virtual protected void DisconnectAllLinks() { foreach (var node in mVscNodes) { Check(node != null); if (node.VscNode != IntPtr.Zero) { int numLinks = vsc_Api_V3_t.Node_GetNumLinks(node.VscNode); for (int i = 0; i < numLinks; ++i) { VscLink link = vsc_Api_V3_t.Node_GetLink(node.VscNode, i); Check(link != IntPtr.Zero); Check(vsc_Api_V3_t.Link_GetConnectionStatus(link) != VscConnectionStatus.UNCONNECTED); VscResult disconnectRes = vsc_Api_V3_t.Link_Disconnect(link); //check(!vsc_IsError(disconnectRes)); } } } bool done = false; float timeout = 5.0f; while (!done && timeout > 0) { done = true; // We're done unless we find a Node with Links that are not disconnected. foreach (var node in mVscNodes) { Check(node != null); if (node.VscNode != IntPtr.Zero) { vsc_Api_V3_t.Node_Service(node.VscNode, vsc_Api_V3_t.GetInvalidSimtime(), vsc_Api_V3_t.GetInvalidSimtime(), true); } } foreach (var node in mVscNodes) { Check(node != null); if (node.VscNode != IntPtr.Zero) { if (0 == vsc_Api_V3_t.Node_GetNumLinks(node.VscNode)) { } else { done = false; vsc_Api_V3_t.Node_Service(node.VscNode, vsc_Api_V3_t.GetInvalidSimtime(), vsc_Api_V3_t.GetInvalidSimtime(), true); } } } if (!done) { const float sleepTime = 0.100f; ///< Seconds to sleep during each iteration while disconnecting. System.Threading.Thread.Sleep((int)(1000 * sleepTime)); timeout -= sleepTime; } } }
virtual protected VscResult ProcessContractRequest(VscLink link, VscContract contract) { VscResult retRes; var node = VsConnectNode.GetInstanceFromLink(link); retRes = ShouldProcessCallback(node, link); if (retRes.IsError()) { LogWarning("VS Connect - Ignoring Contract Request."); } else { var tsMode = vsc_Api_V3_t.Contract_GetTsMode(contract); if (vsc_Api_V3_t.Contract_GetLocalRole(contract) == VscRole.RECEIVER && VS_TS_REQUIRE_FIXED_FRAME_RATE == 1 && tsMode != VscTimeSyncMode.NONE //&& !GEngine->bUseFixedFrameRate ) { LogError("VS Connect - Rejecting Incoming Contract with Time Sync (TS) enabled. Unity must be operating in fixed framerate mode to use TS."); retRes = VscResult.ERROR_UNSUPPORTED; } else { VscSchema schema = vsc_Api_V3_t.Contract_GetSchema(contract); var numSchemaFields = vsc_Api_V3_t.Schema_GetNumFields(schema); var localRole = vsc_Api_V3_t.Contract_GetLocalRole(contract); Check(schema != IntPtr.Zero); if (localRole == VscRole.SENDER) { Log($"VS Connect outgoing Contract request received: {contract.ptr.ToInt64():x}.\n"); } else if (localRole == VscRole.RECEIVER) { Log($"VS Connect outgoing Contract request received: {contract.ptr.ToInt64():x}.\n"); } else { LogWarning($"VS Connect outgoing Contract request received: {contract.ptr.ToInt64():x}.\n"); } for (int fieldIndex = 0; fieldIndex < numSchemaFields; ++fieldIndex) { var field = vsc_Api_V3_t.Schema_GetField(schema, fieldIndex); string objectName = ""; objectName = (vsc_Api_V3_t.Field_GetObjectName(field)).ToString(); var solver = GetObject(objectName); if (solver == null) { LogWarning($"Unable to locate VS Connect Object {objectName} for Contract request {contract.ptr.ToInt64():x}."); } else { string fieldName = vsc_Api_V3_t.Field_GetPropertyName(field); string fieldParams = vsc_Api_V3_t.Field_GetParams(field); var arraySize = vsc_Api_V3_t.Field_GetNumElements(field); var vscDataType = vsc_Api_V3_t.Field_GetDataType(field); var dataSize = vsc_Api_V3_t.Field_GetElementSizeInBits(field); fieldName.TrimAndUnquoteInPlace(); fieldParams.TrimAndUnquoteInPlace(); VSVarBase vsVar = null; if (localRole == VscRole.SENDER) { if (vscDataType == VscDataType.FLOAT && dataSize == 64 && arraySize == 1) { vsVar = new VsConnectVar <double>(solver, fieldName, solver.GetDoubleFuncs[fieldName]); } if (vscDataType == VscDataType.FLOAT && dataSize == 64 && arraySize > 1) { vsVar = new VsConnectVar <double[]>(solver, fieldName, solver.GetDoubleArrayFuncs[fieldName]); } if (vsVar == null) { LogWarning($"VS Connect - Contract request %llx, Object {objectName}: No output Property {fieldName} with params {fieldParams}) for ."); } } else if (localRole == VscRole.RECEIVER) { if (vscDataType == VscDataType.FLOAT && dataSize == 64 && arraySize == 1) { vsVar = new VsConnectVar <double>(solver, fieldName, solver.SetDoubleActions[fieldName]); } if (vscDataType == VscDataType.FLOAT && dataSize == 64 && arraySize > 1) { vsVar = new VsConnectVar <double[]>(solver, fieldName, solver.SetDoubleArrayActions[fieldName]); } if (vsVar == null) { LogWarning($"VS Connect - Contract request {contract.ptr.ToInt64():x}, Object {objectName}: No input Property {fieldName} with params {fieldParams}) for ."); } } else { vsVar = null; LogError(("VS Connect - Unhandled VS Connect Role.\n")); } Check(vsc_Api_V3_t.Field_GetAppData(field) == IntPtr.Zero); if (vsVar != null) { // Store the handle in the schema: vsc_Api_V3_t.Field_SetAppData(field, ToPtr(vsVar)); } } } Check(vsc_Api_V3_t.Contract_GetAppData(contract) == IntPtr.Zero); vsc_Api_V3_t.Contract_SetAppData(contract, ToPtr(Singleton)); vsc_Api_V3_t.Contract_SetPreDestroyCallback(contract, SPreDestroyContract_); retRes = VscResult.OK; } } return(retRes); }
void PingCallback(VscNode vscNode, VscLink link) { /* Note that Ping Statistics include system times from both the Requester * (us) and the Responder (our peer). When both peers are running on the same * system and within the same session, they MIGHT both use a common reference * clock, and therefore the "system time" of the two peers can be directly * compared with one another. HOWEVER, this is not guaranteed on on all * operating systems, or even on different versions of the same operating * system. * * To be safe, you should always assume that the system clocks of two peers do * not refer to the same underlying reference clock, and can therefore NOT be * compared with one another. */ /// Peer's system time when VS Connect received our Ping request. double peerReceiveTime = vsc_Api_V3_t.Link_PingGetStat(link , PingMilestone.RESPONDER_REQUEST_RECEIVED , TimeMomentAttr.SYSTEM_TIME); /// Peer's system time when it processed our request inside of its /// Node_Service() call. double peerProcessTime = vsc_Api_V3_t.Link_PingGetStat(link , PingMilestone.RESPONDER_REQUEST_PROCESSED , TimeMomentAttr.SYSTEM_TIME); /// Peer's system time when it queued the Ping reponse. This happens during /// the the next call to Node_Service() after the Ping request is processed. double peerResponseTime = vsc_Api_V3_t.Link_PingGetStat(link , PingMilestone.RESPONDER_RESPONSE_QUEUED , TimeMomentAttr.SYSTEM_TIME); /// The deduced period at which the Peer is calling Node_Service(). double peerApparentSericePeriod = peerResponseTime - peerProcessTime; /// The time it took for the peer to process and respond to our request. double peerTotalProcessingTime = peerResponseTime - peerReceiveTime; /// The time that VS Connect queued our initial request for transmission. double ourRequestTransmitTime = vsc_Api_V3_t.Link_PingGetStat(link , PingMilestone.REQUESTER_REQUEST_QUEUED , TimeMomentAttr.SYSTEM_TIME); /// The time that VS Connect receved the peer's response. double ourResponseReceiveTime = vsc_Api_V3_t.Link_PingGetStat(link , PingMilestone.REQUESTER_RESPONSE_RECEIVED , TimeMomentAttr.SYSTEM_TIME); /// Time between when we sent the request and when we received the response. double totalRoundTripTime = ourResponseReceiveTime - ourRequestTransmitTime; /** This value will vary dramatically in this example due to the slow * service frequency of both peers. The higher the service frequency of the * peers, the lower and more stable this value will be, and the closer it will * be to the actual network transport latency. */ double apparentTotalRoundNetworkLatency = totalRoundTripTime - peerTotalProcessingTime; // We set manual ping's appData to 1 so we can tell the difference between // automatic/periodic ping requests and those that we initiated manually. bool isManualPing = (vsc_Api_V3_t.Link_PingGetAppData(link).ToInt64() == 1); /* * printf( "\nPing Results%s: Peer update period: %g" * "\tRound-trip communication time: %g" * , isManualPing? " (manual)" : " (automatic)" * , peerApparentSericePeriod * , apparentTotalRoundNetworkLatency ); */ }
/// <summary> /// Called by VS Connect when it needs new data to send. /// </summary> /// <param name="link"></param> /// <param name="contract"></param> /// <param name="out_data"></param> /// <returns></returns> unsafe virtual protected VscResult SendCallback(VscLink link, VscContract contract, VscUpdateData out_data) { VscResult retRes; var node = VsConnectNode.GetInstanceFromLink(link); retRes = ShouldProcessCallback(node, link); if (!retRes.IsError()) { VscSchema schema = vsc_Api_V3_t.Contract_GetSchema(contract); var numSchemaFields = vsc_Api_V3_t.Schema_GetNumFields(schema); var localRole = vsc_Api_V3_t.Contract_GetLocalRole(contract); Check(localRole == VscRole.SENDER); Check(schema != IntPtr.Zero); Check((IntPtr)vsc_Api_V3_t.UpdateData_GetSchema(out_data) == (IntPtr)schema); retRes = VscResult.OK; for (int fieldIndex = 0; VscResult.OK == retRes && fieldIndex < numSchemaFields; ++fieldIndex) { var field = vsc_Api_V3_t.Schema_GetField(schema, fieldIndex); double *fieldValue = (double *)vsc_Api_V3_t.UpdateData_GetFieldValue(out_data, fieldIndex); var vsVar = ToObject(vsc_Api_V3_t.Field_GetAppData(field)) as VSVarBase; Check(fieldValue != null); if (vsVar == null) { vsc_Api_V3_t.InvalidateDouble(fieldValue); } else { var arraySize = vsc_Api_V3_t.Field_GetNumElements(field); var vscDataType = vsc_Api_V3_t.Field_GetDataType(field); var dataSize = vsc_Api_V3_t.Field_GetElementSizeInBits(field); Check(vscDataType == VscDataType.FLOAT); Check(64 == dataSize); Check(arraySize > 0); if (vsVar.VariableType == typeof(double)) { *fieldValue = ((VsConnectVar <double>)vsVar).Value; } if (vsVar.VariableType == typeof(double[])) { int index = 0; foreach (var value in ((VsConnectVar <double[]>)vsVar).Value) { fieldValue[index] = value; index++; } } } } } return(retRes); }