add split-screen info to multiplayer peer
This commit is contained in:
parent
812251e7ae
commit
56ca0f5e81
|
@ -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:
|
||||
|
|
|
@ -24,9 +24,15 @@ namespace StardewModdingAPI.Framework.Networking
|
|||
/// <inheritdoc />
|
||||
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; }
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
@ -46,6 +47,13 @@ namespace StardewModdingAPI.Framework
|
|||
private readonly Action OnGameExiting;
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>The singleton instance.</summary>
|
||||
public static SGameRunner Instance => (SGameRunner)GameRunner.instance;
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue