/// <summary> /// Copy the contents of this buffer into the target one, trimming away unused space. /// </summary> public void CopyTo (Buffer target) { BinaryWriter w = target.BeginWriting(false); int bytes = size; if (bytes > 0) w.Write(buffer, position, bytes); target.EndWriting(); }
/// <summary> /// Create a new buffered remote function call. /// </summary> public void CreateRFC(uint inID, string funcName, Buffer buffer) { if (closed || buffer == null) { return; } Buffer b = Buffer.Create(); b.BeginWriting(false).Write(buffer.buffer, buffer.position, buffer.size); b.BeginReading(); for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.uid == inID && r.functionName == funcName) { if (r.buffer != null) { r.buffer.Recycle(); } r.buffer = b; return; } } RFC rfc = new RFC(); rfc.uid = inID; rfc.buffer = b; rfc.functionName = funcName; rfcs.Add(rfc); }
/// <summary> /// Add a new saved remote function call. /// </summary> public void AddRFC(uint uid, string funcName, Buffer buffer) { if (closed || buffer == null) { return; } uint objID = (uid >> 8); // Ignore objects that don't exist if (objID < 32768) { if (destroyed.Contains(objID)) { return; } } else if (!mCreatedObjectDictionary.ContainsKey(objID)) { return; } Buffer b = Buffer.Create(); b.BeginWriting(false).Write(buffer.buffer, buffer.position, buffer.size); b.EndWriting(); for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.uid == uid && r.functionName == funcName) { if (r.data != null) { r.data.Recycle(); } r.data = b; return; } } RFC rfc = new RFC(); rfc.uid = uid; rfc.data = b; rfc.functionName = funcName; rfcs.Add(rfc); }
/// <summary> /// Receive incoming data. /// </summary> void OnReceive(IAsyncResult result) { if (!isActive) { return; } int bytes = 0; try { bytes = mSocket.EndReceiveFrom(result, ref mEndPoint); } catch (System.Exception ex) { Error(new IPEndPoint(Tools.localAddress, 0), ex.Message); } if (bytes > 4) { // This datagram is now ready to be processed Buffer buffer = Buffer.Create(); buffer.BeginWriting(false).Write(mTemp, 0, bytes); buffer.BeginReading(4); // The 'endPoint', gets reassigned rather than updated. Datagram dg = new Datagram(); dg.buffer = buffer; dg.ip = (IPEndPoint)mEndPoint; lock (mIn) mIn.Enqueue(dg); } // Queue up the next receive operation if (mSocket != null) { mEndPoint = mDefaultEndPoint; try { mSocket.BeginReceiveFrom(mTemp, 0, mTemp.Length, SocketFlags.None, ref mEndPoint, OnReceive, null); } catch (System.Exception ex) { Error(new IPEndPoint(Tools.localAddress, 0), ex.Message); } } }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(int bytes) { if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(mTemp, 0, bytes); } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(mTemp, 0, bytes); } for (int available = mReceiveBuffer.size - mOffset; available >= 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); if (mExpected < 0 || mExpected > 16777216) { Close(true); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet available -= 4; // If the entire packet is present if (available == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; break; } else if (available > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; Buffer temp = Buffer.Create(); // Extract the packet and move past its size component temp.BeginWriting(false).Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(temp); // Skip this packet available -= mExpected; mOffset += realSize; mExpected = 0; } else { break; } } return(true); }
/// <summary> /// Load the channel's data from the specified file. /// </summary> public bool LoadFrom(BinaryReader reader) { int version = reader.ReadInt32(); if (version < 20160207) { #if UNITY_EDITOR UnityEngine.Debug.LogWarning("Incompatible data: " + version); #endif return(false); } // Clear all RFCs, just in case for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.data != null) { r.data.Recycle(); } } rfcs.Clear(); created.Clear(); destroyed.Clear(); mCreatedObjectDictionary.Clear(); level = reader.ReadString(); dataNode = reader.ReadDataNode(); objectCounter = reader.ReadUInt32(); password = reader.ReadString(); persistent = reader.ReadBoolean(); playerLimit = reader.ReadUInt16(); int size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { RFC rfc = new RFC(); rfc.uid = reader.ReadUInt32(); if (rfc.functionID == 0) { rfc.functionName = reader.ReadString(); } Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); b.EndWriting(); rfc.data = b; rfcs.Add(rfc); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { CreatedObject co = new CreatedObject(); co.playerID = reader.ReadInt32(); co.objectID = reader.ReadUInt32(); co.type = 1; Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); b.EndWriting(); co.buffer = b; AddCreatedObject(co); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { uint uid = reader.ReadUInt32(); if (uid < 32768) { destroyed.Add(uid); } } isLocked = reader.ReadBoolean(); return(true); }
/// <summary> /// Channel joining process involves multiple steps. It's faster to perform them all at once. /// </summary> public void FinishJoiningChannel() { Buffer buffer = Buffer.Create(); // Step 2: Tell the player who else is in the channel BinaryWriter writer = buffer.BeginPacket(Packet.ResponseJoiningChannel); { writer.Write(channel.id); writer.Write((short)channel.players.size); for (int i = 0; i < channel.players.size; ++i) { TcpPlayer tp = channel.players[i]; writer.Write(tp.id); writer.Write(string.IsNullOrEmpty(tp.name) ? "Guest" : tp.name); } } // End the first packet, but remember where it ended int offset = buffer.EndPacket(); // Step 3: Inform the player of who is hosting if (channel.host == null) { channel.host = this; } buffer.BeginPacket(Packet.ResponseSetHost, offset); writer.Write(channel.host.id); offset = buffer.EndTcpPacketStartingAt(offset); // Step 4: Send the channel's data if (!string.IsNullOrEmpty(channel.data)) { buffer.BeginPacket(Packet.ResponseSetChannelData, offset); writer.Write(channel.data); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 5: Inform the player of what level we're on buffer.BeginPacket(Packet.ResponseLoadLevel, offset); writer.Write(string.IsNullOrEmpty(channel.level) ? "" : channel.level); offset = buffer.EndTcpPacketStartingAt(offset); // Step 6: Send the list of objects that have been created for (int i = 0; i < channel.created.size; ++i) { Channel.CreatedObject obj = channel.created.buffer[i]; buffer.BeginPacket(Packet.ResponseCreate, offset); writer.Write(obj.playerID); writer.Write(obj.objectID); writer.Write(obj.uniqueID); writer.Write(obj.buffer.buffer, obj.buffer.position, obj.buffer.size); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 7: Send the list of objects that have been destroyed if (channel.destroyed.size != 0) { buffer.BeginPacket(Packet.ResponseDestroy, offset); writer.Write((ushort)channel.destroyed.size); for (int i = 0; i < channel.destroyed.size; ++i) { writer.Write(channel.destroyed.buffer[i]); } offset = buffer.EndTcpPacketStartingAt(offset); } // Step 8: Send all buffered RFCs to the new player for (int i = 0; i < channel.rfcs.size; ++i) { Buffer rfcBuff = channel.rfcs[i].buffer; rfcBuff.BeginReading(); buffer.BeginWriting(offset); writer.Write(rfcBuff.buffer, rfcBuff.position, rfcBuff.size); offset = buffer.EndWriting(); } // Step 9: The join process is now complete buffer.BeginPacket(Packet.ResponseJoinChannel, offset); writer.Write(true); offset = buffer.EndTcpPacketStartingAt(offset); // Send the entire buffer SendTcpPacket(buffer); buffer.Recycle(); }
/// <summary> /// Copy the contents of this buffer into the target one, trimming away unused space. /// </summary> public void CopyTo(Buffer target) { BinaryWriter w = target.BeginWriting(false); int bytes = size; if (bytes > 0) w.Write(buffer, position, bytes); target.EndWriting(); }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(int bytes) { if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(mTemp, 0, bytes); mExpected = 0; mOffset = 0; } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(mTemp, 0, bytes); } for (int available = mReceiveBuffer.size - mOffset; available >= 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); // "GET " -- HTTP GET request sent by a web browser if (mExpected == 542393671) { if (httpGetSupport) { if (stage == Stage.Verifying || stage == Stage.WebBrowser) { stage = Stage.WebBrowser; string request = Encoding.ASCII.GetString(mReceiveBuffer.buffer, mOffset, available); mReceiveBuffer.BeginPacket(Packet.RequestHTTPGet).Write(request); mReceiveBuffer.EndPacket(); mReceiveBuffer.BeginReading(4); lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } return(true); } mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } else if (mExpected < 0 || mExpected > 16777216) { #if UNITY_EDITOR UnityEngine.Debug.LogError("Malformed data packet: " + mOffset + ", " + available + " / " + mExpected); #else Tools.LogError("Malformed data packet: " + mOffset + ", " + available + " / " + mExpected); #endif mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet available -= 4; // If the entire packet is present if (available == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; break; } else if (available > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; Buffer temp = Buffer.Create(); // Extract the packet and move past its size component BinaryWriter bw = temp.BeginWriting(false); bw.Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(temp); // Skip this packet available -= mExpected; mOffset += realSize; mExpected = 0; } else { break; } } return(true); }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer) { buffer.MarkAsUsed(); BinaryReader reader = buffer.BeginReading(); #if DEBUG_PACKETS && !STANDALONE Packet packet = (Packet)buffer.PeekByte(4); if (packet != Packet.RequestPing && packet != Packet.ResponsePing) { UnityEngine.Debug.Log("Sending: " + packet + " to " + name + " (" + (buffer.size - 5).ToString("N0") + " bytes)"); } #endif if (mSocket != null && mSocket.Connected) { lock (mOut) { mOut.Enqueue(buffer); // If it's the first packet, let's begin the send process if (mOut.Count == 1) { try { #if !UNITY_WINRT mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); #endif } catch (System.Exception ex) { mOut.Clear(); buffer.Recycle(); RespondWithError(ex); CloseNotThreadSafe(false); } } } return; } if (sendQueue != null) { if (buffer.position != 0) { // Offline mode sends packets individually and they should not be reused #if UNITY_EDITOR Debug.LogWarning("Packet's position is " + buffer.position + " instead of 0. Potentially sending the same packet more than once. Ignoring..."); #endif return; } // Skip the packet's size int size = reader.ReadInt32(); if (size == buffer.size) { // Note that after this the buffer can no longer be used again as its offset is +4 lock (sendQueue) sendQueue.Enqueue(buffer); return; } // Multi-part packet -- split it up into separate ones lock (sendQueue) { for (;;) { byte[] bytes = reader.ReadBytes(size); Buffer temp = Buffer.Create(); BinaryWriter writer = temp.BeginWriting(); writer.Write(size); writer.Write(bytes); temp.BeginReading(4); temp.EndWriting(); sendQueue.Enqueue(temp); if (buffer.size > 0) { size = reader.ReadInt32(); } else { break; } } } } buffer.Recycle(); }
/// <summary> /// Load the channel's data from the specified file. /// </summary> public bool LoadFrom(BinaryReader reader) { int version = reader.ReadInt32(); if (version != Player.version) { return(false); } // Clear all RFCs, just in case for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.buffer != null) { r.buffer.Recycle(); } } rfcs.Clear(); created.Clear(); destroyed.Clear(); level = reader.ReadString(); data = reader.ReadString(); objectCounter = reader.ReadUInt32(); password = reader.ReadString(); persistent = reader.ReadBoolean(); playerLimit = reader.ReadUInt16(); int size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { RFC rfc = new RFC(); rfc.id = reader.ReadUInt32(); Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); rfc.buffer = b; rfcs.Add(rfc); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { CreatedObject co = new CreatedObject(); co.playerID = reader.ReadInt32(); co.uniqueID = reader.ReadUInt32(); co.objectID = reader.ReadUInt16(); Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); co.buffer = b; created.Add(co); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { destroyed.Add(reader.ReadUInt32()); } return(true); }
/// <summary> /// Channel joining process involves multiple steps. It's faster to perform them all at once. /// </summary> public void FinishJoiningChannel() { Buffer buffer = Buffer.Create(); // Step 2: Tell the player who else is in the channel BinaryWriter writer = buffer.BeginPacket(Packet.ResponseJoiningChannel); { writer.Write(channel.id); writer.Write((short)channel.players.size); for (int i = 0; i < channel.players.size; ++i) { TcpPlayer tp = channel.players[i]; writer.Write(tp.id); writer.Write(string.IsNullOrEmpty(tp.name) ? "Guest" : tp.name); #if STANDALONE if (tp.data == null) { writer.Write((byte)0); } else { writer.Write((byte[])tp.data); } #else writer.WriteObject(tp.data); #endif } } // End the first packet, but remember where it ended int offset = buffer.EndPacket(); // Step 3: Inform the player of who is hosting if (channel.host == null) { channel.host = this; } buffer.BeginPacket(Packet.ResponseSetHost, offset); writer.Write(channel.host.id); offset = buffer.EndTcpPacketStartingAt(offset); // Step 4: Send the channel's data if (!string.IsNullOrEmpty(channel.data)) { buffer.BeginPacket(Packet.ResponseSetChannelData, offset); writer.Write(channel.data); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 5: Inform the player of what level we're on buffer.BeginPacket(Packet.ResponseLoadLevel, offset); writer.Write(string.IsNullOrEmpty(channel.level) ? "" : channel.level); offset = buffer.EndTcpPacketStartingAt(offset); // Step 6: Send the list of objects that have been created for (int i = 0; i < channel.created.size; ++i) { Channel.CreatedObject obj = channel.created.buffer[i]; bool isPresent = false; for (int b = 0; b < channel.players.size; ++b) { if (channel.players[b].id == obj.playerID) { isPresent = true; break; } } // If the previous owner is not present, transfer ownership to the host if (!isPresent) { obj.playerID = channel.host.id; } buffer.BeginPacket(Packet.ResponseCreate, offset); writer.Write(obj.playerID); writer.Write(obj.objectIndex); writer.Write(obj.objectID); writer.Write(obj.buffer.buffer, obj.buffer.position, obj.buffer.size); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 7: Send the list of objects that have been destroyed if (channel.destroyed.size != 0) { buffer.BeginPacket(Packet.ResponseDestroy, offset); writer.Write((ushort)channel.destroyed.size); for (int i = 0; i < channel.destroyed.size; ++i) { writer.Write(channel.destroyed.buffer[i]); } offset = buffer.EndTcpPacketStartingAt(offset); } // Step 8: Send all buffered RFCs to the new player for (int i = 0; i < channel.rfcs.size; ++i) { Channel.RFC rfc = channel.rfcs[i]; buffer.BeginWriting(offset); writer.Write(rfc.buffer.buffer, 0, rfc.buffer.size); offset = buffer.EndWriting(); } // Step 9: The join process is now complete buffer.BeginPacket(Packet.ResponseJoinChannel, offset); writer.Write(true); offset = buffer.EndTcpPacketStartingAt(offset); // Send the entire buffer SendTcpPacket(buffer); buffer.Recycle(); }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(byte[] bytes, int offset, int byteCount) { if (offset + byteCount > bytes.Length) { LogError("ProcessBuffer(" + bytes.Length + " bytes, offset " + offset + ", count " + byteCount); return(false); } if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(bytes, offset, byteCount); mExpected = 0; mOffset = 0; } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(bytes, offset, byteCount); } for (mAvailable = mReceiveBuffer.size - mOffset; mAvailable > 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); // "GET " -- HTTP GET request sent by a web browser if (mExpected == 542393671) { if (httpGetSupport) { if (stage == Stage.Verifying || stage == Stage.WebBrowser) { stage = Stage.WebBrowser; string request = Encoding.ASCII.GetString(mReceiveBuffer.buffer, mOffset, mAvailable); mReceiveBuffer.BeginPacket(Packet.RequestHTTPGet).Write(request); mReceiveBuffer.EndPacket(); mReceiveBuffer.BeginReading(4); lock (mIn) { mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } } return(true); } mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } else if (mExpected < 0 || mExpected > 16777216) { #if UNITY_EDITOR LogError("Malformed data packet: " + mOffset + ", " + mAvailable + " / " + mExpected); var temp = new byte[mReceiveBuffer.size]; for (int i = 0; i < byteCount; ++i) { temp[i] = mReceiveBuffer.buffer[i]; } var fn = "error_" + lastReceivedTime + ".full"; Tools.WriteFile(fn, temp); Debug.Log("Packet saved as " + fn); #else LogError("Malformed data packet: " + mOffset + ", " + mAvailable + " / " + mExpected); #endif mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet mAvailable -= 4; // If the entire packet is present if (mAvailable == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) { mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } break; } else if (mAvailable > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; var temp = Buffer.Create(); // Extract the packet and move past its size component var bw = temp.BeginWriting(); bw.Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) { mIn.Enqueue(temp); // Skip this packet mAvailable -= mExpected; mOffset += realSize; mExpected = 0; } } else { break; } } return(true); }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(int bytes) { if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(mTemp, 0, bytes); } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(mTemp, 0, bytes); } for (int available = mReceiveBuffer.size - mOffset; available >= 4; ) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); if (mExpected == -1) break; if (mExpected == 0) { Close(true); return false; } } // The first 4 bytes of any packet always contain the number of bytes in that packet available -= 4; // If the entire packet is present if (available == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; break; } else if (available > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; Buffer temp = Buffer.Create(); // Extract the packet and move past its size component temp.BeginWriting(false).Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(temp); // Skip this packet available -= mExpected; mOffset += realSize; mExpected = 0; } else break; } return true; }