/// <summary> /// Ensure all required dependencies are added for this NST to work. Can be called often in edit mode, and should be. /// </summary> /// <param name="nst"></param> /// <param name="silence"></param> public static void EnsureAllNSTDependencies(this NetworkSyncTransform nst, SerializedObject serializedObject, bool silence = false) { EnsureSceneNetLibDependencies(); if (Application.isPlaying) { return; } // If user tried to put NST where it shouldn't be... remove it and all of the required components it added. if (nst.transform.parent != null) { XDebug.LogError("NetworkSyncTransform must be on the root of an prefab object."); nst.nstElementsEngine = nst.transform.GetComponent <NSTElementsEngine>(); NSTNetAdapter.RemoveAdapter(nst); Object.DestroyImmediate(nst); if (nst.nstElementsEngine != null) { Object.DestroyImmediate(nst.nstElementsEngine); EditorUtility.SetDirty(nst.gameObject); } return; } nst.nstElementsEngine = NSTElementsEngine.EnsureExistsOnRoot(nst.transform, false); nst.na = EditorUtils.EnsureRootComponentExists <NSTNetAdapter>(nst.gameObject, false); //AddRewindEngine(nst); //// Add this NST to the prefab spawn list (and as player prefab if none exists yet) as an idiot prevention NSTNetAdapter.AddAsRegisteredPrefab(nst.gameObject, true, silence); return; }
public override void OnInspectorGUI() { //serializedObject.Update(); base.OnInspectorGUI(); //SerializedProperty test = serializedObject.FindProperty("test"); NetworkSyncTransform nst = (NetworkSyncTransform)target; //LayerMask tempMask = EditorGUILayout.MaskField(InternalEditorUtility.LayerMaskToConcatenatedLayersMask(myLayerMask), InternalEditorUtility.layers); //myLayerMask = (int)InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(tempMask); // EditorGUILayout.PropertyField(posSendType); // EditorGUILayout.PropertyField(cullUpperBits); // if (cullUpperBits.boolValue == true) // { // EditorGUILayout.PropertyField(sequentialKeys); // EditorGUILayout.PropertyField(keyRate); // } // EditorGUILayout.PropertyField(rotationElements, true); // Make sure the object is active, to prevent users from spawning inactive gameobjects (which will break things) if (!nst.gameObject.activeSelf) // && AssetDatabase.Contains(target)) { Debug.LogWarning("Prefabs with NetworkSyncTransform on them MUST be enabled. If you are trying to disable this so it isn't in your scene when you test it, no worries - NST destroys all scene objects with the NST component at startup."); nst.gameObject.SetActive(true); } //serializedObject.ApplyModifiedProperties(); }
/// <summary> /// Reads update headers for each NST frame update in the incoming bitstream, and passes the bitstream to that NST to read out its /// update information. /// </summary> /// <param name="mirror">True if this is the server, and this is the incoming bitstream. Tells the server that the outstream /// needs to be populated for retransmission to all clients. Also false if this is the server running its own outgoing update.</param> public static void ReceiveUpdate(ref UdpBitStream bitstream, ref UdpBitStream outstream, bool mirror, int senderId) { // Create a new bitstream to ensure ptr is at 0. Same data as master though. bitstream.ptr = 0; int frameid = bitstream.ReadInt(6); if (mirror) { outstream.WriteInt(frameid, 6); } bool isOfftick = frameid == 60; // remove this safety once working //TEST int safety = 0; UpdateType updateType; do { safety++; BandwidthUsage.Start(ref bitstream, BandwidthLogType.UpdateRcv); //stop looking when header is EOS bool notEOS = bitstream.ReadBool(); int mirrorUpdateStartPtr = outstream.ptr; BandwidthUsage.AddUsage(ref bitstream, "NotEOS"); if (mirror) { outstream.WriteBool(notEOS); } if (!notEOS) { break; } // First three bits are the msgtype //TODO this might only need to be two updateType = (UpdateType)bitstream.ReadInt(3); BandwidthUsage.AddUsage(ref bitstream, "UpdateType"); int updateBitstreamPos = outstream.ptr; if (mirror) { outstream.WriteInt((int)updateType, 3); } // Next variable is the NstId - get it to know where to send the rest of the bitstream uint nstid = bitstream.ReadUInt(HeaderSettings.single.BitsForNstId); BandwidthUsage.AddUsage(ref bitstream, "NstId"); if (mirror) { outstream.WriteUInt(nstid, HeaderSettings.single.BitsForNstId); } lastNST = NSTTools.GetNstFromId(nstid); BandwidthUsage.SetName(lastNST); int updatelength = bitstream.ReadInt(UPDATELENGTH_BYTE_COUNT_SIZE); if (mirror) { outstream.WriteInt(updatelength, UPDATELENGTH_BYTE_COUNT_SIZE); } BandwidthUsage.AddUsage(ref bitstream, "DataLength"); //Note the starting pos in stream int bodyPtr = bitstream.ptr; // The start pos for modifying update lenght for mirror int mirrorBodyPtr = outstream.ptr; // This mising NST handler is NOT FULLY TESTED. Uses the updatelength value to jump ahead in the bitstream if the NST it is // addressed to doesn't exist for some reason. if (lastNST == null) { //DebugX.LogWarning(!DebugX.logWarnings ? null : DebugX.Log( ("Message for an NST Object " + nstid + " arrived but that object does not exist. (yet/anymore?) This is normal during startup and shutdown.")); // Forward to the next update start in the incoming stream. bitstream.ptr = bodyPtr + (updatelength << 3); // rewind to the EOS marker and pretend this arrival never occured for the outgoing mirror stream. outstream.ptr = mirrorUpdateStartPtr; continue; } // Tell this nst to read its mail. updateType may get modified by server receive for things like teleport. Frame frame = lastNST.ReadUpdate(ref bitstream, ref outstream, frameid, isOfftick, updateType, updatelength, mirror); updateType = frame.updateType; // overwrite the updateType of the server outgoing in case it has changed. if (mirror) { outstream.WriteIntAtPos((int)updateType, 3, updateBitstreamPos); } //Advance ptr to next update in stream by force, in case the last update wasn't read for any reason (such as the NST leaving the game) bitstream.ptr = bodyPtr + (updatelength << 3); // write the update byte length for the mirror (not the same value as the incoming due to server side adjustments) if (mirror) { int holdPos = outstream.ptr; outstream.ptr = mirrorBodyPtr - UPDATELENGTH_BYTE_COUNT_SIZE; // get the bytesused rounded up. int bytes = ((holdPos - mirrorBodyPtr) >> 3) + (((holdPos - mirrorBodyPtr) % 8 == 0) ? 0 : 1); outstream.WriteInt(bytes, UPDATELENGTH_BYTE_COUNT_SIZE); outstream.ptr = mirrorBodyPtr + (bytes << 3); } } while (safety < 100); /// NST updates are finished - any data to append to the master update can go here IntegrityCheck.ReadCheck(ref bitstream, ref outstream, "End of All Update Reads", mirror); MasterRTT.Rcv(ref bitstream, ref outstream, mirror, senderId); BandwidthUsage.AddUsage(ref bitstream, "RTT checks"); // Very last thing... report the bits that were used. This is conditional to the editor only BandwidthUsage.ReportMasterBits(ref bitstream, BandwidthLogType.MasterIn); }
/// <summary> /// Get the RTT to the player who owns this NST /// </summary> public static float GetRTT(NetworkSyncTransform nstOfOwner) { return(nstOfOwner.na.GetRTT()); }
public static float GetRTT(NetworkSyncTransform nst) { int id = nst.na.ClientId; return(RTT.ContainsKey(id) ? RTT[id] : 0); }
void Reset() { nst = transform.root.GetComponent<NetworkSyncTransform>(); if (nst == null) nst = transform.root.gameObject.AddComponent<NetworkSyncTransform>(); }
public virtual void Initialize(NetworkSyncTransform _nst) { nst = _nst; nstElementsEngine = nst.nstElementsEngine; }
public override void Initialize(NetworkSyncTransform _nst) { base.Initialize(_nst); target = Localized; snapshot = Localized; // move all of the inspector xyz values into arrays for easy looping xyzBits = new int[3]; xyzBits[0] = xBits; xyzBits[1] = yBits; xyzBits[2] = zBits; xyzLimit = new bool[3]; xyzLimit[0] = xLimitRange; xyzLimit[1] = yLimitRange; xyzLimit[2] = zLimitRange; xyzMin = new float[3]; xyzMin[0] = xMinValue; xyzMin[1] = yMinValue; xyzMin[2] = zMinValue; xyzMax = new float[3]; xyzMax[0] = xMaxValue; xyzMax[1] = yMaxValue; xyzMax[2] = zMaxValue; xyzRange = new float[3]; xyzMult = new float[3]; xyzUnmult = new float[3]; xyzWrappoint = new float[3]; // Clean up the ranges for (int i = 0; i < 3; i++) { if (xyzLimit[i]) { if (xyzMax[i] < xyzMin[i]) { xyzMax[i] += 360; } // If the range is greater than 360, get the max down into range. Likely user selected bad min/max values. if (xyzMax[i] - xyzMin[i] > 360) { xyzMax[i] -= 360; } } else { xyzMin[i] = 0; xyzMax[i] = 360; } xyzRange[i] = xyzMax[i] - xyzMin[i]; // Do the heavier division work here so only one multipy per encode/decode is needed xyzMult[i] = (maxValue[xyzBits[i]]) / xyzRange[i]; xyzUnmult[i] = xyzRange[i] / (maxValue[xyzBits[i]]); xyzWrappoint[i] = xyzRange[i] + (360 - xyzRange[i]) / 2; } }
void Awake() { rb = GetComponent <Rigidbody>(); weapon = GetComponent <Weapon>(); nst = GetComponent <NetworkSyncTransform>(); }
/// <summary> /// Reads update headers for each NST frame update in the incoming bitstream, and passes the bitstream to that NST to read out its /// update information. /// </summary> /// <param name="mirror">True if this is the server, and this is the incoming bitstream. Tells the server that the outstream /// needs to be populated for retransmission to all clients. Also false if this is the server running its own outgoing update.</param> public static void ReceiveUpdate(ref UdpBitStream bitstream, ref UdpBitStream outstream, bool mirror, int senderId) { // Create a new bitstream to ensure ptr is at 0. Same data as master though. bitstream.ptr = 0; int frameid = bitstream.ReadInt(6); if (mirror) { outstream.WriteInt(frameid, 6); } bool isOfftick = frameid == FRAME_COUNT; int sceneIndex = NSTSceneManager.Deserialize(ref bitstream, ref outstream, mirror); bool sceneOutOfSync = HeaderSettings.single.includeSceneIndex && sceneIndex != NSTSceneManager.CurrentSceneIndex; if (sceneOutOfSync) { Debug.LogWarning(frameid + " Out of sync " + sceneIndex + " " + NSTSceneManager.CurrentSceneIndex); } // remove this safety once working //TEST int safety = 0; UpdateType updateType; do { safety++; BandwidthUsage.Start(ref bitstream, BandwidthLogType.UpdateRcv); //stop looking when header is EOS bool notEOS = bitstream.ReadBool(); int mirrorUpdateStartPtr = outstream.ptr; BandwidthUsage.AddUsage(ref bitstream, "NotEOS"); if (mirror) { outstream.WriteBool(notEOS); } if (!notEOS) { break; } // First three bits are the msgtype //TODO this might only need to be two updateType = (UpdateType)bitstream.ReadInt(3); BandwidthUsage.AddUsage(ref bitstream, "UpdateType"); int updateBitstreamPos = outstream.ptr; if (mirror) { outstream.WriteInt((int)updateType, 3); } // Next variable is the NstId - get it to know where to send the rest of the bitstream uint nstid = bitstream.ReadUInt(HeaderSettings.single.BitsForNstId); BandwidthUsage.AddUsage(ref bitstream, "NstId"); if (mirror) { outstream.WriteUInt(nstid, HeaderSettings.single.BitsForNstId); } lastNST = NSTTools.GetNstFromId(nstid); BandwidthUsage.SetName(lastNST); int updatelength = bitstream.ReadInt(UPDATELENGTH_BYTE_COUNT_SIZE); if (mirror) { outstream.WriteInt(updatelength, UPDATELENGTH_BYTE_COUNT_SIZE); } BandwidthUsage.AddUsage(ref bitstream, "DataLength"); //Note the starting pos in stream int bodyPtr = bitstream.ptr; // The start pos for modifying update lenght for mirror int mirrorBodyPtr = outstream.ptr; XDebug.LogWarning(!XDebug.logWarnings ? null : ("Incoming Update for nstid: " + nstid + " was from a different scene. Ignoring update to avoid data corruption due to different compression settings."), sceneOutOfSync); XDebug.Log(!XDebug.logInfo ? null : //Debug.Log( ("Message for an NST Object " + nstid + " arrived but that object does not exist. (yet/anymore?) This is normal during startup and shutdown."), lastNST == null); /// Skip reading if the target NST doesn't exist, or if this we compressed with a different scene (codec mismatch likely) if (lastNST == null || sceneOutOfSync) { if (sceneOutOfSync) { Debug.LogWarning(frameid + " skipped entirely due to sceneID mismatch"); } // Forward to the next update start in the incoming stream. bitstream.ptr = bodyPtr + (updatelength << 3); // rewind to the EOS marker and pretend this arrival never occured for the outgoing mirror stream. if (mirror) { /// TODO : Rather than jump ahead, Server should bulk copy the ignored data to outstream in case other clients can use it. outstream.ptr = mirrorUpdateStartPtr; } continue; } // Tell this nst to read its mail. updateType may get modified by server receive for things like teleport. Frame frame = lastNST.ReadUpdate(ref bitstream, ref outstream, frameid, isOfftick, updateType, updatelength, sceneIndex, sceneOutOfSync, mirror); updateType = frame.updateType; // overwrite the updateType of the server outgoing in case it has changed. if (mirror) { outstream.WriteIntAtPos((int)updateType, 3, updateBitstreamPos); } //Advance ptr to next update in stream by force, in case the last update wasn't read for any reason (such as the NST leaving the game) bitstream.ptr = bodyPtr + (updatelength << 3); // write the update byte length for the mirror (not the same value as the incoming due to server side adjustments) if (mirror) { int holdPos = outstream.ptr; outstream.ptr = mirrorBodyPtr - UPDATELENGTH_BYTE_COUNT_SIZE; // get the bytesused rounded up. int bytes = ((holdPos - mirrorBodyPtr) >> 3) + (((holdPos - mirrorBodyPtr) % 8 == 0) ? 0 : 1); outstream.WriteInt(bytes, UPDATELENGTH_BYTE_COUNT_SIZE); outstream.ptr = mirrorBodyPtr + (bytes << 3); } } while (safety < 100); /// NST updates are finished - any data to append to the master update can go here IntegrityCheck.ReadCheck(ref bitstream, ref outstream, "End of All Update Reads", mirror); if (!isOfftick) { MasterRTT.Rcv(ref bitstream, ref outstream, mirror, senderId); } BandwidthUsage.AddUsage(ref bitstream, "RTT checks"); // Very last thing... report the bits that were used. This is conditional to the editor only BandwidthUsage.ReportMasterBits(ref bitstream, BandwidthLogType.MasterIn); }
/// <summary> /// OnCustomMsgSndEvent fires on the originating client when a custom event is sent. The position and rotation information will contain the same /// lossy rounding errors/ranges that are being sent to the network. Useful for ensuring that your local events use the exact same pos/rot data /// the server and clients will be using (such as projectile vectors). /// </summary> /// <param name="rootPos">Lossy position after compression - exactly what is sent.</param> /// <param name="rotations">Lossy rotation after compression - exactly what is sent.</param> private static void OnCustomMsgSnd(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform nst, Vector3 rootPos, List <GenericX> positions, List <GenericX> rotations) { Weapon wpn = nst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if (weaponFireMsg.weaponId == (byte)WeaponType.Bullet) { wpn.FireBullet(rootPos, rotations[0], weaponFireMsg); } else if (weaponFireMsg.weaponId == (byte)WeaponType.Mine) { wpn.FireMine(wpn.turret.transform.position, rotations[1], weaponFireMsg); } }
/// <summary> /// When a custom message is taken from the buffer and applied for interpolation, the OnCustomMsgRcvEvent is fired. Note that the rotations /// will only be correct if you have the NST set to update rotations on events. If it is set to 'changes only' these rotations values will be zero. /// </summary> private static void OnCustomMsgApply(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform nst, Vector3 pos, List <GenericX> positions, List <GenericX> rotations) { Weapon wpn = nst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if (nst == NetworkSyncTransform.lclNST) { return; } if (weaponFireMsg.weaponId == (byte)WeaponType.Mine) { wpn.FireMine(wpn.turret.transform.position, rotations[1], weaponFireMsg); } }
/// <summary> /// When a custom message is received, the OnCustomMsgRcvEvent is fired. Note that the rotations will only be correct if you have the NST set to update /// rotations on events. If it is set to 'changes only' these rotations values will be zero. /// </summary> private static void OnCustomMsgRcv(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform shooterNst, Vector3 pos, List <GenericX> positions, List <GenericX> rotations) { //For this example we fired locally already when the custom message was sent (with OnCustomMsgSend). Firing again here on the local player would cause repeat fire events. //Note however that code for the local player can be added here to sync the projectile with the server, by adding a projectileID to your custom events. Weapon wpn = shooterNst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if ((WeaponType)weaponFireMsg.weaponId == WeaponType.Bullet) { if (!shooterNst.isLocalPlayer) { wpn.FireBullet(pos, rotations[0], weaponFireMsg); } } // Hitscan arrive on server/other clients else if ((WeaponType)weaponFireMsg.weaponId == WeaponType.Hitscan) { //DebugText.Log(weaponFireMsg.hitmask.PrintBitMask() + " RCV " + (WeaponType)weaponFireMsg.weaponId); uint confirmedHitmask = 0; // Draw the graphic if this isn't the local player if (!shooterNst.isLocalPlayer) { wpn.DrawRay(pos, rotations[0]); } // Server needs to test if this was a hit. if (NetworkServer.active) { for (int i = 0; i < 32; i++) { if (weaponFireMsg.hitmask.GetBitInMask(i) == false) { continue; } NetworkSyncTransform hitNst = NetworkSyncTransform.GetNstFromId((uint)i); bool hit = hitNst.TestHitscanAgainstRewind(ownerConn, new Ray(pos, (Quaternion)rotations[0] * Vector3.forward)); if (hit) { ((int)hitNst.NstId).SetBitInMask(ref confirmedHitmask, true); } } DebugText.Log("Rewind Confirmation Mask : \n" + confirmedHitmask.PrintBitMask(), true); } } }
/// <summary> /// Reads update headers for each NST frame update in the incoming bitstream, and passes the bitstream to that NST to read out its /// update information. /// </summary> /// <param name="mirror">True if this is the server, and this is the incoming bitstream. Tells the server that the outstream /// needs to be populated for retransmission to all clients. Also false if this is the server running its own outgoing update.</param> public static void ReceiveUpdate(ref UdpBitStream bitstream, ref UdpBitStream outstream, bool mirror) { // Create a new bitstream to ensure ptr is at 0. Same data as master though. bitstream.Ptr = 0; // remove this safety once working //TEST int safety = 0; UpdateType updateType; do { safety++; BandwidthUsage.Start(ref bitstream, BandwidthLogType.UpdateRcv); //stop looking when header is EOS bool notEOS = bitstream.ReadBool(); int mirrorUpdateStartPtr = outstream.Ptr; BandwidthUsage.AddUsage(ref bitstream, "NotEOS"); if (mirror) { outstream.WriteBool(notEOS); } if (!notEOS) { break; } // First three bits are the msgtype //TODO this might only need to be two updateType = (UpdateType)bitstream.ReadInt(3); BandwidthUsage.AddUsage(ref bitstream, "UpdateType"); int updateBitstreamPos = outstream.Ptr; if (mirror) { outstream.WriteInt((int)updateType, 3); } // Next variable is the NstId - get it to know where to send the rest of the bitstream uint nstid = bitstream.ReadUInt(NSTSettings.single.bitsForNstId); BandwidthUsage.AddUsage(ref bitstream, "NstId"); if (mirror) { outstream.WriteUInt(nstid, NSTSettings.single.bitsForNstId); } NetworkSyncTransform nst = NetworkSyncTransform.GetNstFromId(nstid); BandwidthUsage.SetName(nst); int updatelength = bitstream.ReadInt(UPDATELENGTH_BYTE_COUNT_SIZE); if (mirror) { outstream.WriteInt(updatelength, UPDATELENGTH_BYTE_COUNT_SIZE); } BandwidthUsage.AddUsage(ref bitstream, "DataLength"); //Note the starting pos in stream int bodyPtr = bitstream.Ptr; // The start pos for modifying update lenght for mirror int mirrorBodyPtr = outstream.Ptr; // This mising NST handler is NOT FULLY TESTED. Uses the updatelength value to jump ahead in the bitstream if the NST it is // addressed to doesn't exist for some reason. if (nst == null) { DebugX.LogWarning(!DebugX.logWarnings ? "" : ("Message for an NST Object arrived but that object does not exist.")); // Forward to the next update start in the incoming stream. bitstream.Ptr = bodyPtr + (updatelength << 3); // rewind to the EOS marker and pretend this arrival never occured for the outgoing mirror stream. outstream.Ptr = mirrorUpdateStartPtr; continue; } // Tell this nst to read its mail. updateType may get modified by server receive for things like teleport. updateType = nst.ReceieveGeneric(ref bitstream, ref outstream, updateType, updatelength, mirror); // overwrite the updateType of the server outgoing in case it has changed. if (mirror) { outstream.WriteIntAtPos((int)updateType, 3, updateBitstreamPos); } //Advance ptr to next update in stream by force, in case the last update wasn't read for any reason (such as the NST leaving the game) bitstream.Ptr = bodyPtr + (updatelength << 3); // write the update byte length for the mirror (not the same value as the incoming due to server side adjustments) if (mirror) { int holdPos = outstream.Ptr; outstream.Ptr = mirrorBodyPtr - UPDATELENGTH_BYTE_COUNT_SIZE; // get the bytesused rounded up. int bytes = ((holdPos - mirrorBodyPtr) >> 3) + (((holdPos - mirrorBodyPtr) % 8 == 0) ? 0 : 1); outstream.WriteInt(bytes, UPDATELENGTH_BYTE_COUNT_SIZE); outstream.Ptr = mirrorBodyPtr + (bytes << 3); } } while (safety < 100); }
private void Awake() { nst = GetComponent <NetworkSyncTransform>(); }