diff --git a/MyceliumNetworkingForCW/MyceliumNetwork.cs b/MyceliumNetworkingForCW/MyceliumNetwork.cs
index 1fcf720..92328be 100644
--- a/MyceliumNetworkingForCW/MyceliumNetwork.cs
+++ b/MyceliumNetworkingForCW/MyceliumNetwork.cs
@@ -43,47 +43,74 @@ public static class MyceliumNetwork
///
/// The current lobby
///
- public static CSteamID Lobby;
+ public static CSteamID Lobby { get; private set; }
+
+ ///
+ /// Are we in a lobby?
+ ///
+ public static bool InLobby { get; private set; }
///
/// Called when a lobby is created by the local player
///
- public static Action LobbyCreated;
+ public static event Action LobbyCreated;
///
/// Called when a lobby is entered by the local player
///
- public static Action LobbyEntered;
+ public static event Action LobbyEntered;
///
/// Called when a lobby is left by the local player
///
- public static Action LobbyLeft;
+ public static event Action LobbyLeft;
///
/// Called when lobby creation has failed on the local player
///
- public static Action LobbyCreationFailed;
+ public static event Action LobbyCreationFailed;
///
/// Called a player enters the lobby
///
- public static Action PlayerEntered;
+ public static event Action PlayerEntered;
///
/// Called when a player leaves the lobby
///
- public static Action PlayerLeft;
+ public static event Action PlayerLeft;
+
+ ///
+ /// Called when a player's data is updated, or when a player is promoted to host.
+ /// If the new player data value is the same as the previous, this will not be called.
+ /// Provides the CSteamID of the player who's data was changed, and a list of the keys of player data that were changed.
+ ///
+ public static event Action> PlayerDataUpdated;
+
+ ///
+ /// Called when the lobby's data is updated, when a lobby is created, joined, or when the lobby owner changes.
+ /// If the new lobby data value is the same as the previous, this will not be called.
+ /// Provides a list of the keys of lobby data that were changed.
+ ///
+ public static event Action> LobbyDataUpdated;
+
+ static List lobbyDataKeys = new List();
+ static List playerDataKeys = new List();
+
+ static Dictionary> lastPlayerData = new Dictionary>();
+ static Dictionary lastLobbyData = new Dictionary();
static Callback _c2;
static Callback _c3;
static Callback _c4;
+ static Callback _c5;
public static void Initialize()
{
_c2 = Callback.Create(OnLobbyEnter);
_c3 = Callback.Create(OnLobbyCreated);
_c4 = Callback.Create(OnLobbyChatUpdate);
+ _c5 = Callback.Create(OnLobbyDataUpdate);
}
static void OnLobbyEnter(LobbyEnter_t param)
@@ -91,6 +118,7 @@ static void OnLobbyEnter(LobbyEnter_t param)
RugLogger.Log($"Entering lobby {param.m_ulSteamIDLobby}");
Lobby = new CSteamID(param.m_ulSteamIDLobby);
+ InLobby = true;
RefreshPlayerList();
@@ -128,6 +156,14 @@ static void OnLobbyChatUpdate(LobbyChatUpdate_t param)
PlayerEntered?.Invoke(steamID);
break;
case EChatMemberStateChange.k_EChatMemberStateChangeLeft:
+ if(steamID == SteamUser.GetSteamID())
+ {
+ lastLobbyData.Clear();
+ lastPlayerData.Clear();
+ InLobby = false;
+ LobbyLeft?.Invoke();
+ }
+ break;
case EChatMemberStateChange.k_EChatMemberStateChangeDisconnected:
case EChatMemberStateChange.k_EChatMemberStateChangeKicked:
case EChatMemberStateChange.k_EChatMemberStateChangeBanned:
@@ -213,6 +249,240 @@ public static void RPC(uint modId, string methodName, ReliableType reliable, par
}
#endregion
+ #region LobbyData
+ public static void RegisterLobbyDataKey(string key)
+ {
+ if(lobbyDataKeys.Contains(key))
+ {
+ RugLogger.LogError($"Lobby data key {key} is already defined");
+ }
+ else
+ {
+ lobbyDataKeys.Add(key);
+ }
+ }
+
+ public static void RegisterPlayerDataKey(string key)
+ {
+ if(playerDataKeys.Contains(key))
+ {
+ RugLogger.LogError($"Player data key {key} is already defined");
+ }
+ else
+ {
+ playerDataKeys.Add(key);
+ }
+ }
+
+ static void OnLobbyDataUpdate(LobbyDataUpdate_t param)
+ {
+ // OnLobbyDataUpdate is also triggered by a RequestLobbyData call, so we have to check
+ if(!InLobby)
+ return;
+
+ if(param.m_ulSteamIDLobby != Lobby.m_SteamID)
+ return;
+
+ if(param.m_ulSteamIDLobby == param.m_ulSteamIDMember) // Lobby data update
+ {
+ List changedKeys = new List();
+
+ for(int i = 0; i < lobbyDataKeys.Count; i++)
+ {
+ string key = lobbyDataKeys[i];
+ string data = SteamMatchmaking.GetLobbyData(Lobby, key);
+
+ if(lastLobbyData.ContainsKey(key))
+ {
+ if(!lastLobbyData[key].Equals(data))
+ {
+ changedKeys.Add(key);
+ }
+ }
+ else
+ {
+ changedKeys.Add(key);
+ }
+
+ lastLobbyData[key] = data;
+ }
+
+ // sometimes nothing changes
+ if(changedKeys.Count > 0)
+ {
+ LobbyDataUpdated?.Invoke(changedKeys);
+ }
+ }
+ else // Player data update
+ {
+ var player = new CSteamID(param.m_ulSteamIDMember);
+
+ if(!lastPlayerData.ContainsKey(player))
+ {
+ lastPlayerData[player] = new Dictionary();
+ }
+
+ List changedKeys = new List();
+
+ for(int i = 0; i < playerDataKeys.Count; i++)
+ {
+ string key = playerDataKeys[i];
+ string data = SteamMatchmaking.GetLobbyMemberData(Lobby, player, key);
+
+ if(lastPlayerData[player].ContainsKey(key))
+ {
+ if(!lastPlayerData[player][key].Equals(data))
+ {
+ changedKeys.Add(key);
+ }
+ }
+ else
+ {
+ changedKeys.Add(key);
+ }
+
+ lastPlayerData[player][key] = data;
+ }
+
+ // sometimes nothing changes
+ if(changedKeys.Count > 0)
+ {
+ PlayerDataUpdated?.Invoke(player, changedKeys);
+ }
+ }
+ }
+
+ ///
+ /// Assign a value to a lobby data key. Syncs for all players. Invokes LobbyDataUpdated. Can only be called by the lobby host.
+ ///
+ /// The key to set
+ /// The value to assign the key
+ public static void SetLobbyData(string key, object value)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot set lobby data when not in lobby.");
+ return;
+ }
+
+ if(!SteamMatchmaking.SetLobbyData(Lobby, key, value.ToString()))
+ {
+ RugLogger.LogError("Error setting lobby data.");
+ }
+ }
+
+ ///
+ /// Check if a lobby data key is defined. Can be called by any player.
+ ///
+ ///
+ /// True if the key is defined
+ public static bool HasLobbyData(string key)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot get lobby data when not in lobby.");
+ return false;
+ }
+
+ string value = SteamMatchmaking.GetLobbyData(Lobby, key.ToString());
+
+ return !string.IsNullOrEmpty(value);
+ }
+
+ ///
+ /// Get the value of a lobby data key for a specific lobby. Can be called by any player.
+ ///
+ /// The type of the data (ex. int, float, bool)
+ /// The key to get the value of
+ /// The value from the key
+ public static T GetLobbyData(string key)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot get lobby data when not in lobby.");
+ return default(T);
+ }
+
+ string value = SteamMatchmaking.GetLobbyData(Lobby, key.ToString());
+
+ try
+ {
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+ catch(Exception ex)
+ {
+ Debug.LogError($"Could not parse lobby data [{key}, {value}] as {typeof(T).Name}: {ex.Message}");
+ }
+
+ return default(T);
+ }
+
+ ///
+ /// Assign a value to a player data key. Syncs for all players. Invokes PlayerDataUpdated. Can be called by any player to set their own data.
+ ///
+ /// The key to set
+ /// The value to assign the key
+ public static void SetPlayerData(string key, object value)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot set player data when not in lobby.");
+ return;
+ }
+
+ SteamMatchmaking.SetLobbyMemberData(Lobby, key.ToString(), value.ToString());
+ }
+
+ ///
+ /// Check if a player data key is defined. Can be called by any player, on any player.
+ ///
+ ///
+ /// True if the key is defined
+ public static bool HasPlayerData(CSteamID player, string key)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot get player data when not in lobby.");
+ return false;
+ }
+
+ string value = SteamMatchmaking.GetLobbyMemberData(MyceliumNetwork.Lobby, player, key.ToString());
+
+ return !string.IsNullOrEmpty(value);
+ }
+
+ ///
+ /// Get the data associated with a key for a player. Can be called by any player, on any player.
+ /// Note that player data takes a few hundred miliseconds to load in before it can be accessed when a player first joins.
+ ///
+ /// The value from the key
+ public static T GetPlayerData(CSteamID player, string key)
+ {
+ if(!InLobby)
+ {
+ RugLogger.LogError("Cannot get player data when not in lobby.");
+ return default(T);
+ }
+
+ string value = SteamMatchmaking.GetLobbyMemberData(Lobby, player, key.ToString());
+
+ // If this key has been set
+ if(!string.IsNullOrEmpty(value))
+ {
+ try
+ {
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+ catch(Exception ex)
+ {
+ Debug.LogError($"Could not parse [{key}, {value}] as {typeof(T).Name}: {ex.Message}");
+ }
+ }
+
+ return default(T);
+ }
+ #endregion
+
///
/// Send a byte array over the network to a specific player
///
diff --git a/MyceliumNetworkingForCW/MyceliumNetworkingForCW.csproj b/MyceliumNetworkingForCW/MyceliumNetworkingForCW.csproj
index 78589c2..5a2b7d8 100644
--- a/MyceliumNetworkingForCW/MyceliumNetworkingForCW.csproj
+++ b/MyceliumNetworkingForCW/MyceliumNetworkingForCW.csproj
@@ -38,11 +38,6 @@
latest
-
-
- enable
-
-
diff --git a/MyceliumNetworkingForCW/ts-assets/CHANGELOG.md b/MyceliumNetworkingForCW/ts-assets/CHANGELOG.md
index dee32c4..359ff23 100644
--- a/MyceliumNetworkingForCW/ts-assets/CHANGELOG.md
+++ b/MyceliumNetworkingForCW/ts-assets/CHANGELOG.md
@@ -3,6 +3,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [Unreleased]
+- The LobbyLeft callback should now properly fire // NOT TESTED
+- Added LobbyData and PlayerData functionality, which allows you to define synced variables associated with the lobby (perfect for config syncing) or individual players
+- General code cleanup
+
## [1.0.6]
- Automated Thunderstore release