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()
{
}
}