Create CLre_server mod and port CLre basic functionality
This commit is contained in:
parent
5dd67a88f4
commit
14a5fc8a54
24 changed files with 1967 additions and 0 deletions
6
CLre.sln
6
CLre.sln
|
@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29609.76
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLre", "CLre\CLre.csproj", "{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLre", "CLre\CLre.csproj", "{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLre_server", "CLre_server\CLre_server.csproj", "{89B354CF-C654-4E48-8166-5E20BC6E4836}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -15,6 +17,10 @@ Global
|
||||||
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E0EEA15D-AB3C-4C73-A000-C49B5AE9EA66}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{89B354CF-C654-4E48-8166-5E20BC6E4836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{89B354CF-C654-4E48-8166-5E20BC6E4836}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{89B354CF-C654-4E48-8166-5E20BC6E4836}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{89B354CF-C654-4E48-8166-5E20BC6E4836}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
10
CLre_server/API/Engines/ICLreEngine.cs
Normal file
10
CLre_server/API/Engines/ICLreEngine.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Game.DataLoader;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Engines
|
||||||
|
{
|
||||||
|
public interface ICLreEngine : IQueryingEntitiesEngine, IEngine, IDataAccess
|
||||||
|
{
|
||||||
|
IEntityFactory entityFactory { get; set; }
|
||||||
|
}
|
||||||
|
}
|
61
CLre_server/API/Engines/ServerEngines.cs
Normal file
61
CLre_server/API/Engines/ServerEngines.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using Game.DataLoader;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Engines
|
||||||
|
{
|
||||||
|
public abstract class ServerEnginePreBuild : ICLreEngine
|
||||||
|
{
|
||||||
|
public ServerEnginePreBuild()
|
||||||
|
{
|
||||||
|
MainGameServer_BuildDeprecatedEngines_Patch.beforeBuildEngines.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Ready();
|
||||||
|
public abstract IEntitiesDB entitiesDB { get; set; }
|
||||||
|
public abstract IEntityFactory entityFactory { get; set; }
|
||||||
|
public IDataDB dataDB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ServerEnginePostBuild : ICLreEngine
|
||||||
|
{
|
||||||
|
public ServerEnginePostBuild()
|
||||||
|
{
|
||||||
|
MainGameServer_BuildDeprecatedEngines_Patch.afterBuildEngines.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Ready();
|
||||||
|
public abstract IEntitiesDB entitiesDB { get; set; }
|
||||||
|
public abstract IEntityFactory entityFactory { get; set; }
|
||||||
|
public IDataDB dataDB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(GameServer.GameFramework.MainGameServer), "BuildDeprecatedEngines")]
|
||||||
|
class MainGameServer_BuildDeprecatedEngines_Patch
|
||||||
|
{
|
||||||
|
internal static FasterList<ServerEnginePreBuild> beforeBuildEngines = new FasterList<ServerEnginePreBuild>();
|
||||||
|
|
||||||
|
internal static FasterList<ServerEnginePostBuild> afterBuildEngines = new FasterList<ServerEnginePostBuild>();
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void BeforeMethodCall(GameServer.GameFramework.MainGameServer __instance, IEntityFactory ____entityFactory)
|
||||||
|
{
|
||||||
|
foreach (ICLreEngine e in beforeBuildEngines)
|
||||||
|
{
|
||||||
|
e.entityFactory = ____entityFactory;
|
||||||
|
__instance.AddEngine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(GameServer.GameFramework.MainGameServer __instance, IEntityFactory ____entityFactory)
|
||||||
|
{
|
||||||
|
foreach (ICLreEngine e in afterBuildEngines)
|
||||||
|
{
|
||||||
|
e.entityFactory = ____entityFactory;
|
||||||
|
__instance.AddEngine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
CLre_server/API/MainServer/Server.cs
Normal file
147
CLre_server/API/MainServer/Server.cs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using GameServer;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.Context;
|
||||||
|
|
||||||
|
namespace CLre_server.API.MainServer
|
||||||
|
{
|
||||||
|
public class Server
|
||||||
|
{
|
||||||
|
// static
|
||||||
|
|
||||||
|
private static Server _instance = null;
|
||||||
|
|
||||||
|
public static Server Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instance == null) _instance = new Server();
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// instance events
|
||||||
|
public event EventHandler<StartingEventArgs> InitStart
|
||||||
|
{
|
||||||
|
add => MainGameServer_Constructor_Patch.preConstructor += value;
|
||||||
|
remove => MainGameServer_Constructor_Patch.preConstructor -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<StartedEventArgs> InitComplete
|
||||||
|
{
|
||||||
|
add => ServerReadyEngine.serverEngineReady += value;
|
||||||
|
remove => ServerReadyEngine.serverEngineReady -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<StartedEventArgs> FrameworkReady
|
||||||
|
{
|
||||||
|
add => ServerReadyEngine.serverFrameworkReady += value;
|
||||||
|
remove => ServerReadyEngine.serverFrameworkReady -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<StopEventArgs> FrameworkExit
|
||||||
|
{
|
||||||
|
add => ServerReadyEngine.serverFrameworkDestroyed += value;
|
||||||
|
remove => ServerReadyEngine.serverFrameworkDestroyed -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// properties
|
||||||
|
|
||||||
|
public GameServerSettings GameServerSettings
|
||||||
|
{
|
||||||
|
get => MainGameServer_SetupMods_Patch._gameServerSettings;
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
MainGameServer_SetupMods_Patch._gameServerSettings = value;
|
||||||
|
Traverse.Create(MainGameServer_Constructor_Patch.mgs).Field<GameServerSettings>("_gameServerSettings").Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Server()
|
||||||
|
{
|
||||||
|
new ServerReadyEngine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
class MainGameServer_SetupMods_Patch
|
||||||
|
{
|
||||||
|
internal static GameServerSettings _gameServerSettings;
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(GameServerSettings ____gameServerSettings)
|
||||||
|
{
|
||||||
|
_gameServerSettings = ____gameServerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTargetMethod]
|
||||||
|
public static MethodBase Target()
|
||||||
|
{
|
||||||
|
return AccessTools.Method("GameServer.GameFramework.MainGameServer:SetupMods");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
class MainGameServer_Constructor_Patch
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static ICompositionRoot mgs = null;
|
||||||
|
|
||||||
|
internal static event EventHandler<StartingEventArgs> preConstructor;
|
||||||
|
|
||||||
|
internal static event EventHandler<StartingEventArgs> postConstructor;
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void BeforeMethodCall()
|
||||||
|
{
|
||||||
|
if (preConstructor != null) preConstructor(null, default(StartingEventArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(ICompositionRoot __instance)
|
||||||
|
{
|
||||||
|
mgs = __instance;
|
||||||
|
if (postConstructor != null) postConstructor(__instance, default(StartingEventArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTargetMethod]
|
||||||
|
public static MethodBase Target()
|
||||||
|
{
|
||||||
|
return AccessTools.Constructor(AccessTools.TypeByName("GameServer.GameFramework.MainGameServer"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(PhotonNetwork), "ConnectUsingSettings")]
|
||||||
|
class PhotonNetwork_ConnectUsingSettings_Patch
|
||||||
|
{
|
||||||
|
internal static event EventHandler<StartedEventArgs> preConnect;
|
||||||
|
|
||||||
|
internal static event EventHandler<StartedEventArgs> postConnect;
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(string gameVersion)
|
||||||
|
{
|
||||||
|
if (postConnect != null) postConnect(null, new StartedEventArgs
|
||||||
|
{
|
||||||
|
photonVersion = gameVersion,
|
||||||
|
photonRegion = PhotonNetwork.CloudRegion,
|
||||||
|
worldName = MainGameServer_SetupMods_Patch._gameServerSettings.GetWorldName(),
|
||||||
|
gameGuid = MainGameServer_SetupMods_Patch._gameServerSettings.GetGameGuid(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void BeforeMethodCall(string gameVersion)
|
||||||
|
{
|
||||||
|
if (preConnect != null) preConnect(null, new StartedEventArgs
|
||||||
|
{
|
||||||
|
photonVersion = gameVersion,
|
||||||
|
photonRegion = PhotonNetwork.CloudRegion,
|
||||||
|
worldName = MainGameServer_SetupMods_Patch._gameServerSettings.GetWorldName(),
|
||||||
|
gameGuid = MainGameServer_SetupMods_Patch._gameServerSettings.GetGameGuid(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
CLre_server/API/MainServer/ServerEngines.cs
Normal file
50
CLre_server/API/MainServer/ServerEngines.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using System;
|
||||||
|
using CLre_server.API.Engines;
|
||||||
|
using Game.DataLoader;
|
||||||
|
using GameServer;
|
||||||
|
using Svelto.Context;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace CLre_server.API.MainServer
|
||||||
|
{
|
||||||
|
class ServerReadyEngine : ServerEnginePostBuild, IWaitForFrameworkInitialization, IWaitForFrameworkDestruction
|
||||||
|
{
|
||||||
|
internal static event EventHandler<StartedEventArgs> serverEngineReady;
|
||||||
|
|
||||||
|
internal static event EventHandler<StartedEventArgs> serverFrameworkReady;
|
||||||
|
|
||||||
|
internal static event EventHandler<StopEventArgs> serverFrameworkDestroyed;
|
||||||
|
|
||||||
|
public override void Ready()
|
||||||
|
{
|
||||||
|
GameServerSettings gss = Server.Instance.GameServerSettings;
|
||||||
|
if (serverEngineReady != null) serverEngineReady(this, new StartedEventArgs
|
||||||
|
{
|
||||||
|
photonVersion = PhotonNetwork.gameVersion,
|
||||||
|
photonRegion = PhotonNetwork.CloudRegion,
|
||||||
|
gameGuid = gss.GetGameGuid(),
|
||||||
|
worldName = gss.GetWorldName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEntitiesDB entitiesDB { get; set; }
|
||||||
|
public override IEntityFactory entityFactory { get; set; }
|
||||||
|
|
||||||
|
public void OnFrameworkInitialized()
|
||||||
|
{
|
||||||
|
GameServerSettings gss = Server.Instance.GameServerSettings;
|
||||||
|
if (serverFrameworkReady != null) serverFrameworkReady(this, new StartedEventArgs
|
||||||
|
{
|
||||||
|
photonVersion = PhotonNetwork.gameVersion,
|
||||||
|
photonRegion = PhotonNetwork.CloudRegion,
|
||||||
|
gameGuid = gss.GetGameGuid(),
|
||||||
|
worldName = gss.GetWorldName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnFrameworkDestroyed()
|
||||||
|
{
|
||||||
|
if (serverFrameworkDestroyed != null) serverFrameworkDestroyed(this, new StopEventArgs{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
CLre_server/API/MainServer/ServerEventArgs.cs
Normal file
14
CLre_server/API/MainServer/ServerEventArgs.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace CLre_server.API.MainServer
|
||||||
|
{
|
||||||
|
public struct StartingEventArgs{}
|
||||||
|
|
||||||
|
public struct StartedEventArgs
|
||||||
|
{
|
||||||
|
public string photonVersion;
|
||||||
|
public CloudRegionCode photonRegion;
|
||||||
|
public string worldName;
|
||||||
|
public string gameGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct StopEventArgs{}
|
||||||
|
}
|
55
CLre_server/API/Tools/AccessToolsWarnings.cs
Normal file
55
CLre_server/API/Tools/AccessToolsWarnings.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
namespace CLre_server.API.Tools
|
||||||
|
{
|
||||||
|
public static class AccessToolsWarnings
|
||||||
|
{
|
||||||
|
internal static bool isEnabled = false;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
|
{
|
||||||
|
isEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Disable()
|
||||||
|
{
|
||||||
|
isEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(AccessTools), "TypeByName")]
|
||||||
|
class AccessTools_TypeByName_Patch
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(Type __result, string name)
|
||||||
|
{
|
||||||
|
if (!AccessToolsWarnings.isEnabled) return;
|
||||||
|
if (__result == null)
|
||||||
|
{
|
||||||
|
var method = (new StackTrace()).GetFrame(2).GetMethod();
|
||||||
|
Utility.Logging.LogWarning($"[{method.DeclaringType.FullName}.{method.Name}] AccessTools.TypeByName(\"{name}\") returned null result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(AccessTools), "Method",
|
||||||
|
new Type[] {typeof(string), typeof(Type[]), typeof(Type[])})]
|
||||||
|
class AccessTools_Method_Patch
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(MethodInfo __result, string typeColonMethodname)
|
||||||
|
{
|
||||||
|
if (!AccessToolsWarnings.isEnabled) return;
|
||||||
|
if (__result == null)
|
||||||
|
{
|
||||||
|
var method = (new StackTrace()).GetFrame(2).GetMethod();
|
||||||
|
Utility.Logging.LogWarning($"[{method.DeclaringType.FullName}.{method.Name}] AccessTools.Method(\"{typeColonMethodname}\") returned null result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
101
CLre_server/API/Tools/NetServerListener.cs
Normal file
101
CLre_server/API/Tools/NetServerListener.cs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using GameNetworkLayer.Shared;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.DataStructures.Experimental;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Tools
|
||||||
|
{
|
||||||
|
public class NetServerListener
|
||||||
|
{
|
||||||
|
internal static bool isEnabled = false;
|
||||||
|
|
||||||
|
private static FasterDictionary<short, FasterList<NetReceiveMessageCallback>> callbacks = new FasterDictionary<short,FasterList<NetReceiveMessageCallback>>();
|
||||||
|
|
||||||
|
public delegate void NetReceiveMessageCallback(NetworkDispatcherCode code, byte[] data, int playerId);
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
|
{
|
||||||
|
isEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Disable()
|
||||||
|
{
|
||||||
|
isEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DebugReceiveMessage(NetworkDispatcherCode code, NetReceiveMessageCallback callback)
|
||||||
|
{
|
||||||
|
short key = (short)code;
|
||||||
|
if (callbacks.TryGetValue(key, out FasterList<NetReceiveMessageCallback> handlers))
|
||||||
|
{
|
||||||
|
handlers.Add(callback);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FasterList<NetReceiveMessageCallback> newHandlers = new FasterList<NetReceiveMessageCallback>(new [] {callback});
|
||||||
|
callbacks.Add(key, newHandlers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool RunDebugCallbacks(NetworkDispatcherCode code, byte[] data, int playerId)
|
||||||
|
{
|
||||||
|
short key = (short)code;
|
||||||
|
if (callbacks.TryGetValue(key, out FasterList<NetReceiveMessageCallback> handlers))
|
||||||
|
{
|
||||||
|
foreach (NetReceiveMessageCallback callback in handlers)
|
||||||
|
{
|
||||||
|
callback(code, data, playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Log(NetworkDispatcherCode code, byte[] data, int playerId)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("Received ");
|
||||||
|
sb.Append(code.ToString());
|
||||||
|
sb.Append(" for player #");
|
||||||
|
sb.Append(playerId);
|
||||||
|
sb.Append(": 0x");
|
||||||
|
foreach (byte b in data)
|
||||||
|
{
|
||||||
|
sb.Append(b.ToString("X"));
|
||||||
|
}
|
||||||
|
Utility.Logging.Log(sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
class NetMessageClientListener_HandleAllMessages_Patch
|
||||||
|
{
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void BeforeMethodCall(object ____deserializer, int playerId, object value)
|
||||||
|
{
|
||||||
|
if (!NetServerListener.isEnabled) return;
|
||||||
|
// TODO optimize this to not use Traverse
|
||||||
|
Traverse result = Traverse.Create(____deserializer).Method("Deserialize", value);
|
||||||
|
NetworkDispatcherCode code = result.Field<NetworkDispatcherCode>("dispatcherCode").Value;
|
||||||
|
byte[] data = result.Field<byte[]>("bytes").Value;
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
Utility.Logging.LogWarning("Network message data was deserialized as null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool isHandled = NetServerListener.RunDebugCallbacks(code, data, playerId);
|
||||||
|
if (!isHandled) Utility.Logging.Log($"Received network message for player {playerId} (code: {code.ToString()}, len: {data.Length})");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTargetMethod]
|
||||||
|
public static MethodBase Target()
|
||||||
|
{
|
||||||
|
return AccessTools.Method("GameNetworkLayer.Server.NetMessageServerListener:HandleAllMessages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
CLre_server/API/Tools/NetServerSender.cs
Normal file
114
CLre_server/API/Tools/NetServerSender.cs
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using GameNetworkLayer.Shared;
|
||||||
|
using HarmonyLib;
|
||||||
|
using NetworkFramework.Shared;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Tools
|
||||||
|
{
|
||||||
|
public class NetServerSender
|
||||||
|
{
|
||||||
|
private struct DummyNetDataStruct : ISerializedNetData
|
||||||
|
{
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(byte[] data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly MethodInfo _genericSendMessage = AccessTools.Method("GameNetworkLayer.Server.NetMessageServerSender:SendMessage");
|
||||||
|
|
||||||
|
private static readonly MethodInfo _genericGetSendMessageMethod =
|
||||||
|
AccessTools.Method(typeof(NetServerSender), "GetSendMessageMethod",parameters: new Type[0]);/*
|
||||||
|
/*((Func<MethodInfo>) GetSendMessageMethod<DummyNetDataStruct>).Method
|
||||||
|
.GetBaseDefinition()
|
||||||
|
.GetGenericMethodDefinition();*/
|
||||||
|
|
||||||
|
private static readonly MethodInfo _genericLog =
|
||||||
|
AccessTools.Method(typeof(NetServerSender), "Log");/*
|
||||||
|
((Action<NetworkDispatcherCode, DummyNetDataStruct>) Log<DummyNetDataStruct>).Method
|
||||||
|
.GetBaseDefinition()
|
||||||
|
.GetGenericMethodDefinition();*/
|
||||||
|
|
||||||
|
private static readonly MethodInfo _genericGetLogMethod =
|
||||||
|
AccessTools.Method(typeof(NetServerSender), "GetLogMethod", new Type[0]);/*
|
||||||
|
((Func<MethodInfo>) GetLogMethod<DummyNetDataStruct>).Method
|
||||||
|
.GetBaseDefinition()
|
||||||
|
.GetGenericMethodDefinition();*/
|
||||||
|
|
||||||
|
public static MethodInfo GetSendMessageMethod(Type t)
|
||||||
|
{
|
||||||
|
return (MethodInfo) _genericGetSendMessageMethod.MakeGenericMethod(t)
|
||||||
|
.Invoke(null, new object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo GetSendMessageMethod<T>() where T : struct, ISerializedNetData
|
||||||
|
{
|
||||||
|
return _genericSendMessage.MakeGenericMethod(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo DebugSendMessage<T>(Harmony instance = null, MethodInfo before = null, MethodInfo after = null, MethodInfo transpiler = null, MethodInfo finalizer = null) where T : struct, ISerializedNetData
|
||||||
|
{
|
||||||
|
return DebugSendMessage(typeof(T), instance, before, after, transpiler, finalizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo DebugSendMessage(Type generic, Harmony instance = null, MethodInfo before = null, MethodInfo after = null, MethodInfo transpiler = null, MethodInfo finalizer = null)
|
||||||
|
{
|
||||||
|
return DebugSendMessage(
|
||||||
|
generic, instance,
|
||||||
|
before == null ? null : new HarmonyMethod(before),
|
||||||
|
after == null ? null : new HarmonyMethod(after),
|
||||||
|
transpiler == null ? null : new HarmonyMethod(transpiler),
|
||||||
|
finalizer == null ? null : new HarmonyMethod(finalizer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo DebugSendMessage<T>(Harmony instance = null, HarmonyMethod before = null, HarmonyMethod after = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null) where T : struct, ISerializedNetData
|
||||||
|
{
|
||||||
|
return DebugSendMessage(typeof(T), instance, before, after, transpiler, finalizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo DebugSendMessage(Type generic, Harmony instance = null, HarmonyMethod before = null, HarmonyMethod after = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null)
|
||||||
|
{
|
||||||
|
if (instance == null) instance = CLre.harmonyInstance;
|
||||||
|
MethodInfo target = GetSendMessageMethod(generic);
|
||||||
|
return instance.Patch(target,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
transpiler,
|
||||||
|
finalizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo GetLogMethod(Type t)
|
||||||
|
{
|
||||||
|
return (MethodInfo) _genericGetLogMethod.MakeGenericMethod(t)
|
||||||
|
.Invoke(null, new object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodInfo GetLogMethod<T>() where T : struct, ISerializedNetData
|
||||||
|
{
|
||||||
|
return _genericLog.MakeGenericMethod(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Log<T>(NetworkDispatcherCode code, ref T data) where T : struct, ISerializedNetData
|
||||||
|
{
|
||||||
|
//Utility.Logging.Log($"Sending ISerializedNetData {data.GetType().FullName} (code: {code.ToString()})");
|
||||||
|
Traverse d = Traverse.Create(data);
|
||||||
|
StringBuilder sb = new StringBuilder($"Sending ISerializedNetData {data.GetType().FullName} (code: {code.ToString()})");
|
||||||
|
foreach (string fieldName in d.Fields())
|
||||||
|
{
|
||||||
|
Traverse field = d.Field(fieldName);
|
||||||
|
sb.Append("\n");
|
||||||
|
sb.Append("\"");
|
||||||
|
sb.Append(fieldName.Substring(fieldName.IndexOf('<')+1, fieldName.LastIndexOf('>')-1));
|
||||||
|
sb.Append("\": ");
|
||||||
|
sb.Append(field.GetValue());
|
||||||
|
}
|
||||||
|
Utility.Logging.Log(sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
CLre_server/API/Utility/Logging.cs
Normal file
88
CLre_server/API/Utility/Logging.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Utility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class to access Cardlife's built-in logging capabilities.
|
||||||
|
/// The log is saved to outputLog#.Log
|
||||||
|
/// </summary>
|
||||||
|
public static class Logging
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Log(string msg)
|
||||||
|
{
|
||||||
|
Svelto.Console.Log(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a regular message to Cardlife's log
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to log</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Log(object obj)
|
||||||
|
{
|
||||||
|
Svelto.Console.Log(obj.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void LogError(string msg, Dictionary<string, string> extraData = null)
|
||||||
|
{
|
||||||
|
Svelto.Console.LogError(msg, extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write an error message to Cardlife's log
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to log</param>
|
||||||
|
/// <param name="extraData">The extra data to pass to the ILogger</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void LogError(object obj, Dictionary<string, string> extraData = null)
|
||||||
|
{
|
||||||
|
Svelto.Console.LogError(obj.ToString(), extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write an exception to Cardlife's log and to the screen and exit game
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">The exception to log</param>
|
||||||
|
/// <param name="extraData">The extra data to pass to the ILogger.
|
||||||
|
/// This is automatically populated with "OuterException#" and "OuterStacktrace#" entries</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void LogException(Exception e, string msg = null, Dictionary<string, string> extraData = null)
|
||||||
|
{
|
||||||
|
Svelto.Console.LogException(msg, e, extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void LogWarning(string msg)
|
||||||
|
{
|
||||||
|
Svelto.Console.LogWarning(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a warning message to Cardlife's log
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to log</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void LogWarning(object obj)
|
||||||
|
{
|
||||||
|
Svelto.Console.LogWarning(obj.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// descriptive logging
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a descriptive message to Cardlife's log including the calling method's name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to log</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void MetaLog(object obj)
|
||||||
|
{
|
||||||
|
var method = (new StackTrace()).GetFrame(1).GetMethod();
|
||||||
|
Log($"[{method.DeclaringType.FullName}.{method.Name}] {obj.ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
CLre_server/API/Utility/Reflection.cs
Normal file
119
CLre_server/API/Utility/Reflection.cs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using GameNetworkLayer.Shared;
|
||||||
|
using HarmonyLib;
|
||||||
|
using NetworkFramework.Shared;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
namespace CLre_server.API.Utility
|
||||||
|
{
|
||||||
|
public static class Reflection
|
||||||
|
{
|
||||||
|
// useful function & method prototypes
|
||||||
|
public delegate T Getter<T>();
|
||||||
|
|
||||||
|
public delegate bool ExistsV1(EGID egid);
|
||||||
|
|
||||||
|
public delegate bool ExistsV2(int id, int groupid);
|
||||||
|
|
||||||
|
public delegate T QueryEntityViewV1<T>(EGID egid);
|
||||||
|
|
||||||
|
public delegate T QueryEntityViewV2<T>(int id, ExclusiveGroup.ExclusiveGroupStruct groupid);
|
||||||
|
|
||||||
|
public delegate ReadOnlyCollectionStruct<T> QueryEntityViews<T>(int group) where T : class, IEntityViewStruct;
|
||||||
|
|
||||||
|
public delegate T[] QueryEntitiesV2<T>(int group, out int count) where T : IEntityStruct;
|
||||||
|
|
||||||
|
public delegate T[] QueryEntitiesV1<T>(ExclusiveGroup.ExclusiveGroupStruct group, out int count) where T : IEntityStruct;
|
||||||
|
|
||||||
|
public delegate object[] SendMessage<T>(NetworkDispatcherCode dispatcherCode, ref T value) where T : struct, ISerializedNetData;
|
||||||
|
|
||||||
|
// useful reflection functions
|
||||||
|
public static TFuncProto BuildDelegate<TFuncProto>(MethodInfo method) where TFuncProto : Delegate
|
||||||
|
{
|
||||||
|
return (TFuncProto) Delegate.CreateDelegate(typeof(TFuncProto), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Delegate BuildDelegateRaw(MethodInfo method, Type TFuncProto)
|
||||||
|
{
|
||||||
|
return Delegate.CreateDelegate(TFuncProto, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TFuncProto BuildDelegate<TFuncProto>(MethodInfo method, object instance) where TFuncProto : Delegate
|
||||||
|
{
|
||||||
|
return (TFuncProto) Delegate.CreateDelegate(typeof(TFuncProto), instance, method, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Delegate BuildDelegateRaw(MethodInfo method, object instance, Type TFuncProto)
|
||||||
|
{
|
||||||
|
return Delegate.CreateDelegate(TFuncProto, instance, method, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TFuncProto MethodAsDelegate<TFuncProto>(Type class_, string methodName, Type[] parameters = null, Type[] generics = null, object instance = null) where TFuncProto : Delegate
|
||||||
|
{
|
||||||
|
MethodInfo method = AccessTools.Method(class_, methodName, parameters, generics);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
return BuildDelegate<TFuncProto>(method, instance);
|
||||||
|
}
|
||||||
|
return BuildDelegate<TFuncProto>(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Delegate MethodAsDelegateRaw(Type class_, Type TFuncProto, string methodName, Type[] parameters = null, Type[] generics = null, object instance = null)
|
||||||
|
{
|
||||||
|
MethodInfo method = AccessTools.Method(class_, methodName, parameters, generics);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
return BuildDelegateRaw(method, instance, TFuncProto);
|
||||||
|
}
|
||||||
|
return BuildDelegateRaw(method, TFuncProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TFuncProto MethodAsDelegate<TFuncProto>(string typeColonName, Type[] parameters = null, Type[] generics = null, object instance = null) where TFuncProto : Delegate
|
||||||
|
{
|
||||||
|
MethodInfo method = AccessTools.Method(typeColonName, parameters, generics);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
return BuildDelegate<TFuncProto>(method, instance);
|
||||||
|
}
|
||||||
|
return BuildDelegate<TFuncProto>(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Delegate MethodAsDelegateRaw(string typeColonName, Type TFuncProto, Type[] parameters = null, Type[] generics = null, object instance = null)
|
||||||
|
{
|
||||||
|
MethodInfo method = AccessTools.Method(typeColonName, parameters, generics);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
return BuildDelegateRaw(method, instance, TFuncProto);
|
||||||
|
}
|
||||||
|
return BuildDelegateRaw(method, TFuncProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyInfo GetIndexer(this Type type, Type[] arguments = null, BindingFlags bindingFlags = BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.SetField | BindingFlags.SetProperty | BindingFlags.Static)
|
||||||
|
{
|
||||||
|
foreach (PropertyInfo p in type.GetProperties(bindingFlags))
|
||||||
|
{
|
||||||
|
if (arguments == null && p.GetIndexParameters().Length != 0) return p;
|
||||||
|
if (arguments != null)
|
||||||
|
{
|
||||||
|
uint count = 0;
|
||||||
|
foreach (ParameterInfo param in p.GetIndexParameters())
|
||||||
|
{
|
||||||
|
if (param.ParameterType != arguments[count])
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count == arguments.Length)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
CLre_server/CLre_server.cs
Normal file
102
CLre_server/CLre_server.cs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using CLre_server.API.Tools;
|
||||||
|
using CLre_server.WebStatus;
|
||||||
|
using GameNetworkLayer.Shared;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace CLre_server
|
||||||
|
{
|
||||||
|
public class CLre : IllusionPlugin.IEnhancedPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin
|
||||||
|
{
|
||||||
|
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
|
||||||
|
|
||||||
|
public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||||
|
|
||||||
|
internal static Harmony harmonyInstance = null;
|
||||||
|
|
||||||
|
// called when Cardlife shuts down
|
||||||
|
public override void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
WebServer.Deinit();
|
||||||
|
harmonyInstance.UnpatchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when Cardlife starts up
|
||||||
|
public override void OnApplicationStart()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
FileLog.Reset();
|
||||||
|
Harmony.DEBUG = true;
|
||||||
|
// enable CLre debug functionality
|
||||||
|
AccessToolsWarnings.Enable();
|
||||||
|
NetServerListener.Enable();
|
||||||
|
#endif
|
||||||
|
// init all Harmony patches in project
|
||||||
|
harmonyInstance = new Harmony(Name);
|
||||||
|
harmonyInstance.PatchAll();
|
||||||
|
|
||||||
|
// patches for bugs
|
||||||
|
Fixes.InitLogSooner.Init();
|
||||||
|
|
||||||
|
// misc
|
||||||
|
LogIPAPlugins(); // log plugins again so they show up in the log, and not just stdout
|
||||||
|
Fixes.BugfixAttributeUtility.LogBugfixes(); // log bugfixes that are applied
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
// test CLre debug functionality
|
||||||
|
Type netData = AccessTools.TypeByName("Game.Handhelds.DrawingStateMessage");
|
||||||
|
NetServerSender.DebugSendMessage(netData, harmonyInstance,
|
||||||
|
NetServerSender.GetLogMethod(netData));
|
||||||
|
API.Utility.Logging.MetaLog("Patched SendMessage<Game.Handhelds.DrawingStateMessage>");
|
||||||
|
|
||||||
|
netData = AccessTools.TypeByName("Shared.Inventory.HandheldEquipmentRequest");
|
||||||
|
NetServerSender.DebugSendMessage(netData, harmonyInstance,
|
||||||
|
NetServerSender.GetLogMethod(netData));
|
||||||
|
API.Utility.Logging.MetaLog("Patched SendMessage<Shared.Inventory.HandheldEquipmentRequest>");
|
||||||
|
|
||||||
|
NetServerListener.DebugReceiveMessage(NetworkDispatcherCode.EACMessageServerToClient,
|
||||||
|
NetServerListener.Log);
|
||||||
|
|
||||||
|
// API debug and testing
|
||||||
|
API.MainServer.Server.Instance.FrameworkReady += (_, __) => API.Utility.Logging.MetaLog("(!) Server framework ready for business");
|
||||||
|
API.MainServer.Server.Instance.FrameworkExit += (_, __) => API.Utility.Logging.MetaLog("(!) Server framework shutting down"); // this seems to never happen
|
||||||
|
API.MainServer.Server.Instance.InitStart += (_, __) => API.Utility.Logging.MetaLog("(!) Server initialising");
|
||||||
|
API.MainServer.Server.Instance.InitComplete += (_, __) => API.Utility.Logging.MetaLog("(!) Server successfully initialised");
|
||||||
|
#endif
|
||||||
|
WebServer.Init();
|
||||||
|
// Log info
|
||||||
|
API.Utility.Logging.MetaLog($"{Name} init complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogIPAPlugins()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendFormat("Running on Unity {0}\n", Application.unityVersion);
|
||||||
|
sb.AppendFormat("Running on CardLife Server {0} (aka {1})\n", Game.Utilities.VersionReader.GetVersion(), Application.version);
|
||||||
|
sb.AppendFormat("-----------------------------\n");
|
||||||
|
sb.AppendFormat("Loading plugins from {0} and found {1}\n", System.IO.Path.Combine(Environment.CurrentDirectory, "Plugins"), IllusionInjector.PluginManager.Plugins.Count());
|
||||||
|
sb.AppendFormat("-----------------------------\n");
|
||||||
|
foreach (IllusionPlugin.IPlugin plugin in IllusionInjector.PluginManager.Plugins)
|
||||||
|
{
|
||||||
|
|
||||||
|
sb.AppendFormat(" {0}: {1}\n", plugin.Name, plugin.Version);
|
||||||
|
}
|
||||||
|
sb.AppendFormat("-----------------------------\n");
|
||||||
|
API.Utility.Logging.Log(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI()
|
||||||
|
{
|
||||||
|
if (GUI.Button(new Rect(10, 10, 50, 50), "QUIT"))
|
||||||
|
{
|
||||||
|
Application.Quit(); // yeet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
292
CLre_server/CLre_server.csproj
Normal file
292
CLre_server/CLre_server.csproj
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Version>0.0.2</Version>
|
||||||
|
<Authors>NGnius</Authors>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageProjectUrl>https://git.exmods.org/NGnius/CLre</PackageProjectUrl>
|
||||||
|
<NeutralLanguage>en-CA</NeutralLanguage>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.0.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!--Start Dependencies-->
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Accessibility">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Accessibility.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="BehaviorDesignerRuntime">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\BehaviorDesignerRuntime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DOTween">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\DOTween.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EasyAntiCheat.Client">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\EasyAntiCheat.Client.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EasyAntiCheat.Server">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\EasyAntiCheat.Server.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Fabric.AudioSpline">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Fabric.AudioSpline.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Fabric.Core">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Fabric.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="log4net">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\log4net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="mscorlib">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\mscorlib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Novell.Directory.Ldap">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Novell.Directory.Ldap.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Photon3Unity3D">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Photon3Unity3D.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Rewired_Core">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Rewired_Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Rewired_Windows_Lib">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Rewired_Windows_Lib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Svelto.Common">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Svelto.Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Svelto.ECS">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Svelto.ECS.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Svelto.Tasks">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Svelto.Tasks.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Postprocessing.Runtime">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.TextMeshPro">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\Unity.TextMeshPro.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AccessibilityModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AIModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.AIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AnimationModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ARModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ARModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AssetBundleModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AudioModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.BaselibModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.BaselibModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClothModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterInputModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterRendererModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CrashReportingModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.DirectorModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.FileSystemHttpModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.FileSystemHttpModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GameCenterModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GridModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.GridModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.HotReloadModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ImageConversionModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.IMGUIModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.InputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.JSONSerializeModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.LocalizationModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.Networking">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.Networking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ParticleSystemModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PerformanceReportingModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.Physics2DModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PhysicsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ProfilerModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ProfilerModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ScreenCaptureModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SharedInternalsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpatialTracking">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.SpatialTracking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteMaskModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteShapeModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.StreamingModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.StyleSheetsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.StyleSheetsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SubstanceModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainPhysicsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextCoreModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TextCoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextRenderingModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TilemapModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.Timeline">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.Timeline.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TimelineModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TimelineModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TLSModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UI">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIElementsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UmbraModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UNETModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UNETModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityAnalyticsModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityConnectModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityTestProtocolModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VehiclesModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VFXModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VideoModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VRModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.VRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.WindModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.WindModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.XRModule">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\UnityEngine.XRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="IllusionInjector">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\IllusionInjector.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="IllusionPlugin">
|
||||||
|
<HintPath>..\..\cl\CardlifeGameServer_Data\Managed\IllusionPlugin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="WebStatus\Assets\index.html" />
|
||||||
|
<EmbeddedResource Include="WebStatus\Assets\index.html" />
|
||||||
|
<None Remove="WebStatus\Assets\error404.html" />
|
||||||
|
<EmbeddedResource Include="WebStatus\Assets\error404.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
<!--End Dependencies-->
|
||||||
|
|
||||||
|
</Project>
|
118
CLre_server/Fixes/BugfixAttribute.cs
Normal file
118
CLre_server/Fixes/BugfixAttribute.cs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CLre_server.Fixes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class BugfixAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public string description { get; set; }
|
||||||
|
public Type target { get; set; }
|
||||||
|
public string more { get; set; }
|
||||||
|
public uint id { get; set; }
|
||||||
|
public BugfixType component { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BugfixType : byte
|
||||||
|
{
|
||||||
|
Miscellaneous = 0x00,
|
||||||
|
HarmonyPatch,
|
||||||
|
Initialiser,
|
||||||
|
Workaround,
|
||||||
|
API,
|
||||||
|
Debug
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class BugfixAttributeUtility
|
||||||
|
{
|
||||||
|
public static void LogBugfixes()
|
||||||
|
{
|
||||||
|
List<uint> keys = new List<uint>();
|
||||||
|
Dictionary<uint, BugfixStruct> fixes = new Dictionary<uint, BugfixStruct>();
|
||||||
|
foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
|
||||||
|
{
|
||||||
|
BugfixAttribute b = t.GetCustomAttribute<BugfixAttribute>(true);
|
||||||
|
if (b != null)
|
||||||
|
{
|
||||||
|
if (!fixes.ContainsKey(b.id))
|
||||||
|
{
|
||||||
|
BugfixStruct bugfixStruct = new BugfixStruct{id = b.id};
|
||||||
|
bugfixStruct.Merge(b);
|
||||||
|
fixes[b.id] = bugfixStruct;
|
||||||
|
keys.Add(b.id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BugfixStruct bugfixStruct = fixes[b.id];
|
||||||
|
bugfixStruct.Merge(b);
|
||||||
|
fixes[b.id] = bugfixStruct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys.Sort();
|
||||||
|
//keys.Sort((u, u1) => u.CompareTo(u1));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendFormat("Applying {0} CLre fixes\n", keys.Count);
|
||||||
|
sb.AppendFormat("-----------------------------\n");
|
||||||
|
foreach (uint i in keys)
|
||||||
|
{
|
||||||
|
sb.Append(fixes[i].ToString());
|
||||||
|
sb.Append("\n");
|
||||||
|
}
|
||||||
|
sb.AppendFormat("-----------------------------\n");
|
||||||
|
API.Utility.Logging.Log(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct BugfixStruct
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string description;
|
||||||
|
public Type target;
|
||||||
|
public string more;
|
||||||
|
public uint id;
|
||||||
|
private uint total;
|
||||||
|
private uint[] bugfixTypeCount;
|
||||||
|
|
||||||
|
public void Merge(BugfixAttribute b)
|
||||||
|
{
|
||||||
|
if (name == null && b.name != null) name = b.name;
|
||||||
|
if (description == null && b.description != null) description = b.description;
|
||||||
|
if (target == null && b.target != null) target = b.target;
|
||||||
|
if (more == null && b.more != null) more = b.more;
|
||||||
|
total++;
|
||||||
|
if (bugfixTypeCount == null) bugfixTypeCount = new uint[Enum.GetNames(typeof(BugfixType)).Length];
|
||||||
|
bugfixTypeCount[(byte) b.component]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendFormat(" {0}: ", name);
|
||||||
|
if (more != null)
|
||||||
|
{
|
||||||
|
sb.AppendFormat("[MORE: {0}] ", more);
|
||||||
|
}
|
||||||
|
else if (description != null)
|
||||||
|
{
|
||||||
|
sb.Append(description);
|
||||||
|
sb.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != null)
|
||||||
|
{
|
||||||
|
sb.AppendFormat("[TARGET: {0}] ", target.FullName);
|
||||||
|
}
|
||||||
|
sb.AppendFormat("[ID: {0}] ", id);
|
||||||
|
sb.AppendFormat("({0}M/{1}P/{2}I/{3}W/{4}A/{5}D/{6}T)",
|
||||||
|
bugfixTypeCount[(byte) BugfixType.Miscellaneous], bugfixTypeCount[(byte) BugfixType.HarmonyPatch],
|
||||||
|
bugfixTypeCount[(byte) BugfixType.Initialiser], bugfixTypeCount[(byte) BugfixType.Workaround],
|
||||||
|
bugfixTypeCount[(byte) BugfixType.API], bugfixTypeCount[(byte) BugfixType.Debug], total);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
CLre_server/Fixes/InitLogSooner.cs
Normal file
84
CLre_server/Fixes/InitLogSooner.cs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace CLre_server.Fixes
|
||||||
|
{
|
||||||
|
[Bugfix(name = "InitLogSooner",
|
||||||
|
description = "Start the logger slightly sooner than Cardlife does",
|
||||||
|
component = BugfixType.Initialiser, id = 0)]
|
||||||
|
public static class InitLogSooner
|
||||||
|
{
|
||||||
|
public static int millisecondsTimeout = 5000;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CustomLoggerThread_CreateGameObject_Patch.allowed = true;
|
||||||
|
CustomLoggerThread.CreateGameObject();
|
||||||
|
CustomLoggerThread_CreateGameObject_Patch.allowed = false;
|
||||||
|
API.Utility.Logging.Log($"Completed early log init, hello!");
|
||||||
|
//System.IO.File.WriteAllText("InitLogSooner.log", $"Done at " + System.DateTime.Now.ToString());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
API.Utility.Logging.Log($"Failed to initialise log sooner, reason:\n" + e);
|
||||||
|
System.IO.File.WriteAllText("InitLogSooner.log", e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Bugfix(name = "InitLogSooner",
|
||||||
|
target = typeof(CustomLoggerThread),
|
||||||
|
component = BugfixType.HarmonyPatch, id = 0)]
|
||||||
|
[HarmonyPatch(typeof(CustomLoggerThread), "CreateGameObject")]
|
||||||
|
class CustomLoggerThread_CreateGameObject_Patch
|
||||||
|
{
|
||||||
|
internal static bool allowed = false;
|
||||||
|
|
||||||
|
public static bool Prefix()
|
||||||
|
{
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Bugfix(name = "InitLogSooner",
|
||||||
|
component = BugfixType.HarmonyPatch, id = 0)]
|
||||||
|
[HarmonyPatch(typeof(CustomLoggerThread), "StartQueue")]
|
||||||
|
class CustomLoggerThread_StartQueue_Patch
|
||||||
|
{
|
||||||
|
internal static volatile bool IsLogStarted = false;
|
||||||
|
|
||||||
|
private delegate void Flusher();
|
||||||
|
|
||||||
|
public static bool Prefix()
|
||||||
|
{
|
||||||
|
// setup thru reflection
|
||||||
|
FieldInfo quitThreadField = AccessTools.Field(typeof(CustomLoggerThread), "_quitThread");
|
||||||
|
|
||||||
|
MethodInfo flushLoggerMethod = AccessTools.Method(typeof(CustomLoggerThread), "FlushLogger");
|
||||||
|
Flusher flushLogger = (Flusher) Delegate.CreateDelegate(typeof(Action), null, flushLoggerMethod);
|
||||||
|
|
||||||
|
MethodInfo forceFlushMethod = AccessTools.Method("CustomLogger:ForceFlush");
|
||||||
|
Flusher forceFlush = (Flusher) Delegate.CreateDelegate(typeof(Action), null, forceFlushMethod);
|
||||||
|
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
IsLogStarted = true;
|
||||||
|
while (!(bool) quitThreadField.GetValue(null))
|
||||||
|
{
|
||||||
|
flushLogger();
|
||||||
|
forceFlush();
|
||||||
|
Thread.Sleep(millisecondsTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsLogStarted = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
CLre_server/WebStatus/AssetEndpoints.cs
Normal file
74
CLre_server/WebStatus/AssetEndpoints.cs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public static class AssetEndpoints
|
||||||
|
{
|
||||||
|
[WebEndpoint("/")]
|
||||||
|
public static void LandingPage(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
ctx.Response.Headers.Add("Content-Type", "text/html");
|
||||||
|
Asset(ctx, "CLre_server.WebStatus.Assets.index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
[WebEndpoint("/asset")]
|
||||||
|
public static void AllAssets(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
ctx.Response.Headers.Add("Content-Type", "text/html");
|
||||||
|
Asset(ctx, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
[WebEndpoint("/asset/404")]
|
||||||
|
public static void Asset404(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
ctx.Response.Headers.Add("Content-Type", "text/html");
|
||||||
|
Asset(ctx, "CLre_server.WebStatus.Assets.error404.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Asset(HttpListenerContext ctx, string name)
|
||||||
|
{
|
||||||
|
Assembly asm = Assembly.GetCallingAssembly();
|
||||||
|
Stream resource = asm.GetManifestResourceStream(name);
|
||||||
|
if (resource == null)
|
||||||
|
{
|
||||||
|
string assetStr = ListAssetsHtml(asm);
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes(assetStr);
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resource.CopyTo(ctx.Response.OutputStream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ListAssetsHtml(Assembly target)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'><title>Asset not found</title></head><body><h1>");
|
||||||
|
sb.Append(target.GetName().Name);
|
||||||
|
sb.Append(" available assets</h1>");
|
||||||
|
foreach (string asset in target.GetManifestResourceNames())
|
||||||
|
{
|
||||||
|
sb.Append("<li>");
|
||||||
|
sb.Append(asset);
|
||||||
|
sb.Append("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append("</ul></body></html>");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ListAssetsText(Assembly target)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(target.FullName);
|
||||||
|
foreach (string asset in target.GetManifestResourceNames())
|
||||||
|
{
|
||||||
|
sb.Append("\n\t");
|
||||||
|
sb.Append(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
CLre_server/WebStatus/Assets/error404.html
Normal file
13
CLre_server/WebStatus/Assets/error404.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>404 Not Found</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Page not found</h1>
|
||||||
|
<div>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
CLre_server/WebStatus/Assets/index.html
Normal file
16
CLre_server/WebStatus/Assets/index.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>CLre Server Status</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 style="margin:auto; text-align: center; justify-content: center;">Welcome to the CLre HTTP server!</h1>
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<a href="/l/current">View log</a>
|
||||||
|
<br/>
|
||||||
|
<iframe src="/l/current" style="width: 100%; height: available;">Loading...</iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
35
CLre_server/WebStatus/Attributes.cs
Normal file
35
CLre_server/WebStatus/Attributes.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public class Attributes
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.AttributeUsage(System.AttributeTargets.Method)]
|
||||||
|
public class WebEndpointAttribute : System.Attribute
|
||||||
|
{
|
||||||
|
private readonly string endpoint;
|
||||||
|
|
||||||
|
public WebEndpointAttribute(string path)
|
||||||
|
{
|
||||||
|
endpoint = path;
|
||||||
|
Assembly asm = Assembly.GetCallingAssembly();
|
||||||
|
if (!WebServer._assembliesToCheck.Contains(asm))
|
||||||
|
{
|
||||||
|
WebServer._assembliesToCheck.Add(asm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WebServer.MainInstance != null && WebServer.MainInstance.IsRunning)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string GetPath()
|
||||||
|
{
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
CLre_server/WebStatus/ConfigurationEndpoints.cs
Normal file
21
CLre_server/WebStatus/ConfigurationEndpoints.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public class ConfigurationEndpoints
|
||||||
|
{
|
||||||
|
[WebEndpoint("/c/game.json")]
|
||||||
|
public static void GameServerSettings (HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
ctx.Response.Headers.Add("Content-Type", "application/json");
|
||||||
|
GameServer.GameServerSettings gss = API.MainServer.Server.Instance.GameServerSettings;
|
||||||
|
string json = UnityEngine.JsonUtility.ToJson(gss);
|
||||||
|
#if DEBUG
|
||||||
|
API.Utility.Logging.MetaLog("JSONified settings: " + json);
|
||||||
|
#endif
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes(json);
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
CLre_server/WebStatus/DebugEndpoints.cs
Normal file
44
CLre_server/WebStatus/DebugEndpoints.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public static class DebugEndpoints
|
||||||
|
{
|
||||||
|
[WebEndpoint("/d/ping")]
|
||||||
|
private static void PingPong(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes("pong");
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WebEndpoint("/d/version")]
|
||||||
|
internal static void VersionInfo(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append("CardLife Version (Unity): \t");
|
||||||
|
sb.Append(UnityEngine.Application.version);
|
||||||
|
sb.Append("\n");
|
||||||
|
sb.Append("CardLife Version (Game): \t");
|
||||||
|
sb.Append(Game.Utilities.VersionReader.GetVersion());
|
||||||
|
sb.Append("\n");
|
||||||
|
sb.Append("Unity Version: \t\t\t");
|
||||||
|
sb.Append(UnityEngine.Application.unityVersion);
|
||||||
|
sb.Append("\n");
|
||||||
|
sb.Append("CLre Version: \t\t\t");
|
||||||
|
sb.Append(System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||||
|
sb.Append("\n");
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes(sb.ToString());
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
[WebEndpoint("/d/test")]
|
||||||
|
internal static void Experiment(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
string test = "";
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes(test);
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
49
CLre_server/WebStatus/LogEndpoints.cs
Normal file
49
CLre_server/WebStatus/LogEndpoints.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public class LogEndpoints
|
||||||
|
{
|
||||||
|
[WebEndpoint("/l/current")]
|
||||||
|
private static void FullLog(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
if (CustomLogger_GetFileNameToUse_Patch.currentLogFile == null)
|
||||||
|
{
|
||||||
|
byte[] output = Encoding.UTF8.GetBytes("No log file available");
|
||||||
|
ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// copy file because log is already open for writing
|
||||||
|
string copyFilename = CustomLogger_GetFileNameToUse_Patch.currentLogFile + ".copy";
|
||||||
|
File.Copy(CustomLogger_GetFileNameToUse_Patch.currentLogFile, copyFilename, true);
|
||||||
|
FileStream logFile = new FileStream(copyFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
logFile.CopyTo(ctx.Response.OutputStream);
|
||||||
|
logFile.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
class CustomLogger_GetFileNameToUse_Patch
|
||||||
|
{
|
||||||
|
internal static string currentLogFile = null;
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void AfterMethodCall(string __result)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
API.Utility.Logging.MetaLog($"Current logfile is {__result}");
|
||||||
|
#endif
|
||||||
|
currentLogFile = __result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTargetMethod]
|
||||||
|
public static MethodBase Target()
|
||||||
|
{
|
||||||
|
return AccessTools.Method("CustomLogger:GetFileNameToUse");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
192
CLre_server/WebStatus/StatusEndpoints.cs
Normal file
192
CLre_server/WebStatus/StatusEndpoints.cs
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEntitiesDB entitiesDB { get; set; }
|
||||||
|
public override IEntityFactory entityFactory { get; set; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
CLre_server/WebStatus/WebServer.cs
Normal file
162
CLre_server/WebStatus/WebServer.cs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
|
||||||
|
namespace CLre_server.WebStatus
|
||||||
|
{
|
||||||
|
public class WebServer
|
||||||
|
{
|
||||||
|
private const uint DEFAULT_PORT = 5030;
|
||||||
|
private const string DEFAULT_IP = "localhost";
|
||||||
|
private readonly HttpListener _httpListener;
|
||||||
|
|
||||||
|
public delegate void RequestHandler(HttpListenerContext ctx);
|
||||||
|
|
||||||
|
private Dictionary<string, RequestHandler> _handlers = new Dictionary<string, RequestHandler>();
|
||||||
|
|
||||||
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => _httpListener.IsListening;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _ip_addr;
|
||||||
|
private uint _port;
|
||||||
|
|
||||||
|
public static WebServer MainInstance { get; internal set; }
|
||||||
|
|
||||||
|
internal static List<Assembly> _assembliesToCheck = new List<Assembly>(new []{typeof(CLre).Assembly});
|
||||||
|
|
||||||
|
public WebServer() : this(DEFAULT_IP, DEFAULT_PORT)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebServer(string ip, uint port)
|
||||||
|
{
|
||||||
|
if (!HttpListener.IsSupported)
|
||||||
|
{
|
||||||
|
API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. It will fail to start.");
|
||||||
|
}
|
||||||
|
_httpListener = new HttpListener();
|
||||||
|
_httpListener.Prefixes.Add($"http://{ip}:{port}/");
|
||||||
|
_ip_addr = ip;
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
if (Environment.GetCommandLineArgs().Contains("-web"))
|
||||||
|
{
|
||||||
|
API.Utility.Logging.Log("Starting status web server");
|
||||||
|
StatusEndpoints.Init();
|
||||||
|
MainInstance = new WebServer();
|
||||||
|
MainInstance.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
API.Utility.Logging.Log("Not starting web server (use CLI argument -web to enable)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Deinit()
|
||||||
|
{
|
||||||
|
if (MainInstance != null) MainInstance.Stop();
|
||||||
|
MainInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
LoadHandlers();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpListener.Start();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
API.Utility.Logging.LogWarning(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleAllRequests().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpListener.Stop();
|
||||||
|
_httpListener.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
API.Utility.Logging.LogWarning(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator HandleAllRequests()
|
||||||
|
{
|
||||||
|
API.Utility.Logging.MetaLog($"Started HTTP web server at http://{_ip_addr}:{_port}/");
|
||||||
|
while (_httpListener.IsListening)
|
||||||
|
{
|
||||||
|
var awaiter = _httpListener.GetContextAsync();
|
||||||
|
awaiter.GetAwaiter().OnCompleted(() => DoRequest(awaiter.Result));
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
API.Utility.Logging.MetaLog("Terminated HTTP web server");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoRequest(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
string endpoint = ctx.Request.Url.LocalPath.ToLower();
|
||||||
|
#if DEBUG
|
||||||
|
API.Utility.Logging.LogWarning($"Handling HTTP request {endpoint}");
|
||||||
|
#endif
|
||||||
|
bool handled = false;
|
||||||
|
foreach (string path in _handlers.Keys)
|
||||||
|
{
|
||||||
|
if (endpoint == path)
|
||||||
|
{
|
||||||
|
handled = true;
|
||||||
|
_handlers[path](ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handled)
|
||||||
|
{
|
||||||
|
AssetEndpoints.Asset404(ctx);
|
||||||
|
}
|
||||||
|
//byte[] output = Encoding.UTF8.GetBytes(endpoint);
|
||||||
|
//ctx.Response.OutputStream.Write(output, 0, output.Length);
|
||||||
|
ctx.Response.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadHandlers()
|
||||||
|
{
|
||||||
|
_handlers = new Dictionary<string, RequestHandler>();
|
||||||
|
foreach (Assembly asm in _assembliesToCheck.ToArray())
|
||||||
|
{
|
||||||
|
foreach (Type t in asm.GetTypes())
|
||||||
|
{
|
||||||
|
foreach (MethodInfo m in t.GetMethods(AccessTools.all))
|
||||||
|
{
|
||||||
|
WebEndpointAttribute attr = m.GetCustomAttribute<WebEndpointAttribute>();
|
||||||
|
if (attr != null)
|
||||||
|
{
|
||||||
|
// TODO validate that method signature matches that of RequestHandler
|
||||||
|
string key = attr.GetPath().ToLower();
|
||||||
|
API.Utility.Logging.MetaLog($"{t.FullName}:{m.Name} is handling {key}");
|
||||||
|
_handlers.Add(key, (RequestHandler) Delegate.CreateDelegate(typeof(RequestHandler), m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue