@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Reflection; | |||
using HarmonyLib; | |||
using User; | |||
namespace CLre.API.App | |||
{ | |||
@@ -53,6 +54,12 @@ namespace CLre.API.App | |||
add => GameFrameworkEngine.gameFrameworkExit += value; | |||
remove => GameFrameworkEngine.gameFrameworkExit += value; | |||
} | |||
public static event EventHandler<GameJoin> GameJoin | |||
{ | |||
add => ClientGameJoinSequence_OnUserValidated_Patch.gameJoin += value; | |||
remove => ClientGameJoinSequence_OnUserValidated_Patch.gameJoin += value; | |||
} | |||
public static string Version | |||
{ | |||
@@ -114,4 +121,27 @@ namespace CLre.API.App | |||
return AccessTools.Method("FrontEnd.FrontEndGuiEngine:SetMainMenuEnabled"); | |||
} | |||
} | |||
// TODO patch OnUserValidationFailed as well | |||
[HarmonyPatch] | |||
class ClientGameJoinSequence_OnUserValidated_Patch | |||
{ | |||
internal static event EventHandler<GameJoin> gameJoin; | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(object __instance, ref SerializedAccountInfo validated) | |||
{ | |||
if (gameJoin != null) gameJoin(__instance, new GameJoin | |||
{ | |||
Success = true, | |||
Data = validated, | |||
}); | |||
} | |||
[HarmonyTargetMethod] | |||
public static MethodBase Target() | |||
{ | |||
return AccessTools.Method("MultiplayerClient.ClientGameJoinSequence:OnUserValidated"); | |||
} | |||
} | |||
} |
@@ -1,3 +1,5 @@ | |||
using User; | |||
namespace CLre.API.App | |||
{ | |||
@@ -8,4 +10,10 @@ namespace CLre.API.App | |||
public struct GameExit{} | |||
public struct MenuReady{} | |||
public struct GameJoin | |||
{ | |||
public bool Success; | |||
public SerializedAccountInfo Data; | |||
} | |||
} |
@@ -0,0 +1,83 @@ | |||
using System.Collections; | |||
using CLre.API.Engines; | |||
using GameNetworkLayer.Shared; | |||
using HarmonyLib; | |||
using Svelto.ECS; | |||
namespace CLre.API.Synergy | |||
{ | |||
public class ClientHandshakeEngine : GameObsoleteEnginePreBuild | |||
{ | |||
internal static ClientHandshakeEngine Instance = null; | |||
internal const NetworkDispatcherCode CLre_HANDSHAKE_NETCODE = (NetworkDispatcherCode) 218; | |||
private Utility.Reflection.INetMsgClientSender_SendMessage<SerializedCLreHandshake> _sendMessage; | |||
private Utility.Reflection.INetMsgClientListener_RegisterListener<SerializedCLreHandshake> _registerListener; | |||
public override void Ready() | |||
{ | |||
//Utility.Logging.MetaLog("Building send message delegate"); | |||
_sendMessage = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgClientSender_SendMessage<SerializedCLreHandshake>>( | |||
"GameNetworkLayer.Client.NetMessageClientSender:SendMessage", | |||
generics: new [] {typeof(SerializedCLreHandshake)}, | |||
instance: MainLevel_BuildClasses_Patch.netMessageSender); | |||
//Utility.Logging.MetaLog("Building register listener delegate"); | |||
_registerListener = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgClientListener_RegisterListener<SerializedCLreHandshake>>( | |||
"GameNetworkLayer.Client.NetMessageClientListener:RegisterListener", | |||
generics: new [] {typeof(SerializedCLreHandshake)}, | |||
instance: MainLevel_BuildClasses_Patch.netMessageListener); | |||
_registerListener(CLre_HANDSHAKE_NETCODE, OnHandshakeReceived); | |||
} | |||
public void OnHandshakeReceived(ref SerializedCLreHandshake p) | |||
{ | |||
// TODO validate handshake msg | |||
Utility.Logging.MetaLog($"Received CLre handshake! {p}"); | |||
} | |||
public override IEntitiesDB entitiesDB { get; set; } | |||
public override IEntityFactory entityFactory { get; set; } | |||
public IEnumerator Sender(SerializedCLreHandshake payload) | |||
{ | |||
yield return null; | |||
Utility.Logging.MetaLog("Sending Client CLre handshake"); | |||
_sendMessage(CLre_HANDSHAKE_NETCODE, ref payload); | |||
yield return null; | |||
} | |||
internal static void Init() | |||
{ | |||
Instance = new ClientHandshakeEngine(); | |||
} | |||
public ClientHandshakeEngine(): base() | |||
{ | |||
App.Client.GameJoin += (_, __) => | |||
{ | |||
SerializedCLreHandshake payload = SerializedCLreHandshake.Current(); | |||
Sender(payload).Run(); | |||
}; | |||
} | |||
} | |||
[HarmonyPatch(typeof(GameFramework.MainLevel), "BuildClasses")] | |||
class MainLevel_BuildClasses_Patch | |||
{ | |||
internal static object netMessageListener; | |||
internal static object netMessageSender; | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(object ____netMessageListener, object ____netMessageSender) | |||
{ | |||
netMessageListener = ____netMessageListener; | |||
netMessageSender = ____netMessageSender; | |||
} | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using GameNetworkLayer.Shared; | |||
using Svelto.Context; | |||
using Svelto.ECS; | |||
namespace CLre.API.Synergy | |||
{ | |||
class ClientMessagingEngine: Engines.GameObsoleteEnginePostBuild, IWaitForFrameworkDestruction, IWaitForFrameworkInitialization | |||
{ | |||
private struct MessageQueueItem | |||
{ | |||
public SerializedCLreMessage msg; | |||
public NetworkDispatcherCode code; | |||
} | |||
private Utility.Reflection.INetMsgClientSender_SendMessage<SerializedCLreMessage> _sendMessage; | |||
private Utility.Reflection.INetMsgClientListener_RegisterListener<SerializedCLreMessage> _registerListener; | |||
private Queue<MessageQueueItem> _messageQueue = new Queue<MessageQueueItem>(10); | |||
private bool _isRunning = false; | |||
public override void Ready() | |||
{ | |||
//Utility.Logging.MetaLog("Building send message delegate"); | |||
_sendMessage = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgClientSender_SendMessage<SerializedCLreMessage>>( | |||
"GameNetworkLayer.Client.NetMessageClientSender:SendMessage", | |||
generics: new [] {typeof(SerializedCLreMessage)}, | |||
instance: MainLevel_BuildClasses_Patch.netMessageSender); | |||
//Utility.Logging.MetaLog("Building register listener delegate"); | |||
_registerListener = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgClientListener_RegisterListener<SerializedCLreMessage>>( | |||
"GameNetworkLayer.Client.NetMessageClientListener:RegisterListener", | |||
generics: new [] {typeof(SerializedCLreMessage)}, | |||
instance: MainLevel_BuildClasses_Patch.netMessageListener); | |||
_registerListener(Message.CLre_MESSAGE_NETCODE, OnMessageReceived); | |||
} | |||
private void OnMessageReceived(ref SerializedCLreMessage data) | |||
{ | |||
Message.HandleMessageReceive(ref data); | |||
} | |||
internal void EnqueueMessage(ref SerializedCLreMessage msg) | |||
{ | |||
_messageQueue.Enqueue(new MessageQueueItem | |||
{ | |||
msg = msg, | |||
code = Message.CLre_MESSAGE_NETCODE, | |||
}); | |||
} | |||
public override IEntitiesDB entitiesDB { get; set; } | |||
public override IEntityFactory entityFactory { get; set; } | |||
public ClientMessagingEngine(): base() | |||
{ | |||
App.Client.GameJoin += (_, __) => { MessageSender().Run(); }; | |||
} | |||
public IEnumerator MessageSender() | |||
{ | |||
while (!_isRunning) | |||
{ | |||
yield return null; | |||
} | |||
while (_isRunning) | |||
{ | |||
while (_messageQueue.Count != 0) | |||
{ | |||
MessageQueueItem item = _messageQueue.Dequeue(); | |||
API.Utility.Logging.MetaLog($"Sending message with id {item.msg.Id}"); | |||
_sendMessage(item.code, ref item.msg); | |||
} | |||
yield return null; | |||
} | |||
} | |||
public void OnFrameworkDestroyed() | |||
{ | |||
_isRunning = false; | |||
} | |||
public void OnFrameworkInitialized() | |||
{ | |||
_isRunning = true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using GameNetworkLayer.Shared; | |||
namespace CLre.API.Synergy | |||
{ | |||
public static class Message | |||
{ | |||
internal const NetworkDispatcherCode CLre_MESSAGE_NETCODE = (NetworkDispatcherCode) 219; | |||
private static readonly Dictionary<uint, Action<ReceiveMessageArgs>> handlers = | |||
new Dictionary<uint, Action<ReceiveMessageArgs>>(); | |||
private static readonly ClientMessagingEngine msgEngine = new ClientMessagingEngine(); | |||
public static void SendCLreMessage(ref SerializedCLreMessage message) | |||
{ | |||
msgEngine.EnqueueMessage(ref message); | |||
} | |||
public static void RegisterListener(uint id, Action<ReceiveMessageArgs> handler) | |||
{ | |||
if (handlers.TryGetValue(id, out Action<ReceiveMessageArgs> existing)) | |||
{ | |||
existing += handler; | |||
handlers[id] = existing; | |||
} | |||
else | |||
{ | |||
handlers[id] = handler; | |||
} | |||
} | |||
internal static void HandleMessageReceive(ref SerializedCLreMessage msg) | |||
{ | |||
ReceiveMessageArgs payload = new ReceiveMessageArgs | |||
{ | |||
Message = msg, | |||
}; | |||
if (handlers.TryGetValue(msg.Id, out Action<ReceiveMessageArgs> h)) | |||
{ | |||
h(payload); | |||
} | |||
} | |||
} | |||
public struct ReceiveMessageArgs | |||
{ | |||
public SerializedCLreMessage Message; | |||
} | |||
} |
@@ -0,0 +1,161 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Reflection; | |||
using NetworkFramework.Shared; | |||
namespace CLre.API.Synergy | |||
{ | |||
public struct SerializedCLreHandshake: ISerializedNetData | |||
{ | |||
private byte major; | |||
private byte minor; | |||
private byte patch; | |||
private HandshakeFlag flags; | |||
private List<string> modInfo; | |||
public Version Version | |||
{ | |||
get => new Version(major, minor, patch); | |||
set | |||
{ | |||
major = (byte)value.Major; | |||
minor = (byte)value.Minor; | |||
patch = (byte)value.Build; | |||
} | |||
} | |||
public IEnumerable<string> Mods | |||
{ | |||
get => modInfo.ToArray(); | |||
set | |||
{ | |||
modInfo.Clear(); | |||
foreach (var mod in value) | |||
{ | |||
modInfo.Add(mod); | |||
} | |||
} | |||
} | |||
public byte[] Serialize() | |||
{ | |||
using (MemoryStream stream = new MemoryStream()) | |||
{ | |||
using (BinaryWriter writer = new BinaryWriter(stream)) | |||
{ | |||
// version | |||
writer.Write(major); | |||
writer.Write(minor); | |||
writer.Write(patch); | |||
writer.Write((uint)flags); | |||
writer.Write(modInfo.Count); | |||
foreach (string mod in modInfo) | |||
{ | |||
writer.Write(mod); | |||
} | |||
return stream.ToArray(); | |||
} | |||
} | |||
} | |||
public void Deserialize(byte[] data) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
using (BinaryReader reader = new BinaryReader(stream)) | |||
{ | |||
// version | |||
major = reader.ReadByte(); | |||
minor = reader.ReadByte(); | |||
patch = reader.ReadByte(); | |||
flags = (HandshakeFlag) reader.ReadUInt32(); | |||
int modCount = reader.ReadInt32(); | |||
modInfo = new List<string>(modCount); | |||
for (int i = 0; i < modCount; i++) | |||
{ | |||
modInfo.Add(reader.ReadString()); | |||
} | |||
} | |||
} | |||
} | |||
public bool HasFlag(HandshakeFlag f) | |||
{ | |||
return (flags & f) != HandshakeFlag.None; | |||
} | |||
public void SetFlag(HandshakeFlag f) | |||
{ | |||
flags |= f; | |||
} | |||
public void UnsetFlag(HandshakeFlag f) | |||
{ | |||
flags &= ~f; | |||
} | |||
public static SerializedCLreHandshake Current() | |||
{ | |||
Version v = Assembly.GetExecutingAssembly().GetName().Version; | |||
List<string> mods = new List<string>(); | |||
foreach (var plugin in IllusionInjector.PluginManager.Plugins) | |||
{ | |||
mods.Add(plugin.Name); | |||
} | |||
return new SerializedCLreHandshake | |||
{ | |||
major = (byte) v.Major, | |||
minor = (byte) v.Minor, | |||
patch = (byte) v.Build, | |||
flags = HandshakeFlag.Client, | |||
modInfo = mods, | |||
}; | |||
} | |||
public static SerializedCLreHandshake RequireCLre() | |||
{ | |||
Version v = Assembly.GetExecutingAssembly().GetName().Version; | |||
List<string> mods = new List<string>(new []{Assembly.GetExecutingAssembly().GetName().Name}); | |||
return new SerializedCLreHandshake | |||
{ | |||
major = (byte) v.Major, | |||
minor = (byte) v.Minor, | |||
patch = (byte) v.Build, | |||
flags = HandshakeFlag.Client | HandshakeFlag.RequireAll, | |||
modInfo = mods, | |||
}; | |||
} | |||
public static SerializedCLreHandshake Empty() | |||
{ | |||
return new SerializedCLreHandshake | |||
{ | |||
major = 0, | |||
minor = 0, | |||
patch = 0, | |||
modInfo = new List<string>(), | |||
}; | |||
} | |||
public override string ToString() | |||
{ | |||
return $"CLre {Version} ({modInfo.Count} mods)"; | |||
} | |||
} | |||
[Flags] | |||
public enum HandshakeFlag : uint | |||
{ | |||
None = 0, | |||
Client = 1, | |||
Server = 1 << 1, | |||
RequireAll = 1 << 2, | |||
OptionalAll = 1 << 3, | |||
Confirm = 1 << 4, | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
using System.IO; | |||
using NetworkFramework.Shared; | |||
namespace CLre.API.Synergy | |||
{ | |||
public struct SerializedCLreMessage: ISerializedNetData | |||
{ | |||
public uint Id; | |||
public byte[] Data; | |||
public byte[] Serialize() | |||
{ | |||
using (MemoryStream stream = new MemoryStream()) | |||
{ | |||
using (BinaryWriter writer = new BinaryWriter(stream)) | |||
{ | |||
writer.Write(Id); | |||
writer.Write(Data.Length); | |||
foreach (byte b in Data) | |||
{ | |||
writer.Write(b); | |||
} | |||
return stream.ToArray(); | |||
} | |||
} | |||
} | |||
public void Deserialize(byte[] data) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
using (BinaryReader reader = new BinaryReader(stream)) | |||
{ | |||
Id = reader.ReadUInt32(); | |||
Data = new byte[reader.ReadInt32()]; | |||
for (int i = 0; i < Data.Length; i++) | |||
{ | |||
Data[i] = reader.ReadByte(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -98,13 +98,23 @@ namespace CLre.API.Tools | |||
{ | |||
//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()})"); | |||
string codeName = (short) code > 217 ? "CUSTOM" : code.ToString(); | |||
StringBuilder sb = new StringBuilder($"Sending ISerializedNetData {data.GetType().FullName} (code: {codeName} {(short)code})"); | |||
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)); | |||
int start = fieldName.IndexOf('<'); | |||
int len = fieldName.LastIndexOf('>'); | |||
if (start != -1 && len > 0) | |||
{ | |||
sb.Append(fieldName.Substring(start+1, len-1)); | |||
} | |||
else | |||
{ | |||
sb.Append(fieldName); | |||
} | |||
sb.Append("\": "); | |||
sb.Append(field.GetValue()); | |||
} | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Reflection; | |||
using GameNetworkLayer.Client; | |||
using GameNetworkLayer.Shared; | |||
using HarmonyLib; | |||
using NetworkFramework.Shared; | |||
@@ -28,6 +29,10 @@ namespace CLre.API.Utility | |||
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; | |||
public delegate void INetMsgClientSender_SendMessage<T>(NetworkDispatcherCode code, ref T value) where T : struct, ISerializedNetData; | |||
public delegate void INetMsgClientListener_RegisterListener<T>(NetworkDispatcherCode code, NetCBClient<T> proc) where T: struct, ISerializedNetData; | |||
// useful reflection functions | |||
public static TFuncProto BuildDelegate<TFuncProto>(MethodInfo method) where TFuncProto : Delegate | |||
@@ -5,6 +5,7 @@ using System.Linq; | |||
using System.Reflection; | |||
using System.Text; | |||
using CLre.API.Characters; | |||
using CLre.API.Synergy; | |||
using CLre.API.Tools; | |||
using GameNetworkLayer.Shared; | |||
using HarmonyLib; | |||
@@ -51,6 +52,9 @@ namespace CLre | |||
Fixes.MiniScreenHelper.Init(); | |||
Fixes.UnderStructureCollider.Init(); | |||
// API init | |||
API.Synergy.ClientHandshakeEngine.Init(); | |||
// misc | |||
LogIPAPlugins(); | |||
Fixes.BugfixAttributeUtility.LogBugfixes(); | |||
@@ -71,10 +75,23 @@ namespace CLre | |||
NetClientSender.DebugSendMessage(netData, harmonyInstance, | |||
NetClientSender.GetLogMethod(netData)); | |||
API.Utility.Logging.MetaLog("Patched SendMessage<Shared.Inventory.HandheldEquipmentRequest>"); | |||
netData = typeof(API.Synergy.SerializedCLreHandshake); | |||
NetClientSender.DebugSendMessage(netData, harmonyInstance, | |||
NetClientSender.GetLogMethod(netData)); | |||
API.Utility.Logging.MetaLog("Patched SendMessage<SerializedCLreHandshake>"); | |||
NetClientListener.DebugReceiveMessage(NetworkDispatcherCode.EACMessageServerToClient, | |||
NetClientListener.Log); | |||
NetClientListener.DebugReceiveMessage(API.Synergy.ClientHandshakeEngine.CLre_HANDSHAKE_NETCODE, | |||
NetClientListener.Log); | |||
NetClientListener.DebugReceiveMessage(NetworkDispatcherCode.SendIsPvEToClient, | |||
NetClientListener.Log); | |||
API.Utility.Logging.MetaLog($"Highest NetworkDispatcherCode number is {(int) NetworkDispatcherCode.StructureDestroyed} damn it Photon"); | |||
// API debug and testing | |||
API.App.Client.InitComplete += (_, __) => | |||
{ | |||
@@ -3,7 +3,7 @@ | |||
<PropertyGroup> | |||
<TargetFramework>net472</TargetFramework> | |||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | |||
<Version>0.0.2</Version> | |||
<Version>0.0.3</Version> | |||
<Authors>NGnius</Authors> | |||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | |||
<PackageProjectUrl>https://git.exmods.org/NGnius/CLre</PackageProjectUrl> | |||
@@ -1,7 +1,9 @@ | |||
using System; | |||
using System.Reflection; | |||
using GameNetworkLayer.Shared; | |||
using GameServer; | |||
using HarmonyLib; | |||
using NetworkFramework.Server; | |||
using Svelto.Context; | |||
namespace CLre_server.API.MainServer | |||
@@ -45,6 +47,24 @@ namespace CLre_server.API.MainServer | |||
add => ServerReadyEngine.serverFrameworkDestroyed += value; | |||
remove => ServerReadyEngine.serverFrameworkDestroyed -= value; | |||
} | |||
public event EventHandler<StartedEventArgs> Connected | |||
{ | |||
add => PhotonNetwork_ConnectUsingSettings_Patch.postConnect += value; | |||
remove => PhotonNetwork_ConnectUsingSettings_Patch.postConnect -= value; | |||
} | |||
public event EventHandler<PlayerConnectArgs> PlayerConnect | |||
{ | |||
add => MainGameServer_SetupContainer_Patch.playerConnected += value; | |||
remove => MainGameServer_SetupContainer_Patch.playerConnected -= value; | |||
} | |||
public event EventHandler<PlayerConnectArgs> PlayerDisconnect | |||
{ | |||
add => MainGameServer_SetupContainer_Patch.playerDisconnected += value; | |||
remove => MainGameServer_SetupContainer_Patch.playerDisconnected -= value; | |||
} | |||
// properties | |||
@@ -73,6 +93,9 @@ namespace CLre_server.API.MainServer | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(GameServerSettings ____gameServerSettings) | |||
{ | |||
#if DEBUG | |||
Utility.Logging.MetaLog("Got GameServerSettings"); | |||
#endif | |||
_gameServerSettings = ____gameServerSettings; | |||
} | |||
@@ -144,4 +167,38 @@ namespace CLre_server.API.MainServer | |||
}); | |||
} | |||
} | |||
[HarmonyPatch(typeof(GameServer.GameFramework.MainGameServer), "SetupContainer")] | |||
class MainGameServer_SetupContainer_Patch | |||
{ | |||
internal static event EventHandler<PlayerConnectArgs> playerConnected; | |||
internal static event EventHandler<PlayerConnectArgs> playerDisconnected; | |||
private static GameServer.GameFramework.MainGameServer mgs = null; | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(GameServer.GameFramework.MainGameServer __instance, ref IPlayerConnectedCallbacks ____playerConnectedCallbacks) | |||
{ | |||
mgs = __instance; | |||
____playerConnectedCallbacks.OnPlayerConnected += OnConnect; | |||
____playerConnectedCallbacks.OnPlayerDisconnected += OnDisconnect; | |||
} | |||
public static void OnConnect(int playerId) | |||
{ | |||
if (playerConnected != null) playerConnected(mgs, new PlayerConnectArgs | |||
{ | |||
PlayerId = playerId, | |||
}); | |||
} | |||
public static void OnDisconnect(int playerId) | |||
{ | |||
if (playerDisconnected != null) playerDisconnected(mgs, new PlayerConnectArgs | |||
{ | |||
PlayerId = playerId, | |||
}); | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using System; | |||
using System.Collections; | |||
using CLre_server.API.Engines; | |||
using Game.DataLoader; | |||
using GameServer; | |||
@@ -22,8 +23,8 @@ namespace CLre_server.API.MainServer | |||
{ | |||
photonVersion = PhotonNetwork.gameVersion, | |||
photonRegion = PhotonNetwork.CloudRegion, | |||
gameGuid = gss.GetGameGuid(), | |||
worldName = gss.GetWorldName(), | |||
gameGuid = gss == null ? "" : gss.GetGameGuid(), | |||
worldName = gss == null ? "" : gss.GetWorldName(), | |||
}); | |||
} | |||
@@ -37,8 +38,8 @@ namespace CLre_server.API.MainServer | |||
{ | |||
photonVersion = PhotonNetwork.gameVersion, | |||
photonRegion = PhotonNetwork.CloudRegion, | |||
gameGuid = gss.GetGameGuid(), | |||
worldName = gss.GetWorldName(), | |||
gameGuid = gss == null ? "" : gss.GetGameGuid(), | |||
worldName = gss == null ? "" : gss.GetWorldName(), | |||
}); | |||
} | |||
@@ -11,4 +11,9 @@ namespace CLre_server.API.MainServer | |||
} | |||
public struct StopEventArgs{} | |||
public struct PlayerConnectArgs | |||
{ | |||
public int PlayerId; | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
using System; | |||
using System.Reflection; | |||
using GameNetworkLayer.Shared; | |||
using HarmonyLib; | |||
using Svelto.Context; | |||
using Svelto.ECS; | |||
namespace CLre_server.API.MainServer | |||
{ | |||
public class UserVerification | |||
{ | |||
public static UserVerification Instance { get; internal set; } | |||
private delegate void DisconnectPlayer_VerificationFailedProto(int playerId, string error); | |||
private delegate void DisconnectPlayerProto(int playerId, NetworkDispatcherCode code); | |||
private readonly DisconnectPlayer_VerificationFailedProto _disconnectPlayerVerificationFailed; | |||
private readonly DisconnectPlayerProto _disconnectPlayer; | |||
// This doesn't seem to do actually generate a popup, but it will stop the player from loading in | |||
/*public void DisconnectPlayer_VerificationFailed(int playerId, string error) | |||
{ | |||
_disconnectPlayerVerificationFailed(playerId, error); | |||
}*/ | |||
public void DisconnectPlayer(int playerId, NetworkDispatcherCode code = NetworkDispatcherCode.ModVerificationFail) | |||
{ | |||
_disconnectPlayer(playerId, code); | |||
} | |||
internal UserVerification(IQueryingEntitiesEngine uvs) | |||
{ | |||
Type uvsType = AccessTools.TypeByName("User.Server.UserVerificationServer"); | |||
_disconnectPlayerVerificationFailed = | |||
Utility.Reflection.MethodAsDelegate<DisconnectPlayer_VerificationFailedProto>(uvsType, | |||
"DisconnectPlayer_VerificationFailed", | |||
//parameters: new [] {typeof(int), typeof(string)}, | |||
instance: uvs); | |||
_disconnectPlayer = | |||
Utility.Reflection.MethodAsDelegate<DisconnectPlayerProto>(uvsType, "DisconnectPlayer", instance: uvs); | |||
} | |||
} | |||
// This seems to think that __instance is always simply a System.Object (caused by Harmony?), which doesn't work | |||
/*[HarmonyPatch] | |||
class UserVerificationServer_Constructor_Patch | |||
{ | |||
private static IQueryingEntitiesEngine uvs = null; | |||
[HarmonyPrefix] | |||
public static void BeforeMethodCall() | |||
{ | |||
} | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(IQueryingEntitiesEngine __instance) | |||
{ | |||
uvs = __instance; | |||
UserVerification.Instance = new UserVerification(__instance); | |||
} | |||
[HarmonyTargetMethod] | |||
public static MethodBase Target() | |||
{ | |||
return AccessTools.Constructor(AccessTools.TypeByName("User.Server.UserVerificationServer")); | |||
} | |||
}*/ | |||
[HarmonyPatch] | |||
class MainGameServer_CreatePlayerDisconnectionSequence_Patch | |||
{ | |||
private static IQueryingEntitiesEngine uvs = null; | |||
[HarmonyPrefix] | |||
public static void BeforeMethodCall() | |||
{ | |||
} | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(IQueryingEntitiesEngine userVerificationEng) | |||
{ | |||
uvs = userVerificationEng; | |||
UserVerification.Instance = new UserVerification(userVerificationEng); | |||
} | |||
[HarmonyTargetMethod] | |||
public static MethodBase Target() | |||
{ | |||
return AccessTools.Method("GameServer.GameFramework.MainGameServer:CreatePlayerDisconnectionSequence"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using GameNetworkLayer.Shared; | |||
namespace CLre_server.API.Synergy | |||
{ | |||
public static class Message | |||
{ | |||
internal const NetworkDispatcherCode CLre_MESSAGE_NETCODE = (NetworkDispatcherCode) 219; | |||
private static readonly Dictionary<uint, Action<ReceiveMessageArgs>> handlers = | |||
new Dictionary<uint, Action<ReceiveMessageArgs>>(); | |||
private static readonly ServerMessagingEngine msgEngine = new ServerMessagingEngine(); | |||
private static readonly List<int> clrePlayers = new List<int>(); | |||
public static void SendToAllCLreClients(ref SerializedCLreMessage message) | |||
{ | |||
foreach (var playerId in clrePlayers) | |||
{ | |||
SendCLreMessage(playerId, ref message); | |||
} | |||
} | |||
public static void SendCLreMessage(int playerId, ref SerializedCLreMessage message) | |||
{ | |||
msgEngine.EnqueueMessage(playerId, ref message); | |||
} | |||
public static void RegisterListener(uint id, Action<ReceiveMessageArgs> handler) | |||
{ | |||
if (handlers.TryGetValue(id, out Action<ReceiveMessageArgs> existing)) | |||
{ | |||
existing += handler; | |||
handlers[id] = existing; | |||
} | |||
else | |||
{ | |||
handlers[id] = handler; | |||
} | |||
} | |||
internal static void HandleMessageReceive(int playerId, ref SerializedCLreMessage msg) | |||
{ | |||
ReceiveMessageArgs payload = new ReceiveMessageArgs | |||
{ | |||
Message = msg, | |||
PlayerId = playerId, | |||
}; | |||
if (handlers.TryGetValue(msg.Id, out Action<ReceiveMessageArgs> h)) | |||
{ | |||
h(payload); | |||
} | |||
} | |||
internal static void RegisterCLreClient(int playerId) | |||
{ | |||
clrePlayers.Add(playerId); | |||
} | |||
} | |||
public struct ReceiveMessageArgs | |||
{ | |||
public SerializedCLreMessage Message; | |||
public int PlayerId; | |||
} | |||
} |
@@ -0,0 +1,161 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Reflection; | |||
using NetworkFramework.Shared; | |||
namespace CLre_server.API.Synergy | |||
{ | |||
struct SerializedCLreHandshake: ISerializedNetData | |||
{ | |||
private byte major; | |||
private byte minor; | |||
private byte patch; | |||
private HandshakeFlag flags; | |||
private List<string> modInfo; | |||
public Version Version | |||
{ | |||
get => new Version(major, minor, patch); | |||
set | |||
{ | |||
major = (byte)value.Major; | |||
minor = (byte)value.Minor; | |||
patch = (byte)value.Build; | |||
} | |||
} | |||
public IEnumerable<string> Mods | |||
{ | |||
get => modInfo.ToArray(); | |||
set | |||
{ | |||
modInfo.Clear(); | |||
foreach (var mod in value) | |||
{ | |||
modInfo.Add(mod); | |||
} | |||
} | |||
} | |||
public byte[] Serialize() | |||
{ | |||
using (MemoryStream stream = new MemoryStream()) | |||
{ | |||
using (BinaryWriter writer = new BinaryWriter(stream)) | |||
{ | |||
// version | |||
writer.Write(major); | |||
writer.Write(minor); | |||
writer.Write(patch); | |||
writer.Write((uint)flags); | |||
writer.Write(modInfo.Count); | |||
foreach (string mod in modInfo) | |||
{ | |||
writer.Write(mod); | |||
} | |||
return stream.ToArray(); | |||
} | |||
} | |||
} | |||
public void Deserialize(byte[] data) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
using (BinaryReader reader = new BinaryReader(stream)) | |||
{ | |||
// version | |||
major = reader.ReadByte(); | |||
minor = reader.ReadByte(); | |||
patch = reader.ReadByte(); | |||
flags = (HandshakeFlag) reader.ReadUInt32(); | |||
int modCount = reader.ReadInt32(); | |||
modInfo = new List<string>(modCount); | |||
for (int i = 0; i < modCount; i++) | |||
{ | |||
modInfo.Add(reader.ReadString()); | |||
} | |||
} | |||
} | |||
} | |||
public bool HasFlag(HandshakeFlag f) | |||
{ | |||
return (flags & f) != HandshakeFlag.None; | |||
} | |||
public void SetFlag(HandshakeFlag f) | |||
{ | |||
flags |= f; | |||
} | |||
public void UnsetFlag(HandshakeFlag f) | |||
{ | |||
flags &= ~f; | |||
} | |||
public static SerializedCLreHandshake Current() | |||
{ | |||
Version v = Assembly.GetExecutingAssembly().GetName().Version; | |||
List<string> mods = new List<string>(); | |||
foreach (var plugin in IllusionInjector.PluginManager.Plugins) | |||
{ | |||
mods.Add(plugin.Name); | |||
} | |||
return new SerializedCLreHandshake | |||
{ | |||
major = (byte) v.Major, | |||
minor = (byte) v.Minor, | |||
patch = (byte) v.Build, | |||
flags = HandshakeFlag.Server, | |||
modInfo = mods, | |||
}; | |||
} | |||
public static SerializedCLreHandshake RequireCLre() | |||
{ | |||
Version v = Assembly.GetExecutingAssembly().GetName().Version; | |||
List<string> mods = new List<string>(new []{Assembly.GetExecutingAssembly().GetName().Name}); | |||
return new SerializedCLreHandshake | |||
{ | |||
major = (byte) v.Major, | |||
minor = (byte) v.Minor, | |||
patch = (byte) v.Build, | |||
flags = HandshakeFlag.Client | HandshakeFlag.RequireAll, | |||
modInfo = mods, | |||
}; | |||
} | |||
public static SerializedCLreHandshake Empty() | |||
{ | |||
return new SerializedCLreHandshake | |||
{ | |||
major = 0, | |||
minor = 0, | |||
patch = 0, | |||
modInfo = new List<string>(), | |||
}; | |||
} | |||
public override string ToString() | |||
{ | |||
return $"CLre {Version} ({modInfo.Count} mods)"; | |||
} | |||
} | |||
[Flags] | |||
enum HandshakeFlag : uint | |||
{ | |||
None = 0, | |||
Client = 1, | |||
Server = 1 << 1, | |||
RequireAll = 1 << 2, | |||
OptionalAll = 1 << 3, | |||
Confirm = 1 << 4, | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
using System.IO; | |||
using NetworkFramework.Shared; | |||
namespace CLre_server.API.Synergy | |||
{ | |||
public struct SerializedCLreMessage: ISerializedNetData | |||
{ | |||
public uint Id; | |||
public byte[] Data; | |||
public byte[] Serialize() | |||
{ | |||
using (MemoryStream stream = new MemoryStream()) | |||
{ | |||
using (BinaryWriter writer = new BinaryWriter(stream)) | |||
{ | |||
writer.Write(Id); | |||
writer.Write(Data.Length); | |||
foreach (byte b in Data) | |||
{ | |||
writer.Write(b); | |||
} | |||
return stream.ToArray(); | |||
} | |||
} | |||
} | |||
public void Deserialize(byte[] data) | |||
{ | |||
using (MemoryStream stream = new MemoryStream(data)) | |||
{ | |||
using (BinaryReader reader = new BinaryReader(stream)) | |||
{ | |||
Id = reader.ReadUInt32(); | |||
Data = new byte[reader.ReadInt32()]; | |||
for (int i = 0; i < Data.Length; i++) | |||
{ | |||
Data[i] = reader.ReadByte(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
using System.Collections; | |||
using GameNetworkLayer.Shared; | |||
using HarmonyLib; | |||
using Svelto.ECS; | |||
namespace CLre_server.API.Synergy | |||
{ | |||
class ServerHandshakeEngine : Engines.ServerEnginePreBuild | |||
{ | |||
internal static ServerHandshakeEngine Instance = null; | |||
internal const NetworkDispatcherCode CLre_HANDSHAKE_NETCODE = (NetworkDispatcherCode) 218; | |||
private Utility.Reflection.INetMsgServerSender_SendMessage<SerializedCLreHandshake> _sendMessage; | |||
private Utility.Reflection.INetMsgServerListener_RegisterListener<SerializedCLreHandshake> _registerListener; | |||
public override void Ready() | |||
{ | |||
Utility.Logging.MetaLog("Building send message delegate"); | |||
_sendMessage = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgServerSender_SendMessage<SerializedCLreHandshake>>( | |||
"GameNetworkLayer.Server.NetMessageServerSender:SendMessage", | |||
generics: new [] {typeof(SerializedCLreHandshake)}, | |||
instance: MainGameServer_SetupContainer_Patch.netMessageSender); | |||
Utility.Logging.MetaLog("Building register listener delegate"); | |||
_registerListener = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgServerListener_RegisterListener<SerializedCLreHandshake>>( | |||
"GameNetworkLayer.Server.NetMessageServerListener:RegisterListener", | |||
generics: new [] {typeof(SerializedCLreHandshake)}, | |||
instance: MainGameServer_SetupContainer_Patch.netMessageListener); | |||
_registerListener(CLre_HANDSHAKE_NETCODE, OnHandshakeReceived); | |||
} | |||
public void OnHandshakeReceived(int playerId, ref SerializedCLreHandshake p) | |||
{ | |||
// TODO validate handshake msg | |||
Utility.Logging.MetaLog($"Received CLre handshake from player {playerId}! {p}"); | |||
Message.RegisterCLreClient(playerId); | |||
SerializedCLreHandshake payload = SerializedCLreHandshake.Current(); | |||
payload.SetFlag(HandshakeFlag.Confirm); | |||
Sender(payload, playerId).Run(); | |||
} | |||
public override IEntitiesDB entitiesDB { get; set; } | |||
public override IEntityFactory entityFactory { get; set; } | |||
public IEnumerator Sender(SerializedCLreHandshake payload, int playerId) | |||
{ | |||
yield return null; | |||
Utility.Logging.MetaLog("Sending Server CLre handshake"); | |||
_sendMessage(CLre_HANDSHAKE_NETCODE, ref payload, playerId); | |||
yield return null; | |||
} | |||
internal static void Init() | |||
{ | |||
Instance = new ServerHandshakeEngine(); | |||
} | |||
} | |||
[HarmonyPatch(typeof(GameServer.GameFramework.MainGameServer), "SetupContainer")] | |||
class MainGameServer_SetupContainer_Patch | |||
{ | |||
internal static object netMessageListener; | |||
internal static object netMessageSender; | |||
[HarmonyPostfix] | |||
public static void AfterMethodCall(object ____netMessageListener, object ____netMessageSender) | |||
{ | |||
Utility.Logging.MetaLog($"Got NetMessage objects"); | |||
netMessageListener = ____netMessageListener; | |||
netMessageSender = ____netMessageSender; | |||
} | |||
} | |||
} |
@@ -0,0 +1,96 @@ | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using GameNetworkLayer.Shared; | |||
using Svelto.Context; | |||
using Svelto.ECS; | |||
namespace CLre_server.API.Synergy | |||
{ | |||
class ServerMessagingEngine: Engines.ServerEnginePreBuild, IWaitForFrameworkDestruction, IWaitForFrameworkInitialization | |||
{ | |||
private struct MessageQueueItem | |||
{ | |||
public SerializedCLreMessage msg; | |||
public int playerId; | |||
public NetworkDispatcherCode code; | |||
} | |||
private Utility.Reflection.INetMsgServerSender_SendMessage<SerializedCLreMessage> _sendMessage; | |||
private Utility.Reflection.INetMsgServerListener_RegisterListener<SerializedCLreMessage> _registerListener; | |||
private Queue<MessageQueueItem> _messageQueue = new Queue<MessageQueueItem>(10); | |||
private bool _isRunning = false; | |||
public override void Ready() | |||
{ | |||
//Utility.Logging.MetaLog("Building send message delegate"); | |||
_sendMessage = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgServerSender_SendMessage<SerializedCLreMessage>>( | |||
"GameNetworkLayer.Server.NetMessageServerSender:SendMessage", | |||
generics: new [] {typeof(SerializedCLreMessage)}, | |||
instance: MainGameServer_SetupContainer_Patch.netMessageSender); | |||
//Utility.Logging.MetaLog("Building register listener delegate"); | |||
_registerListener = | |||
Utility.Reflection.MethodAsDelegate<Utility.Reflection.INetMsgServerListener_RegisterListener<SerializedCLreMessage>>( | |||
"GameNetworkLayer.Server.NetMessageServerListener:RegisterListener", | |||
generics: new [] {typeof(SerializedCLreMessage)}, | |||
instance: MainGameServer_SetupContainer_Patch.netMessageListener); | |||
_registerListener(Message.CLre_MESSAGE_NETCODE, OnMessageReceived); | |||
} | |||
private void OnMessageReceived(int playerId, ref SerializedCLreMessage data) | |||
{ | |||
Message.HandleMessageReceive(playerId, ref data); | |||
} | |||
internal void EnqueueMessage(int playerId, ref SerializedCLreMessage msg) | |||
{ | |||
_messageQueue.Enqueue(new MessageQueueItem | |||
{ | |||
msg = msg, | |||
playerId = playerId, | |||
code = Message.CLre_MESSAGE_NETCODE, | |||
}); | |||
} | |||
public override IEntitiesDB entitiesDB { get; set; } | |||
public override IEntityFactory entityFactory { get; set; } | |||
public ServerMessagingEngine(): base() | |||
{ | |||
MainServer.Server.Instance.Connected += (_, __) => { MessageSender().Run(); }; | |||
} | |||
public IEnumerator MessageSender() | |||
{ | |||
while (!_isRunning) | |||
{ | |||
yield return null; | |||
} | |||
while (_isRunning) | |||
{ | |||
while (_messageQueue.Count != 0) | |||
{ | |||
MessageQueueItem item = _messageQueue.Dequeue(); | |||
API.Utility.Logging.MetaLog($"Sending message with id {item.msg.Id}"); | |||
_sendMessage(item.code, ref item.msg, item.playerId); | |||
} | |||
yield return null; | |||
} | |||
} | |||
public void OnFrameworkDestroyed() | |||
{ | |||
_isRunning = false; | |||
} | |||
public void OnFrameworkInitialized() | |||
{ | |||
_isRunning = true; | |||
} | |||
} | |||
} |
@@ -98,13 +98,23 @@ namespace CLre_server.API.Tools | |||
{ | |||
//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()})"); | |||
string codeName = (short) code > 217 ? "CUSTOM" : code.ToString(); | |||
StringBuilder sb = new StringBuilder($"Sending ISerializedNetData {data.GetType().FullName} (code: {codeName} {(short)code})"); | |||
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)); | |||
int start = fieldName.IndexOf('<'); | |||
int len = fieldName.LastIndexOf('>'); | |||
if (start != -1 && len > 0) | |||
{ | |||
sb.Append(fieldName.Substring(start+1, len-1)); | |||
} | |||
else | |||
{ | |||
sb.Append(fieldName); | |||
} | |||
sb.Append("\": "); | |||
sb.Append(field.GetValue()); | |||
} | |||
@@ -28,6 +28,11 @@ namespace CLre_server.API.Utility | |||
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; | |||
public delegate void INetMsgServerSender_SendMessage<T>(NetworkDispatcherCode code, ref T value, int playerId) where T : struct, ISerializedNetData; | |||
public delegate void INetMsgServerListener_RegisterListener<T>(NetworkDispatcherCode code, NetCBServer<T> proc) where T: struct, ISerializedNetData; | |||
// useful reflection functions | |||
public static TFuncProto BuildDelegate<TFuncProto>(MethodInfo method) where TFuncProto : Delegate | |||
@@ -44,6 +44,9 @@ namespace CLre_server | |||
// patches for bugs | |||
Fixes.InitLogSooner.Init(); | |||
// API init | |||
API.Synergy.ServerHandshakeEngine.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 | |||
@@ -59,10 +62,21 @@ namespace CLre_server | |||
NetServerSender.DebugSendMessage(netData, harmonyInstance, | |||
NetServerSender.GetLogMethod(netData)); | |||
API.Utility.Logging.MetaLog("Patched SendMessage<Shared.Inventory.HandheldEquipmentRequest>"); | |||
netData = typeof(API.Synergy.SerializedCLreHandshake); | |||
NetServerSender.DebugSendMessage(netData, harmonyInstance, | |||
NetServerSender.GetLogMethod(netData)); | |||
API.Utility.Logging.MetaLog("Patched SendMessage<SerializedCLreHandshake>"); | |||
NetServerListener.DebugReceiveMessage(NetworkDispatcherCode.EACMessageServerToClient, | |||
NetServerListener.Log); | |||
NetServerListener.DebugReceiveMessage(NetworkDispatcherCode.SendIsPvEToClient, | |||
NetServerListener.Log); | |||
NetServerListener.DebugReceiveMessage(API.Synergy.ServerHandshakeEngine.CLre_HANDSHAKE_NETCODE, | |||
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 | |||
@@ -3,7 +3,7 @@ | |||
<PropertyGroup> | |||
<TargetFramework>net472</TargetFramework> | |||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | |||
<Version>0.0.2</Version> | |||
<Version>0.0.3</Version> | |||
<Authors>NGnius</Authors> | |||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | |||
<PackageProjectUrl>https://git.exmods.org/NGnius/CLre</PackageProjectUrl> | |||