Network relay component for a cooperative soundboard. Ensures a host-side singleton GameObject is spawned, lets clients call SharePadPress which sends an Rpc to the host (RequestPad), the host resolves caller info and rebroadcasts via ReceivePad to all clients where SoundBoardCoop.OnNetworkPad is invoked.
using System;
namespace SSoundboard;
/// <summary>
/// Network relay for co-op pad presses. Host owns one scene object; clients
/// call Rpc.Host then host rebroadcasts so every machine hears the chaos.
/// </summary>
public sealed class SoundBoardNet : Component
{
public static SoundBoardNet Instance { get; private set; }
protected override void OnEnabled()
{
Instance = this;
}
protected override void OnDisabled()
{
if ( Instance == this )
Instance = null;
}
protected override void OnDestroy()
{
if ( Instance == this )
Instance = null;
}
/// <summary>Host creates a networked singleton when a multiplayer session is live.</summary>
public static void EnsureHostRelay()
{
try
{
if ( !Networking.IsActive )
return;
if ( Instance.IsValid() )
return;
if ( !Networking.IsHost )
return;
var go = new GameObject( true, "SoundBoardNet" );
go.Components.Create<SoundBoardNet>();
go.NetworkSpawn();
Log.Info( "[S&Soundboard] CO-OP network relay spawned" );
}
catch ( Exception e )
{
Log.Warning( $"[S&Soundboard] CO-OP relay spawn failed: {e.Message}" );
}
}
/// <summary>Local player pressed a pad while CO-OP is on — share with the lobby.</summary>
public static void SharePadPress( SoundPad pad )
{
if ( pad is null || string.IsNullOrWhiteSpace( pad.SoundId ) )
return;
if ( !SoundBoardCoop.Enabled )
return;
try
{
if ( !Networking.IsActive )
return;
EnsureHostRelay();
var net = Instance;
if ( !net.IsValid() )
return;
// Calling Rpc on a non-networked object throws ("must be networked").
try
{
if ( net.GameObject is null || !net.GameObject.Network.Active )
return;
}
catch
{
return;
}
net.RequestPad(
pad.SoundId,
pad.Label ?? "",
pad.Color ?? "#ffffff",
pad.Volume <= 0f ? 1f : pad.Volume );
}
catch ( Exception e )
{
// Never let lobby sync break local button presses.
Log.Warning( $"[S&Soundboard] CO-OP share failed: {e.GetType().Name}: {e.Message}" );
}
}
[Rpc.Host]
void RequestPad( string soundId, string label, string color, float volume )
{
try
{
if ( string.IsNullOrWhiteSpace( soundId ) )
return;
var caller = Rpc.Caller;
var name = ResolveDisplayName( caller );
var steamId = ResolveSteamId( caller );
// Host rebroadcasts to everyone (including the original presser).
ReceivePad( soundId, label ?? "", color ?? "#ffffff", volume, name, steamId );
}
catch ( Exception e )
{
Log.Warning( $"[S&Soundboard] CO-OP RequestPad failed: {e.GetType().Name}: {e.Message}" );
}
}
static string ResolveDisplayName( Connection caller )
{
if ( caller is null )
return "Player";
try
{
if ( !string.IsNullOrWhiteSpace( caller.DisplayName ) )
return caller.DisplayName;
}
catch { /* ignore */ }
try
{
if ( !string.IsNullOrWhiteSpace( caller.Name ) )
return caller.Name;
}
catch { /* ignore */ }
return "Player";
}
static long ResolveSteamId( Connection caller )
{
if ( caller is null )
return 0;
// Prefer string parse — SteamId / Friend.Id shapes vary; never throw on cast.
try
{
var id = ParseSteamId( caller.FriendInfo.Id );
if ( id != 0 )
return id;
}
catch { /* ignore */ }
try
{
var id = ParseSteamId( caller.SteamId );
if ( id != 0 )
return id;
}
catch { /* ignore */ }
try
{
return ParseSteamId( caller.OwnerSteamId );
}
catch
{
return 0;
}
}
static long ParseSteamId( 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 ) )
return 0;
// SteamId.ToString() is typically the numeric account id.
if ( long.TryParse( s, out var parsed ) )
return parsed;
}
catch { /* ignore */ }
return 0;
}
[Rpc.Broadcast( NetFlags.HostOnly | NetFlags.Reliable )]
void ReceivePad( string soundId, string label, string color, float volume, string playerName, long steamId )
{
SoundBoardCoop.OnNetworkPad( soundId, label, color, volume, playerName, steamId );
}
}