/// <summary> /// Create a new buffered remote function call. /// </summary> public void CreateRFC(uint inID, string funcName, Buffer buffer) { if (closed || buffer == null) { return; } buffer.MarkAsUsed(); for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.id == inID && r.funcName == funcName) { if (r.buffer != null) { r.buffer.Recycle(); } r.buffer = buffer; return; } } RFC rfc = new RFC(); rfc.id = inID; rfc.buffer = buffer; rfc.funcName = funcName; rfcs.Add(rfc); }
/// <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> /// 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> /// Import a previously exported object. Returns its object ID, or '0' if failed. /// </summary> public uint ImportObject(int playerID, BinaryReader reader) { #if !MODDING // Create a new object and read its RCC data var co = new CreatedObject(); co.objectID = GetUniqueID(); co.type = 1; co.buffer = Buffer.Create(); var data = reader.ReadBytes(reader.ReadInt32()); co.buffer.BeginWriting(false).Write(data); co.buffer.EndWriting(); AddCreatedObject(co); // We need to inform all the players in the channel of this object's creation var packet = Buffer.Create(); var writer = packet.BeginPacket(Packet.ResponseCreateObject); writer.Write(playerID); writer.Write(id); writer.Write(co.objectID); writer.Write(data); packet.EndPacket(); SendToAll(packet); packet.Recycle(); // Now read all the RFCs var size = reader.ReadInt32(); if (size != 0) { for (int i = 0; i < size; ++i) { var rfc = new RFC(); rfc.uid = reader.ReadUInt32(); rfc.objectID = co.objectID; if (rfc.functionID == 0) { rfc.functionName = reader.ReadString(); } data = reader.ReadBytes(reader.ReadInt32()); var b = Buffer.Create(); b.BeginWriting(false).Write(data); b.EndWriting(); rfc.data = b; rfcs.Add(rfc); packet = Buffer.Create(); rfc.WritePacket(id, packet, 0); SendToAll(packet); packet.Recycle(); } } return(co.objectID); #else return(0); #endif }
/// <summary> /// Transfer the specified object to another channel, changing its Object ID in the process. /// </summary> public CreatedObject TransferObject(uint objectID, Channel other) { if (objectID < 32768) { Tools.LogError("Transferring objects only works with objects that were instantiated at run-time."); } else if (mCreatedObjectDictionary.Remove(objectID)) { for (int i = 0; i < created.size; ++i) { CreatedObject obj = created[i]; if (obj.objectID == objectID) { // Move the created object over to the other channel obj.objectID = other.GetUniqueID(); // If the other channel doesn't contain the object's owner, assign a new owner bool changeOwner = true; for (int b = 0; b < other.players.size; ++b) { if (other.players[b].id == obj.playerID) { changeOwner = false; break; } } if (changeOwner) { obj.playerID = (other.host != null) ? other.host.id : 0; } created.RemoveAt(i); other.created.Add(obj); other.mCreatedObjectDictionary[obj.objectID] = true; // Move RFCs over to the other channel for (int b = rfcs.size; b > 0;) { RFC r = rfcs[--b]; if (r.objectID == objectID) { r.objectID = obj.objectID; rfcs.RemoveAt(b); other.rfcs.Add(r); } } return(obj); } } } return(null); }
/// <summary> /// Add a new saved remote function call. /// </summary> public void AddRFC(uint uid, string funcName, Buffer buffer) { #if !MODDING 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; } var b = Buffer.Create(); b.BeginWriting(false).Write(buffer.buffer, buffer.position, buffer.size); b.EndWriting(); for (int i = 0; i < rfcs.size; ++i) { var r = rfcs[i]; if (r.uid == uid && r.functionName == funcName) { if (r.data != null) { r.data.Recycle(); } r.data = b; // Move this RFC to the end of the list so that it gets called in correct order on load rfcs.RemoveAt(i); rfcs.Add(r); return; } } var rfc = new RFC(); rfc.uid = uid; rfc.data = b; rfc.functionName = funcName; rfcs.Add(rfc); #endif }
/// <summary> /// Delete the specified remote function call. /// </summary> public void DeleteRFC(uint inID, string funcName) { for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.uid == inID && r.functionName == funcName) { rfcs.RemoveAt(i); r.data.Recycle(); } } }
/// <summary> /// Delete the specified remote function call. /// </summary> public void DestroyObjectRFCs(uint objectID) { for (int i = rfcs.size; i > 0;) { RFC r = rfcs[--i]; if (r.objectID == objectID) { rfcs.RemoveAt(i); r.data.Recycle(); } } }
/// <summary> /// Delete the specified remote function call. /// </summary> public void DestroyObjectRFCs(uint objectID) { for (int i = 0; i < rfcs.size;) { RFC r = rfcs[i]; if ((r.id >> 8) == objectID) { rfcs.RemoveAt(i); r.buffer.Recycle(); continue; } ++i; } }
/// <summary> /// Rebuild the list of known RFC calls. /// </summary> void RebuildMethodList() { rebuildMethodList = false; mDict0.Clear(); mDict1.Clear(); MonoBehaviour[] mbs = GetComponentsInChildren <MonoBehaviour>(true); for (int i = 0, imax = mbs.Length; i < imax; ++i) { MonoBehaviour mb = mbs[i]; System.Type type = mb.GetType(); MethodInfo[] methods = type.GetMethods( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int b = 0, bmax = methods.Length; b < bmax; ++b) { MethodInfo method = methods[b]; if (method.IsDefined(typeof(RFC), true)) { CachedFunc ent = new CachedFunc(); ent.obj = mb; ent.func = method; RFC tnc = (RFC)ent.func.GetCustomAttributes(typeof(RFC), true)[0]; if (tnc.id > 0) { if (tnc.id < 256) { mDict0[tnc.id] = ent; } else { Debug.LogError("RFC IDs need to be between 1 and 255 (1 byte). If you need more, just don't specify an ID and use the function's name instead."); } } else { mDict1[method.Name] = ent; } } } } }
/// <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> /// Save the channel's data into the specified file. /// </summary> public void SaveTo(BinaryWriter writer) { writer.Write(Player.version); writer.Write(level); writer.Write(data); writer.Write(objectCounter); writer.Write(password); writer.Write(persistent); writer.Write(playerLimit); writer.Write(rfcs.size); for (int i = 0; i < rfcs.size; ++i) { RFC rfc = rfcs[i]; writer.Write(rfc.id); writer.Write(rfc.buffer.size); if (rfc.buffer.size > 0) { rfc.buffer.BeginReading(); writer.Write(rfc.buffer.buffer, rfc.buffer.position, rfc.buffer.size); } } writer.Write(created.size); for (int i = 0; i < created.size; ++i) { CreatedObject co = created[i]; writer.Write(co.playerID); writer.Write(co.uniqueID); writer.Write(co.objectID); writer.Write(co.buffer.size); if (co.buffer.size > 0) { co.buffer.BeginReading(); writer.Write(co.buffer.buffer, co.buffer.position, co.buffer.size); } } writer.Write(destroyed.size); for (int i = 0; i < destroyed.size; ++i) { writer.Write(destroyed[i]); } }
/// <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> /// Create a new buffered remote function call. /// </summary> public void CreateRFC(uint inID, string funcName, Buffer buffer) { if (closed || buffer == null) return; buffer.MarkAsUsed(); for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.id == inID && r.funcName == funcName) { if (r.buffer != null) r.buffer.Recycle(); r.buffer = buffer; return; } } RFC rfc = new RFC(); rfc.id = inID; rfc.buffer = buffer; rfc.funcName = funcName; rfcs.Add(rfc); }
/// <summary> /// Load the channel's data from the specified file. /// </summary> public bool LoadFrom(BinaryReader reader, bool keepInMemory = false) { #if !MODDING var start = reader.BaseStream.Position; 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) { var 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) { var rfc = new RFC(); rfc.uid = reader.ReadUInt32(); if (rfc.functionID == 0) { rfc.functionName = reader.ReadString(); } var 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) { var co = new CreatedObject(); co.playerID = reader.ReadInt32(); co.objectID = reader.ReadUInt32(); co.playerID = 0; // The player ID is no longer valid as player IDs reset on reload co.type = 1; var b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); b.EndWriting(); co.buffer = b; AddCreatedObject(co); // TODO: Remove /*var r = b.BeginReading(); * var rccID = r.ReadByte(); * var funcName = (rccID == 0) ? r.ReadString() : null; * * if (funcName == "OnSpawn") * { * var prefab = r.ReadString(); * * if (prefab == "Vehicle S0") * { * var pos = ProceduralTerrain.GetTilePosition(id); * pos.x = Game.UnitsToMeters(pos.x); * pos.y = Game.UnitsToMeters(pos.y); * Debug.Log("/goto " + MathD.RoundToInt(pos.x) + " " + MathD.RoundToInt(pos.y)); * } * } * b.position = 0;*/ } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { uint uid = reader.ReadUInt32(); if (uid < 32768) { destroyed.Add(uid); } } isLocked = reader.ReadBoolean(); mSource = null; #if STANDALONE if (!keepInMemory && players.size == 0) { Reset(); var end = reader.BaseStream.Position; reader.BaseStream.Position = start; mSourceSize = (int)(end - start); mSource = reader.ReadBytes(mSourceSize); } #endif #endif return(true); }
/// <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> /// 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> /// 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> /// Save the channel's data into the specified file. /// </summary> public void SaveTo(BinaryWriter writer) { writer.Write(Player.version); writer.Write(level); writer.Write(dataNode); writer.Write(objectCounter); writer.Write(password); writer.Write(persistent); writer.Write(playerLimit); // Record which objects are temporary and which ones are not for (int i = 0; i < created.size; ++i) { CreatedObject co = created[i]; if (co.type == 1) { mCreatedOBJs.Add(co); mCleanedOBJs.Add(co.objectID); } } // Record all RFCs that don't belong to temporary objects for (int i = 0; i < rfcs.size; ++i) { RFC rfc = rfcs[i]; uint objID = rfc.objectID; if (objID < 32768) { mCreatedRFCs.Add(rfc); } else { for (int b = 0; b < mCleanedOBJs.size; ++b) { if (mCleanedOBJs.buffer[b] == objID) { mCreatedRFCs.Add(rfc); break; } } } } writer.Write(mCreatedRFCs.size); for (int i = 0; i < mCreatedRFCs.size; ++i) { RFC rfc = mCreatedRFCs[i]; writer.Write(rfc.uid); if (rfc.functionID == 0) { writer.Write(rfc.functionName); } writer.Write(rfc.data.size); if (rfc.data.size > 0) { writer.Write(rfc.data.buffer, rfc.data.position, rfc.data.size); } } writer.Write(mCreatedOBJs.size); for (int i = 0; i < mCreatedOBJs.size; ++i) { CreatedObject co = mCreatedOBJs[i]; writer.Write(co.playerID); writer.Write(co.objectID); writer.Write(co.buffer.size); if (co.buffer.size > 0) { writer.Write(co.buffer.buffer, co.buffer.position, co.buffer.size); } } writer.Write(destroyed.size); for (int i = 0; i < destroyed.size; ++i) { writer.Write(destroyed[i]); } mCleanedOBJs.Clear(); mCreatedOBJs.Clear(); mCreatedRFCs.Clear(); writer.Write(isLocked); }
/// <summary> /// Save the channel's data into the specified file. /// </summary> public void SaveTo(BinaryWriter writer) { writer.Write(Player.version); writer.Write(level); writer.Write(data); writer.Write(objectCounter); writer.Write(password); writer.Write(persistent); writer.Write(playerLimit); List <uint> tempObjs = new List <uint>(); List <CreatedObject> cleanedObjs = new List <CreatedObject>(); List <RFC> cleanedRFCs = new List <RFC>(); // Record which objects are temporary and which ones are not for (int i = 0; i < created.size; ++i) { CreatedObject co = created[i]; if (co.type == 1) { cleanedObjs.Add(co); } else { tempObjs.Add(co.uniqueID); } } // Record all RFCs that don't belong to temporary objects for (int i = 0; i < rfcs.size; ++i) { RFC rfc = rfcs[i]; if (!tempObjs.Contains(rfc.objectID)) { cleanedRFCs.Add(rfc); } } writer.Write(cleanedRFCs.size); for (int i = 0; i < cleanedRFCs.size; ++i) { RFC rfc = cleanedRFCs[i]; writer.Write(rfc.uid); if (rfc.functionID == 0) { writer.Write(rfc.functionName); } writer.Write(rfc.buffer.size); if (rfc.buffer.size > 0) { rfc.buffer.BeginReading(); writer.Write(rfc.buffer.buffer, rfc.buffer.position, rfc.buffer.size); } } writer.Write(cleanedObjs.size); for (int i = 0; i < cleanedObjs.size; ++i) { CreatedObject co = cleanedObjs[i]; writer.Write(co.playerID); writer.Write(co.uniqueID); writer.Write(co.objectID); writer.Write(co.buffer.size); if (co.buffer.size > 0) { co.buffer.BeginReading(); writer.Write(co.buffer.buffer, co.buffer.position, co.buffer.size); } } writer.Write(destroyed.size); for (int i = 0; i < destroyed.size; ++i) { writer.Write(destroyed[i]); } }