using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/Networking", order = 1)] public class Networking : ScriptableObject { public string host; public string path; public int port; public GameObject soulPrefab; public GameObject playerPrefab; public LoginInfo loginInfo; private ClientWebSocket sock; private int seq; async public void Connect() { if (sock != null) { if (sock.State == WebSocketState.Open) { return; } sock = null; } Application.quitting += autoDisconnect; sock = new ClientWebSocket(); UriBuilder b = new UriBuilder(); b.Scheme = "ws"; b.Host = host; b.Port = port; b.Path = path; Debug.LogFormat("Connecting to: {0}", b.Uri); await sock.ConnectAsync(b.Uri, CancellationToken.None); Debug.LogFormat("Finished connection task with status: {0}", sock.State); if (loginInfo.playerName != "" && loginInfo.password != "") { SendLogin(); } return; } public void SendCollectSoul(string playerName, Vector3 position) { seq++; CollectSoul info; info.seq = seq; info.cmd = "collect-soul"; info.playerName = playerName; info.position = position; string msg = JsonUtility.ToJson(info); ArraySegment buf = new ArraySegment(Encoding.UTF8.GetBytes(msg)); sock.SendAsync(buf, WebSocketMessageType.Text, true, CancellationToken.None); } public void SendDeath(Vector3 position) { seq++; Death death; death.seq = seq; death.cmd = "death"; death.position = position; string msg = JsonUtility.ToJson(death); ArraySegment buf = new ArraySegment(Encoding.UTF8.GetBytes(msg)); sock.SendAsync(buf, WebSocketMessageType.Text, true, CancellationToken.None); } public void SendLogin() { seq++; Login login; login.seq = seq; login.cmd = "login"; login.username = loginInfo.playerName; login.password = loginInfo.password; loginInfo.sentLogin = true; loginInfo.loginError = ""; string msg = JsonUtility.ToJson(login); ArraySegment buf = new ArraySegment(Encoding.UTF8.GetBytes(msg)); Debug.LogFormat("sending login user: {0} pass: {1}", loginInfo.playerName, loginInfo.password); sock.SendAsync(buf, WebSocketMessageType.Text, true, CancellationToken.None); } public IEnumerator ReadMessages() { ArraySegment readBuffer = new ArraySegment(new byte[32000]); Task readTask = null; while (true) { if (!isConnected()) { yield return null; continue; } if (readTask == null) { readTask = sock.ReceiveAsync(readBuffer, CancellationToken.None); } switch (readTask.Status) { case TaskStatus.Created: case TaskStatus.WaitingForActivation: case TaskStatus.WaitingToRun: case TaskStatus.Running: case TaskStatus.WaitingForChildrenToComplete: yield return null; continue; case TaskStatus.RanToCompletion: WebSocketReceiveResult result = readTask.Result; string msg = Encoding.UTF8.GetString(readBuffer.Array, 0, result.Count); parseMessage(msg); break; case TaskStatus.Canceled: break; case TaskStatus.Faulted: break; } readTask = null; yield return null; } } private void parseMessage(string msg) { string[] parts = msg.Split(new char[]{' '}, 2); if (parts.Length != 2) { Debug.LogFormat("dunno how to handle this msg: {0}", msg); return; } switch (parts[0]) { case "spawn-soul": SpawnSoul spawned = JsonUtility.FromJson(parts[1]); onSpawnSoul(spawned); break; case "soul-collected": CollectSoul collected = JsonUtility.FromJson(parts[1]); onSoulCollected(collected); break; case "login-result": Debug.LogFormat("received message: {0}", msg); LoginResult login = JsonUtility.FromJson(parts[1]); onLoginResult(login); break; case "tick": break; default: Debug.LogFormat("also can't handle this one: {0} {1}", parts[0], parts[1]); break; } } private void onSpawnSoul(SpawnSoul spawn) { Debug.LogFormat("spawn a soul: {0} at {1}", spawn.playerName, spawn.position); GameObject soul = Instantiate(soulPrefab, spawn.position, Quaternion.identity); if (spawn.playerName != "") { soul.name = spawn.playerName; } else { soul.name = "FUCK"; } GameObject allSouls = GameObject.Find("Souls"); if (allSouls == null) { Debug.LogError("unable to find souls container!"); } else { soul.transform.SetParent(allSouls.transform); SoulController sc = soul.GetComponent(); sc.playerName = spawn.playerName; } } private void onSoulCollected(CollectSoul collected) { Debug.LogFormat("a soul was collected: {0}", collected); string objName = "Souls/" + collected.playerName; if (collected.playerName == "") { objName = "Souls/FUCK"; } GameObject soul = GameObject.Find(objName); Destroy(soul); if (collected.playerName == loginInfo.playerName) { GameObject currentPlayer = GameObject.Find("Player"); if (currentPlayer == null) { GameObject player = Instantiate(playerPrefab, loginInfo.startPosition, Quaternion.identity); Camera cam = Camera.main; CameraController cc = cam.GetComponent(); cc.player = player.transform; } } } private void onLoginResult(LoginResult result) { Debug.LogFormat("received login result: {0}", result); loginInfo.sentLogin = false; if (result.passed) { loginInfo.isLoggedIn = true; } else { loginInfo.loginError = result.error; Debug.LogErrorFormat("failed login: {0}", result.error); } } private void autoDisconnect() { if (isConnected()) { Debug.Log("disconnecting websocket via autoDisconnect"); Task task = sock.CloseAsync(WebSocketCloseStatus.NormalClosure, "closed", CancellationToken.None); task.Wait(); } } public bool isConnected() { return sock != null && sock.State == WebSocketState.Open; } private struct CollectSoul { public int seq; public string cmd; public string playerName; public Vector3 position; } private struct Death { public int seq; public string cmd; public Vector3 position; } private struct SpawnSoul { public string playerName; public Vector3 position; } private struct Login { public int seq; public string cmd; public string username; public string password; } private struct LoginResult { public bool passed; public string error; public bool dead; } }