using System; using System.IO; using Galaxy.Api; using StardewModdingAPI.Framework.Reflection; using StardewValley; 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; /// The peer connections. private readonly Bimap Peers; /// The underlying net server. private readonly IReflectedField Server; /// The underlying method which handles incoming connections. private readonly Action BaseReceiveConnection; /// The underlying method which handles incoming disconnections. private readonly Action BaseReceiveDisconnect; /// The underlying method which handles incoming errors. private readonly Action BaseReceiveError; /********* ** Public methods *********/ /// Construct an instance. /// The underlying game server. /// Simplifies access to private code. /// 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, Reflector reflection, Action, Action> onProcessingMessage) : base(gameServer) { this.OnProcessingMessage = onProcessingMessage; this.Peers = reflection.GetField>(this, "peers").GetValue(); this.Server = reflection.GetField(this, "server"); this.BaseReceiveConnection = (Action)Delegate.CreateDelegate(typeof(Action), this, reflection.GetMethod(this, "onReceiveConnection").MethodInfo); this.BaseReceiveDisconnect = (Action)Delegate.CreateDelegate(typeof(Action), this, reflection.GetMethod(this, "onReceiveDisconnect").MethodInfo); this.BaseReceiveError = (Action)Delegate.CreateDelegate(typeof(Action), this, reflection.GetMethod(this, "onReceiveError").MethodInfo); } /// Receive and process messages from the client. public override void receiveMessages() { GalaxySocket server = this.Server.GetValue(); if (server == null) return; server.Receive(this.BaseReceiveConnection, this.OnReceiveMessage, this.BaseReceiveDisconnect, this.BaseReceiveError); server.Heartbeat(server.LobbyMembers()); foreach (GalaxyID connection in server.Connections) { if (server.GetPingWith(connection) > 30000L) server.Kick(connection); } } /// Read and process a message from the client. /// The Galaxy peer ID. /// The data to process. private void OnReceiveMessage(GalaxyID peerID, Stream data) { using (IncomingMessage message = new IncomingMessage()) using (BinaryReader reader = new BinaryReader(data)) { message.Read(reader); this.OnProcessingMessage(message, outgoing => this.sendMessage(peerID, outgoing), () => { if (this.Peers.ContainsLeft(message.FarmerID) && (long)this.Peers[message.FarmerID] == (long)peerID.ToUint64()) { this.gameServer.processIncomingMessage(message); } else if (message.MessageType == Multiplayer.playerIntroduction) { NetFarmerRoot farmer = Game1.multiplayer.readFarmer(message.Reader); GalaxyID capturedPeer = new GalaxyID(peerID.ToUint64()); this.gameServer.checkFarmhandRequest(Convert.ToString(peerID.ToUint64()), farmer, msg => this.sendMessage(capturedPeer, msg), () => this.Peers[farmer.Value.UniqueMultiplayerID] = capturedPeer.ToUint64()); } }); } } } }