/// <summary> /// Remove an object with the specified unique identifier. /// </summary> public bool DestroyObject(uint objID) { if (objID < 32768) { // Static objects have ID below 32768 if (!destroyed.Contains(objID)) { destroyed.Add(objID); DestroyObjectRFCs(objID); return(true); } } else if (mCreatedObjectDictionary.Remove(objID)) { // Dynamic objects are always a part of the 'created' array and the lookup table for (int i = 0; i < created.size; ++i) { Channel.CreatedObject obj = created[i]; if (obj.objectID == objID) { if (obj.buffer != null) { obj.buffer.Recycle(); } created.RemoveAt(i); DestroyObjectRFCs(objID); return(true); } } } return(false); }
/// <summary> /// Remove an object with the specified unique identifier. /// </summary> public bool DestroyObject(uint uniqueID) { if (!destroyed.Contains(uniqueID)) { for (int i = 0; i < created.size; ++i) { Channel.CreatedObject obj = created[i]; if (obj.uniqueID == uniqueID) { if (obj.buffer != null) { obj.buffer.Recycle(); } created.RemoveAt(i); DestroyObjectRFCs(uniqueID); return(true); } } destroyed.Add(uniqueID); DestroyObjectRFCs(uniqueID); return(true); } return(false); }
/// <summary> /// Remove the specified player from the channel. /// </summary> public void RemovePlayer(TcpPlayer p, List <uint> destroyedObjects) { destroyedObjects.Clear(); if (players.Remove(p)) { // When the host leaves, clear the host (it gets changed in SendLeaveChannel) if (p == host) { host = null; } // Remove all of the non-persistent objects that were created by this player for (int i = created.size; i > 0;) { Channel.CreatedObject obj = created[--i]; if (obj.playerID == p.id) { if (obj.type == 2) { if (obj.buffer != null) { obj.buffer.Recycle(); } uint objID = obj.objectID; created.RemoveAt(i); destroyedObjects.Add(objID); if (objID >= 32768) { mCreatedObjectDictionary.Remove(objID); } DestroyObjectRFCs(objID); } else if (players.size != 0) { // The same operation happens on the client as well obj.playerID = players[0].id; } } } // Close the channel if it wasn't persistent if ((!persistent || playerLimit < 1) && players.size == 0) { closed = true; for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.data != null) { r.data.Recycle(); } } rfcs.Clear(); } } }
/// <summary> /// Remove the specified player from the channel. /// </summary> public void RemovePlayer(TcpPlayer p, List <uint> destroyedObjects) { destroyedObjects.Clear(); if (p == host) { host = null; } if (players.Remove(p)) { // Remove all of the non-persistent objects that were created by this player for (int i = created.size; i > 0;) { Channel.CreatedObject obj = created[--i]; if (obj.type == 2 && obj.playerID == p.id) { if (obj.buffer != null) { obj.buffer.Recycle(); } created.RemoveAt(i); destroyedObjects.Add(obj.uniqueID); DestroyObjectRFCs(obj.uniqueID); } } // Close the channel if it wasn't persistent if ((!persistent || playerLimit < 1) && players.size == 0) { closed = true; for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.buffer != null) { r.buffer.Recycle(); } } rfcs.Clear(); } } }
/// <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> /// 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> /// Process a packet from the player. /// </summary> void ProcessChannelPacket(TcpPlayer player, Buffer buffer, BinaryReader reader, Packet request) { switch (request) { case Packet.RequestCreate: { // Create a new object ushort objectIndex = reader.ReadUInt16(); byte type = reader.ReadByte(); uint uniqueID = 0; if (type != 0) { uniqueID = --player.channel.objectCounter; // 1-32767 is reserved for existing scene objects. // 32768 - 16777215 is for dynamically created objects. if (uniqueID < 32768) { player.channel.objectCounter = 0xFFFFFF; uniqueID = 0xFFFFFF; } Channel.CreatedObject obj = new Channel.CreatedObject(); obj.playerID = player.id; obj.objectID = objectIndex; obj.uniqueID = uniqueID; obj.type = type; if (buffer.size > 0) { obj.buffer = buffer; buffer.MarkAsUsed(); } player.channel.created.Add(obj); } // Inform the channel BinaryWriter writer = BeginSend(Packet.ResponseCreate); writer.Write(player.id); writer.Write(objectIndex); writer.Write(uniqueID); if (buffer.size > 0) { writer.Write(buffer.buffer, buffer.position, buffer.size); } EndSend(true, player.channel, null); break; } case Packet.RequestDestroy: { // Destroy the specified network object uint uniqueID = reader.ReadUInt32(); // Remove this object if (player.channel.DestroyObject(uniqueID)) { // Inform all players in the channel that the object should be destroyed BinaryWriter writer = BeginSend(Packet.ResponseDestroy); writer.Write((ushort)1); writer.Write(uniqueID); EndSend(true, player.channel, null); } break; } case Packet.RequestLoadLevel: { // Change the currently loaded level if (player.channel.host == player) { player.channel.Reset(); player.channel.level = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseLoadLevel); writer.Write(string.IsNullOrEmpty(player.channel.level) ? "" : player.channel.level); EndSend(true, player.channel, null); } break; } case Packet.RequestSetHost: { // Transfer the host state from one player to another if (player.channel.host == player) { TcpPlayer newHost = GetPlayer(reader.ReadInt32()); if (newHost != null && newHost.channel == player.channel) { SendSetHost(newHost); } } break; } case Packet.RequestLeaveChannel: { SendLeaveChannel(player, true); break; } case Packet.RequestCloseChannel: { player.channel.persistent = false; player.channel.closed = true; break; } case Packet.RequestSetPlayerLimit: { player.channel.playerLimit = reader.ReadUInt16(); break; } case Packet.RequestRemoveRFC: { uint id = reader.ReadUInt32(); string funcName = ((id & 0xFF) == 0) ? reader.ReadString() : null; player.channel.DeleteRFC(id, funcName); break; } case Packet.RequestSetChannelData: { player.channel.data = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseSetChannelData); writer.Write(player.channel.data); EndSend(true, player.channel, null); break; } } }
/// <summary> /// Process a packet from the player. /// </summary> void ProcessChannelPacket(TcpPlayer player, Buffer buffer, BinaryReader reader, Packet request) { switch (request) { case Packet.RequestCreate: { // Create a new object ushort objectIndex = reader.ReadUInt16(); byte type = reader.ReadByte(); uint uniqueID = 0; if (type != 0) { uniqueID = --player.channel.objectCounter; // 1-32767 is reserved for existing scene objects. // 32768 - 16777215 is for dynamically created objects. if (uniqueID < 32768) { player.channel.objectCounter = 0xFFFFFF; uniqueID = 0xFFFFFF; } Channel.CreatedObject obj = new Channel.CreatedObject(); obj.playerID = player.id; obj.objectID = objectIndex; obj.uniqueID = uniqueID; obj.type = type; if (buffer.size > 0) { obj.buffer = buffer; buffer.MarkAsUsed(); } player.channel.created.Add(obj); } // Inform the channel BinaryWriter writer = BeginSend(Packet.ResponseCreate); writer.Write(player.id); writer.Write(objectIndex); writer.Write(uniqueID); if (buffer.size > 0) writer.Write(buffer.buffer, buffer.position, buffer.size); EndSend(true, player.channel, null); break; } case Packet.RequestDestroy: { // Destroy the specified network object uint uniqueID = reader.ReadUInt32(); // Remove this object if (player.channel.DestroyObject(uniqueID)) { // Inform all players in the channel that the object should be destroyed BinaryWriter writer = BeginSend(Packet.ResponseDestroy); writer.Write((ushort)1); writer.Write(uniqueID); EndSend(true, player.channel, null); } break; } case Packet.RequestLoadLevel: { // Change the currently loaded level if (player.channel.host == player) { player.channel.Reset(); player.channel.level = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseLoadLevel); writer.Write(string.IsNullOrEmpty(player.channel.level) ? "" : player.channel.level); EndSend(true, player.channel, null); } break; } case Packet.RequestSetHost: { // Transfer the host state from one player to another if (player.channel.host == player) { TcpPlayer newHost = GetPlayer(reader.ReadInt32()); if (newHost != null && newHost.channel == player.channel) SendSetHost(newHost); } break; } case Packet.RequestLeaveChannel: { SendLeaveChannel(player, true); break; } case Packet.RequestCloseChannel: { player.channel.persistent = false; player.channel.closed = true; break; } case Packet.RequestSetPlayerLimit: { player.channel.playerLimit = reader.ReadUInt16(); break; } case Packet.RequestRemoveRFC: { uint id = reader.ReadUInt32(); string funcName = ((id & 0xFF) == 0) ? reader.ReadString() : null; player.channel.DeleteRFC(id, funcName); break; } case Packet.RequestSetChannelData: { player.channel.data = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseSetChannelData); writer.Write(player.channel.data); EndSend(true, player.channel, null); break; } } }