JCEngine.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using BestHTTP.WebSocket;
  5. using Newtonsoft.Json;
  6. using Newtonsoft.Json.Linq;
  7. using JC.Unity;
  8. using UnityEngine;
  9. namespace JCEngineCore {
  10. public class JCEngine {
  11. private static string url;
  12. public static void boot(string url, JCEntity entity) {
  13. JCEngine.url = url;
  14. new JCEngineCore.WebSocketServer(url, entity);
  15. }
  16. public static void reboot(JCEntity entity) {
  17. new JCEngineCore.WebSocketServer(url, entity);
  18. }
  19. }
  20. public class JCEntity {
  21. public int id;
  22. public JCEngineCore.Channel channel;
  23. public bool isValid;
  24. public bool loaded;
  25. public Dictionary<string, object> components = new Dictionary<string, object>();
  26. public virtual void onLoad() {}
  27. public virtual void onReload() {}
  28. public virtual void onDestroy() {}
  29. public virtual void onMiss() {}
  30. public bool call(string func, params object[] args) {
  31. return call(func, args, null);
  32. }
  33. public bool call(string func, object[] args = null, Delegate callback = null) {
  34. if (this.isValid) {
  35. string uuid = "";
  36. int type = Convert.ToInt32(JCEngineCore.DataType.FUNCTION);
  37. if (func.IndexOf(".") > -1) {
  38. type = Convert.ToInt32(JCEngineCore.DataType.METHOD);
  39. uuid = JCEngineCore.CallbackHandler.addCallback(callback);
  40. }
  41. if (args == null) {
  42. args = new object[]{};
  43. }
  44. JCEngineCore.Data data = new JCEngineCore.Data();
  45. data.uuid = uuid;
  46. data.type = type;
  47. data.func = func;
  48. data.args = args;
  49. this.channel.writeAndFlush(JsonConvert.SerializeObject(data));
  50. return true;
  51. }
  52. return false;
  53. }
  54. }
  55. public class Channel {
  56. private WebSocket webSocket;
  57. public Channel (WebSocket webSocket) {
  58. this.webSocket = webSocket;
  59. }
  60. public void writeAndFlush(string text) {
  61. this.webSocket.Send(text);
  62. }
  63. public void close() {
  64. this.webSocket.Close();
  65. }
  66. }
  67. class WebSocketServer {
  68. private WebSocket webSocket;
  69. private JCEntity tempEntity;
  70. private Coroutine heartBeatCoroutine;
  71. public WebSocketServer(string url, JCEntity entity) {
  72. this.webSocket = new WebSocket(new Uri(url));
  73. this.webSocket.StartPingThread = true;
  74. this.tempEntity = entity;
  75. this.webSocket.OnOpen += delegate(WebSocket webSocket) {
  76. this.call("loadTempEntity");
  77. };
  78. this.webSocket.OnClosed += delegate(WebSocket webSocket, UInt16 code, string message) {
  79. this.destroyTempEntity();
  80. };
  81. this.webSocket.OnError += delegate(WebSocket webSocket, Exception ex) {
  82. this.destroyTempEntity();
  83. };
  84. this.webSocket.OnMessage += delegate(WebSocket webSocket, string message) {
  85. this.invoke(JsonConvert.DeserializeObject<Data>(message));
  86. };
  87. this.webSocket.Open();
  88. }
  89. private void call(string func, object[] args = null) {
  90. if (args == null) {
  91. args = new object[]{};
  92. }
  93. Data data = new Data();
  94. data.uuid = "";
  95. data.type = Convert.ToInt32(DataType.EVENT);
  96. data.func = func;
  97. data.args = args;
  98. this.webSocket.Send(JsonConvert.SerializeObject(data));
  99. }
  100. private void invoke(Data data) {
  101. DataType dataType = (DataType) data.type;
  102. if (dataType == DataType.EVENT) {
  103. System.Reflection.MethodInfo method = this.GetType().GetMethod(data.func);
  104. Utility.FormatArgsType(data.args, method.GetParameters());
  105. method.Invoke(this, data.args);
  106. return;
  107. }
  108. if (dataType == DataType.FUNCTION) {
  109. if (this.tempEntity.isValid) {
  110. string func = data.func;
  111. object context = this.tempEntity;;
  112. int pointIndex = func.LastIndexOf(".");
  113. if (pointIndex > -1) {
  114. context = null;
  115. string key = func.Substring(0, pointIndex);
  116. object matchContext;
  117. this.tempEntity.components.TryGetValue(key, out matchContext);
  118. if (matchContext != null) {
  119. func = func.Substring(pointIndex + 1);
  120. context = matchContext;
  121. }
  122. }
  123. if (context != null) {
  124. System.Reflection.MethodInfo method = context.GetType().GetMethod(func);
  125. Utility.FormatArgsType(data.args, method.GetParameters());
  126. method.Invoke(context, data.args);
  127. }
  128. }
  129. return;
  130. }
  131. if (dataType == DataType.METHOD) {
  132. CallbackHandler.handleCallback(data);
  133. }
  134. }
  135. public void loadTempEntity(int id) {
  136. this.tempEntity.id = id;
  137. this.tempEntity.channel = new Channel(this.webSocket);
  138. this.tempEntity.isValid = true;
  139. try {
  140. if (this.tempEntity.loaded) {
  141. this.tempEntity.onReload();
  142. } else {
  143. this.tempEntity.onLoad();
  144. }
  145. } catch (Exception) {}
  146. this.tempEntity.loaded = true;
  147. if (heartBeatCoroutine == null) {
  148. heartBeatCoroutine = CoroutineStarter.Start(doHeartBeat());
  149. }
  150. }
  151. public void destroyTempEntity() {
  152. if (heartBeatCoroutine != null) {
  153. CoroutineStarter.Stop(heartBeatCoroutine);
  154. heartBeatCoroutine = null;
  155. }
  156. if (this.tempEntity.isValid) {
  157. this.tempEntity.isValid = false;
  158. this.tempEntity.onDestroy();
  159. } else {
  160. this.tempEntity.onMiss();
  161. }
  162. }
  163. IEnumerator doHeartBeat() {
  164. while (true) {
  165. try {
  166. call("doHeartBeat");
  167. } catch (System.Exception) {}
  168. yield return new WaitForSecondsRealtime(5f);
  169. }
  170. }
  171. }
  172. class CallbackHandler {
  173. private static int nextID = 0;
  174. private static Dictionary<string, CallbackInfo> mapper = new Dictionary<string, CallbackInfo>();
  175. private static string uuid() {
  176. nextID++;
  177. return nextID.ToString();
  178. }
  179. public static string addCallback(Delegate callback) {
  180. string uuid = CallbackHandler.uuid();
  181. if (callback != null) {
  182. CallbackInfo callbackInfo = new CallbackInfo();
  183. callbackInfo.callback = callback;
  184. callbackInfo.deadTime = Utility.GetTimestamp() + 10 * 1000;
  185. mapper.Add(uuid, callbackInfo);
  186. }
  187. return uuid;
  188. }
  189. public static void handleCallback(Data data) {
  190. if (mapper.Count > 10) {
  191. long now = Utility.GetTimestamp();
  192. LinkedList<string> outKeys = new LinkedList<string>();
  193. foreach (var item in mapper) {
  194. if (now >= item.Value.deadTime) {
  195. outKeys.AddLast(item.Key);
  196. }
  197. }
  198. foreach (var item in outKeys) {
  199. mapper.Remove(item);
  200. }
  201. }
  202. CallbackInfo callbackInfo;
  203. mapper.TryGetValue(data.uuid, out callbackInfo);
  204. if (callbackInfo != null) {
  205. mapper.Remove(data.uuid);
  206. object target = callbackInfo.callback.Target;
  207. System.Reflection.MethodInfo method = callbackInfo.callback.Method;
  208. Utility.FormatArgsType(data.args, method.GetParameters());
  209. method.Invoke(target, data.args);
  210. }
  211. }
  212. }
  213. class Utility {
  214. public static long GetTimestamp() {
  215. TimeSpan ts = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1);
  216. return (long)ts.TotalMilliseconds;
  217. }
  218. public static void FormatArgsType(object[] args, System.Reflection.ParameterInfo[] parameters) {
  219. int i = 0;
  220. foreach (var param in parameters) {
  221. Type type = param.ParameterType;
  222. if (IsBaseType(type)) {
  223. args[i] = Convert.ChangeType(args[i], type);
  224. } else if (args[i].GetType().IsSubclassOf(typeof(JContainer))) {
  225. args[i] = ((JContainer)args[i]).ToObject(type);
  226. }
  227. i++;
  228. }
  229. }
  230. public static bool IsBaseType(Type type) {
  231. if (typeof(System.Int32).Equals(type)) return true;
  232. if (typeof(System.Int64).Equals(type)) return true;
  233. if (typeof(System.Single).Equals(type)) return true;
  234. if (typeof(System.Double).Equals(type)) return true;
  235. if (typeof(System.String).Equals(type)) return true;
  236. if (typeof(System.Boolean).Equals(type)) return true;
  237. return false;
  238. }
  239. }
  240. class CallbackInfo {
  241. public Delegate callback;
  242. public long deadTime;
  243. }
  244. class Data {
  245. public string uuid;
  246. public int type;
  247. public string func;
  248. public object[] args;
  249. }
  250. enum DataType {
  251. EVENT,
  252. FUNCTION,
  253. METHOD
  254. }
  255. }