using BestHTTP.WebSocket; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using MiniJSON; using UnityEngine.SceneManagement; using System.Linq; public class NetHost : MonoBehaviour { public int LocalPlayerID = -1; private int nextLocalItemId = 0; [Serializable] public struct NamedItem { public string Type; public Item Item; } public HashSet<int> Players = new HashSet<int>(); // Start is called before the first frame update void Start() { DontDestroyOnLoad(this); Connect(); } public NamedItem[] ItemsByType; private WebSocket socket; public void Connect() { Clear(); socket = new WebSocket(new System.Uri("wss://cells.mhack.io/websocket")); //socket.PingFrequency = 15000; //socket.StartPingThread = true; socket.OnOpen += Opened; socket.OnClosed += Closed; socket.OnMessage += Message; socket.OnError += Error; socket.Open(); } private void Error(WebSocket webSocket, string reason) { Debug.Log("NET: ERROR: " + reason); } private void Closed(WebSocket webSocket, ushort code, string message) { Debug.Log("NET: CLOSED: " + message); Clear(); } private void Message(WebSocket webSocket, string message) { Debug.Log("NET: MESSAGE: " + message); Dictionary<string, object> json = Json.Deserialize(message) as Dictionary<string, object>; if (json != null && json.ContainsKey("type")) { switch ((string)json["type"]) { case "connected": LocalPlayerID = Convert.ToInt32(json["player_id"]); Debug.Log("NET: local player: " + LocalPlayerID); break; case "enter_room": { DestroyRoom(); string map = (string)json["map"]; if (SceneManager.GetActiveScene().name != map) { SceneManager.LoadScene(map); } List<Dictionary<string, object>> items = json.List("items"); foreach (Dictionary<string, object> item in items) { UpdateItem(item); } List<Dictionary<string, object>> tags = FindObjectsOfType<ServerTag>().Select(t => t.GetJSON()).ToList(); Dictionary<string, object> tagMessage = new Dictionary<string, object>(); tagMessage.Add("type", "server_tags"); tagMessage.Add("tags", tags); socket.Send(Json.Serialize(tagMessage)); //Item player = SpawnItemLocal("player", p => //{ // p.transform.position = new Vector3(UnityEngine.Random.Range(-4f, 4f), 1, UnityEngine.Random.Range(-4f, 4f)); //}); break; } case "update_items": { List<Dictionary<string, object>> items = json.List("items"); foreach (Dictionary<string, object> item in items) { UpdateItem(item); } break; } case "take_ownership": { List<Dictionary<string, object>> items = json.List("items"); foreach (Dictionary<string, object> item in items) { TakeOwnership(item); } break; } case "item_command": { List<string> itemKeys = json.ListString("item_keys"); Dictionary<string, object> details = json.Obj("details"); string command = "" + json["command"]; foreach (string key in itemKeys) { if (items.ContainsKey(key) && items[key].IsLocal) { items[key].RootTransform.SendMessage(command, details, SendMessageOptions.DontRequireReceiver); } } break; } } } } private List<Item> updatesThisFrame = new List<Item>(); private List<Dictionary<string, object>> manualUpdatesThisFrame = new List<Dictionary<string, object>>(); public void SendItemUpdate(Item item, bool manual = false) { if (item.IsLocal) { if (manual) { manualUpdatesThisFrame.Add(item.GetItemData()); } else { updatesThisFrame.Add(item); } } } void LateUpdate() { if (updatesThisFrame.Count > 0 || manualUpdatesThisFrame.Count > 0) { List<Dictionary<string, object>> updates = new List<Dictionary<string, object>>(); updates.AddRange(updatesThisFrame.Select(i => i.GetItemData())); updates.AddRange(manualUpdatesThisFrame); Dictionary<string, object> message = new Dictionary<string, object>(); message.Add("type", "update_items"); message.Add("items", updates); socket.Send(Json.Serialize(message)); updatesThisFrame.ForEach( i => { i.Details.Remove("__change_ownership_old_key__"); } ); updatesThisFrame.Clear(); manualUpdatesThisFrame.Clear(); } } private void DestroyRoom() { foreach (Item item in items.Values) { Destroy(item.RootTransform); } items.Clear(); } private Dictionary<string, Item> items = new Dictionary<string, Item>(); public Item GetItemByType(string type) { return ItemsByType.FirstOrDefault(n => n.Type == type).Item; } public Item SpawnItemLocal(string type, Action<Item> setupBeforeInitializing = null) { Item prefab = GetItemByType(type); if (prefab == null) return null; Item item = Instantiate<Item>(prefab); item.Type = type; item.PlayerID = LocalPlayerID; item.ItemID = nextLocalItemId++; if (setupBeforeInitializing != null) setupBeforeInitializing(item); item.RootTransform.SendMessage("StartLocal", null, SendMessageOptions.DontRequireReceiver); SendItemUpdate(item); items[item.Key] = item; return item; } private void UpdateItem(Dictionary<string, object> itemData) { string key = (string)itemData["key"]; if (itemData.ContainsKey("__change_ownership_old_key__")) { string oldKey = (string)itemData["__change_ownership_old_key__"]; if (oldKey != key) { itemData.Remove("__change_ownership_old_key__"); if (items.ContainsKey(oldKey)) { int playerId = itemData.Key("player_id", Convert.ToInt32); if (playerId != LocalPlayerID) { int itemId = itemData.Key("item_id", Convert.ToInt32); Item item = items[oldKey]; items.Remove(oldKey); item.PlayerID = playerId; item.ItemID = itemId; items[item.Key] = item; } } else { //Debug.Log("Received rename key but didn't have the original"); } } else { //Debug.Log("Received rename key but already renamed"); } } bool shouldDelete = itemData.ContainsKey("__delete__"); if (items.ContainsKey(key)) { if (shouldDelete) { DeleteItemInternal(items[key], key, false); } else { items[key].UpdateFromData(itemData); } } else if (!shouldDelete) { int playerId = itemData.Key("player_id", Convert.ToInt32); int itemId = itemData.Key("item_id", Convert.ToInt32); string type = (string)itemData["type"]; Item itemPrefab = GetItemByType(type); if (itemPrefab != null) { if (itemId < 0) { if (playerId != LocalPlayerID) { Debug.Log("Info about non-spawned, non-owned item?"); return; } itemId = nextLocalItemId++; } Item item = Instantiate<Item>(itemPrefab); item.Type = type; item.PlayerID = playerId; item.ItemID = itemId; item.UpdateFromData(itemData); items[item.Key] = item; if (item.IsLocal) { item.RootTransform.SendMessage("StartLocal", null, SendMessageOptions.DontRequireReceiver); SendItemUpdate(item); } else { item.RootTransform.SendMessage("StartRemote", null, SendMessageOptions.DontRequireReceiver); } } else { Debug.LogError("Can't find Item type " + type); } } } public void DestroyItem(Item item) { if (item.IsLocal) { item.Details["__delete__"] = true; DeleteItemInternal(item); } } private void DeleteItemInternal(Item item, string useKey = null, bool sendUpdate = true) { items.Remove(useKey == null ? item.Key : useKey); if (sendUpdate) { SendItemUpdate(item, true); } if (item.ManualDelete) { item.RootTransform.SendMessage("ItemDestroy", null, SendMessageOptions.DontRequireReceiver); } else { Destroy(item.RootTransform.gameObject); } } public void SendItemCommand(string itemKey, string command, Dictionary<string, object> details = null) { SendItemsCommand(new List<string> { itemKey }, command, details); } public void SendItemsCommand(List<string> itemKeys, string command, Dictionary<string, object> details = null) { if (details == null) details = new Dictionary<string, object>(); Dictionary<string, object> message = new Dictionary<string, object>(); message["type"] = "item_command"; message["command"] = command; message["details"] = details; message["item_keys"] = itemKeys; socket.Send(Json.Serialize(message)); } private void TakeOwnership(Dictionary<string, object> itemData) { int playerId = itemData.Key("player_id", Convert.ToInt32); if (playerId != LocalPlayerID) return; string key = (string)itemData["key"]; if (items.ContainsKey(key)) { Item item = items[key]; items.Remove(key); item.PlayerID = LocalPlayerID; item.ItemID = nextLocalItemId++; item.Details["__change_ownership_old_key__"] = key; item.RootTransform.SendMessage("StartLocal", null, SendMessageOptions.DontRequireReceiver); SendItemUpdate(item); } } private void Opened(WebSocket webSocket) { Debug.Log("NET: OPENED"); } private void Clear() { if (socket != null) { socket.OnOpen -= Opened; socket.OnClosed -= Closed; socket.OnMessage -= Message; socket.OnError -= Error; } socket = null; } // Update is called once per frame void Update() { } }