From 6053b8c01f4bf12bb6addb48c31e1032dc2e2b30 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 8 Oct 2018 19:50:09 -0400 Subject: prevent game crash caused by invalid dialogue --- src/SMAPI/Patches/CatchDialogueErrorPatch.cs | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/SMAPI/Patches/CatchDialogueErrorPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/CatchDialogueErrorPatch.cs b/src/SMAPI/Patches/CatchDialogueErrorPatch.cs new file mode 100644 index 00000000..6f5143fd --- /dev/null +++ b/src/SMAPI/Patches/CatchDialogueErrorPatch.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Harmony; +using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Framework.Reflection; +using StardewValley; + +namespace StardewModdingAPI.Patches +{ + /// A Harmony patch for method which intercepts invalid dialogue lines and logs an error instead of crashing. + internal class DialoguePatch : IHarmonyPatch + { + /********* + ** Private methods + *********/ + /// Writes messages to the console and log file on behalf of the game. + private static IMonitor MonitorForGame; + + /// Simplifies access to private code. + private static Reflector Reflection; + + + /********* + ** Accessors + *********/ + /// A unique name for this patch. + public string Name => $"{nameof(GameLocation)}.{nameof(GameLocation.updateSeasonalTileSheets)}"; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Writes messages to the console and log file on behalf of the game. + /// Simplifies access to private code. + public DialoguePatch(IMonitor monitorForGame, Reflector reflector) + { + DialoguePatch.MonitorForGame = monitorForGame; + DialoguePatch.Reflection = reflector; + } + + + /// Apply the Harmony patch. + /// The Harmony instance. + public void Apply(HarmonyInstance harmony) + { + ConstructorInfo constructor = AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(DialoguePatch.Prefix)); + + harmony.Patch(constructor, new HarmonyMethod(prefix), null); + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of the Dialogue constructor. + /// The instance being patched. + /// The dialogue being parsed. + /// The NPC for which the dialogue is being parsed. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix(Dialogue __instance, string masterDialogue, NPC speaker) + { + // get private members + bool nameArraysTranslated = DialoguePatch.Reflection.GetField(typeof(Dialogue), "nameArraysTranslated").GetValue(); + IReflectedMethod translateArraysOfStrings = DialoguePatch.Reflection.GetMethod(typeof(Dialogue), "TranslateArraysOfStrings"); + IReflectedMethod parseDialogueString = DialoguePatch.Reflection.GetMethod(__instance, "parseDialogueString"); + IReflectedMethod checkForSpecialDialogueAttributes = DialoguePatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); + IReflectedField> dialogues = DialoguePatch.Reflection.GetField>(__instance, "dialogues"); + + // replicate base constructor + if (dialogues.GetValue() == null) + dialogues.SetValue(new List()); + + // duplicate code with try..catch + try + { + if (!nameArraysTranslated) + translateArraysOfStrings.Invoke(); + __instance.speaker = speaker; + parseDialogueString.Invoke(masterDialogue); + checkForSpecialDialogueAttributes.Invoke(); + } + catch (Exception baseEx) when (baseEx.InnerException is TargetInvocationException invocationEx && invocationEx.InnerException is Exception ex) + { + string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; + DialoguePatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{ex}", LogLevel.Error); + + parseDialogueString.Invoke("..."); + checkForSpecialDialogueAttributes.Invoke(); + } + + return false; + } + } +} -- cgit From 688ee69ee64e03aee7a693e6c15092daf229ac5e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 28 Oct 2018 19:34:07 -0400 Subject: clarify dialogue patch name --- src/SMAPI/Patches/CatchDialogueErrorPatch.cs | 100 --------------------------- src/SMAPI/Patches/DialogueErrorPatch.cs | 100 +++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 100 deletions(-) delete mode 100644 src/SMAPI/Patches/CatchDialogueErrorPatch.cs create mode 100644 src/SMAPI/Patches/DialogueErrorPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/CatchDialogueErrorPatch.cs b/src/SMAPI/Patches/CatchDialogueErrorPatch.cs deleted file mode 100644 index 6f5143fd..00000000 --- a/src/SMAPI/Patches/CatchDialogueErrorPatch.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using Harmony; -using StardewModdingAPI.Framework.Patching; -using StardewModdingAPI.Framework.Reflection; -using StardewValley; - -namespace StardewModdingAPI.Patches -{ - /// A Harmony patch for method which intercepts invalid dialogue lines and logs an error instead of crashing. - internal class DialoguePatch : IHarmonyPatch - { - /********* - ** Private methods - *********/ - /// Writes messages to the console and log file on behalf of the game. - private static IMonitor MonitorForGame; - - /// Simplifies access to private code. - private static Reflector Reflection; - - - /********* - ** Accessors - *********/ - /// A unique name for this patch. - public string Name => $"{nameof(GameLocation)}.{nameof(GameLocation.updateSeasonalTileSheets)}"; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// Writes messages to the console and log file on behalf of the game. - /// Simplifies access to private code. - public DialoguePatch(IMonitor monitorForGame, Reflector reflector) - { - DialoguePatch.MonitorForGame = monitorForGame; - DialoguePatch.Reflection = reflector; - } - - - /// Apply the Harmony patch. - /// The Harmony instance. - public void Apply(HarmonyInstance harmony) - { - ConstructorInfo constructor = AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }); - MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(DialoguePatch.Prefix)); - - harmony.Patch(constructor, new HarmonyMethod(prefix), null); - } - - - /********* - ** Private methods - *********/ - /// The method to call instead of the Dialogue constructor. - /// The instance being patched. - /// The dialogue being parsed. - /// The NPC for which the dialogue is being parsed. - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] - private static bool Prefix(Dialogue __instance, string masterDialogue, NPC speaker) - { - // get private members - bool nameArraysTranslated = DialoguePatch.Reflection.GetField(typeof(Dialogue), "nameArraysTranslated").GetValue(); - IReflectedMethod translateArraysOfStrings = DialoguePatch.Reflection.GetMethod(typeof(Dialogue), "TranslateArraysOfStrings"); - IReflectedMethod parseDialogueString = DialoguePatch.Reflection.GetMethod(__instance, "parseDialogueString"); - IReflectedMethod checkForSpecialDialogueAttributes = DialoguePatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); - IReflectedField> dialogues = DialoguePatch.Reflection.GetField>(__instance, "dialogues"); - - // replicate base constructor - if (dialogues.GetValue() == null) - dialogues.SetValue(new List()); - - // duplicate code with try..catch - try - { - if (!nameArraysTranslated) - translateArraysOfStrings.Invoke(); - __instance.speaker = speaker; - parseDialogueString.Invoke(masterDialogue); - checkForSpecialDialogueAttributes.Invoke(); - } - catch (Exception baseEx) when (baseEx.InnerException is TargetInvocationException invocationEx && invocationEx.InnerException is Exception ex) - { - string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; - DialoguePatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{ex}", LogLevel.Error); - - parseDialogueString.Invoke("..."); - checkForSpecialDialogueAttributes.Invoke(); - } - - return false; - } - } -} diff --git a/src/SMAPI/Patches/DialogueErrorPatch.cs b/src/SMAPI/Patches/DialogueErrorPatch.cs new file mode 100644 index 00000000..d8905fd1 --- /dev/null +++ b/src/SMAPI/Patches/DialogueErrorPatch.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Harmony; +using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Framework.Reflection; +using StardewValley; + +namespace StardewModdingAPI.Patches +{ + /// A Harmony patch for the constructor which intercepts invalid dialogue lines and logs an error instead of crashing. + internal class DialogueErrorPatch : IHarmonyPatch + { + /********* + ** Private methods + *********/ + /// Writes messages to the console and log file on behalf of the game. + private static IMonitor MonitorForGame; + + /// Simplifies access to private code. + private static Reflector Reflection; + + + /********* + ** Accessors + *********/ + /// A unique name for this patch. + public string Name => $"{nameof(DialogueErrorPatch)}"; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Writes messages to the console and log file on behalf of the game. + /// Simplifies access to private code. + public DialogueErrorPatch(IMonitor monitorForGame, Reflector reflector) + { + DialogueErrorPatch.MonitorForGame = monitorForGame; + DialogueErrorPatch.Reflection = reflector; + } + + + /// Apply the Harmony patch. + /// The Harmony instance. + public void Apply(HarmonyInstance harmony) + { + ConstructorInfo constructor = AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(DialogueErrorPatch.Prefix)); + + harmony.Patch(constructor, new HarmonyMethod(prefix), null); + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of the Dialogue constructor. + /// The instance being patched. + /// The dialogue being parsed. + /// The NPC for which the dialogue is being parsed. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix(Dialogue __instance, string masterDialogue, NPC speaker) + { + // get private members + bool nameArraysTranslated = DialogueErrorPatch.Reflection.GetField(typeof(Dialogue), "nameArraysTranslated").GetValue(); + IReflectedMethod translateArraysOfStrings = DialogueErrorPatch.Reflection.GetMethod(typeof(Dialogue), "TranslateArraysOfStrings"); + IReflectedMethod parseDialogueString = DialogueErrorPatch.Reflection.GetMethod(__instance, "parseDialogueString"); + IReflectedMethod checkForSpecialDialogueAttributes = DialogueErrorPatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); + IReflectedField> dialogues = DialogueErrorPatch.Reflection.GetField>(__instance, "dialogues"); + + // replicate base constructor + if (dialogues.GetValue() == null) + dialogues.SetValue(new List()); + + // duplicate code with try..catch + try + { + if (!nameArraysTranslated) + translateArraysOfStrings.Invoke(); + __instance.speaker = speaker; + parseDialogueString.Invoke(masterDialogue); + checkForSpecialDialogueAttributes.Invoke(); + } + catch (Exception baseEx) when (baseEx.InnerException is TargetInvocationException invocationEx && invocationEx.InnerException is Exception ex) + { + string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; + DialogueErrorPatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{ex}", LogLevel.Error); + + parseDialogueString.Invoke("..."); + checkForSpecialDialogueAttributes.Invoke(); + } + + return false; + } + } +} -- cgit From e5e4ce411cc5a5e5066552978517904b21900066 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 31 Oct 2018 17:29:32 -0400 Subject: sync SMAPI context between players in multiplayer (#480) --- src/SMAPI/Patches/NetworkingPatch.cs | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/SMAPI/Patches/NetworkingPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/NetworkingPatch.cs b/src/SMAPI/Patches/NetworkingPatch.cs new file mode 100644 index 00000000..12ccf84c --- /dev/null +++ b/src/SMAPI/Patches/NetworkingPatch.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using Harmony; +using Lidgren.Network; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Networking; +using StardewModdingAPI.Framework.Patching; +using StardewValley; +using StardewValley.Network; + +namespace StardewModdingAPI.Patches +{ + /// A Harmony patch to enable the SMAPI multiplayer metadata handshake. + internal class NetworkingPatch : IHarmonyPatch + { + /********* + ** Properties + *********/ + /// The constructor for the internal NetBufferReadStream type. + private static readonly ConstructorInfo NetBufferReadStreamConstructor = NetworkingPatch.GetNetBufferReadStreamConstructor(); + + + /********* + ** Accessors + *********/ + /// A unique name for this patch. + public string Name => $"{nameof(NetworkingPatch)}"; + + + /********* + ** Public methods + *********/ + /// Apply the Harmony patch. + /// The Harmony instance. + public void Apply(HarmonyInstance harmony) + { + MethodInfo method = AccessTools.Method(typeof(LidgrenServer), "parseDataMessageFromClient"); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(NetworkingPatch.Prefix_LidgrenServer_ParseDataMessageFromClient)); + harmony.Patch(method, new HarmonyMethod(prefix), null); + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of the method. + /// The instance being patched. + /// The raw network message to parse. + /// The private peers field on the instance. + /// The private gameServer field on the instance. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix_LidgrenServer_ParseDataMessageFromClient(LidgrenServer __instance, NetIncomingMessage dataMsg, Bimap ___peers, IGameServer ___gameServer) + { + // get SMAPI overrides + SMultiplayer multiplayer = ((SGame)Game1.game1).Multiplayer; + SLidgrenServer server = (SLidgrenServer)__instance; + + // add hook to call multiplayer core + NetConnection peer = dataMsg.SenderConnection; + using (IncomingMessage message = new IncomingMessage()) + using (Stream readStream = (Stream)NetworkingPatch.NetBufferReadStreamConstructor.Invoke(new object[] { dataMsg })) + using (BinaryReader reader = new BinaryReader(readStream)) + { + while (dataMsg.LengthBits - dataMsg.Position >= 8) + { + message.Read(reader); + if (___peers.ContainsLeft(message.FarmerID) && ___peers[message.FarmerID] == peer) + ___gameServer.processIncomingMessage(message); + else if (message.MessageType == Multiplayer.playerIntroduction) + { + NetFarmerRoot farmer = multiplayer.readFarmer(message.Reader); + ___gameServer.checkFarmhandRequest("", farmer, msg => server.SendMessage(peer, msg), () => ___peers[farmer.Value.UniqueMultiplayerID] = peer); + } + else + multiplayer.ProcessMessageFromUnknownFarmhand(__instance, dataMsg, message); // added hook + } + } + + return false; + } + + /// Get the constructor for the internal NetBufferReadStream type. + private static ConstructorInfo GetNetBufferReadStreamConstructor() + { + // get type + string typeName = $"StardewValley.Network.NetBufferReadStream, {Constants.GameAssemblyName}"; + Type type = Type.GetType(typeName); + if (type == null) + throw new InvalidOperationException($"Can't find type: {typeName}"); + + // get constructor + ConstructorInfo constructor = type.GetConstructor(new[] { typeof(NetBuffer) }); + if (constructor == null) + throw new InvalidOperationException($"Can't find constructor for type: {typeName}"); + + return constructor; + } + } +} -- cgit From bfb40202793f2f7f2c9c73272f01a477b23edfa2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 4 Nov 2018 21:34:48 -0500 Subject: rewrite multiplayer sync to use generic callbacks from client/server for better extensibility (#480) --- src/SMAPI/Patches/LidgrenServerPatch.cs | 89 +++++++++++++++++++++++++++ src/SMAPI/Patches/NetworkingPatch.cs | 103 -------------------------------- 2 files changed, 89 insertions(+), 103 deletions(-) create mode 100644 src/SMAPI/Patches/LidgrenServerPatch.cs delete mode 100644 src/SMAPI/Patches/NetworkingPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/LidgrenServerPatch.cs b/src/SMAPI/Patches/LidgrenServerPatch.cs new file mode 100644 index 00000000..6f937665 --- /dev/null +++ b/src/SMAPI/Patches/LidgrenServerPatch.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using Harmony; +using Lidgren.Network; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Networking; +using StardewModdingAPI.Framework.Patching; +using StardewValley; +using StardewValley.Network; + +namespace StardewModdingAPI.Patches +{ + /// A Harmony patch to let SMAPI override methods. + internal class LidgrenServerPatch : IHarmonyPatch + { + /********* + ** Accessors + *********/ + /// A unique name for this patch. + public string Name => $"{nameof(LidgrenServerPatch)}"; + + + /********* + ** Public methods + *********/ + /// Apply the Harmony patch. + /// The Harmony instance. + public void Apply(HarmonyInstance harmony) + { + // override parseDataMessageFromClient + { + MethodInfo method = AccessTools.Method(typeof(LidgrenServer), "parseDataMessageFromClient"); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LidgrenServerPatch.Prefix_LidgrenServer_ParseDataMessageFromClient)); + harmony.Patch(method, new HarmonyMethod(prefix), null); + } + + // override sendMessage + { + MethodInfo method = typeof(LidgrenServer).GetMethod("sendMessage", BindingFlags.NonPublic | BindingFlags.Instance, null, new [] { typeof(NetConnection), typeof(OutgoingMessage) }, null); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LidgrenServerPatch.Prefix_LidgrenServer_SendMessage)); + harmony.Patch(method, new HarmonyMethod(prefix), null); + } + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of the method. + /// The instance being patched. + /// The raw network message to parse. + /// The private peers field on the instance. + /// The private gameServer field on the instance. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix_LidgrenServer_ParseDataMessageFromClient(LidgrenServer __instance, NetIncomingMessage dataMsg, Bimap ___peers, IGameServer ___gameServer) + { + if (__instance is SLidgrenServer smapiServer) + { + smapiServer.ParseDataMessageFromClient(dataMsg); + return false; + } + + return true; + } + + /// The method to call instead of the method. + /// The instance being patched. + /// The connection to which to send the message. + /// The private peers field on the instance. + /// The private gameServer field on the instance. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix_LidgrenServer_SendMessage(LidgrenServer __instance, NetConnection connection, OutgoingMessage message, Bimap ___peers, IGameServer ___gameServer) + { + if (__instance is SLidgrenServer smapiServer) + { + smapiServer.SendMessage(connection, message); + return false; + } + + return true; + } + } +} diff --git a/src/SMAPI/Patches/NetworkingPatch.cs b/src/SMAPI/Patches/NetworkingPatch.cs deleted file mode 100644 index 12ccf84c..00000000 --- a/src/SMAPI/Patches/NetworkingPatch.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Reflection; -using Harmony; -using Lidgren.Network; -using StardewModdingAPI.Framework; -using StardewModdingAPI.Framework.Networking; -using StardewModdingAPI.Framework.Patching; -using StardewValley; -using StardewValley.Network; - -namespace StardewModdingAPI.Patches -{ - /// A Harmony patch to enable the SMAPI multiplayer metadata handshake. - internal class NetworkingPatch : IHarmonyPatch - { - /********* - ** Properties - *********/ - /// The constructor for the internal NetBufferReadStream type. - private static readonly ConstructorInfo NetBufferReadStreamConstructor = NetworkingPatch.GetNetBufferReadStreamConstructor(); - - - /********* - ** Accessors - *********/ - /// A unique name for this patch. - public string Name => $"{nameof(NetworkingPatch)}"; - - - /********* - ** Public methods - *********/ - /// Apply the Harmony patch. - /// The Harmony instance. - public void Apply(HarmonyInstance harmony) - { - MethodInfo method = AccessTools.Method(typeof(LidgrenServer), "parseDataMessageFromClient"); - MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(NetworkingPatch.Prefix_LidgrenServer_ParseDataMessageFromClient)); - harmony.Patch(method, new HarmonyMethod(prefix), null); - } - - - /********* - ** Private methods - *********/ - /// The method to call instead of the method. - /// The instance being patched. - /// The raw network message to parse. - /// The private peers field on the instance. - /// The private gameServer field on the instance. - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] - private static bool Prefix_LidgrenServer_ParseDataMessageFromClient(LidgrenServer __instance, NetIncomingMessage dataMsg, Bimap ___peers, IGameServer ___gameServer) - { - // get SMAPI overrides - SMultiplayer multiplayer = ((SGame)Game1.game1).Multiplayer; - SLidgrenServer server = (SLidgrenServer)__instance; - - // add hook to call multiplayer core - NetConnection peer = dataMsg.SenderConnection; - using (IncomingMessage message = new IncomingMessage()) - using (Stream readStream = (Stream)NetworkingPatch.NetBufferReadStreamConstructor.Invoke(new object[] { dataMsg })) - using (BinaryReader reader = new BinaryReader(readStream)) - { - while (dataMsg.LengthBits - dataMsg.Position >= 8) - { - message.Read(reader); - if (___peers.ContainsLeft(message.FarmerID) && ___peers[message.FarmerID] == peer) - ___gameServer.processIncomingMessage(message); - else if (message.MessageType == Multiplayer.playerIntroduction) - { - NetFarmerRoot farmer = multiplayer.readFarmer(message.Reader); - ___gameServer.checkFarmhandRequest("", farmer, msg => server.SendMessage(peer, msg), () => ___peers[farmer.Value.UniqueMultiplayerID] = peer); - } - else - multiplayer.ProcessMessageFromUnknownFarmhand(__instance, dataMsg, message); // added hook - } - } - - return false; - } - - /// Get the constructor for the internal NetBufferReadStream type. - private static ConstructorInfo GetNetBufferReadStreamConstructor() - { - // get type - string typeName = $"StardewValley.Network.NetBufferReadStream, {Constants.GameAssemblyName}"; - Type type = Type.GetType(typeName); - if (type == null) - throw new InvalidOperationException($"Can't find type: {typeName}"); - - // get constructor - ConstructorInfo constructor = type.GetConstructor(new[] { typeof(NetBuffer) }); - if (constructor == null) - throw new InvalidOperationException($"Can't find constructor for type: {typeName}"); - - return constructor; - } - } -} -- cgit From 90ecd377c88ba5a4c08a3c7e67618435358e5685 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Nov 2018 00:11:09 -0500 Subject: rework multiplayer code to allow for upcoming Galaxy server overrides (#480) --- src/SMAPI/Patches/LidgrenServerPatch.cs | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/LidgrenServerPatch.cs b/src/SMAPI/Patches/LidgrenServerPatch.cs index 6f937665..47acd4c4 100644 --- a/src/SMAPI/Patches/LidgrenServerPatch.cs +++ b/src/SMAPI/Patches/LidgrenServerPatch.cs @@ -1,13 +1,9 @@ -using System; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Reflection; using Harmony; using Lidgren.Network; -using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Networking; using StardewModdingAPI.Framework.Patching; -using StardewValley; using StardewValley.Network; namespace StardewModdingAPI.Patches @@ -35,13 +31,6 @@ namespace StardewModdingAPI.Patches MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LidgrenServerPatch.Prefix_LidgrenServer_ParseDataMessageFromClient)); harmony.Patch(method, new HarmonyMethod(prefix), null); } - - // override sendMessage - { - MethodInfo method = typeof(LidgrenServer).GetMethod("sendMessage", BindingFlags.NonPublic | BindingFlags.Instance, null, new [] { typeof(NetConnection), typeof(OutgoingMessage) }, null); - MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LidgrenServerPatch.Prefix_LidgrenServer_SendMessage)); - harmony.Patch(method, new HarmonyMethod(prefix), null); - } } @@ -66,24 +55,5 @@ namespace StardewModdingAPI.Patches return true; } - - /// The method to call instead of the method. - /// The instance being patched. - /// The connection to which to send the message. - /// The private peers field on the instance. - /// The private gameServer field on the instance. - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] - private static bool Prefix_LidgrenServer_SendMessage(LidgrenServer __instance, NetConnection connection, OutgoingMessage message, Bimap ___peers, IGameServer ___gameServer) - { - if (__instance is SLidgrenServer smapiServer) - { - smapiServer.SendMessage(connection, message); - return false; - } - - return true; - } } } -- cgit From 15acbc8f230dd2c4ba394960cdcc12a22a831bbf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Nov 2018 01:36:43 -0500 Subject: patch GalaxyNetServer to support context sync pending game code changes to make it public (#480) --- src/SMAPI/Patches/GalaxyNetServerPatch.cs | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/SMAPI/Patches/GalaxyNetServerPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/GalaxyNetServerPatch.cs b/src/SMAPI/Patches/GalaxyNetServerPatch.cs new file mode 100644 index 00000000..e01ac329 --- /dev/null +++ b/src/SMAPI/Patches/GalaxyNetServerPatch.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using Galaxy.Api; +using Harmony; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Patching; +using StardewValley.Network; + +namespace StardewModdingAPI.Patches +{ + /// A Harmony patch to let SMAPI override methods. + internal class GalaxyNetServerPatch : IHarmonyPatch + { + /********* + ** Properties + *********/ + /// SMAPI's implementation of the game's core multiplayer logic. + private static Lazy Multiplayer; + + /// The name of the internal GalaxyNetServer class. + private static readonly string ServerTypeName = $"StardewValley.SDKs.GalaxyNetServer, {Constants.GameAssemblyName}"; + + /// The method which sends an arbitrary message. + private static MethodInfo SendMessageMethod; + + + /********* + ** Accessors + *********/ + /// A unique name for this patch. + public string Name => $"{nameof(GalaxyNetServerPatch)}"; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// SMAPI's implementation of the game's core multiplayer logic. + public GalaxyNetServerPatch(Func multiplayer) + { + // init + GalaxyNetServerPatch.Multiplayer = new Lazy(multiplayer); + + // get server.sendMessage method + Type type = Type.GetType(GalaxyNetServerPatch.ServerTypeName); + if (type == null) + throw new InvalidOperationException($"Can't find type '{GalaxyNetServerPatch.ServerTypeName}'."); + GalaxyNetServerPatch.SendMessageMethod = type.GetMethod("sendMessage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(GalaxyID), typeof(OutgoingMessage) }, null); + if (GalaxyNetServerPatch.SendMessageMethod == null) + throw new InvalidOperationException($"Can't find method 'sendMessage' on '{GalaxyNetServerPatch.ServerTypeName}'."); + } + + /// Apply the Harmony patch. + /// The Harmony instance. + public void Apply(HarmonyInstance harmony) + { + // override parseDataMessageFromClient + { + MethodInfo method = AccessTools.Method(Type.GetType($"StardewValley.SDKs.GalaxyNetServer, {Constants.GameAssemblyName}"), "onReceiveMessage"); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(GalaxyNetServerPatch.Prefix_GalaxyNetServer_OnReceiveMessage)); + harmony.Patch(method, new HarmonyMethod(prefix), null); + } + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of the method. + /// The instance being patched. + /// The Galaxy peer ID. + /// The data to process. + /// The private peers field on the instance. + /// The private gameServer field on the instance. + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] + private static bool Prefix_GalaxyNetServer_OnReceiveMessage(Server __instance, GalaxyID peer, Stream messageStream, Bimap ___peers, IGameServer ___gameServer) + { + SMultiplayer multiplayer = GalaxyNetServerPatch.Multiplayer.Value; + + using (IncomingMessage message = new IncomingMessage()) + using (BinaryReader reader = new BinaryReader(messageStream)) + { + message.Read(reader); + multiplayer.OnServerProcessingMessage(message, outgoing => GalaxyNetServerPatch.SendMessageMethod.Invoke(__instance, new object[] { peer, outgoing }), () => + { + if (___peers.ContainsLeft(message.FarmerID) && (long)___peers[message.FarmerID] == (long)peer.ToUint64()) + { + ___gameServer.processIncomingMessage(message); + } + else if (message.MessageType == StardewValley.Multiplayer.playerIntroduction) + { + NetFarmerRoot farmer = multiplayer.readFarmer(message.Reader); + GalaxyID capturedPeer = new GalaxyID(peer.ToUint64()); + ___gameServer.checkFarmhandRequest(Convert.ToString(peer.ToUint64()), farmer, msg => GalaxyNetServerPatch.SendMessageMethod.Invoke(__instance, new object[] { capturedPeer, msg }), () => ___peers[farmer.Value.UniqueMultiplayerID] = capturedPeer.ToUint64()); + } + }); + } + + return false; + } + } +} -- cgit From 0a50cdb162d7ba1c9219d1982dccbb5828b070c3 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 14 Nov 2018 18:18:32 -0500 Subject: update multiplayer code for Stardew Valley 1.3.22 (#480) --- src/SMAPI/Patches/GalaxyNetServerPatch.cs | 106 ------------------------------ src/SMAPI/Patches/LidgrenServerPatch.cs | 59 ----------------- 2 files changed, 165 deletions(-) delete mode 100644 src/SMAPI/Patches/GalaxyNetServerPatch.cs delete mode 100644 src/SMAPI/Patches/LidgrenServerPatch.cs (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/GalaxyNetServerPatch.cs b/src/SMAPI/Patches/GalaxyNetServerPatch.cs deleted file mode 100644 index e01ac329..00000000 --- a/src/SMAPI/Patches/GalaxyNetServerPatch.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Reflection; -using Galaxy.Api; -using Harmony; -using StardewModdingAPI.Framework; -using StardewModdingAPI.Framework.Patching; -using StardewValley.Network; - -namespace StardewModdingAPI.Patches -{ - /// A Harmony patch to let SMAPI override methods. - internal class GalaxyNetServerPatch : IHarmonyPatch - { - /********* - ** Properties - *********/ - /// SMAPI's implementation of the game's core multiplayer logic. - private static Lazy Multiplayer; - - /// The name of the internal GalaxyNetServer class. - private static readonly string ServerTypeName = $"StardewValley.SDKs.GalaxyNetServer, {Constants.GameAssemblyName}"; - - /// The method which sends an arbitrary message. - private static MethodInfo SendMessageMethod; - - - /********* - ** Accessors - *********/ - /// A unique name for this patch. - public string Name => $"{nameof(GalaxyNetServerPatch)}"; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// SMAPI's implementation of the game's core multiplayer logic. - public GalaxyNetServerPatch(Func multiplayer) - { - // init - GalaxyNetServerPatch.Multiplayer = new Lazy(multiplayer); - - // get server.sendMessage method - Type type = Type.GetType(GalaxyNetServerPatch.ServerTypeName); - if (type == null) - throw new InvalidOperationException($"Can't find type '{GalaxyNetServerPatch.ServerTypeName}'."); - GalaxyNetServerPatch.SendMessageMethod = type.GetMethod("sendMessage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(GalaxyID), typeof(OutgoingMessage) }, null); - if (GalaxyNetServerPatch.SendMessageMethod == null) - throw new InvalidOperationException($"Can't find method 'sendMessage' on '{GalaxyNetServerPatch.ServerTypeName}'."); - } - - /// Apply the Harmony patch. - /// The Harmony instance. - public void Apply(HarmonyInstance harmony) - { - // override parseDataMessageFromClient - { - MethodInfo method = AccessTools.Method(Type.GetType($"StardewValley.SDKs.GalaxyNetServer, {Constants.GameAssemblyName}"), "onReceiveMessage"); - MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(GalaxyNetServerPatch.Prefix_GalaxyNetServer_OnReceiveMessage)); - harmony.Patch(method, new HarmonyMethod(prefix), null); - } - } - - - /********* - ** Private methods - *********/ - /// The method to call instead of the method. - /// The instance being patched. - /// The Galaxy peer ID. - /// The data to process. - /// The private peers field on the instance. - /// The private gameServer field on the instance. - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] - private static bool Prefix_GalaxyNetServer_OnReceiveMessage(Server __instance, GalaxyID peer, Stream messageStream, Bimap ___peers, IGameServer ___gameServer) - { - SMultiplayer multiplayer = GalaxyNetServerPatch.Multiplayer.Value; - - using (IncomingMessage message = new IncomingMessage()) - using (BinaryReader reader = new BinaryReader(messageStream)) - { - message.Read(reader); - multiplayer.OnServerProcessingMessage(message, outgoing => GalaxyNetServerPatch.SendMessageMethod.Invoke(__instance, new object[] { peer, outgoing }), () => - { - if (___peers.ContainsLeft(message.FarmerID) && (long)___peers[message.FarmerID] == (long)peer.ToUint64()) - { - ___gameServer.processIncomingMessage(message); - } - else if (message.MessageType == StardewValley.Multiplayer.playerIntroduction) - { - NetFarmerRoot farmer = multiplayer.readFarmer(message.Reader); - GalaxyID capturedPeer = new GalaxyID(peer.ToUint64()); - ___gameServer.checkFarmhandRequest(Convert.ToString(peer.ToUint64()), farmer, msg => GalaxyNetServerPatch.SendMessageMethod.Invoke(__instance, new object[] { capturedPeer, msg }), () => ___peers[farmer.Value.UniqueMultiplayerID] = capturedPeer.ToUint64()); - } - }); - } - - return false; - } - } -} diff --git a/src/SMAPI/Patches/LidgrenServerPatch.cs b/src/SMAPI/Patches/LidgrenServerPatch.cs deleted file mode 100644 index 47acd4c4..00000000 --- a/src/SMAPI/Patches/LidgrenServerPatch.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using Harmony; -using Lidgren.Network; -using StardewModdingAPI.Framework.Networking; -using StardewModdingAPI.Framework.Patching; -using StardewValley.Network; - -namespace StardewModdingAPI.Patches -{ - /// A Harmony patch to let SMAPI override methods. - internal class LidgrenServerPatch : IHarmonyPatch - { - /********* - ** Accessors - *********/ - /// A unique name for this patch. - public string Name => $"{nameof(LidgrenServerPatch)}"; - - - /********* - ** Public methods - *********/ - /// Apply the Harmony patch. - /// The Harmony instance. - public void Apply(HarmonyInstance harmony) - { - // override parseDataMessageFromClient - { - MethodInfo method = AccessTools.Method(typeof(LidgrenServer), "parseDataMessageFromClient"); - MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(LidgrenServerPatch.Prefix_LidgrenServer_ParseDataMessageFromClient)); - harmony.Patch(method, new HarmonyMethod(prefix), null); - } - } - - - /********* - ** Private methods - *********/ - /// The method to call instead of the method. - /// The instance being patched. - /// The raw network message to parse. - /// The private peers field on the instance. - /// The private gameServer field on the instance. - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] - private static bool Prefix_LidgrenServer_ParseDataMessageFromClient(LidgrenServer __instance, NetIncomingMessage dataMsg, Bimap ___peers, IGameServer ___gameServer) - { - if (__instance is SLidgrenServer smapiServer) - { - smapiServer.ParseDataMessageFromClient(dataMsg); - return false; - } - - return true; - } - } -} -- cgit