Co-op helper for a soundboard. Manages whether co-op sharing is enabled, tracks recent remote/local pad highlights (player name, Steam ID, color, expiry), signals UI updates, and routes incoming network pad events to play remote sounds.
using System;
using System.Collections.Generic;
namespace SSoundboard;
/// <summary>
/// Local CO-OP preference + remote pad highlights (name + Steam avatar).
/// Starts ON — when the session is multiplayer, pad presses are shared.
/// Network transport lives in <see cref="SoundBoardNet"/>.
/// </summary>
public static class SoundBoardCoop
{
/// <summary>When true, share presses and play/show everyone else's presses.</summary>
public static bool Enabled { get; private set; } = true;
/// <summary>Bumps when highlights or enabled flag change — UI rebuild signal.</summary>
public static int Revision { get; private set; }
public static event Action StateChanged;
/// <summary>How long a remote (or co-op local) highlight stays on a pad.</summary>
const float HighlightSeconds = 2.6f;
static readonly Dictionary<string, Highlight> _highlights = new( StringComparer.OrdinalIgnoreCase );
static float _lastExpireTick = -1f;
public readonly struct Highlight
{
public string PlayerName { get; init; }
public long SteamId { get; init; }
public float Until { get; init; }
public string Color { get; init; }
}
public static void Toggle()
{
SetEnabled( !Enabled );
}
public static void SetEnabled( bool on )
{
if ( Enabled == on )
return;
Enabled = on;
if ( !Enabled )
{
// Instant off: drop name/pfp overlays and silence lobby-sourced audio.
// Stay in the multiplayer session — only stop sharing/hearing presses.
_highlights.Clear();
SoundBoardManager.KillRemoteVoices();
}
else
{
SoundBoardNet.EnsureHostRelay();
}
Bump();
}
/// <summary>Own press while co-op is on — show your pfp/name locally too.</summary>
public static void RegisterLocalPress( SoundPad pad )
{
if ( !Enabled || pad is null || string.IsNullOrWhiteSpace( pad.SoundId ) )
return;
var local = Connection.Local;
var name = local?.DisplayName;
if ( string.IsNullOrWhiteSpace( name ) )
name = "You";
long steamId = ResolveLocalSteamId( local );
UpsertHighlight( pad.SoundId, name, steamId, pad.Color );
}
/// <summary>Incoming lobby press (or echo). Skips re-play for the original presser.</summary>
public static void OnNetworkPad(
string soundId,
string label,
string color,
float volume,
string playerName,
long steamId )
{
if ( !Enabled )
return;
if ( string.IsNullOrWhiteSpace( soundId ) )
return;
if ( string.IsNullOrWhiteSpace( playerName ) )
playerName = "Player";
if ( string.IsNullOrWhiteSpace( color ) )
color = "#7cf9ff";
if ( volume <= 0f )
volume = 1f;
UpsertHighlight( soundId, playerName, steamId, color );
// Already played locally for our own press.
if ( IsLocalSteamId( steamId ) )
return;
var pad = new SoundPad
{
SoundId = soundId,
Label = string.IsNullOrWhiteSpace( label ) ? "REMOTE" : label,
Color = color,
Volume = volume
};
SoundBoardManager.PlayRemote( pad, playerName );
}
public static bool TryGetHighlight( string soundId, out Highlight highlight )
{
highlight = default;
if ( string.IsNullOrWhiteSpace( soundId ) )
return false;
if ( !_highlights.TryGetValue( soundId, out var h ) )
return false;
if ( Time.Now >= h.Until )
{
_highlights.Remove( soundId );
return false;
}
highlight = h;
return true;
}
public static void Tick()
{
// Cheap expire pass once per frame max.
var now = Time.Now;
if ( MathF.Abs( now - _lastExpireTick ) < 0.00001f )
return;
_lastExpireTick = now;
// Host relay only when a multiplayer session already exists — never CreateLobby/Query here.
if ( Enabled )
{
try
{
if ( Networking.IsActive )
SoundBoardNet.EnsureHostRelay();
}
catch { /* never break the board */ }
}
if ( _highlights.Count == 0 )
return;
var removed = false;
List<string> dead = null;
foreach ( var kv in _highlights )
{
if ( now < kv.Value.Until )
continue;
dead ??= new List<string>( 4 );
dead.Add( kv.Key );
}
if ( dead is null )
return;
for ( var i = 0; i < dead.Count; i++ )
{
_highlights.Remove( dead[i] );
removed = true;
}
if ( removed )
Bump();
}
public static string StatusHint()
{
if ( !Enabled )
return "";
try
{
if ( !Networking.IsActive )
return "start multiplayer lobby";
var n = 0;
foreach ( var _ in Connection.All )
n++;
if ( n <= 1 )
return "lobby · waiting for players";
return $"lobby · {n} players";
}
catch
{
return "lobby";
}
}
static void UpsertHighlight( string soundId, string playerName, long steamId, string color )
{
_highlights[soundId] = new Highlight
{
PlayerName = playerName,
SteamId = steamId,
Until = Time.Now + HighlightSeconds,
Color = color ?? "#7cf9ff"
};
Bump();
}
static bool IsLocalSteamId( long steamId )
{
if ( steamId == 0 )
return false;
try
{
return ResolveLocalSteamId( Connection.Local ) == steamId;
}
catch
{
return false;
}
}
static long ResolveLocalSteamId( Connection local )
{
try
{
if ( local is not null )
{
try
{
var fromFriend = ParseSteamIdObject( local.FriendInfo.Id );
if ( fromFriend != 0 )
return fromFriend;
}
catch { /* ignore */ }
try
{
var fromConn = ParseSteamIdObject( local.SteamId );
if ( fromConn != 0 )
return fromConn;
}
catch { /* ignore */ }
}
}
catch { /* fall through */ }
try
{
return ParseSteamIdObject( Game.SteamId );
}
catch
{
return 0;
}
}
static long ParseSteamIdObject( object value )
{
if ( value is null )
return 0;
try
{
switch ( value )
{
case long l:
return l;
case ulong ul:
return unchecked( (long)ul );
case int i:
return i;
case uint ui:
return ui;
}
}
catch { /* fall through */ }
try
{
var s = value.ToString();
if ( !string.IsNullOrWhiteSpace( s ) && long.TryParse( s, out var parsed ) )
return parsed;
}
catch { /* ignore */ }
return 0;
}
static void Bump()
{
Revision++;
StateChanged?.Invoke();
}
}