/// <summary>Process an incoming network message as a farmhand.</summary> /// <param name="message">The message to process.</param> /// <param name="sendMessage">Send an arbitrary message through the client.</param> /// <param name="resume">Resume processing the message using the game's default logic.</param> /// <returns>Returns whether the message was handled.</returns> public void OnClientProcessingMessage(IncomingMessage message, Action <OutgoingMessage> sendMessage, Action resume) { if (this.LogNetworkTraffic) { this.Monitor.Log($"CLIENT RECV {(MessageType)message.MessageType} {message.FarmerID}", LogLevel.Trace); } switch (message.MessageType) { // mod context sync (step 4) case (byte)MessageType.ModContext: { // parse message RemoteContextModel model = this.ReadContext(message.Reader); this.Monitor.Log($"Received context for {(model?.IsHost == true ? "host" : "farmhand")} {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.", LogLevel.Trace); // store peer MultiplayerPeer peer = new MultiplayerPeer(message.FarmerID, model, sendMessage, isHost: model?.IsHost ?? this.HostPeer == null); if (peer.IsHost && this.HostPeer != null) { this.Monitor.Log($"Rejected mod context from host player {peer.PlayerID}: already received host data from {(peer.PlayerID == this.HostPeer.PlayerID ? "that player" : $"player {peer.PlayerID}")}.", LogLevel.Error); return; }
/// <summary>Perform cleanup needed when a multiplayer session ends.</summary> public void CleanupOnMultiplayerExit() { this.Peers.Clear(); this.HostPeer = null; }
/// <summary>Process an incoming network message as the host player.</summary> /// <param name="message">The message to process.</param> /// <param name="sendMessage">A method which sends the given message to the client.</param> /// <param name="resume">Process the message using the game's default logic.</param> public void OnServerProcessingMessage(IncomingMessage message, Action <OutgoingMessage> sendMessage, Action resume) { if (this.LogNetworkTraffic) { this.Monitor.Log($"SERVER RECV {(MessageType)message.MessageType} {message.FarmerID}", LogLevel.Trace); } switch (message.MessageType) { // sync mod context (step 2) case (byte)MessageType.ModContext: { // parse message RemoteContextModel model = this.ReadContext(message.Reader); this.Monitor.Log($"Received context for farmhand {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.", LogLevel.Trace); // store peer MultiplayerPeer newPeer = new MultiplayerPeer(message.FarmerID, model, sendMessage, isHost: false); if (this.Peers.ContainsKey(message.FarmerID)) { this.Monitor.Log($"Received mod context from farmhand {message.FarmerID}, but the game didn't see them disconnect. This may indicate issues with the network connection.", LogLevel.Info); this.Peers.Remove(message.FarmerID); return; } this.AddPeer(newPeer, canBeHost: false, raiseEvent: false); // reply with own context this.Monitor.VerboseLog(" Replying with host context..."); newPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, Game1.player.UniqueMultiplayerID, this.GetContextSyncMessageFields())); // reply with other players' context foreach (MultiplayerPeer otherPeer in this.Peers.Values.Where(p => p.PlayerID != newPeer.PlayerID)) { this.Monitor.VerboseLog($" Replying with context for player {otherPeer.PlayerID}..."); newPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, otherPeer.PlayerID, this.GetContextSyncMessageFields(otherPeer))); } // forward to other peers if (this.Peers.Count > 1) { object[] fields = this.GetContextSyncMessageFields(newPeer); foreach (MultiplayerPeer otherPeer in this.Peers.Values.Where(p => p.PlayerID != newPeer.PlayerID)) { this.Monitor.VerboseLog($" Forwarding context to player {otherPeer.PlayerID}..."); otherPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, newPeer.PlayerID, fields)); } } // raise event this.EventManager.PeerContextReceived.Raise(new PeerContextReceivedEventArgs(newPeer)); } break; // handle player intro case (byte)MessageType.PlayerIntroduction: // store peer if new if (!this.Peers.ContainsKey(message.FarmerID)) { this.Monitor.Log($"Received connection for vanilla player {message.FarmerID}.", LogLevel.Trace); MultiplayerPeer peer = new MultiplayerPeer(message.FarmerID, null, sendMessage, isHost: false); this.AddPeer(peer, canBeHost: false); } resume(); break; // handle mod message case (byte)MessageType.ModMessage: this.ReceiveModMessage(message); break; default: resume(); break; } }