|
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Net;
- using System.Reflection;
- using System.Text;
- using CLre_server.API.Engines;
- using CLre_server.API.MainServer;
- using Game.CommonComponents;
- using HarmonyLib;
- using Svelto.DataStructures;
- using Svelto.ECS;
- using User.Server;
-
- namespace CLre_server.WebStatus
- {
- public static class StatusEndpoints
- {
- private struct ServerStateCache
- {
- public List<PlayerData> OnlinePlayers;
-
- public int MaxPlayers;
-
- public RunState State;
-
- public static ServerStateCache Empty()
- {
- return new ServerStateCache
- {
- OnlinePlayers = new List<PlayerData>(),
- MaxPlayers = -1,
- State = RunState.Initialising,
- };
- }
-
- public string Json()
- {
- // Unity's built-in JSON serializer does not work with arrays or lists :(
- // I miss Newtonsoft...
- StringBuilder sb = new StringBuilder($"{{\"PlayersMax\":{MaxPlayers},\"PlayerCount\":{OnlinePlayers.Count},\"Status\":\"{State.ToString()}\",\"OnlinePlayers\":[");
- foreach (PlayerData p in OnlinePlayers.ToArray())
- {
- sb.Append(UnityEngine.JsonUtility.ToJson(p));
- sb.Append(",");
- }
- if (OnlinePlayers.Count > 0) sb.Remove(sb.Length - 1, 1);
- sb.Append("]}");
- return sb.ToString();
- }
- }
-
- [Serializable]
- private struct PlayerData
- {
- public string id;
- public string name;
- public bool isDev;
- public float x;
- public float y;
- public float z;
- }
-
- private enum RunState : short
- {
- Initialising,
- Initialised,
- Online,
- Quitting,
- }
-
- private class StatusPollingEngine : ServerEnginePostBuild
- {
- public override void Ready()
- {
- API.Utility.Logging.MetaLog("StatusPolling Engine ready");
- pollLoop().Run();
- }
-
- private delegate void PlayerPositionFunc();
-
- private PlayerPositionFunc _playerPositionFunc = null;
-
- private IEnumerator pollLoop()
- {
- FieldInfo f = AccessTools.Field(AccessTools.TypeByName("User.Server.AccountExclusiveGroups"), "accountGroup");
- ExclusiveGroup accountGroup = (ExclusiveGroup) f.GetValue(null);
- while (_clState.State != RunState.Quitting)
- {
- ReadOnlyCollectionStruct<AccountIdServerNode> accounts =
- entitiesDB.QueryEntityViews<AccountIdServerNode>(accountGroup);
- int index = 0;
- foreach (AccountIdServerNode user in accounts)
- {
- if (index < _clState.OnlinePlayers.Count)
- {
- PlayerData p = _clState.OnlinePlayers[index];
- p.id = user.accountId.publicId.ToString();
- p.name = user.accountId.displayName;
- p.isDev = (user.accountId.userFlags & UserFlags.Dev) == UserFlags.Dev;
- _clState.OnlinePlayers[index] = p;
- }
- else
- {
- PlayerData p = default(PlayerData);
- p.id = user.accountId.publicId.ToString();
- p.name = user.accountId.displayName;
- p.isDev = (user.accountId.userFlags & UserFlags.Dev) == UserFlags.Dev;
- _clState.OnlinePlayers.Add(p);
- }
- index++;
- }
- if (index != 0) syncPlayerPositions();
- if (index < _clState.OnlinePlayers.Count) _clState.OnlinePlayers.RemoveRange(index, _clState.OnlinePlayers.Count - index);
- //API.Utility.Logging.MetaLog($"Polled {index} Online Users");
- yield return null;
- }
- }
-
- private void syncPlayerPositions()
- {
- if (_playerPositionFunc == null)
- {
- // build non-generic method using reflection
- MethodInfo method = AccessTools.Method(typeof(StatusPollingEngine), "getPlayerPositionsGeneric",
- generics: new[] {AccessTools.TypeByName("Game.Character.ServerCharacterPositionNode")});
- _playerPositionFunc = API.Utility.Reflection.BuildDelegate<PlayerPositionFunc>(method, this);
- }
-
- _playerPositionFunc();
- }
-
- #pragma warning disable 0618
- private void getPlayerPositionsGeneric<T>() where T : EntityView // EntityView is deprecated lol
- {
- ReadOnlyCollectionStruct<T> scpnCollection = entitiesDB.QueryEntityViews<T>(DEPRECATED_SveltoExtensions.DEPRECATED_GROUP);
- //API.Utility.Logging.MetaLog($"Found {scpnCollection.Count} player positions");
- int i = 0;
- foreach (T scpn in scpnCollection)
- {
- PlayerData p = _clState.OnlinePlayers[i];
- UnityEngine.Vector3 pos = Traverse.Create(scpn).Field<IPositionComponent>("positionComponent")
- .Value.position;
- p.x = pos.x;
- p.y = pos.y;
- p.z = pos.z;
- _clState.OnlinePlayers[i] = p;
- i++;
- }
- }
- #pragma warning restore 0618
- }
-
- private static ServerStateCache _clState = ServerStateCache.Empty();
-
- internal static void Init()
- {
- #if DEBUG
- API.Utility.Logging.MetaLog("Status Endpoint initialising");
- #endif
- new StatusPollingEngine();
- // register API event callbacks
- Server.Instance.InitStart += (_, __) => _clState.State = RunState.Initialising;
- Server.Instance.InitComplete += (_, __) =>
- {
- _clState.State = RunState.Initialised;
- _clState.MaxPlayers = Server.Instance.GameServerSettings.GetMaxPlayers();
- };
- Server.Instance.FrameworkReady += (_, __) =>
- {
- _clState.State = RunState.Online;
- _clState.MaxPlayers = Server.Instance.GameServerSettings.GetMaxPlayers();
- };
- Server.Instance.FrameworkExit += (_, __) => _clState.State = RunState.Quitting;
- }
-
- [WebEndpoint("/status.json")]
- internal static void StatusJson(HttpListenerContext ctx)
- {
- ctx.Response.Headers.Add("Content-Type", "application/json");
- string json = _clState.Json();
- #if DEBUG
- API.Utility.Logging.MetaLog("JSONified status: " + json);
- #endif
- byte[] output = Encoding.UTF8.GetBytes(json);
- ctx.Response.OutputStream.Write(output, 0, output.Length);
- }
- }
- }
|