/// <summary> /// Deserialize a previously serialized game object. /// </summary> static void DeserializeComponents(this GameObject go, DataNode root) { DataNode scriptNode = root.GetChild("Components"); if (scriptNode == null) { return; } for (int i = 0; i < scriptNode.children.size; ++i) { DataNode node = scriptNode.children[i]; System.Type type = UnityTools.GetType(node.name); if (type != null && type.IsSubclassOf(typeof(Component))) { Component comp = go.GetComponent(type); if (comp == null) { comp = go.AddComponent(type); } comp.Deserialize(node); } } }
/// <summary> /// Create a packet that will send a custom object creation call. /// It is expected that the first byte that follows will identify which function will be parsing this packet later. /// </summary> static public void CreateEx(int rccID, bool persistent, GameObject go, params object[] objs) { if (go != null) { int index = IndexOf(go); if (isConnected) { if (index != -1) { BinaryWriter writer = mInstance.mClient.BeginSend(Packet.RequestCreate); writer.Write((ushort)index); writer.Write(GetFlag(go, persistent)); writer.Write((byte)rccID); writer.WriteArray(objs); EndSend(); return; } else { Debug.LogError("\"" + go.name + "\" has not been added to TNManager's list of objects, so it cannot be instantiated.\n" + "Consider placing it into the Resources folder and passing its name instead.", go); } } objs = BinaryExtensions.CombineArrays(go, objs); UnityTools.ExecuteAll(GetRCCs(), (byte)rccID, objs); UnityTools.Clear(objs); } }
/// <summary> /// Create a packet that will send a custom object creation call. /// It is expected that the first byte that follows will identify which function will be parsing this packet later. /// </summary> static public void CreateEx(int rccID, bool persistent, string path, params object[] objs) { GameObject go = LoadGameObject(path); if (go != null) { if (isConnected) { if (mInstance != null && mInstance.mClient.isSwitchingScenes) { Debug.LogWarning("Trying to create an object while switching scenes. Call will be ignored."); } BinaryWriter writer = mInstance.mClient.BeginSend(Packet.RequestCreate); byte flag = GetFlag(go, persistent); writer.Write((ushort)65535); writer.Write(flag); writer.Write(path); writer.Write((byte)rccID); writer.WriteArray(objs); EndSend(); return; } objs = BinaryExtensions.CombineArrays(go, objs); UnityTools.ExecuteAll(GetRCCs(), (byte)rccID, objs); UnityTools.Clear(objs); } else { Debug.LogError("Unable to load " + path); } }
/// <summary> /// Draw the list of known LAN servers. /// </summary> void DrawServerList(Rect rect) { GUI.color = new Color(1f, 1f, 1f, mAlpha * mAlpha * 0.5f); GUI.Box(UnityTools.PadRect(rect, 8f), ""); GUI.color = new Color(1f, 1f, 1f, mAlpha * mAlpha); GUILayout.BeginArea(rect); { GUILayout.Label("LAN Server List", text); // List of discovered servers List <ServerList.Entry> list = TNLobbyClient.knownServers.list; // Server list example script automatically collects servers that have recently announced themselves for (int i = 0; i < list.size; ++i) { ServerList.Entry ent = list[i]; // NOTE: I am using 'internalAddress' here because I know all servers are hosted on LAN. // If you are hosting outside of your LAN, you should probably use 'externalAddress' instead. if (GUILayout.Button(ent.internalAddress.ToString(), button)) { TNManager.Connect(ent.internalAddress, ent.internalAddress); mMessage = "Connecting..."; } } } GUILayout.EndArea(); GUI.color = Color.white; }
/// <summary> /// Serialize the entire mesh into the specified DataNode. /// </summary> static public void Serialize(this Mesh mesh, DataNode node) { if (!mFullSerialization) { return; } node.AddChild("name", mesh.name); string path = UnityTools.LocateResource(mesh); if (!string.IsNullOrEmpty(path)) { node.AddChild("path", path); return; } Add(node, "vertices", mesh.vertices); Add(node, "normals", mesh.normals); Add(node, "uv1", mesh.uv); Add(node, "uv2", mesh.uv2); Add(node, "tangents", mesh.tangents); Add(node, "colors", mesh.colors32); Add(node, "weights", mesh.boneWeights); Add(node, "poses", mesh.bindposes); Add(node, "triangles", mesh.triangles); }
/// <summary> /// Execute this function with the specified number of parameters. /// </summary> public object Execute(params object[] pars) { if (func == null) { return(null); } if (parameters == null) { parameters = func.GetParameters(); } try { return((parameters.Length == 1 && parameters[0].ParameterType == typeof(object[])) ? func.Invoke(obj, new object[] { pars }) : func.Invoke(obj, pars)); } catch (System.Exception ex) { if (ex.GetType() == typeof(System.NullReferenceException)) { return(null); } UnityTools.PrintException(ex, this, 0, func.Name, pars); return(null); } }
/// <summary> /// Instantiate a new game object given its previously serialized DataNode. /// You can serialize game objects by using GameObject.Serialize(), but be aware that serializing only /// works fully in the Unity Editor. Prefabs can't be located automatically outside of the Unity Editor. /// </summary> static public GameObject Instantiate(this DataNode data) { GameObject child = null; byte[] assetBytes = data.GetChild <byte[]>("assetBundle"); if (assetBytes != null) { GameObject prefab; if (!mCachedBundles.TryGetValue(assetBytes, out prefab)) { #if UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 AssetBundle qr = AssetBundle.CreateFromMemoryImmediate(assetBytes); #else AssetBundle qr = AssetBundle.LoadFromMemory(assetBytes); #endif if (qr != null) { prefab = qr.mainAsset as GameObject; } if (prefab == null) { prefab = new GameObject(data.name); } mCachedBundles[assetBytes] = prefab; } child = GameObject.Instantiate(prefab) as GameObject; child.name = data.name; } else { string path = data.GetChild <string>("prefab"); if (!string.IsNullOrEmpty(path)) { GameObject prefab = UnityTools.LoadPrefab(path); if (prefab != null) { child = GameObject.Instantiate(prefab) as GameObject; child.name = data.name; child.SetActive(true); } else { child = new GameObject(data.name); } } else { child = new GameObject(data.name); } child.Deserialize(data, true); } return(child); }
/// <summary> /// Adjust the server list's alpha based on whether it should be shown or not. /// </summary> void Update() { if (Application.isPlaying) { float target = (TNLobbyClient.knownServers.list.size == 0) ? 0f : 1f; mAlpha = UnityTools.SpringLerp(mAlpha, target, 8f, Time.deltaTime); } }
/// <summary> /// Invoke the function specified by the function name. /// </summary> public bool Execute(string funcName, params object[] parameters) { if (mParent != null) { return(mParent.Execute(funcName, parameters)); } if (rebuildMethodList) { RebuildMethodList(); } return(UnityTools.ExecuteAll(mRFCs, funcName, parameters)); }
/// <summary> /// Instantiate a new game object given its previously serialized DataNode. /// You can serialize game objects by using GameObject.Serialize(), but be aware that serializing only /// works fully in the Unity Editor. Prefabs can't be located automatically outside of the Unity Editor. /// </summary> static public GameObject Instantiate(this DataNode data) { GameObject child = null; byte[] assetBytes = data.GetChild <byte[]>("assetBundle"); if (assetBytes != null) { AssetBundle ab = UnityTools.LoadAssetBundle(assetBytes); if (ab != null) { var go = ab.mainAsset as GameObject; if (go != null) { child = GameObject.Instantiate(go) as GameObject; child.name = data.name; } } } else { string path = data.GetChild <string>("prefab"); if (!string.IsNullOrEmpty(path)) { GameObject prefab = UnityTools.LoadPrefab(path); if (prefab != null) { child = GameObject.Instantiate(prefab) as GameObject; child.name = data.name; child.SetActive(true); } else { child = new GameObject(data.name); } } else { child = new GameObject(data.name); } child.Deserialize(data, true); } return(child); }
/// <summary> /// Deserialize a previously serialized game object. /// </summary> static void DeserializeHierarchy(this GameObject go, DataNode root) { SerializationEntry ent = new SerializationEntry(); ent.go = go; ent.node = root; mSerList.Add(ent); Transform trans = go.transform; trans.localPosition = root.GetChild <Vector3>("position", trans.localPosition); trans.localEulerAngles = root.GetChild <Vector3>("rotation", trans.localEulerAngles); trans.localScale = root.GetChild <Vector3>("scale", trans.localScale); go.layer = root.GetChild <int>("layer", go.layer); DataNode childNode = root.GetChild("Children"); if (childNode != null && childNode.children.size > 0) { for (int i = 0; i < childNode.children.size; ++i) { DataNode node = childNode.children[i]; GameObject child = null; GameObject prefab = UnityTools.Load <GameObject>(node.GetChild <string>("prefab")); if (prefab != null) { child = GameObject.Instantiate(prefab) as GameObject; } if (child == null) { child = new GameObject(); } child.name = node.name; Transform t = child.transform; t.parent = trans; t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; child.DeserializeHierarchy(node); } } }
/// <summary> /// On success -- join a channel. /// </summary> void OnNetworkConnect(bool result, string message) { if (result) { // Make it possible to use UDP using a random port if (allowUDP) { TNManager.StartUDP(Random.Range(10000, 50000)); } TNManager.JoinChannel(channelID, firstLevel, persistent, 10000, null); } else if (!string.IsNullOrEmpty(failureFunctionName)) { UnityTools.Broadcast(failureFunctionName, message); } else { Debug.LogError(message); } }
/// <summary> /// Joined a channel (or failed to). /// </summary> void OnNetworkJoinChannel(bool result, string message) { if (result) { if (!string.IsNullOrEmpty(successFunctionName)) { UnityTools.Broadcast(successFunctionName); } } else { if (!string.IsNullOrEmpty(failureFunctionName)) { UnityTools.Broadcast(failureFunctionName, message); } else { Debug.LogError(message); } TNManager.Disconnect(); } }
/// <summary> /// Create a new game object. /// </summary> static GameObject CreateGameObject(GameObject prefab, BinaryReader reader) { if (prefab != null) { // The first byte is always the type that identifies what kind of data will follow byte type = reader.ReadByte(); if (type == 0) { // Just a plain game object return(Instantiate(prefab) as GameObject); } else { // Custom creation function object[] objs = reader.ReadArray(prefab); object retVal; if (!UnityTools.ExecuteFirst(GetRCCs(), type, out retVal, objs)) { Debug.LogError("[TNet] Failed to call RCC #" + type + ".\nDid you forget to register it in Awake() via TNManager.AddRCCs?"); UnityTools.Clear(objs); return(null); } UnityTools.Clear(objs); if (retVal == null) { Debug.LogError("[TNet] Instantiating \"" + prefab.name + "\" via RCC #" + type + " returned null.\nDid you forget to return the game object from your RCC?"); } return(retVal as GameObject); } } return(null); }
/// <summary> /// Notification of a player being renamed. /// </summary> void OnRenamePlayer(Player p, string previous) { mPlayer.name = p.name; UnityTools.Broadcast("OnNetworkPlayerRenamed", p, previous); }
/// <summary> /// Notification of another player leaving the channel. /// </summary> void OnPlayerLeft(Player p) { UnityTools.Broadcast("OnNetworkPlayerLeave", p); }
/// <summary> /// Notification sent when leaving a channel. /// Also sent just before a disconnect (if inside a channel when it happens). /// </summary> void OnLeftChannel() { mJoiningChannel = false; UnityTools.Broadcast("OnNetworkLeaveChannel"); }
/// <summary> /// Invoke the function specified by the ID. /// </summary> public bool Execute (byte funcID, params object[] parameters) { if (mParent != null) return mParent.Execute(funcID, parameters); if (rebuildMethodList) RebuildMethodList(); return UnityTools.ExecuteAll(mRFCs, funcID, parameters); }
/// <summary> /// Notification that happens when the client gets disconnected from the server. /// </summary> void OnDisconnect() { UnityTools.Broadcast("OnNetworkDisconnect"); }
/// <summary> /// Error notification. /// </summary> void OnError(string err) { UnityTools.Broadcast("OnNetworkError", err); }
/// <summary> /// Load a game object prefab at the specified path. This is equivalent to Resources.Load, but it will /// also consider DataNode-exported binary assets as well, automatically loading them as if they were /// regular prefabs. /// </summary> static public GameObject LoadPrefab(string path) { if (string.IsNullOrEmpty(path)) { return(null); } if (!Application.isPlaying) { return(Resources.Load(path, typeof(GameObject)) as GameObject); } GameObject prefab = null; if (mPrefabRoot == null) { GameObject go = new GameObject("Prefabs"); Object.DontDestroyOnLoad(go); mPrefabRoot = go.transform; mPrefabs.Clear(); } // Try to get it from cache if (mPrefabs.TryGetValue(path, out prefab)) { return(prefab); } if (prefab == null) { // Load it from resources as a Game Object prefab = Resources.Load(path, typeof(GameObject)) as GameObject; if (prefab == null) { // Load it from resources as a binary asset byte[] bytes = UnityTools.LoadBinary(path); if (bytes != null) { // Parse the DataNode hierarchy DataNode data = DataNode.Read(bytes); if (data != null) { // Instantiate and immediately disable the object prefab = data.Instantiate(); if (prefab != null) { mPrefabs.Add(path, prefab); Object.DontDestroyOnLoad(prefab); prefab.transform.parent = mPrefabRoot; prefab.SetActive(false); return(prefab); } } } } } if (prefab == null) { #if UNITY_EDITOR Debug.LogError("[TNet] Attempting to create a game object that can't be found in the Resources folder: [" + path + "]"); #endif prefab = GetDummyObject(); } mPrefabs.Add(path, prefab); return(prefab); }
/// <summary> /// Convert a serialized string reference to an actual object reference. /// </summary> static public UnityEngine.Object StringToReference(this GameObject go, string path) { if (string.IsNullOrEmpty(path)) { return(null); } string[] split = path.Split(new char[] { '|' }, 3); if (split.Length == 3) { System.Type myType = UnityTools.FindType(split[1]); if (myType != null) { if (myType == typeof(Shader)) { return(Shader.Find(split[2])); } else if (split[0] == "asset") { return(Resources.Load(split[2], myType)); } else if (split[0] == "ref") { Transform t = go.transform; string[] splitPath = split[2].Split('/'); for (int i = 0; i < splitPath.Length; ++i) { string s = splitPath[i]; if (s == "..") { t = t.parent; if (t == null) { break; } } else if (!string.IsNullOrEmpty(s)) { t = t.FindChild(s); if (t == null) { break; } } } if (t != null) { if (myType == typeof(GameObject)) { return(t.gameObject); } return(t.GetComponent(myType)); } else { Debug.LogWarning("Hierarchy path not found: " + split[2], go); } } } } return(null); }
/// <summary> /// Execute this function with the specified number of parameters. /// </summary> public object Execute(params object[] pars) { if (mi == null) { return(null); } var parameters = this.parameters; if (pars == null && mParamCount != 0) { pars = new object[parameters.Length]; } if (mParamCount == 1 && parameters[0].ParameterType == typeof(object[])) { pars = new object[] { pars } } ; try { if (mAutoCast) { for (int i = 0; i < mParamCount; ++i) { var passed = pars[i].GetType(); if (mTypes[i] != passed) { pars[i] = Serialization.CastValue(pars[i], mTypes[i]); } } } return(mi.Invoke(obj, pars)); } catch (Exception ex) { if (ex.GetType() == typeof(NullReferenceException)) { return(null); } var tryAgain = false; if (mParamCount == pars.Length) { if (mTypes == null) { mTypes = new Type[mParamCount]; for (int i = 0; i < mParamCount; ++i) { mTypes[i] = parameters[i].ParameterType; } } for (int i = 0; i < mParamCount; ++i) { var passed = (pars[i] != null) ? pars[i].GetType() : mTypes[i]; if (mTypes[i] != passed) { pars[i] = Serialization.CastValue(pars[i], mTypes[i]); if (pars[i] != null) { tryAgain = true; } } } } if (tryAgain) { try { if (parameters.Length == 1 && parameters[0].ParameterType == typeof(object[])) { pars = new object[] { pars } } ; var retVal = mi.Invoke(obj, pars); mAutoCast = true; return(retVal); } catch (Exception ex2) { ex = ex2; } } UnityTools.PrintException(ex, this, 0, mi.Name, pars); return(null); } } }
[RCC(2)] static GameObject OnCreate2(GameObject go, Vector3 pos, Quaternion rot, Vector3 velocity, Vector3 angularVelocity) { return(UnityTools.Instantiate(go, pos, rot, velocity, angularVelocity)); }
/// <summary> /// Notification sent when attempting to join a channel, indicating a success or failure. /// </summary> void OnJoinChannel(bool success, string message) { UnityTools.Broadcast("OnNetworkJoinChannel", success, message); }
/// <summary> /// Notification sent when leaving a channel. /// Also sent just before a disconnect (if inside a channel when it happens). /// </summary> void OnLeftChannel() { UnityTools.Broadcast("OnNetworkLeaveChannel"); }
/// <summary> /// This menu is shown if the client has not yet connected to the server. /// </summary> void DrawConnectMenu() { Rect rect = new Rect(Screen.width * 0.5f - 200f * 0.5f - mAlpha * 120f, Screen.height * 0.5f - 100f, 200f, 220f); // Show a half-transparent box around the upcoming UI GUI.color = new Color(1f, 1f, 1f, 0.5f); GUI.Box(UnityTools.PadRect(rect, 8f), ""); GUI.color = Color.white; GUILayout.BeginArea(rect); { GUILayout.Label("Server Address", text); mAddress = GUILayout.TextField(mAddress, input, GUILayout.Width(200f)); if (GUILayout.Button("Connect", button)) { // We want to connect to the specified destination when the button is clicked on. // "OnConnect" function will be called sometime later with the result. TNManager.Connect(mAddress); mMessage = "Connecting..."; } if (TNServerInstance.isActive) { GUI.backgroundColor = Color.red; if (GUILayout.Button("Stop the Server", button)) { // Stop the server, saving all the data TNServerInstance.Stop(); mMessage = "Server stopped"; } } else { GUI.backgroundColor = Color.green; if (GUILayout.Button("Start a LAN Server", button)) { #if UNITY_WEBPLAYER mMessage = "Can't host from the Web Player due to Unity's security restrictions"; #else // Start a local server, loading the saved data if possible // The UDP port of the server doesn't matter much as it's optional, // and the clients get notified of it via Packet.ResponseSetUDP. int udpPort = Random.Range(10000, 40000); TNLobbyClient lobby = GetComponent <TNLobbyClient>(); if (lobby == null) { if (TNServerInstance.Start(serverTcpPort, udpPort, "server.dat")) { TNManager.Connect(); } } else { TNServerInstance.Type type = (lobby is TNUdpLobbyClient) ? TNServerInstance.Type.Udp : TNServerInstance.Type.Tcp; if (TNServerInstance.Start(serverTcpPort, udpPort, lobby.remotePort, "server.dat", type)) { TNManager.Connect(); } } mMessage = "Server started"; #endif } // Start a local server that doesn't use sockets. It's ideal for testing and for single player gameplay. if (GUILayout.Button("Start a Virtual Server", button)) { mMessage = "Server started"; TNServerInstance.Start("server.dat"); TNManager.Connect(); } } GUI.backgroundColor = Color.white; if (!string.IsNullOrEmpty(mMessage)) { GUILayout.Label(mMessage, text); } } GUILayout.EndArea(); if (mAlpha > 0.01f) { rect.x = rect.x + (Screen.width - rect.xMin - rect.xMax) * mAlpha; DrawServerList(rect); } }
/// <summary> /// Notification of a new player joining the channel. /// </summary> void OnPlayerJoined(Player p) { UnityTools.Broadcast("OnNetworkPlayerJoin", p); }
/// <summary> /// Connection result notification. /// </summary> void OnConnect(bool success, string message) { UnityTools.Broadcast("OnNetworkConnect", success, message); }
/// <summary> /// Notification that happens when the client gets disconnected from the server. /// </summary> void OnDisconnect() { mJoiningChannel = false; UnityTools.Broadcast("OnNetworkDisconnect"); }