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/Framework/Networking/SGalaxyNetServer.cs | 63 ++++++++++++++++++++++ src/SMAPI/Framework/Networking/SLidgrenServer.cs | 59 ++++---------------- src/SMAPI/Framework/SCore.cs | 4 +- src/SMAPI/Framework/SMultiplayer.cs | 19 +++---- 4 files changed, 82 insertions(+), 63 deletions(-) create mode 100644 src/SMAPI/Framework/Networking/SGalaxyNetServer.cs (limited to 'src/SMAPI/Framework') diff --git a/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs new file mode 100644 index 00000000..2fc92737 --- /dev/null +++ b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs @@ -0,0 +1,63 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Galaxy.Api; +using StardewValley.Network; +using StardewValley.SDKs; + +namespace StardewModdingAPI.Framework.Networking +{ + /// A multiplayer server used to connect to an incoming player. This is an implementation of that adds support for SMAPI's metadata context exchange. + internal class SGalaxyNetServer : GalaxyNetServer + { + /********* + ** Properties + *********/ + /// A callback to raise when receiving a message. This receives the incoming message, a method to send a message, and a callback to run the default logic. + private readonly Action, Action> OnProcessingMessage; + + /// SMAPI's implementation of the game's core multiplayer logic. + private readonly SMultiplayer Multiplayer; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The underlying game server. + /// SMAPI's implementation of the game's core multiplayer logic. + /// A callback to raise when receiving a message. This receives the incoming message, a method to send a message, and a callback to run the default logic. + public SGalaxyNetServer(IGameServer gameServer, SMultiplayer multiplayer, Action, Action> onProcessingMessage) + : base(gameServer) + { + this.Multiplayer = multiplayer; + this.OnProcessingMessage = onProcessingMessage; + } + + /// Read and process a message from the client. + /// The Galaxy peer ID. + /// The data to process. + [SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "The callback is invoked synchronously.")] + protected override void onReceiveMessage(GalaxyID peer, Stream messageStream) + { + using (IncomingMessage message = new IncomingMessage()) + using (BinaryReader reader = new BinaryReader(messageStream)) + { + message.Read(reader); + this.OnProcessingMessage(message, outgoing => this.sendMessage(peer, outgoing), () => + { + if (this.peers.ContainsLeft(message.FarmerID) && (long)this.peers[message.FarmerID] == (long)peer.ToUint64()) + { + this.gameServer.processIncomingMessage(message); + } + else if (message.MessageType == StardewValley.Multiplayer.playerIntroduction) + { + NetFarmerRoot farmer = this.Multiplayer.readFarmer(message.Reader); + GalaxyID capturedPeer = new GalaxyID(peer.ToUint64()); + this.gameServer.checkFarmhandRequest(Convert.ToString(peer.ToUint64()), farmer, msg => this.sendMessage(capturedPeer, msg), () => this.peers[farmer.Value.UniqueMultiplayerID] = capturedPeer.ToUint64()); + } + }); + } + } + } +} diff --git a/src/SMAPI/Framework/Networking/SLidgrenServer.cs b/src/SMAPI/Framework/Networking/SLidgrenServer.cs index 36f96bc3..37e546a1 100644 --- a/src/SMAPI/Framework/Networking/SLidgrenServer.cs +++ b/src/SMAPI/Framework/Networking/SLidgrenServer.cs @@ -1,11 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Reflection; using Lidgren.Network; -using StardewModdingAPI.Framework.Reflection; -using StardewModdingAPI.Patches; -using StardewValley; using StardewValley.Network; namespace StardewModdingAPI.Framework.Networking @@ -16,46 +12,36 @@ namespace StardewModdingAPI.Framework.Networking /********* ** Properties *********/ - - /// The constructor for the internal NetBufferReadStream type. - private readonly ConstructorInfo NetBufferReadStreamConstructor = SLidgrenServer.GetNetBufferReadStreamConstructor(); - - /// A method which reads farmer data from the given binary reader. - private readonly Func ReadFarmer; + /// SMAPI's implementation of the game's core multiplayer logic. + private readonly SMultiplayer Multiplayer; /// A callback to raise when receiving a message. This receives the incoming message, a method to send a message, and a callback to run the default logic. private readonly Action, Action> OnProcessingMessage; - /// The peer connections. - private readonly Bimap Peers; - /********* ** Public methods *********/ /// Construct an instance. + /// SMAPI's implementation of the game's core multiplayer logic. /// The underlying game server. - /// Simplifies access to private code. - /// A method which reads farmer data from the given binary reader. /// A callback to raise when receiving a message. This receives the incoming message, a method to send a message, and a callback to run the default logic. - public SLidgrenServer(IGameServer gameServer, Reflector reflection, Func readFarmer, Action, Action> onProcessingMessage) + public SLidgrenServer(IGameServer gameServer, SMultiplayer multiplayer, Action, Action> onProcessingMessage) : base(gameServer) { - this.ReadFarmer = readFarmer; + this.Multiplayer = multiplayer; this.OnProcessingMessage = onProcessingMessage; - this.Peers = reflection.GetField>(this, "peers").GetValue(); } /// Parse a data message from a client. /// The raw network message to parse. - /// This is an implementation of which calls . This method is invoked via . [SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "The callback is invoked synchronously.")] - public bool ParseDataMessageFromClient(NetIncomingMessage rawMessage) + protected override void parseDataMessageFromClient(NetIncomingMessage rawMessage) { // add hook to call multiplayer core NetConnection peer = rawMessage.SenderConnection; using (IncomingMessage message = new IncomingMessage()) - using (Stream readStream = (Stream)this.NetBufferReadStreamConstructor.Invoke(new object[] { rawMessage })) + using (Stream readStream = new NetBufferReadStream(rawMessage)) using (BinaryReader reader = new BinaryReader(readStream)) { while (rawMessage.LengthBits - rawMessage.Position >= 8) @@ -63,39 +49,16 @@ namespace StardewModdingAPI.Framework.Networking message.Read(reader); this.OnProcessingMessage(message, outgoing => this.sendMessage(rawMessage.SenderConnection, outgoing), () => { - if (this.Peers.ContainsLeft(message.FarmerID) && this.Peers[message.FarmerID] == peer) + if (this.peers.ContainsLeft(message.FarmerID) && this.peers[message.FarmerID] == peer) this.gameServer.processIncomingMessage(message); - else if (message.MessageType == Multiplayer.playerIntroduction) + else if (message.MessageType == StardewValley.Multiplayer.playerIntroduction) { - NetFarmerRoot farmer = this.ReadFarmer(message.Reader); - this.gameServer.checkFarmhandRequest("", farmer, msg => this.sendMessage(peer, msg), () => this.Peers[farmer.Value.UniqueMultiplayerID] = peer); + NetFarmerRoot farmer = this.Multiplayer.readFarmer(message.Reader); + this.gameServer.checkFarmhandRequest("", farmer, msg => this.sendMessage(peer, msg), () => this.peers[farmer.Value.UniqueMultiplayerID] = peer); } }); } } - - return false; - } - - - /********* - ** Private methods - *********/ - /// 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; } } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 6ad118ce..128659c7 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -169,9 +169,7 @@ namespace StardewModdingAPI.Framework // apply game patches new GamePatcher(this.Monitor).Apply( - new DialogueErrorPatch(this.MonitorForGame, this.Reflection), - new LidgrenServerPatch(), - new GalaxyNetServerPatch(() => this.GameInstance.Multiplayer) + new DialogueErrorPatch(this.MonitorForGame, this.Reflection) ); } diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs index 1777a261..629fce1d 100644 --- a/src/SMAPI/Framework/SMultiplayer.cs +++ b/src/SMAPI/Framework/SMultiplayer.cs @@ -48,9 +48,6 @@ namespace StardewModdingAPI.Framework /// Manages SMAPI events. private readonly EventManager EventManager; - /// The players who are currently disconnecting. - private readonly IList DisconnectingFarmers; - /// A callback to invoke when a mod message is received. private readonly Action OnModMessageReceived; @@ -83,8 +80,6 @@ namespace StardewModdingAPI.Framework this.ModRegistry = modRegistry; this.Reflection = reflection; this.OnModMessageReceived = onModMessageReceived; - - this.DisconnectingFarmers = reflection.GetField>(this, "disconnectingFarmers").GetValue(); } /// Handle sync messages from other players and perform other initial sync logic. @@ -135,14 +130,14 @@ namespace StardewModdingAPI.Framework case LidgrenServer _: { IGameServer gameServer = this.Reflection.GetField(server, "gameServer").GetValue(); - return new SLidgrenServer(gameServer, this.Reflection, this.readFarmer, this.OnServerProcessingMessage); + return new SLidgrenServer(gameServer, this, this.OnServerProcessingMessage); } - //case GalaxyNetServer _: - // { - // IGameServer gameServer = this.Reflection.GetField(server, "gameServer").GetValue(); - // return new SGalaxyNetServer(gameServer, this.Reflection, this.OnServerProcessingMessage); - // } + case GalaxyNetServer _: + { + IGameServer gameServer = this.Reflection.GetField(server, "gameServer").GetValue(); + return new SGalaxyNetServer(gameServer, this, this.OnServerProcessingMessage); + } default: return server; @@ -323,7 +318,7 @@ namespace StardewModdingAPI.Framework /// Remove players who are disconnecting. protected override void removeDisconnectedFarmers() { - foreach (long playerID in this.DisconnectingFarmers) + foreach (long playerID in this.disconnectingFarmers) { if (this.Peers.TryGetValue(playerID, out MultiplayerPeer peer)) { -- cgit