summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md1
-rw-r--r--src/SMAPI/Framework/Networking/MultiplayerPeer.cs10
-rw-r--r--src/SMAPI/Framework/SGame.cs4
-rw-r--r--src/SMAPI/Framework/SGameRunner.cs22
-rw-r--r--src/SMAPI/Framework/SMultiplayer.cs48
-rw-r--r--src/SMAPI/IMultiplayerPeer.cs7
6 files changed, 84 insertions, 8 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index c36d80ed..49ee219a 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -10,6 +10,7 @@
## Upcoming release
* For modders:
* Expanded `PerScreen<T>` API: you can now get/set the value for any screen, get all active values, or clear all values.
+ * Expanded player info received from multiplayer API/events with new `IsSplitScreen` and `ScreenID` fields.
* Added an option to disable rewriting mods for compatibility (thanks to Bpendragon!). This may prevent older mods from loading, but bypasses a Visual Studio crash when debugging.
* For the Error Handler mod:
diff --git a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
index 5eda71f6..3923700f 100644
--- a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
+++ b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
@@ -25,9 +25,15 @@ namespace StardewModdingAPI.Framework.Networking
public bool IsHost { get; }
/// <inheritdoc />
+ public bool IsSplitScreen => this.ScreenID != null;
+
+ /// <inheritdoc />
public bool HasSmapi => this.ApiVersion != null;
/// <inheritdoc />
+ public int? ScreenID { get; }
+
+ /// <inheritdoc />
public GamePlatform? Platform { get; }
/// <inheritdoc />
@@ -45,12 +51,14 @@ namespace StardewModdingAPI.Framework.Networking
*********/
/// <summary>Construct an instance.</summary>
/// <param name="playerID">The player's unique ID.</param>
+ /// <param name="screenID">The player's screen ID, if applicable.</param>
/// <param name="model">The metadata to copy.</param>
/// <param name="sendMessage">A method which sends a message to the peer.</param>
/// <param name="isHost">Whether this is a connection to the host player.</param>
- public MultiplayerPeer(long playerID, RemoteContextModel model, Action<OutgoingMessage> sendMessage, bool isHost)
+ public MultiplayerPeer(long playerID, int? screenID, RemoteContextModel model, Action<OutgoingMessage> sendMessage, bool isHost)
{
this.PlayerID = playerID;
+ this.ScreenID = screenID;
this.IsHost = isHost;
if (model != null)
{
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 42a712ee..634680a0 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -81,6 +81,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether the game is creating the save file and SMAPI has already raised <see cref="IGameLoopEvents.SaveCreating"/>.</summary>
public bool IsBetweenCreateEvents { get; set; }
+ /// <summary>The cached <see cref="Farmer.UniqueMultiplayerID"/> value for this instance's player.</summary>
+ public long? PlayerId { get; private set; }
+
/// <summary>Construct a content manager to read game content files.</summary>
/// <remarks>This must be static because the game accesses it before the <see cref="SGame"/> constructor is called.</remarks>
[NonInstancedStatic]
@@ -167,6 +170,7 @@ namespace StardewModdingAPI.Framework
try
{
this.OnUpdating(this, gameTime, () => base.Update(gameTime));
+ this.PlayerId = Game1.player?.UniqueMultiplayerID;
}
finally
{
diff --git a/src/SMAPI/Framework/SGameRunner.cs b/src/SMAPI/Framework/SGameRunner.cs
index ae06f513..45e7369c 100644
--- a/src/SMAPI/Framework/SGameRunner.cs
+++ b/src/SMAPI/Framework/SGameRunner.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Events;
@@ -49,6 +50,13 @@ namespace StardewModdingAPI.Framework
/*********
** Public methods
*********/
+ /// <summary>The singleton instance.</summary>
+ public static SGameRunner Instance => (SGameRunner)GameRunner.instance;
+
+
+ /*********
+ ** Public methods
+ *********/
/// <summary>Construct an instance.</summary>
/// <param name="monitor">Encapsulates monitoring and logging for SMAPI.</param>
/// <param name="reflection">Simplifies access to private game code.</param>
@@ -99,15 +107,24 @@ namespace StardewModdingAPI.Framework
}
/// <inheritdoc />
- public override void RemoveGameInstance(Game1 instance)
+ public override void RemoveGameInstance(Game1 gameInstance)
{
- base.RemoveGameInstance(instance);
+ base.RemoveGameInstance(gameInstance);
if (this.gameInstances.Count <= 1)
EarlyConstants.LogScreenId = null;
this.UpdateForSplitScreenChanges();
}
+ /// <summary>Get the screen ID for a given player ID, if the player is local.</summary>
+ /// <param name="playerId">The player ID to check.</param>
+ public int? GetScreenId(long playerId)
+ {
+ return this.gameInstances
+ .FirstOrDefault(p => ((SGame)p).PlayerId == playerId)
+ ?.instanceId;
+ }
+
/*********
** Protected methods
@@ -136,6 +153,7 @@ namespace StardewModdingAPI.Framework
this.OnGameUpdating(gameTime, () => base.Update(gameTime));
}
+ /// <summary>Update metadata when a split screen is added or removed.</summary>
private void UpdateForSplitScreenChanges()
{
HashSet<int> oldScreenIds = new HashSet<int>(Context.ActiveScreenIds);
diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs
index 2f89fce9..b2257286 100644
--- a/src/SMAPI/Framework/SMultiplayer.cs
+++ b/src/SMAPI/Framework/SMultiplayer.cs
@@ -196,7 +196,13 @@ namespace StardewModdingAPI.Framework
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);
+ MultiplayerPeer newPeer = new MultiplayerPeer(
+ playerID: message.FarmerID,
+ screenID: this.GetScreenId(message.FarmerID),
+ model: model,
+ sendMessage: 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);
@@ -238,7 +244,13 @@ namespace StardewModdingAPI.Framework
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);
+ MultiplayerPeer peer = new MultiplayerPeer(
+ playerID: message.FarmerID,
+ screenID: this.GetScreenId(message.FarmerID),
+ model: null,
+ sendMessage: sendMessage,
+ isHost: false
+ );
this.AddPeer(peer, canBeHost: false);
}
@@ -280,7 +292,13 @@ namespace StardewModdingAPI.Framework
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);
+ MultiplayerPeer peer = new MultiplayerPeer(
+ playerID: message.FarmerID,
+ screenID: this.GetScreenId(message.FarmerID),
+ model: model,
+ sendMessage: 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);
@@ -297,7 +315,14 @@ namespace StardewModdingAPI.Framework
if (!this.Peers.ContainsKey(message.FarmerID) && this.HostPeer == null)
{
this.Monitor.Log($"Received connection for vanilla host {message.FarmerID}.", LogLevel.Trace);
- this.AddPeer(new MultiplayerPeer(message.FarmerID, null, sendMessage, isHost: true), canBeHost: false);
+ var peer = new MultiplayerPeer(
+ playerID: message.FarmerID,
+ screenID: this.GetScreenId(message.FarmerID),
+ model: null,
+ sendMessage: sendMessage,
+ isHost: true
+ );
+ this.AddPeer(peer, canBeHost: false);
}
resume();
break;
@@ -309,7 +334,13 @@ namespace StardewModdingAPI.Framework
// store peer
if (!this.Peers.TryGetValue(message.FarmerID, out MultiplayerPeer peer))
{
- peer = new MultiplayerPeer(message.FarmerID, null, sendMessage, isHost: this.HostPeer == null);
+ peer = new MultiplayerPeer(
+ playerID: message.FarmerID,
+ screenID: this.GetScreenId(message.FarmerID),
+ model: null,
+ sendMessage: sendMessage,
+ isHost: this.HostPeer == null
+ );
this.Monitor.Log($"Received connection for vanilla {(peer.IsHost ? "host" : "farmhand")} {message.FarmerID}.", LogLevel.Trace);
this.AddPeer(peer, canBeHost: true);
}
@@ -505,6 +536,13 @@ namespace StardewModdingAPI.Framework
}
}
+ /// <summary>Get the screen ID for a given player ID, if the player is local.</summary>
+ /// <param name="playerId">The player ID to check.</param>
+ private int? GetScreenId(long playerId)
+ {
+ return SGameRunner.Instance.GetScreenId(playerId);
+ }
+
/// <summary>Get all connected player IDs, including the current player.</summary>
private IEnumerable<long> GetKnownPlayerIDs()
{
diff --git a/src/SMAPI/IMultiplayerPeer.cs b/src/SMAPI/IMultiplayerPeer.cs
index 0d4d3261..47084174 100644
--- a/src/SMAPI/IMultiplayerPeer.cs
+++ b/src/SMAPI/IMultiplayerPeer.cs
@@ -14,9 +14,16 @@ namespace StardewModdingAPI
/// <summary>Whether this is a connection to the host player.</summary>
bool IsHost { get; }
+ /// <summary>Whether this a local player on the same computer in split-screen mote.</summary>
+ bool IsSplitScreen { get; }
+
/// <summary>Whether the player has SMAPI installed.</summary>
bool HasSmapi { get; }
+ /// <summary>The player's screen ID, if applicable.</summary>
+ /// <remarks>See <see cref="Context.ScreenId"/> for details. This is only visible to players in split-screen mode. A remote player won't see this value, even if the other players are in split-screen mode.</remarks>
+ int? ScreenID { get; }
+
/// <summary>The player's OS platform, if <see cref="HasSmapi"/> is true.</summary>
GamePlatform? Platform { get; }