private void OnLog(string condition, string stackTrace, LogType logType)
        {
            //DO NOT CALL Debug.Log in this function or it will cause an infinite loop!
            //Wait until this network is ready to log.
            if (!sendLogEvents || !networkManager.isRunning || (!networkManager.isServer && !networkManager.isConnected))
            {
                return;
            }

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteString(condition);
                    writer.WriteBool(includeStacktrace);
                    if (includeStacktrace)
                    {
                        writer.WriteString(stackTrace);
                    }
                    writer.WriteByte((byte)logType);
                    if (networkManager.isServer)
                    {
                        //Send to all clients that are expecting it.
                        MessageSender.SendToSpecific(m_ClientsReceivingLogEvents, networkLogMessageType, "NETWORK_INTERNAL", stream);
                    }
                    else
                    {
                        //Send to server
                        MessageSender.Send(networkManager.serverID, networkLogMessageType, networkManager.networkInternalChannel, stream);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Gets a SHA256 hash of parts of the NetworkingConfiguration instance
        /// </summary>
        /// <param name="cache"></param>
        /// <returns></returns>
        public ulong GetConfig(bool cache = true)
        {
            if (ConfigHash != null && cache)
            {
                return(ConfigHash.Value);
            }

            Sort();

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(ProtocolVersion);
                    writer.WriteString(MLAPIConstants.MLAPI_PROTOCOL_VERSION);

                    for (int i = 0; i < Channels.Count; i++)
                    {
                        writer.WriteString(Channels[i].Name);
                        writer.WriteByte((byte)Channels[i].Type);
                    }

                    if (EnableSceneSwitching)
                    {
                        for (int i = 0; i < RegisteredScenes.Count; i++)
                        {
                            writer.WriteString(RegisteredScenes[i]);
                        }
                    }

                    if (HandleObjectSpawning && ForceSamePrefabs)
                    {
                        List <NetworkedPrefab> sortedPrefabList = NetworkedPrefabs.OrderBy(x => x.hash).ToList();
                        for (int i = 0; i < sortedPrefabList.Count; i++)
                        {
                            writer.WriteUInt64Packed(sortedPrefabList[i].hash);
                        }
                    }

                    writer.WriteBool(ForceSamePrefabs);
                    writer.WriteBool(HandleObjectSpawning);
                    writer.WriteBool(EnableEncryption);
                    writer.WriteBool(EnableSceneSwitching);
                    writer.WriteBool(SignKeyExchange);
                    writer.WriteBits((byte)RpcHashSize, 3);
                    writer.WriteBits((byte)PrefabHashSize, 3);
                    stream.PadStream();

                    if (cache)
                    {
                        ConfigHash = stream.ToArray().GetStableHash64();
                        return(ConfigHash.Value);
                    }

                    return(stream.ToArray().GetStableHash64());
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Returns a base64 encoded version of the config
        /// </summary>
        /// <returns></returns>
        public string ToBase64()
        {
            NetworkConfig config = this;

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(config.ProtocolVersion);
                    writer.WriteBits((byte)config.Transport, 5);

                    writer.WriteUInt16Packed((ushort)config.Channels.Count);
                    for (int i = 0; i < config.Channels.Count; i++)
                    {
                        writer.WriteString(config.Channels[i].Name);
                        writer.WriteBits((byte)config.Channels[i].Type, 5);
                    }

                    writer.WriteUInt16Packed((ushort)config.RegisteredScenes.Count);
                    for (int i = 0; i < config.RegisteredScenes.Count; i++)
                    {
                        writer.WriteString(config.RegisteredScenes[i]);
                    }

                    writer.WriteUInt16Packed((ushort)config.NetworkedPrefabs.Count);
                    for (int i = 0; i < config.NetworkedPrefabs.Count; i++)
                    {
                        writer.WriteBool(config.NetworkedPrefabs[i].playerPrefab);
                        writer.WriteString(config.NetworkedPrefabs[i].name);
                    }

                    writer.WriteInt32Packed(config.MessageBufferSize);
                    writer.WriteInt32Packed(config.ReceiveTickrate);
                    writer.WriteInt32Packed(config.MaxReceiveEventsPerTickRate);
                    writer.WriteInt32Packed(config.SendTickrate);
                    writer.WriteInt32Packed(config.EventTickrate);
                    writer.WriteInt32Packed(config.MaxConnections);
                    writer.WriteInt32Packed(config.ConnectPort);
                    writer.WriteString(config.ConnectAddress);
                    writer.WriteInt32Packed(config.ClientConnectionBufferTimeout);
                    writer.WriteBool(config.ConnectionApproval);
                    writer.WriteInt32Packed(config.SecondsHistory);
                    writer.WriteBool(config.HandleObjectSpawning);
                    writer.WriteBool(config.EnableEncryption);
                    writer.WriteBool(config.SignKeyExchange);
                    writer.WriteBool(config.EnableSceneSwitching);
                    writer.WriteInt32Packed(config.LoadSceneTimeOut);
                    writer.WriteBool(config.EnableTimeResync);
                    writer.WriteBits((byte)config.RpcHashSize, 3);
                    stream.PadStream();

                    return(Convert.ToBase64String(stream.ToArray()));
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Gets a SHA256 hash of parts of the NetworkingConfiguration instance
        /// </summary>
        /// <param name="cache"></param>
        /// <returns></returns>
        public ulong GetConfig(bool cache = true)
        {
            if (ConfigHash != null && cache)
            {
                return(ConfigHash.Value);
            }

            Sort();

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(ProtocolVersion);
                    writer.WriteString(MLAPIConstants.MLAPI_PROTOCOL_VERSION);

                    for (int i = 0; i < Channels.Count; i++)
                    {
                        writer.WriteString(Channels[i].Name);
                        writer.WriteByte((byte)Channels[i].Type);
                    }

                    if (EnableSceneSwitching)
                    {
                        for (int i = 0; i < RegisteredScenes.Count; i++)
                        {
                            writer.WriteString(RegisteredScenes[i]);
                        }
                    }

                    if (HandleObjectSpawning && RequireNetworkedPrefabsAreSyncronized)
                    {
                        for (int i = 0; i < NetworkedPrefabs.Count; i++)
                        {
                            writer.WriteString(NetworkedPrefabs[i].name);
                        }
                    }

                    writer.WriteBool(HandleObjectSpawning);
                    writer.WriteBool(EnableEncryption);
                    writer.WriteBool(EnableSceneSwitching);
                    writer.WriteBool(SignKeyExchange);
                    writer.WriteBits((byte)AttributeMessageMode, 3);

                    // Returns a 160 bit / 20 byte / 5 int checksum of the config
                    if (cache)
                    {
                        ConfigHash = stream.ToArray().GetStableHash64();
                        return(ConfigHash.Value);
                    }
                    return(stream.ToArray().GetStableHash64());
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Gets a SHA256 hash of parts of the NetworkingConfiguration instance
        /// </summary>
        /// <param name="cache"></param>
        /// <returns></returns>
        public ulong GetConfig(bool cache = true)
        {
            if (ConfigHash != null && cache)
            {
                return(ConfigHash.Value);
            }

            Sort();

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(ProtocolVersion);
                    writer.WriteString(MLAPIConstants.MLAPI_PROTOCOL_VERSION);

                    if (EnableSceneManagement && !AllowRuntimeSceneChanges)
                    {
                        for (int i = 0; i < RegisteredScenes.Count; i++)
                        {
                            writer.WriteString(RegisteredScenes[i]);
                        }
                    }

                    if (ForceSamePrefabs)
                    {
                        List <NetworkedPrefab> sortedPrefabList = NetworkedPrefabs.OrderBy(x => x.Hash).ToList();
                        for (int i = 0; i < sortedPrefabList.Count; i++)
                        {
                            writer.WriteUInt64Packed(sortedPrefabList[i].Hash);
                        }
                    }

                    writer.WriteBool(EnableNetworkedVar);
                    writer.WriteBool(ForceSamePrefabs);
                    writer.WriteBool(UsePrefabSync);
                    writer.WriteBool(EnableSceneManagement);
                    writer.WriteBool(EnsureNetworkedVarLengthSafety);
                    writer.WriteBool(EnableEncryption);
                    writer.WriteBool(SignKeyExchange);
                    writer.WriteBits((byte)RpcHashSize, 2);
                    stream.PadStream();

                    if (cache)
                    {
                        ConfigHash = stream.ToArray().GetStableHash64();
                        return(ConfigHash.Value);
                    }

                    return(stream.ToArray().GetStableHash64());
                }
            }
        }
예제 #6
0
		/// <summary>
		/// Returns a base64 encoded version of the config
		/// </summary>
		/// <returns></returns>
		public string ToBase64()
		{
			NetworkConfig config = this;
			using (PooledBitStream stream = PooledBitStream.Get())
			{
				using (PooledBitWriter writer = PooledBitWriter.Get(stream))
				{
					writer.WriteString(config.protocalVersion);

					writer.WriteInt32Packed(config.receiveTickrate);
					writer.WriteInt32Packed(config.maxReceiveEventsPerTickRate);
					writer.WriteInt32Packed(config.eventTickrate);
					writer.WriteInt32Packed(config.clientConnectionBufferTimeout);
					writer.WriteInt32Packed(config.secondsHistory);
					writer.WriteBool(config.enableTimeResync);
					writer.WriteBool(config.ensureNetworkedVarLengthSafety);
					writer.WriteBits((byte)config.rpcHashSize, 3);
					writer.WriteBool(recycleNetworkIDs);
					writer.WriteSinglePacked(networkIDRecycleDelay);
					writer.WriteBool(enableNetworkedVar);
                    writer.WriteBool(clientSendSceneEvents);
                    writer.WriteBool(serverSendSceneEvents);
                    stream.PadStream();

					return Convert.ToBase64String(stream.ToArray());
				}
			}
		}
예제 #7
0
		/// <summary>
		/// Gets a SHA256 hash of parts of the NetworkingConfiguration instance
		/// </summary>
		/// <param name="cache"></param>
		/// <returns></returns>
		public ulong GetConfig(bool cache = true)
		{
			if (ConfigHash != null && cache)
				return ConfigHash.Value;

			using (PooledBitStream stream = PooledBitStream.Get())
			{
				using (PooledBitWriter writer = PooledBitWriter.Get(stream))
				{
					writer.WriteString(protocalVersion);

					writer.WriteBool(enableNetworkedVar);
					writer.WriteBool(ensureNetworkedVarLengthSafety);
					writer.WriteBits((byte)rpcHashSize, 3);
					stream.PadStream();

					if (cache)
					{
						ConfigHash = stream.ToArray().GetStableHash64();
						return ConfigHash.Value;
					}

					return stream.ToArray().GetStableHash64();
				}
			}
		}
예제 #8
0
 public void Write(Stream stream)
 {
     using (PooledBitWriter writer = PooledBitWriter.Get(stream))
     {
         writer.WriteUInt64(ID);
         writer.WriteString(Name);
     }
 }
예제 #9
0
 public void Write(Stream stream)
 {
     using (PooledBitWriter writer = PooledBitWriter.Get(stream))
     {
         writer.WriteByte((byte)LocationIndex);
         writer.WriteString(Question);
         writer.WriteIntArrayPacked(Answers, ANSWER_COUNT);
     }
 }
예제 #10
0
    private void SpawnBall()
    {
        var netObj = Instantiate(ball).GetComponent <NetworkedObject>();

        using (PooledBitStream stream = PooledBitStream.Get())
        {
            using (PooledBitWriter writer = PooledBitWriter.Get(stream))
            {
                writer.WriteString("Ball");
                netObj.Spawn(stream, true);
            }
        }
    }
예제 #11
0
    private void SpawnPlayer(bool left, ulong clientId)
    {
        var netObj = Instantiate(pong, new Vector3(left ? -6f : 6f, 0f), Quaternion.identity).GetComponent <NetworkedObject>();

        using (PooledBitStream stream = PooledBitStream.Get())
        {
            using (PooledBitWriter writer = PooledBitWriter.Get(stream))
            {
                writer.WriteString(left ? "Left" : "Right");
                netObj.SpawnAsPlayerObject(clientId, stream, true);
            }
        }
    }
예제 #12
0
        /// <summary>
        /// [2nd step] This function is called for second when anchor is placed 
        /// </summary>
        public void InitVideoStreamingController(List<AcquisitionDelays> acquisitionDelays, string streamId)
        {
            Debug.Log("InitVideoStreamingController called");
            try
            {
                // debug information
                this.snackbarText = GameObject.Find("Snackbar").GetComponentInChildren<Text>();

                // Init code
                this.streamId = String.Copy(streamId);
                this.secBetweenFrame = Settings.Settings.Instance.FPS;
                this.phoneIdCompleteSending = new HashSet<string>();
                this.frameCounter = 0;
                this.acquiringInProgress = false;

                foreach (AcquisitionDelays delay in acquisitionDelays)
                {
                    Debug.Log("Key = " + delay.Key + ", Value = " + delay.Value);
                }
                foreach (MLAPI.Connection.NetworkedClient client in NetworkingManager.Singleton.ConnectedClientsList)
                {
                    Debug.Log("Client ID: " + client.ClientId);
                }
                Debug.Log("StreamID: " + this.streamId);

                foreach (AcquisitionDelays delay in acquisitionDelays)
                {
                    Debug.Log("Invoke remote RPC on: " + delay.Key + " delay send: " + delay.Value);

                    using (PooledBitStream stream = PooledBitStream.Get())
                    {
                        using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                        {
                            writer.WriteString(this.streamId);
                            writer.WriteDouble(delay.Value);

                            InvokeClientRpcOnClientPerformance(InitVideoStreamingPerformance, delay.Key, stream);
                        }
                    }

                        
                    // InvokeClientRpcOnClient(InitVideoStreaming, delay.Key, this.streamId, delay.Value);
                    Debug.Log("RPC client invoked");
                }
            }
            catch (Exception e)
            {
                Debug.LogError("Exception InitVideoStreaming: " + e.ToString());
            }
        }
예제 #13
0
 public void Write(Stream stream)
 {
     using (PooledBitWriter writer = PooledBitWriter.Get(stream)) {
         writer.WriteInt32(stacks.Count);
         foreach (KeyValuePair <string, InventoryItem> kvp in stacks)
         {
             InventoryItem curInvItem = kvp.Value;
             if (curInvItem.Value.name != kvp.Key)
             {
                 Debug.LogWarning("NetworkSharedItem name did not match " + kvp.Key + " != " + curInvItem.Value.name);
             }
             writer.WriteString(kvp.Key);
             writer.WriteInt32(curInvItem.Count);
         }
     }
 }
예제 #14
0
        /// <summary>
        /// Returns a base64 encoded version of the config
        /// </summary>
        /// <returns></returns>
        public string ToBase64()
        {
            NetworkConfig config = this;

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(config.ProtocolVersion);

                    writer.WriteUInt16Packed((ushort)config.RegisteredScenes.Count);

                    for (int i = 0; i < config.RegisteredScenes.Count; i++)
                    {
                        writer.WriteString(config.RegisteredScenes[i]);
                    }

                    writer.WriteInt32Packed(config.ServerTargetFramerate);
                    writer.WriteInt32Packed(config.NetworkedTransformTickrate);
                    writer.WriteInt32Packed(config.ClientCommandTickrate);
                    writer.WriteInt32Packed(config.MaxReceiveEventsPerTickRate);
                    writer.WriteInt32Packed(config.LagCompensationTickRate);
                    writer.WriteInt32Packed(config.EventTickrate);
                    writer.WriteInt32Packed(config.ClientConnectionBufferTimeout);
                    writer.WriteBool(config.ConnectionApproval);
                    writer.WriteInt32Packed(config.SecondsHistory);
                    writer.WriteBool(config.EnableEncryption);
                    writer.WriteBool(config.SignKeyExchange);
                    writer.WriteInt32Packed(config.LoadSceneTimeOut);
                    writer.WriteBool(config.EnableTimeResync);
                    writer.WriteBool(config.EnsureNetworkedVarLengthSafety);
                    writer.WriteBits((byte)config.RpcHashSize, 2);
                    writer.WriteBool(ForceSamePrefabs);
                    writer.WriteBool(UsePrefabSync);
                    writer.WriteBool(EnableSceneManagement);
                    writer.WriteBool(RecycleNetworkIds);
                    writer.WriteSinglePacked(NetworkIdRecycleDelay);
                    writer.WriteBool(EnableNetworkedVar);
                    writer.WriteBool(AllowRuntimeSceneChanges);
                    writer.WriteBool(EnableNetworkLogs);
                    stream.PadStream();

                    return(Convert.ToBase64String(stream.ToArray()));
                }
            }
        }
예제 #15
0
        /// <summary>
        /// Returns a base64 encoded version of the config
        /// </summary>
        /// <returns></returns>
        public string ToBase64()
        {
            NetworkConfig config = this;

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt16Packed(config.ProtocolVersion);

                    writer.WriteUInt16Packed((ushort)config.RegisteredScenes.Count);

                    for (int i = 0; i < config.RegisteredScenes.Count; i++)
                    {
                        writer.WriteString(config.RegisteredScenes[i]);
                    }

                    writer.WriteInt32Packed(config.ReceiveTickrate);
                    writer.WriteInt32Packed(config.MaxReceiveEventsPerTickRate);
                    writer.WriteInt32Packed(config.SendTickrate);
                    writer.WriteInt32Packed(config.EventTickrate);
                    writer.WriteInt32Packed(config.ClientConnectionBufferTimeout);
                    writer.WriteBool(config.ConnectionApproval);
                    writer.WriteInt32Packed(config.SecondsHistory);
                    writer.WriteBool(config.EnableEncryption);
                    writer.WriteBool(config.SignKeyExchange);
                    writer.WriteInt32Packed(config.LoadSceneTimeOut);
                    writer.WriteBool(config.EnableTimeResync);
                    writer.WriteBits((byte)config.RpcHashSize, 3);
                    writer.WriteBool(ForceSamePrefabs);
                    writer.WriteBool(UsePrefabSync);
                    writer.WriteBool(EnableNetworkedVar);
                    stream.PadStream();

                    return(Convert.ToBase64String(stream.ToArray()));
                }
            }
        }
예제 #16
0
    /// <summary>
    /// this function splits FILES into MEMORY SAFE sized chunks and safely sends one before starting another
    ///
    /// files receipient needs to receive the same number of headers with each header packet (packet 1 counts as a header packet)
    /// </summary>
    public IEnumerator SendFilesDownloadRoutine(string[] _paths, ulong _clientID)
    {
        Debug.Log("coroutine started");
        if (State != LargeRPCState.Idle)
        {
            Debug.LogWarning("Cannot start sending files while files are being sent, waiting for Idle state to begin");
            yield break;
        }

        ReceiverID = _clientID;

        ChangeState(LargeRPCState.Send_SendingHeaders);

        #region comment -- header sizes

        /* -- Header sizes --
         * packet 1
         * int fileCount 4b | long downloadSize 8b | <start headers>
         *
         * header packets
         * int fileID 4b | string filename varsize | byte[256] hash 32b | long fileLength 8b | bool isLastInPacket 1bit
         *
         * subsequent packets
         * int fileID 4b | int filedata_length 4b | byte[var] filedata <=netChunkSize | bool isLastInPacket 1byte
         */
        #endregion

        #region Grab Download Information

        // grab info for headers
        foreach (var path in _paths)
        {
            if (File.Exists(path))
            {
                using (FileStream fs = File.Open(path, FileMode.Open))
                {
                    Debug.Log(fs.Name);
                    int    id       = Headers.Count;
                    byte[] fileHash = fs.sha256();
                    yield return(new WaitForEndOfFrame());

                    FileHeader header = new FileHeader(id, Path.GetFileName(path), fileHash, fs.Length);
                    Headers.Add(header);

                    DownloadSize += header.fileSize;
                }

                // let it exit if it needs to, giving StopRoutine() a chance
                yield return(0);
            }
            else
            {
                Debug.LogWarning("File not found, skipping: " + path);
            }
        }

        #endregion

        #region send headers

        PooledBitStream bitStream = PooledBitStream.Get();
        PooledBitWriter writer    = PooledBitWriter.Get(bitStream);

        // fileCount
        writer.WriteInt32(Headers.Count);

        // downloadSize
        writer.WriteInt64(DownloadSize);


        var headersThisPacket = 0;
        var packetsSent       = 0;
        Debug.Log("Sending headers");
        foreach (var header in Headers)
        {
            // let it exit if it needs to, giving StopRoutine() a chance
            yield return(0);

            Debug.Log(headersThisPacket + "          " + packetsSent);

            var path = header.path;
            var id   = header.id;

            headersThisPacket++;

            // fileID
            writer.WriteInt32(header.id);

            // filename
            writer.WriteString(path);

            Debug.Log(Encoding.Unicode.GetString(header.hash));
            // hash
            writer.WriteByteArray(header.hash, 32);

            // fileLength
            writer.WriteInt64(header.fileSize);

            bool isLastPacket = id >= Headers.Count - 1;

            // send it off if we've filled up a packet
            if (headersThisPacket >= headersPerPacket || isLastPacket)
            {
                Debug.Log("message going out");
                // isLastInPacket
                writer.WriteBit(true);



                CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL");

                /* headers are pretty small, they really don't need the receiver to check in here unless it becomes a problem
                 *
                 * // if we haven't sent any packets yet when we get here, wait for an okay from the receiver
                 * if (packetsSent == 0)
                 * {
                 *  ChangeState(LargeRPCState.Send_AwaitingOkayToSend);
                 *
                 *  while (State == LargeRPCState.Send_AwaitingOkayToSend)
                 *  {
                 *      yield return new WaitForSeconds(0.5f);
                 *  }
                 * }*/

                packetsSent++;
                Debug.Log("headers: " + headersThisPacket + "          packets: " + packetsSent);
                headersThisPacket = 0;

                writer.Dispose();
                bitStream.Dispose();

                bitStream = PooledBitStream.Get();
                writer    = PooledBitWriter.Get(bitStream);

                // don't wait on the last one
                if (!isLastPacket)
                {
                    yield return(new WaitForSeconds(1 / 14));
                }
            }
            else
            {
                writer.WriteBit(false);
            }
        }

        writer.Dispose();
        bitStream.Dispose();

        #endregion

        ChangeState(LargeRPCState.Send_AwaitingFilesNeededList);


        ListenForFilesNeededListOrCompletion();

        // loop start
        while (State != LargeRPCState.Complete)
        {
            // let it exit if it needs to, giving StopRoutine() a chance
            yield return(0);

            Debug.Log("Not done, running not-complete loop");
            #region wait for needed files list

            while (State == LargeRPCState.Send_AwaitingFilesNeededList || State == LargeRPCState.Send_EnsuringIntegrity)
            {
                Debug.Log("waiting for list");
                yield return(new WaitForSeconds(0.5f));
            }

            Debug.Log("No longer waiting for list");

            #endregion
            // runs ReceiveFilesNeededListFromReceiver, changes state to either Send_SendingFiles or Complete


            if (filesToSend.Count > 0)
            {
                Debug.Log("client still needs more files, sending");

                #region send files

                bitStream = PooledBitStream.Get();
                writer    = PooledBitWriter.Get(bitStream);

                foreach (var header in Headers)
                {
                    // let it exit if it needs to, giving StopRoutine() a chance
                    yield return(0);

                    Debug.Log("processing header");
                    if (File.Exists(header.path) && filesToSend.Contains(header.id))
                    {
                        Debug.Log("file is needed");
                        using (FileStream fs = File.Open(header.path, FileMode.Open))
                        {
                            // while loop pulled from fs.Read docs from microsoft, a little confusing to the glance but works and will be fast

                            int numBytesToRead = (int)fs.Length;
                            int numBytesRead   = 0;
                            while (numBytesToRead > 0)
                            {
                                Debug.Log("still bytes left");
                                int thisFileChunkSize = fileChunkSize;
                                thisFileChunkSize = Mathf.Min(thisFileChunkSize, numBytesToRead);

                                byte[] fileChunk = new byte[thisFileChunkSize];

                                // Read may return anything from 0 to numBytesToRead.
                                int n = fs.Read(fileChunk, numBytesRead, thisFileChunkSize);



                                foreach (byte[] netChunk in fileChunk.Slices(netChunkSize, false))
                                {
                                    Debug.Log("processing next chunk");

                                    // fileID
                                    writer.WriteInt32(header.id);

                                    //writer.WriteInt32(netChunk.Length);
                                    Debug.Log("netchunk len: " + netChunk.Length);
                                    // filedata
                                    writer.WriteByteArray(netChunk);

                                    // isLastInPacket, need to add in its own size
                                    bool isLastInPacket = bitStream.Length + 1 >= netChunkSize || netChunk.Length < netChunkSize;
                                    writer.WriteBit(isLastInPacket);

                                    if (isLastInPacket)
                                    {
                                        CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL");
                                        Debug.Log("packet sent");

                                        yield return(new WaitForSeconds(1 / 14));

                                        writer.Dispose();
                                        bitStream.Dispose();

                                        bitStream = PooledBitStream.Get();
                                        writer    = PooledBitWriter.Get(bitStream);
                                    }
                                }

                                // Break when the end of the file is reached.
                                if (n == 0)
                                {
                                    Debug.Log("end of file reached, this is a failsafe");
                                    break;
                                }

                                numBytesRead   += n;
                                numBytesToRead -= n;
                            }
                        }
                    }
                }

                Debug.Log("all headers processed");

                filesToSend.Clear();

                // just failsafing these, should be disposed of already
                writer.Dispose();
                bitStream.Dispose();

                #endregion

                ChangeState(LargeRPCState.Send_EnsuringIntegrity);
            }

            Debug.Log("Waiting before checking completion again");
            yield return(new WaitForSeconds(1f));
        }

        StopListening();

        Debug.Log("files sent");
        if (OnDownloadComplete != null)
        {
            OnDownloadComplete(SendOrReceiveFlag.Send, ReceiverID);
        }

        ChangeState(LargeRPCState.Idle);

        yield break;
    }