public ElementFrame(GenericX xform, CompressedElement compXform, bool hasChanged, TransformElement transformElement) { this.xform = xform; this.compXform = compXform; this.hasChanged = hasChanged; this.transformElement = transformElement; }
/// <summary> /// Extrapolate is used when the buffer is empty. /// </summary> public void OnExtrapolate(Frame targFr, Frame currFr, int extrapolationCount, bool svrWaitingForTeleportConfirm) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } bool currIsNull = te.frames[currFr.frameid].xform.type == XType.NULL; // repeat teleportoverride if this is server and is waiting for a teleport confirm bool svrWaiting = svrWaitingForTeleportConfirm && te.teleportOverride; // || currFr.updateType.IsTeleport() && te.teleportOverride; // Don't extrapolate if this was a teleport or if we are exceeded the max number of sequential extrapolates bool dontExtrapolate = (currFr.updateType.IsTeleport() || extrapolationCount >= te.maxExtrapolates); te.frames[targFr.frameid].xform = svrWaiting ? te.lastSentTransform : dontExtrapolate ? currIsNull ? te.Localized : te.frames[currFr.frameid].xform : // If the current frame we are extrapolating was a teleport... just copy te.Extrapolate(); te.frames[targFr.frameid].compXform = svrWaiting ? te.lastSentCompressed : dontExtrapolate ? te.frames[currFr.frameid].compXform : // If the current frame we are extrapolating was a teleport... just copy te.Compress(te.frames[targFr.frameid].xform); } }
public void OnStartInterpolate(Frame frame, bool lateArrival = false, bool midTeleport = false) { // Don't apply the transform for frame offtick updates. Those are for teleports and weapon fire. //TODO: Likely no longer needed, frame 0 shoud only fire onrcv //if (frame.frameid == 0)// && !frame.updateType.IsTeleport()) // return; for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } // Don't overwrite mid interpolation if this is a teleport override element, and we are mid teleport. if (lateArrival && midTeleport && te.teleportOverride) { continue; } // Don't modify elements with late arriving data if it is null. if (lateArrival && te.frames[frame.frameid].xform.type == XType.NULL) { DebugX.Log(!DebugX.logInfo ? null : (Time.time + " <b>Null Late Arrival - NOTE if you keep seeing this davin - remove this test otherwise </b> " + te.snapshotFrameId + " " + te.targetFrameId)); continue; } te.Snapshot(frame, lateArrival, midTeleport); } }
/// <summary> /// Make all TE names uqique... with a priority of changing the newly created one first. /// </summary> public static void MakeAllNamesUnique(GameObject go, TransformElement targetTe = null) { uniqueNames.Clear(); // First add the selected gameobjects names to list - all but the last (should be the last added) INSTTransformElement[] iTransElement = go.GetComponents <INSTTransformElement>(); // Determine if the last element has the name Unnamed... and presume it is a new item if so. TransformElement newElement = (targetTe != null) ? targetTe : (iTransElement.Length > 0 && iTransElement[iTransElement.Length - 1].TransElement.name == "Unnamed") ? iTransElement[iTransElement.Length - 1].TransElement : null; string holdname = (newElement != null) ? newElement.name : ""; // Renmae it temporarily to make sure it doesn't conflict already. if (newElement != null) { newElement.name = "CRAZYPLACEHOLDERNAME"; } // Then add all te names to the list - making them unique if any somehow failed to be iTransElement = go.transform.root.GetComponentsInChildren <INSTTransformElement>(true); for (int i = 0; i < iTransElement.Length; i++) { TransformElement te = iTransElement[i].TransElement; // Disallow empty names if (te.name == "") { te.name = "Unnamed"; } // Add zeros until the name is unique while (uniqueNames.Contains(te.name)) { te.name += "0"; } uniqueNames.Add(te.name); } // Name the new element back to Unnamed, and add zeros until it is unique if (newElement != null) { newElement.name = holdname; while (uniqueNames.Contains(newElement.name)) { newElement.name += "0"; } } }
public void OnSnapshotToRewind(Frame frame) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.history[frame.frameid] = te.Localized; } }
public void OnRewindGhostsToFrame(Frame frame) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.Apply(te.frames[frame.frameid].xform, te.ghostGO); } }
public void NSTBitstreamMirrorFirst(Frame frame, ref UdpBitStream outstream, bool waitingForTeleportConfirm) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.MirrorToClients(ref outstream, frame, te.frames[frame.frameid].hasChanged); // masks[eid].GetBitInMask(frame.frameid)); } }
public void OnInterpolate(float t) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.UpdateInterpolation(t); } }
// callback from NST, extract transform elements public void NSTBitstreamIncomingFirst(Frame frame, Frame currFrame, ref UdpBitStream bitstream, bool isServer) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.frames[frame.frameid].hasChanged = te.Read(ref bitstream, frame, currFrame); } }
public void OnCreateGhost(GameObject srcGO, GameObject ghostGO) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } if (srcGO == te.gameobject) { te.ghostGO = ghostGO; } } }
public void NSTBitstreamOutgoingFirst(Frame frame, ref UdpBitStream bitstream) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } te.Write(ref bitstream, frame); //// Write to the local buffer //te.frames[frame.frameid].xform = te.Localized; } }
/// <summary> /// Apply all of the current transforms to this frames stored transforms. /// </summary> public void CaptureCurrentTransforms() { updateType = UpdateType.Teleport; rootBitCullLevel = BitCullingLevel.NoCulling; RootPos = nst.cachedTransform.position; TransformElement[] tes = ee.transformElements; int count = tes.Length; for (int eid = 0; eid < count; eid++) { TransformElement te = tes[eid]; te.frames[frameid].xform = te.Localized; te.frames[frameid].compXform = te.Compress(); } }
/// <summary> /// Teleport all elements that are flagged with teleportOverride = true; /// </summary> public void OnRcvSvrTeleportCmd(Frame frame) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } // TODO: this likely is only wired to work correctly with offtick if (te.teleportOverride) { te.Teleport(frame); } } }
//public void OnSvrTeleportCmd() //{ // for (int eid = 0; eid < elementCount; ++eid) // { // TransformElement te = transformElements[eid]; // if (te.teleportOverride) // te.Teleport(); // } //} public void OnTeleportApply(Frame frame) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } //TODO this should be checking for the elements mask? // TODO: Uncertain about this check for null - should be testing for whether or not this element has any info to teleport with. if (te.teleportOverride) { te.Teleport(frame); } } }
/// <summary> /// If a Rewind request has been made, this callback interface is called on all registered elements. Each element will populate its history[0] frame with the resuts of the requested rewind time. /// If applyToGhost is true, it will also apply its rewound result to its element on the rewindGhost for this NST. /// </summary> public void OnRewind(HistoryFrame fe, int startFrameid, int endFrameId, float timeBeforeSnapshot, float remainder, bool applyToGhost) { for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } //TODO: this needs to slerp for rotation types te.history[frameCount] = (timeBeforeSnapshot > 0) ? Vector3.Lerp(te.history[startFrameid], te.history[endFrameId], remainder) : Vector3.Lerp(te.history[startFrameid], te.Localized, -remainder); if (applyToGhost) { te.Apply(te.history[frameCount], te.ghostGO); } } }
public NSTElementsEngine Initialize() { if (initialized) { return(this); } initialized = true; // This is redundant with the base class, but with initialize in Awake() rather than NSTAwake not assurance currently that it is set. nst = GetComponent <NetworkSyncTransform>(); frameCount = 60 / nst.sendEveryXTick; // Collect all of the transform elements INSTTransformElement[] iTransElement = GetComponentsInChildren <INSTTransformElement>(true); elementCount = iTransElement.Length; elementIdLookup = new Dictionary <string, int>(elementCount); elementLookup = new Dictionary <string, TransformElement>(elementCount); transformElements = new TransformElement[elementCount]; cache_elementIsEnabled = new bool[elementCount]; for (int i = 0; i < elementCount; ++i) { TransformElement te = iTransElement[i].TransElement; cache_elementIsEnabled[i] = te.sendCullMask != 0 && te.keyRate != 0 && te.crusher.Enabled; if (elementIdLookup.ContainsKey(te.name)) { DebugX.LogError(!DebugX.logErrors ? null : ("Multiple child elements with the same name on '" + nst.name + "'. Check the names of Rotation and Positon elements for any repeats and be sure they all have unique names.")); } else { elementIdLookup.Add(te.name, i); elementLookup.Add(te.name, te); } //// Make note of which of the transforms belongs to the NST root rotation //if (System.Object.ReferenceEquals(te, nst.rootRotationElement)) //{ // Debug.Log("ROOT NST ROTATION FOUND"); // //transformElements[0] = transformElements[i]; //} transformElements[i] = te; transformElements[i].index = i; if (transformElements[i].gameobject == null) { transformElements[i].gameobject = iTransElement[i].SrcGameObject; } transformElements[i].Initialize(nst); //Debug.Log(nst.rootRotationElement.frames[0]); //// TODO: Questionable and stupid hack that replaces NST rootRotationElement with the found interface version (should be the same but arent) //if (iTransElement[i] is NetworkSyncTransform) // nst.rootRotationElement = transformElements[i] as RotationElement; } // init the list //history = new GenericX[frameCount + 1][]; //for (int frameid = 0; frameid < history.Length; frameid++) //{ // history[frameid] = new GenericX[transformElements.Length]; // for (int elementid = 0; elementid < transformElements.Length; elementid++) // { // history[frameid][elementid] = new GenericX(); // } //} //int numbOfElements = transformElements.Length; //elements = new List<XElement>[frameCount + 1]; //for (int fid = 0; fid < elements.Length; fid++) //{ // elements[fid] = new List<XElement>(numbOfElements); // List<XElement> frameElements = elements[fid]; // for (int eid = 0; eid < numbOfElements; eid++) // { // frameElements.Add(new XElement( // transformElements[eid].Localized, // transformElements[eid].Compress(), // false, // transformElements[eid] // )); // } //} //elements = new List<XElement>(numbOfElements); return(this); }
/// <summary> /// Unlike Extrapolate - Reconstruct is used when the buffer isn't empty, but rather we are dealing with a lost packet while there is a future frame in the buffer. /// </summary> public void OnReconstructMissing(Frame nextFrame, Frame currentFrame, Frame nextValidFrame, float t, bool svrWaitingForTeleportConfirm) { //List<XElement> currFrameElements = elements[currentFrame.frameid]; //List<XElement> nextFrameElements = elements[nextFrame.frameid]; //List<XElement> nextValidFrameElements = elements[nextValidFrame.frameid]; // Reconstruct missing frames for (int eid = 0; eid < elementCount; ++eid) { TransformElement te = transformElements[eid]; if (!cache_elementIsEnabled[eid]) { continue; } TransformElement.ElementFrame ce = te.frames[currentFrame.frameid]; // currFrameElements[eid]; TransformElement.ElementFrame ne = te.frames[nextFrame.frameid]; // nextFrameElements[eid]; TransformElement.ElementFrame nve = te.frames[nextValidFrame.frameid]; //TODO are these if's needed for the null checking? Keep an eye on this. // Eliminate any Null genericX values = they indicate no changes if (ce.xform.type == XType.NULL) { ce.xform = te.Localized; ce.compXform = te.Compress(ce.xform); Debug.Log("Current element is null"); } if (nve.xform.type == XType.NULL) { nve.xform = ce.xform; //Debug.LogError("nextvalid element is null"); } // If server his holding for teleport confirm, keep using the same teleport value if (svrWaitingForTeleportConfirm && te.teleportOverride) { ne.xform = te.lastSentTransform; ne.compXform = te.lastSentCompressed; } // There is a future frame to use as a guess target else if (nve.xform.type != XType.NULL) // nst.buffer.masks[eid].GetBitInMask(nextValidFrame.frameid)) { ne.xform = te.Lerp(ce.xform, nve.xform, t); ne.compXform = te.Compress(ne.xform); } // There is no future frame. else { if (ce.xform.type == XType.NULL) { Debug.Log("Houston we have a null here."); } ne.xform = ce.xform; ne.compXform = ce.compXform; } } }
public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(r, label, property); property.serializedObject.ApplyModifiedProperties(); property.serializedObject.Update(); var par = PropertyDrawerUtility.GetParent(property); GameObject parGO; // the parent may be an NST or a TransformElement if (par is NSTElementComponent) { parGO = (par as NSTElementComponent).gameObject; } else { parGO = (par as NetworkSyncTransform).gameObject; } TransformElement te = PropertyDrawerUtility.GetActualObjectForSerializedProperty <TransformElement>(fieldInfo, property); name = property.FindPropertyRelative("name"); isRoot = property.FindPropertyRelative("isRoot"); keyRate = property.FindPropertyRelative("keyRate"); sendCullMask = property.FindPropertyRelative("sendCullMask"); gameobject = property.FindPropertyRelative("gameobject"); extrapolation = property.FindPropertyRelative("extrapolation"); maxExtrapolates = property.FindPropertyRelative("maxExtrapolates"); teleportOverride = property.FindPropertyRelative("teleportOverride"); isPos = (te is IPositionElement); isRot = (te is IRotationElement); string typeLabel = (isPos) ? "Position" : (isRot) ? "Rotation" : "Scale"; margin = 4; realwidth = r.width + 16 - 4; colwidths = realwidth / 4f; colwidths = Mathf.Max(colwidths, 65); // limit the smallest size so things like sliders aren't shrunk too small to draw. currentLine = r.yMin + margin * 2; Color headerblockcolor = (isPos ? positionHeaderBarColor : isRot ? rotationHeaderBarColor : scaleHeaderBarColor); if (!isRoot.boolValue) { EditorGUI.DrawRect(new Rect(margin + 3, r.yMin + 2 + 2, realwidth - 6, LINEHEIGHT + 8), headerblockcolor); } savedIndentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; if (!isRoot.boolValue) { string headerLabel = typeLabel + " Element"; EditorGUI.LabelField(new Rect(r.xMin, currentLine, colwidths * 4, LINEHEIGHT), new GUIContent(headerLabel), "WhiteBoldLabel"); NSTElementComponentEditor.MakeAllNamesUnique(parGO, te); EditorGUI.PropertyField(new Rect(r.xMin, currentLine, r.width - 4, LINEHEIGHT), name, new GUIContent(" ")); currentLine += LINEHEIGHT + 8; } // The only element that will be found on the root (the actual NST component) is rotation else { EditorGUI.LabelField(new Rect(r.xMin, currentLine, r.width, LINEHEIGHT), new GUIContent("Root Rotation Updates"), "BoldLabel"); currentLine += LINEHEIGHT + 4; } EditorGUI.indentLevel = 0; // Section for Send Culling enum flags left = 13; realwidth -= 16; sendCullMask.intValue = System.Convert.ToInt32(EditorGUI.EnumMaskField(new Rect(left, currentLine, realwidth, LINEHEIGHT), new GUIContent("Send On Events:"), (SendCullMask)sendCullMask.intValue)); currentLine += LINEHEIGHT + 4; if (!isRoot.boolValue) { EditorGUI.PropertyField(new Rect(left, currentLine, realwidth, LINEHEIGHT), gameobject, new GUIContent("GameObject:")); currentLine += LINEHEIGHT + 4; } if (((SendCullMask)sendCullMask.intValue).EveryTick() == false) { EditorGUI.PropertyField(new Rect(left, currentLine, realwidth, LINEHEIGHT), keyRate, new GUIContent("Key Every:")); currentLine += LINEHEIGHT + 2; } if (keyRate.intValue == 0 && sendCullMask.intValue == 0) { //noUpdates = true; EditorGUI.HelpBox(new Rect(left, currentLine, realwidth, 48), "Element Disabled. Select one or more 'Send On Events' event to trigger on, and/or set Key Every to a number greater than 0.", MessageType.Warning); currentLine += 50; property.serializedObject.ApplyModifiedProperties(); return; } else { //noUpdates = false; EditorGUI.PropertyField(new Rect(left, currentLine, realwidth, LINEHEIGHT), extrapolation, new GUIContent("Extrapolation:")); currentLine += LINEHEIGHT + 2; EditorGUI.PropertyField(new Rect(left, currentLine, realwidth, LINEHEIGHT), maxExtrapolates, new GUIContent("Max Extrapolations:")); currentLine += LINEHEIGHT + 2; EditorGUI.PropertyField(new Rect(left, currentLine, realwidth, LINEHEIGHT), teleportOverride, new GUIContent("Teleport Override:")); currentLine += LINEHEIGHT + 2; } property.serializedObject.ApplyModifiedProperties(); property.serializedObject.Update(); SerializedProperty crusher = property.FindPropertyRelative("crusher"); float ch = EditorGUI.GetPropertyHeight(crusher); EditorGUI.PropertyField(new Rect(r.xMin, currentLine - 2, r.width, ch), crusher); currentLine += ch; property.serializedObject.ApplyModifiedProperties(); SerializedProperty drawerHeight = property.FindPropertyRelative("drawerHeight"); // revert to original indent level. EditorGUI.indentLevel = savedIndentLevel; // Record the height of this instance of drawer drawerHeight.floatValue = currentLine - r.yMin; EditorGUI.EndProperty(); }