コード例 #1
0
ファイル: RotationElement.cs プロジェクト: lenix2/VRProject
        public override bool Write(ref UdpBitStream bitstream, Frame frame)
        {
            // Base class does some forceUpdate checking, keep it around.
            bool         forceUpdate = IsUpdateForced(frame);
            ElementFrame e           = frames[frame.frameid];

            e.compXform = Compress();
            e.xform     = Localized;
            CompressedElement newComp = e.compXform;

            if (rotationType == RotationType.Quaternion)
            {
                // For frames between forced updates, we need to first send a flag bit for if this element is being sent
                if (!forceUpdate)
                {
                    bool hasChanged = newComp.quat != lastSentCompressed.quat && sendCullMask.OnChanges();
                    bitstream.WriteBool(hasChanged);

                    // if no changes have occured we are done.
                    if (!hasChanged)
                    {
                        return(false);
                    }
                }

                bitstream.WriteULong(newComp.quat, totalBitsForQuat);

                lastSentCompressed.quat = newComp.quat;
                lastSentTransform       = e.xform;

                return(true);
            }

            else
            {
                // For frames between forced updates, we need to first send a flag bit for if this element is being sent
                if (!forceUpdate)
                {
                    bool hasChanged = !CompressedElement.Compare(newComp, lastSentCompressed) && sendCullMask.OnChanges();
                    bitstream.WriteBool(hasChanged);

                    // if no changes have occured we are done.
                    if (!hasChanged)
                    {
                        return(false);
                    }
                }

                for (int axis = 0; axis < 3; axis++)
                {
                    if (includedAxes.IsXYZ(axis))
                    {
                        bitstream.WriteUInt(newComp[axis], axes[axis].bits);
                        lastSentCompressed[axis] = newComp[axis];
                    }
                }

                return(true);
            }
        }
コード例 #2
0
    public static void Send(ref UdpBitStream bitstream, int updateCounter)
    {
        // If this is the server and it is due to ping... ping
        //TODO this hard coded 60 will likely go.
        //TODO this should probably be offset by 1 to keep frame zero from becoming massive with all the keyframes of NST
        if (MasterNetAdapter.ServerIsActive && updateCounter % 20 == 0 && updateCounter != 60)
        {
            svrPingInitiateTime = Time.time;
            bitstream.WriteBool(true);
        }

        // If this is a client, and it has rcvd a server ping, it needs to respond next tick.
        else if (clientNeedsToRespondToPing)
        {
            bitstream.WriteBool(true);
            // write how long the client waited to reply in ms (1023 ms max with 10 bits)
            bitstream.WriteInt((int)((Time.time - clntPingArriveTime) * 1000), 10);
            clientNeedsToRespondToPing = false;
        }

        // nothing happening this pass.
        else
        {
            bitstream.WriteBool(false);
        }
    }
コード例 #3
0
    public byte[] Pack()
    {
        var data   = new byte[1200];
        var stream = new UdpBitStream(data, data.Length);

        //stream.WriteInt(state.index, 32);
        UdpBitStreamExt.WriteVector3(ref stream, state.position);
        UdpBitStreamExt.WriteQuaternion(ref stream, state.rotation);
        UdpBitStreamExt.WriteVector3(ref stream, state.velocity);
        UdpBitStreamExt.WriteVector2(ref stream, movementInput);
        UdpBitStreamExt.WriteVector2(ref stream, lookInput);
        stream.WriteBool(jumpInput);
        stream.WriteBool(shootInput);
        return(data);
    }
コード例 #4
0
ファイル: TransformElement.cs プロジェクト: valourus/GT-O
        public bool Write(ref UdpBitStream bitstream, Frame frame)
        {
            ElementFrame e = frames[frame.frameid];

            e.compXform = Compress();
            e.xform     = Localized;

            CompressedElement newComp = e.compXform;

            bool forceUpdate = IsUpdateForced(frame);

            // For frames between forced updates, we need to first send a flag bit for if this element is being sent
            if (!forceUpdate)
            {
                bool hasChanged = !CompressedElement.Compare(newComp, lastSentCompressed) && sendCullMask.OnChanges();
                bitstream.WriteBool(hasChanged);

                // if no changes have occured we are done.
                if (!hasChanged)
                {
                    return(false);
                }
            }

            crusher.Write(e.compXform, bitstream.Data, ref bitstream.ptr);

            lastSentCompressed = newComp;
            lastSentTransform  = e.xform;

            return(true);
        }
コード例 #5
0
ファイル: NSTMaster.cs プロジェクト: vert0r/NetDemo2
        /// <summary>
        /// Ping all owned NSTs for any due updates. Passes the bitstream to that NST to write to.
        /// </summary>
        public static void PollAllForUpdates()
        {
            //Debug.Log("<b><color=green>Mstr Ping</color></b> " + (Time.time - tempMasterPollTime));
            //tempMasterPollTime = Time.time;

            UdpBitStream bitstream = new UdpBitStream(bitstreamByteArray);
            UdpBitStream outstream = new UdpBitStream();

            bool foundUpdate = false;

            for (int i = 0; i < NetworkSyncTransform.allNsts.Count; i++)
            {
                foundUpdate |= NetworkSyncTransform.allNsts[i].PollForUpdate(ref bitstream);
            }

            // No Updates, we are done.
            if (!foundUpdate)
            {
                return;
            }

            // Write the end of stream marker of 00
            bitstream.WriteBool(false);

            mna.SendUpdate(ref bitstream, ref outstream);

            //Debug.Log("<b><color=blue>Mstr Snd</color></b> " + (Time.time - tempMasterSendTime));
            //tempMasterSendTime = Time.time;
        }
コード例 #6
0
        public bool Write(ref UdpBitStream bitstream, Frame frame)
        {
            ElementFrame      e         = frames[frame.frameid];
            CompressedElement compXform = e.compXform;

            Compress(compXform);
            e.xform = Decompress(compXform);             // Localized;

            /// Experimental - Apply the outgoing lossy values to the GhostGO so owner/authority of object can use it to replicate lossy events like weapon fire.
            if (ghostGO)
            {
                Apply(e.xform, ghostGO);
            }

            if (!nstElement.Enabled)
            {
                bitstream.WriteBool(false);
                return(false);
            }

            bool forceUpdate = IsUpdateForced(frame);

            // For frames between forced updates, we need to first send a flag bit for if this element is being sent
            if (!forceUpdate)
            {
                bool hasChanged = !compXform.Equals(lastSentCompressed) && sendCullMask.OnChanges();
                bitstream.WriteBool(hasChanged);

                // if no changes have occured we are done.
                if (!hasChanged)
                {
                    return(false);
                }
            }
            else
            {
                bitstream.WriteBool(true);
            }

            crusher.Write(compXform, bitstream.Data, ref bitstream.ptr);

            lastSentCompressed.CopyFrom(compXform);
            lastSentTransform = e.xform;

            return(true);
        }
コード例 #7
0
        public void MirrorToClients(ref UdpBitStream outstream, Frame frame, bool hasChanged)
        {
            //// Write the used flag (if this is not a forced update) and determine if an update needs to be written.
            //if (WriteUpdateFlag(ref outstream, frame, hasChanged) == false)
            //	return;

            outstream.WriteBool(hasChanged);

            if (!hasChanged)
            {
                return;
            }

            ElementFrame e = frames[frame.frameid];

            crusher.Write(e.compXform, outstream.Data, ref outstream.ptr);
        }
コード例 #8
0
        /// <summary>
        /// Write the flag bool if this is not a forced update, and return true if the element should be written to the stream (the value of the flag).
        /// </summary>
        protected bool WriteUpdateFlag(ref UdpBitStream outstream, Frame frame, bool hasChanged)
        {
            bool forcedUpdate = IsUpdateForced(frame);

            // For non-forced updates we need to set the used flag.
            if (!forcedUpdate)
            {
                outstream.WriteBool(hasChanged);
            }

            // exit if we are not writing a compressed value.
            if (!hasChanged && !forcedUpdate)
            {
                return(false);
            }

            return(true);
        }
コード例 #9
0
        /// <summary>
        /// Ping all owned NSTs for any due updates (fires on network ticks, generated by the NSTMaster FixedUpdate()). Passes the bitstream to that NST.
        /// </summary>
        public static bool PollAllForNSTUpdates(ref UdpBitStream bitstream, List <NetworkSyncTransform> nsts, int updateCounter, bool isOfftick)
        {
            if (!MasterNetAdapter.ServerIsActive && !MasterNetAdapter.ClientIsActive)
            {
                return(false);
            }

            bool foundUpdate = false;
            int  count       = nsts.Count;

            for (int i = 0; i < count; ++i)
            {
                foundUpdate |= nsts[i].PollForUpdate(ref bitstream, updateCounter, isOfftick);
            }

            // Write the end of stream marker of 00
            bitstream.WriteBool(false);

            IntegrityCheck.WriteCheck(ref bitstream);

            return(foundUpdate);
        }
コード例 #10
0
    public static void Rcv(ref UdpBitStream bitstream, ref UdpBitStream outstream, bool mirror, int clientId)
    {
        bool isPing = bitstream.ReadBool();

        // if this is a ping from a client, a time delta should follow (mirror means this is the server, and this pass is the server pass)
        if (isPing && mirror && clientId != MasterNetAdapter.MasterClientId)
        {
            float clientHeldTime = .001f * bitstream.ReadInt(10);
            float rtt            = (Time.time - svrPingInitiateTime) - clientHeldTime;

            if (RTT.ContainsKey(clientId))
            {
                RTT[clientId] = RTT[clientId] * OLD_SAMP_WEIGHT + rtt * NEW_SAMP_WEIGHT;
            }
            else
            {
                RTT.Add(clientId, rtt);
            }

            //Debug.Log(
            DebugX.Log(!DebugX.logInfo ? null :
                       "MasterRTT ping result " + clientId + " <b>" + RTT[clientId] + " </b>  plus " + clientHeldTime + " of client waiting for next outgoing tick.");
        }

        // this is a dumb client and it just got a ping from server
        else if (isPing && !MasterNetAdapter.ServerIsActive)
        {
            clientNeedsToRespondToPing = true;
            clntPingArriveTime         = Time.time;
        }

        // We don't mirror out pings. The conversation is strictly Server > Player > Server
        if (mirror)
        {
            outstream.WriteBool(false);
        }
    }
コード例 #11
0
        /// <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);
        }
コード例 #12
0
        public override void WriteToBitstream(ref UdpBitStream bitstream, MsgType msgType, bool forceUpdate, bool isKeyframe)
        {
            // Base class does some forceUpdate checking, keep it around.
            //forceUpdate = base.WriteToBitstream(ref bitstream, msgType, forceUpdate);
            //bool hasChanged = false;

            if (rotationType == XType.Quaternion)
            {
                ulong compressedQuat = QuatCompress.CompressQuatToBitsBuffer(Localized, totalBitsForQuat);

                // For frames between forced updates, we need to first send a flag bit for if this element is being sent
                if (!forceUpdate)
                {
                    bool hasChanged = compressedQuat != lastSentCompressed;
                    bitstream.WriteBool(hasChanged);

                    // if no changes have occured we are done.
                    if (!hasChanged)
                    {
                        return;
                    }
                }

                bitstream.WriteULong(compressedQuat, totalBitsForQuat);
                lastSentCompressed = compressedQuat;
                return;
            }

            else
            {
                // Euler types...

                CompressedElement newValues = new CompressedElement(0, 0, 0);

                // populate the new compressed position, and test if any of the axes have changed.
                for (int axis = 0; axis < 3; axis++)
                {
                    if (rotationType.IsXYZ(axis))
                    {
                        newValues[axis] = CompressFloat(((Vector3)Localized)[axis], axis);
                    }
                }

                // For frames between forced updates, we need to first send a flag bit for if this element is being sent
                if (!forceUpdate)
                {
                    bool hasChanged = !CompressedElement.Compare(newValues, lastSentCompressed);
                    bitstream.WriteBool(hasChanged);

                    // if no changes have occured we are done.
                    if (!hasChanged)
                    {
                        return;
                    }
                }

                for (int axis = 0; axis < 3; axis++)
                {
                    if (rotationType.IsXYZ(axis))
                    {
                        bitstream.WriteUInt(newValues[axis], xyzBits[axis]);
                        lastSentCompressed[axis] = newValues[axis];
                    }
                }
            }
        }
コード例 #13
0
        /// <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);
        }
コード例 #14
0
ファイル: NSTMaster.cs プロジェクト: vert0r/NetDemo2
        /// <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);
        }