Unofficial CardLife revival project, pronounced like "celery"
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
7.2KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Net;
  5. using System.Reflection;
  6. using System.Text;
  7. using CLre_server.API.Engines;
  8. using CLre_server.API.MainServer;
  9. using Game.CommonComponents;
  10. using HarmonyLib;
  11. using Svelto.DataStructures;
  12. using Svelto.ECS;
  13. using User.Server;
  14. namespace CLre_server.WebStatus
  15. {
  16. public static class StatusEndpoints
  17. {
  18. private struct ServerStateCache
  19. {
  20. public List<PlayerData> OnlinePlayers;
  21. public int MaxPlayers;
  22. public RunState State;
  23. public static ServerStateCache Empty()
  24. {
  25. return new ServerStateCache
  26. {
  27. OnlinePlayers = new List<PlayerData>(),
  28. MaxPlayers = -1,
  29. State = RunState.Initialising,
  30. };
  31. }
  32. public string Json()
  33. {
  34. // Unity's built-in JSON serializer does not work with arrays or lists :(
  35. // I miss Newtonsoft...
  36. StringBuilder sb = new StringBuilder($"{{\"PlayersMax\":{MaxPlayers},\"PlayerCount\":{OnlinePlayers.Count},\"Status\":\"{State.ToString()}\",\"OnlinePlayers\":[");
  37. foreach (PlayerData p in OnlinePlayers.ToArray())
  38. {
  39. sb.Append(UnityEngine.JsonUtility.ToJson(p));
  40. sb.Append(",");
  41. }
  42. if (OnlinePlayers.Count > 0) sb.Remove(sb.Length - 1, 1);
  43. sb.Append("]}");
  44. return sb.ToString();
  45. }
  46. }
  47. [Serializable]
  48. private struct PlayerData
  49. {
  50. public string id;
  51. public string name;
  52. public bool isDev;
  53. public float x;
  54. public float y;
  55. public float z;
  56. }
  57. private enum RunState : short
  58. {
  59. Initialising,
  60. Initialised,
  61. Online,
  62. Quitting,
  63. }
  64. private class StatusPollingEngine : ServerEnginePostBuild
  65. {
  66. public override void Ready()
  67. {
  68. API.Utility.Logging.MetaLog("StatusPolling Engine ready");
  69. pollLoop().Run();
  70. }
  71. private delegate void PlayerPositionFunc();
  72. private PlayerPositionFunc _playerPositionFunc = null;
  73. private IEnumerator pollLoop()
  74. {
  75. FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
  76. ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
  77. while (_clState.State != RunState.Quitting)
  78. {
  79. ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
  80. entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
  81. int index = 0;
  82. foreach (AccountIdServerNode user in accounts)
  83. {
  84. if (index < _clState.OnlinePlayers.Count)
  85. {
  86. PlayerData p = _clState.OnlinePlayers[index];
  87. p.id = user.accountId.publicId.ToString();
  88. p.name = user.accountId.displayName;
  89. p.isDev = (user.accountId.userFlags & UserFlags.Dev) == UserFlags.Dev;
  90. _clState.OnlinePlayers[index] = p;
  91. }
  92. else
  93. {
  94. PlayerData p = default(PlayerData);
  95. p.id = user.accountId.publicId.ToString();
  96. p.name = user.accountId.displayName;
  97. p.isDev = (user.accountId.userFlags & UserFlags.Dev) == UserFlags.Dev;
  98. _clState.OnlinePlayers.Add(p);
  99. }
  100. index++;
  101. }
  102. if (index != 0) syncPlayerPositions();
  103. if (index < _clState.OnlinePlayers.Count) _clState.OnlinePlayers.RemoveRange(index, _clState.OnlinePlayers.Count - index);
  104. //API.Utility.Logging.MetaLog($"Polled {index} Online Users");
  105. yield return null;
  106. }
  107. }
  108. private void syncPlayerPositions()
  109. {
  110. if (_playerPositionFunc == null)
  111. {
  112. // build non-generic method using reflection
  113. MethodInfo method = AccessTools.Method(typeof(StatusPollingEngine), "getPlayerPositionsGeneric",
  114. generics: new[] {AccessTools.TypeByName("Game.Character.ServerCharacterPositionNode")});
  115. _playerPositionFunc = API.Utility.Reflection.BuildDelegate<PlayerPositionFunc>(method, this);
  116. }
  117. _playerPositionFunc();
  118. }
  119. #pragma warning disable 0618
  120. private void getPlayerPositionsGeneric<T>() where T : EntityView // EntityView is deprecated lol
  121. {
  122. ReadOnlyCollectionStruct<T> scpnCollection = entitiesDB.QueryEntityViews<T>(DEPRECATED_SveltoExtensions.DEPRECATED_GROUP);
  123. //API.Utility.Logging.MetaLog($"Found {scpnCollection.Count} player positions");
  124. int i = 0;
  125. foreach (T scpn in scpnCollection)
  126. {
  127. PlayerData p = _clState.OnlinePlayers[i];
  128. UnityEngine.Vector3 pos = Traverse.Create(scpn).Field<IPositionComponent>("positionComponent")
  129. .Value.position;
  130. p.x = pos.x;
  131. p.y = pos.y;
  132. p.z = pos.z;
  133. _clState.OnlinePlayers[i] = p;
  134. i++;
  135. }
  136. }
  137. #pragma warning restore 0618
  138. }
  139. private static ServerStateCache _clState = ServerStateCache.Empty();
  140. internal static void Init()
  141. {
  142. #if DEBUG
  143. API.Utility.Logging.MetaLog("Status Endpoint initialising");
  144. #endif
  145. new StatusPollingEngine();
  146. // register API event callbacks
  147. Server.Instance.InitStart += (_, __) => _clState.State = RunState.Initialising;
  148. Server.Instance.InitComplete += (_, __) =>
  149. {
  150. _clState.State = RunState.Initialised;
  151. _clState.MaxPlayers = Server.Instance.GameServerSettings.GetMaxPlayers();
  152. };
  153. Server.Instance.FrameworkReady += (_, __) =>
  154. {
  155. _clState.State = RunState.Online;
  156. _clState.MaxPlayers = Server.Instance.GameServerSettings.GetMaxPlayers();
  157. };
  158. Server.Instance.FrameworkExit += (_, __) => _clState.State = RunState.Quitting;
  159. }
  160. [WebEndpoint("/status.json")]
  161. internal static void StatusJson(HttpListenerContext ctx)
  162. {
  163. ctx.Response.Headers.Add("Content-Type", "application/json");
  164. string json = _clState.Json();
  165. #if DEBUG
  166. API.Utility.Logging.MetaLog("JSONified status: " + json);
  167. #endif
  168. byte[] output = Encoding.UTF8.GetBytes(json);
  169. ctx.Response.OutputStream.Write(output, 0, output.Length);
  170. }
  171. }
  172. }