diff --git a/FizzySteamworks/BidirectionalDictionary.cs b/FizzySteamworks/BidirectionalDictionary.cs new file mode 100644 index 00000000..210df66a --- /dev/null +++ b/FizzySteamworks/BidirectionalDictionary.cs @@ -0,0 +1,88 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Mirror.FizzySteam; + +public class BidirectionalDictionary : IEnumerable +{ + private Dictionary t1ToT2Dict = new Dictionary(); + private Dictionary t2ToT1Dict = new Dictionary(); + + public IEnumerable FirstTypes => t1ToT2Dict.Keys; + public IEnumerable SecondTypes => t2ToT1Dict.Keys; + + public IEnumerator GetEnumerator() => t1ToT2Dict.GetEnumerator(); + + public int Count => t1ToT2Dict.Count; + + public void Add(T1 key, T2 value) + { + if (t1ToT2Dict.ContainsKey(key)) + { + Remove(key); + } + + t1ToT2Dict[key] = value; + t2ToT1Dict[value] = key; + } + + public void Add(T2 key, T1 value) + { + if (t2ToT1Dict.ContainsKey(key)) + { + Remove(key); + } + + t2ToT1Dict[key] = value; + t1ToT2Dict[value] = key; + } + + public T2 Get(T1 key) => t1ToT2Dict[key]; + + public T1 Get(T2 key) => t2ToT1Dict[key]; + + public bool TryGetValue(T1 key, out T2 value) => t1ToT2Dict.TryGetValue(key, out value); + + public bool TryGetValue(T2 key, out T1 value) => t2ToT1Dict.TryGetValue(key, out value); + + public bool Contains(T1 key) => t1ToT2Dict.ContainsKey(key); + + public bool Contains(T2 key) => t2ToT1Dict.ContainsKey(key); + + public void Remove(T1 key) + { + if (Contains(key)) + { + T2 val = t1ToT2Dict[key]; + t1ToT2Dict.Remove(key); + t2ToT1Dict.Remove(val); + } + } + public void Remove(T2 key) + { + if (Contains(key)) + { + T1 val = t2ToT1Dict[key]; + t1ToT2Dict.Remove(val); + t2ToT1Dict.Remove(key); + } + } + + public T1 this[T2 key] + { + get => t2ToT1Dict[key]; + set + { + Add(key, value); + } + } + + public T2 this[T1 key] + { + get => t1ToT2Dict[key]; + set + { + Add(key, value); + } + } +} diff --git a/FizzySteamworks/FizzySteamworks.cs b/FizzySteamworks/FizzySteamworks.cs new file mode 100644 index 00000000..74cbe1cd --- /dev/null +++ b/FizzySteamworks/FizzySteamworks.cs @@ -0,0 +1,228 @@ +using Steamworks; +using System; +using UnityEngine; + +namespace Mirror.FizzySteam; + +public class FizzySteamworks : Transport +{ + private const string STEAM_SCHEME = "steam"; + + private static SteamClient client; + private static SteamServer server; + + [SerializeField] + public EP2PSend[] Channels = new EP2PSend[2] { EP2PSend.k_EP2PSendReliable, EP2PSend.k_EP2PSendUnreliableNoDelay }; + + [Tooltip("Timeout for connecting in seconds.")] + public int Timeout = 25; + + private void OnEnable() + { + Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for FizzySteamworks."); + Invoke(nameof(InitRelayNetworkAccess), 1f); + } + + public override void ClientEarlyUpdate() + { + if (enabled) + { + client?.ReceiveData(); + } + } + + public override void ServerEarlyUpdate() + { + if (enabled) + { + server?.ReceiveData(); + } + } + + public override void ClientLateUpdate() + { + if (enabled) + { + client?.FlushData(); + } + } + + public override void ServerLateUpdate() + { + if (enabled) + { + server?.FlushData(); + } + } + + public override bool ClientConnected() => ClientActive() && client.Connected; + public override void ClientConnect(string address) + { + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + InitRelayNetworkAccess(); + + if (ServerActive()) + { + Debug.LogError("Transport already running as server!"); + return; + } + + if (!ClientActive() || client.Error) + { + Debug.Log($"Starting client [SteamSockets], target address {address}."); + client = SteamClient.CreateClient(this, address); + } + else + { + Debug.LogError("Client already running!"); + } + } + catch (Exception ex) + { + Debug.LogError("Exception: " + ex.Message + ". Client could not be started."); + OnClientDisconnected.Invoke(); + } + } + + public override void ClientConnect(Uri uri) + { + if (uri.Scheme != STEAM_SCHEME) + { + throw new ArgumentException($"Invalid url {uri}, use {STEAM_SCHEME}://SteamID instead", nameof(uri)); + } + + ClientConnect(uri.Host); + } + + public override void ClientSend(ArraySegment segment, int channelId) + { + var data = new byte[segment.Count]; + Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count); + client.Send(data, channelId); + } + + public override void ClientDisconnect() + { + if (ClientActive()) + { + Shutdown(); + } + } + + public bool ClientActive() => client != null; + + public override bool ServerActive() => server != null; + + public override void ServerStart() + { + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + InitRelayNetworkAccess(); + + if (ClientActive()) + { + Debug.LogError("Transport already running as client!"); + return; + } + + if (!ServerActive()) + { + Debug.Log($"Starting server [SteamSockets]."); + server = SteamServer.CreateServer(this, NetworkManager.singleton.maxConnections); + } + else + { + Debug.LogError("Server already started!"); + } + } + catch (Exception ex) + { + Debug.LogException(ex); + return; + } + } + + public override Uri ServerUri() + { + var steamBuilder = new UriBuilder + { + Scheme = STEAM_SCHEME, + Host = SteamUser.GetSteamID().m_SteamID.ToString() + }; + + return steamBuilder.Uri; + } + + public override void ServerSend(int connectionId, ArraySegment segment, int channelId) + { + if (ServerActive()) + { + var data = new byte[segment.Count]; + Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count); + server.Send(connectionId, data, channelId); + } + } + public override void ServerDisconnect(int connectionId) + { + if (ServerActive()) + { + server.Disconnect(connectionId); + } + } + public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty; + public override void ServerStop() + { + if (ServerActive()) + { + Shutdown(); + } + } + + public override void Shutdown() + { + if (server != null) + { + server.Shutdown(); + server = null; + Debug.Log("Transport shut down - was server."); + } + + if (client != null) + { + client.Disconnect(); + client = null; + Debug.Log("Transport shut down - was client."); + } + } + + public override int GetMaxPacketSize(int channelId) + => Constants.k_cbMaxSteamNetworkingSocketsMessageSizeSend; + + public override bool Available() + { + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + return true; + } + catch + { + return false; + } + } + + private void InitRelayNetworkAccess() + { + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + } + catch { } + } + + private void OnDestroy() + => Shutdown(); +} diff --git a/FizzySteamworks/FizzySteamworks.csproj b/FizzySteamworks/FizzySteamworks.csproj new file mode 100644 index 00000000..7aa023e9 --- /dev/null +++ b/FizzySteamworks/FizzySteamworks.csproj @@ -0,0 +1,32 @@ + + + + net48 + Mirror.FizzySteam + + + + + F:\Steam\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\com.rlabrecque.steamworks.net.dll + + + ..\Mirror\kcp2k.dll + + + ..\Mirror\Mirror.dll + + + ..\Mirror\Mirror.Components.dll + + + ..\Mirror\Mirror.Transports.dll + + + ..\Mirror\Telepathy.dll + + + F:\Steam\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll + + + + diff --git a/FizzySteamworks/SteamClient.cs b/FizzySteamworks/SteamClient.cs new file mode 100644 index 00000000..9371636a --- /dev/null +++ b/FizzySteamworks/SteamClient.cs @@ -0,0 +1,222 @@ +using Steamworks; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace Mirror.FizzySteam +{ + public class SteamClient : SteamCommon + { + public bool Connected { get; private set; } + public bool Error { get; private set; } + + private TimeSpan ConnectionTimeout; + + private event Action OnReceivedData; + private event Action OnConnected; + private event Action OnDisconnected; + private Callback c_onConnectionChange = null; + + private CancellationTokenSource cancelToken; + private TaskCompletionSource connectedComplete; + private CSteamID hostSteamID = CSteamID.Nil; + private HSteamNetConnection HostConnection; + private List BufferedData; + + private SteamClient(FizzySteamworks transport) + { + ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.Timeout)); + BufferedData = new List(); + } + + public static SteamClient CreateClient(FizzySteamworks transport, string host) + { + var c = new SteamClient(transport); + + c.OnConnected += () => transport.OnClientConnected.Invoke(); + c.OnDisconnected += () => transport.OnClientDisconnected.Invoke(); + c.OnReceivedData += (data, ch) => transport.OnClientDataReceived.Invoke(new ArraySegment(data), ch); + + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + c.Connect(host); + } + catch (Exception ex) + { + Debug.LogException(ex); + c.OnConnectionFailed(); + } + + return c; + } + + private async void Connect(string host) + { + cancelToken = new CancellationTokenSource(); + c_onConnectionChange = Callback.Create(OnConnectionStatusChanged); + + try + { + hostSteamID = new CSteamID(ulong.Parse(host)); + connectedComplete = new TaskCompletionSource(); + OnConnected += SetConnectedComplete; + + var smi = new SteamNetworkingIdentity(); + smi.SetSteamID(hostSteamID); + + var options = new SteamNetworkingConfigValue_t[] { }; + HostConnection = SteamNetworkingSockets.ConnectP2P(ref smi, 0, options.Length, options); + + Task connectedCompleteTask = connectedComplete.Task; + var timeOutTask = Task.Delay(ConnectionTimeout, cancelToken.Token); + + if (await Task.WhenAny(connectedCompleteTask, timeOutTask) != connectedCompleteTask) + { + if (cancelToken.IsCancellationRequested) + { + Debug.LogError($"The connection attempt was cancelled."); + } + else if (timeOutTask.IsCompleted) + { + Debug.LogError($"Connection to {host} timed out."); + } + + OnConnected -= SetConnectedComplete; + OnConnectionFailed(); + } + + OnConnected -= SetConnectedComplete; + } + catch (FormatException) + { + Debug.LogError($"Connection string was not in the right format. Did you enter a SteamId?"); + Error = true; + OnConnectionFailed(); + } + catch (Exception ex) + { + Debug.LogError(ex.Message); + Error = true; + OnConnectionFailed(); + } + finally + { + if (Error) + { + Debug.LogError("Connection failed."); + OnConnectionFailed(); + } + } + } + + private void OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t param) + { + var clientSteamID = param.m_info.m_identityRemote.GetSteamID64(); + if (param.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected) + { + Connected = true; + OnConnected.Invoke(); + Debug.Log("Connection established."); + + if (BufferedData.Count > 0) + { + Debug.Log($"{BufferedData.Count} received before connection was established. Processing now."); + { + foreach (var a in BufferedData) + { + a(); + } + } + } + } + else if (param.m_info.m_eState is ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer or ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally) + { + Debug.Log($"Connection was closed by peer, {param.m_info.m_szEndDebug}"); + Disconnect(); + } + else + { + Debug.Log($"Connection state changed: {param.m_info.m_eState} - {param.m_info.m_szEndDebug}"); + } + } + + public void Disconnect() + { + cancelToken?.Cancel(); + Dispose(); + + if (HostConnection.m_HSteamNetConnection != 0) + { + Debug.Log("Sending Disconnect message"); + SteamNetworkingSockets.CloseConnection(HostConnection, 0, "Graceful disconnect", false); + HostConnection.m_HSteamNetConnection = 0; + } + } + + protected void Dispose() + { + if (c_onConnectionChange != null) + { + c_onConnectionChange.Dispose(); + c_onConnectionChange = null; + } + } + + private void InternalDisconnect() + { + Connected = false; + OnDisconnected.Invoke(); + Debug.Log("Disconnected."); + SteamNetworkingSockets.CloseConnection(HostConnection, 0, "Disconnected", false); + } + + public void ReceiveData() + { + var ptrs = new IntPtr[MAX_MESSAGES]; + int messageCount; + + if ((messageCount = SteamNetworkingSockets.ReceiveMessagesOnConnection(HostConnection, ptrs, MAX_MESSAGES)) > 0) + { + for (var i = 0; i < messageCount; i++) + { + (var data, var ch) = ProcessMessage(ptrs[i]); + if (Connected) + { + OnReceivedData(data, ch); + } + else + { + BufferedData.Add(() => OnReceivedData(data, ch)); + } + } + } + } + + public void Send(byte[] data, int channelId) + { + var res = SendSocket(HostConnection, data, channelId); + + if (res is EResult.k_EResultNoConnection or EResult.k_EResultInvalidParam) + { + Debug.Log($"Connection to server was lost."); + InternalDisconnect(); + } + else if (res != EResult.k_EResultOK) + { + Debug.LogError($"Could not send: {res}"); + } + } + + private void SetConnectedComplete() + => connectedComplete.SetResult(connectedComplete.Task); + + private void OnConnectionFailed() + => OnDisconnected.Invoke(); + + public void FlushData() + => SteamNetworkingSockets.FlushMessagesOnConnection(HostConnection); + } +} diff --git a/FizzySteamworks/SteamCommon.cs b/FizzySteamworks/SteamCommon.cs new file mode 100644 index 00000000..b1074496 --- /dev/null +++ b/FizzySteamworks/SteamCommon.cs @@ -0,0 +1,42 @@ +using Steamworks; +using System; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace Mirror.FizzySteam +{ + public abstract class SteamCommon + { + protected const int MAX_MESSAGES = 256; + + protected EResult SendSocket(HSteamNetConnection conn, byte[] data, int channelId) + { + Array.Resize(ref data, data.Length + 1); + data[data.Length - 1] = (byte)channelId; + + GCHandle pinnedArray = GCHandle.Alloc(data, GCHandleType.Pinned); + IntPtr pData = pinnedArray.AddrOfPinnedObject(); + int sendFlag = channelId == Channels.Unreliable ? Constants.k_nSteamNetworkingSend_Unreliable : Constants.k_nSteamNetworkingSend_Reliable; + EResult res = SteamNetworkingSockets.SendMessageToConnection(conn, pData, (uint)data.Length, sendFlag, out long _); + if (res != EResult.k_EResultOK) + { + Debug.LogWarning($"Send issue: {res}"); + } + + pinnedArray.Free(); + return res; + } + + protected (byte[], int) ProcessMessage(IntPtr ptrs) + { + SteamNetworkingMessage_t data = Marshal.PtrToStructure(ptrs); + byte[] managedArray = new byte[data.m_cbSize]; + Marshal.Copy(data.m_pData, managedArray, 0, data.m_cbSize); + SteamNetworkingMessage_t.Release(ptrs); + + int channel = managedArray[managedArray.Length - 1]; + Array.Resize(ref managedArray, managedArray.Length - 1); + return (managedArray, channel); + } + } +} diff --git a/FizzySteamworks/SteamServer.cs b/FizzySteamworks/SteamServer.cs new file mode 100644 index 00000000..7c5e6299 --- /dev/null +++ b/FizzySteamworks/SteamServer.cs @@ -0,0 +1,208 @@ +using Steamworks; +using System; +using System.Linq; +using UnityEngine; + +namespace Mirror.FizzySteam +{ + public class SteamServer : SteamCommon + { + private event Action OnConnected; + private event Action OnReceivedData; + private event Action OnDisconnected; + private event Action OnReceivedError; + + private BidirectionalDictionary connToMirrorID; + private BidirectionalDictionary steamIDToMirrorID; + private int maxConnections; + private int nextConnectionID; + + private HSteamListenSocket listenSocket; + + private Callback c_onConnectionChange = null; + + private SteamServer(int maxConnections) + { + this.maxConnections = maxConnections; + connToMirrorID = new BidirectionalDictionary(); + steamIDToMirrorID = new BidirectionalDictionary(); + nextConnectionID = 1; + c_onConnectionChange = Callback.Create(OnConnectionStatusChanged); + } + + public static SteamServer CreateServer(FizzySteamworks transport, int maxConnections) + { + SteamServer s = new SteamServer(maxConnections); + + s.OnConnected += (id) => transport.OnServerConnected.Invoke(id); + s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id); + s.OnReceivedData += (id, data, ch) => transport.OnServerDataReceived.Invoke(id, new ArraySegment(data), ch); + s.OnReceivedError += (id, error, reason) => transport.OnServerError.Invoke(id, error, reason); + + try + { + SteamNetworkingUtils.InitRelayNetworkAccess(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + + s.Host(); + + return s; + } + + private void Host() + { + SteamNetworkingConfigValue_t[] options = new SteamNetworkingConfigValue_t[] { }; + listenSocket = SteamNetworkingSockets.CreateListenSocketP2P(0, options.Length, options); + } + + private void OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t param) + { + ulong clientSteamID = param.m_info.m_identityRemote.GetSteamID64(); + if (param.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connecting) + { + if (connToMirrorID.Count >= maxConnections) + { + Debug.Log($"Incoming connection {clientSteamID} would exceed max connection count. Rejecting."); + SteamNetworkingSockets.CloseConnection(param.m_hConn, 0, "Max Connection Count", false); + return; + } + + EResult res; + + if ((res = SteamNetworkingSockets.AcceptConnection(param.m_hConn)) == EResult.k_EResultOK) + { + Debug.Log($"Accepting connection {clientSteamID}"); + } + else + { + Debug.Log($"Connection {clientSteamID} could not be accepted: {res.ToString()}"); + } + } + else if (param.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected) + { + int connectionId = nextConnectionID++; + connToMirrorID.Add(param.m_hConn, connectionId); + steamIDToMirrorID.Add(param.m_info.m_identityRemote.GetSteamID(), connectionId); + OnConnected.Invoke(connectionId); + Debug.Log($"Client with SteamID {clientSteamID} connected. Assigning connection id {connectionId}"); + } + else if (param.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer || param.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally) + { + if (connToMirrorID.TryGetValue(param.m_hConn, out int connId)) + { + InternalDisconnect(connId, param.m_hConn); + } + } + else + { + Debug.Log($"Connection {clientSteamID} state changed: {param.m_info.m_eState.ToString()}"); + } + } + + private void InternalDisconnect(int connId, HSteamNetConnection socket) + { + OnDisconnected.Invoke(connId); + SteamNetworkingSockets.CloseConnection(socket, 0, "Graceful disconnect", false); + connToMirrorID.Remove(connId); + steamIDToMirrorID.Remove(connId); + Debug.Log($"Client with ConnectionID {connId} disconnected."); + } + + public void Disconnect(int connectionId) + { + if (connToMirrorID.TryGetValue(connectionId, out HSteamNetConnection conn)) + { + Debug.Log($"Connection id {connectionId} disconnected."); + SteamNetworkingSockets.CloseConnection(conn, 0, "Disconnected by server", false); + steamIDToMirrorID.Remove(connectionId); + connToMirrorID.Remove(connectionId); + OnDisconnected(connectionId); + } + else + { + Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId); + } + } + + public void FlushData() + { + foreach (HSteamNetConnection conn in connToMirrorID.FirstTypes) + { + SteamNetworkingSockets.FlushMessagesOnConnection(conn); + } + } + + public void ReceiveData() + { + foreach (HSteamNetConnection conn in connToMirrorID.FirstTypes.ToList()) + { + if (connToMirrorID.TryGetValue(conn, out int connId)) + { + IntPtr[] ptrs = new IntPtr[MAX_MESSAGES]; + int messageCount; + + if ((messageCount = SteamNetworkingSockets.ReceiveMessagesOnConnection(conn, ptrs, MAX_MESSAGES)) > 0) + { + for (int i = 0; i < messageCount; i++) + { + (byte[] data, int ch) = ProcessMessage(ptrs[i]); + OnReceivedData(connId, data, ch); + } + } + } + } + } + + public void Send(int connectionId, byte[] data, int channelId) + { + if (connToMirrorID.TryGetValue(connectionId, out HSteamNetConnection conn)) + { + EResult res = SendSocket(conn, data, channelId); + + if (res == EResult.k_EResultNoConnection || res == EResult.k_EResultInvalidParam) + { + Debug.Log($"Connection to {connectionId} was lost."); + InternalDisconnect(connectionId, conn); + } + else if (res != EResult.k_EResultOK) + { + Debug.LogError($"Could not send: {res.ToString()}"); + } + } + else + { + Debug.LogError("Trying to send on unknown connection: " + connectionId); + OnReceivedError.Invoke(connectionId, TransportError.Unexpected, "ERROR Unknown Connection"); + } + } + + public string ServerGetClientAddress(int connectionId) + { + if (steamIDToMirrorID.TryGetValue(connectionId, out CSteamID steamId)) + { + return steamId.ToString(); + } + else + { + Debug.LogError("Trying to get info on unknown connection: " + connectionId); + OnReceivedError.Invoke(connectionId, TransportError.Unexpected, "ERROR Unknown Connection"); + return string.Empty; + } + } + + public void Shutdown() + { + SteamNetworkingSockets.CloseListenSocket(listenSocket); + + if (c_onConnectionChange != null) + { + c_onConnectionChange.Dispose(); + c_onConnectionChange = null; + } + } + } +} \ No newline at end of file diff --git a/QSB.sln b/QSB.sln index bf68ba79..c7610958 100644 --- a/QSB.sln +++ b/QSB.sln @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MirrorWeaver", "MirrorWeaver\MirrorWeaver.csproj", "{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FizzySteamworks", "FizzySteamworks\FizzySteamworks.csproj", "{D47034D8-B92D-47DA-884C-C76F735E2D5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,6 +31,10 @@ Global {DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Release|Any CPU.Build.0 = Release|Any CPU + {D47034D8-B92D-47DA-884C-C76F735E2D5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D47034D8-B92D-47DA-884C-C76F735E2D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D47034D8-B92D-47DA-884C-C76F735E2D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D47034D8-B92D-47DA-884C-C76F735E2D5D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index a89c3a01..3a15ea38 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -7,6 +7,7 @@ using QSB.SaveSync; using QSB.SaveSync.Messages; using QSB.Utility; using QSB.WorldSync; +using Steamworks; using System; using System.Linq; using System.Text; @@ -634,22 +635,22 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart if (!QSBCore.UseKcpTransport) { - /*var productUserId = EOSSDKComponent.LocalUserProductIdString; + var steamUserId = SteamUser.GetSteamID().ToString(); PopupClose += confirm => { if (confirm) { - GUIUtility.systemCopyBuffer = productUserId; + GUIUtility.systemCopyBuffer = steamUserId; } LoadGame(PlayerData.GetWarpedToTheEye()); Delay.RunWhen(() => TimeLoop._initialized, QSBNetworkManager.singleton.StartHost); }; - OpenInfoPopup(string.Format(QSBLocalization.Current.CopyProductUserIDToClipboard, productUserId) + OpenInfoPopup(string.Format(QSBLocalization.Current.CopyProductUserIDToClipboard, steamUserId) , QSBLocalization.Current.Yes - , QSBLocalization.Current.No);*/ + , QSBLocalization.Current.No); } else { diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index b2c5e3e9..c8dd58e1 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -72,6 +72,7 @@ + diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 9a44f550..bf4cd9bc 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -11,6 +11,7 @@ using QSB.SaveSync; using QSB.ServerSettings; using QSB.Utility; using QSB.WorldSync; +using Steamworks; using System; using System.Collections.Generic; using System.IO; @@ -110,9 +111,11 @@ public class QSBCore : ModBehaviour DebugLog.ToConsole($"FATAL - Could not determine game vendor.", MessageType.Fatal); } - DebugLog.DebugWrite($"Determined game vendor as {GameVendor}", MessageType.Info); + DebugLog.ToConsole($"Determined game vendor as {GameVendor}", MessageType.Info); } + private bool _steamworksInitialized; + public void Awake() { // no, we cant localize this - languages are loaded after the splash screen @@ -123,6 +126,39 @@ public class QSBCore : ModBehaviour QSBPatchManager.Init(); QSBPatchManager.DoPatchType(QSBPatchTypes.OnModStart); + + if (GameVendor != GameVendor.Steam) + { + DebugLog.ToConsole($"Not steam, initializing Steamworks..."); + + if (!Packsize.Test()) + { + DebugLog.ToConsole("[Steamworks.NET] Packsize Test returned false, the wrong version of Steamworks.NET is being run in this platform.", MessageType.Error); + } + + if (!DllCheck.Test()) + { + DebugLog.ToConsole("[Steamworks.NET] DllCheck Test returned false, One or more of the Steamworks binaries seems to be the wrong version.", MessageType.Error); + } + + System.Environment.SetEnvironmentVariable("SteamAppId", "480"); + System.Environment.SetEnvironmentVariable("SteamGameId", "480"); + + if (!SteamAPI.Init()) + { + DebugLog.ToConsole($"FATAL - SteamAPI.Init() failed. Refer to Valve's documentation.", MessageType.Fatal); + } + + _steamworksInitialized = true; + } + } + + public void OnDestroy() + { + if (_steamworksInitialized) + { + SteamAPI.Shutdown(); + } } public void Start() @@ -282,6 +318,11 @@ public class QSBCore : ModBehaviour DebugLog.ToConsole($"DEBUG MODE = {DebugSettings.DebugMode}"); } + + if (_steamworksInitialized) + { + SteamAPI.RunCallbacks(); + } } private void CheckCompatibilityMods() diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index b56a025d..86620fd6 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -1,5 +1,6 @@ using Epic.OnlineServices.Logging; using Mirror; +using Mirror.FizzySteam; using OWML.Common; using OWML.Utils; using QSB.Anglerfish.TransformSync; @@ -67,6 +68,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart private (TransportError error, string reason) _lastTransportError = (TransportError.Unexpected, "transport did not give an error. uh oh"); private static kcp2k.KcpTransport _kcpTransport; + private static FizzySteamworks _steamTransport; public override void Awake() { @@ -76,7 +78,11 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart _kcpTransport = gameObject.AddComponent(); } - transport = QSBCore.UseKcpTransport ? _kcpTransport : null; + { + _steamTransport = gameObject.AddComponent(); + } + + transport = QSBCore.UseKcpTransport ? _kcpTransport : _steamTransport; gameObject.SetActive(true); @@ -147,7 +153,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart } if (singleton != null) { - singleton.transport = Transport.active = QSBCore.UseKcpTransport ? _kcpTransport : null; + singleton.transport = Transport.active = QSBCore.UseKcpTransport ? _kcpTransport : _steamTransport; } if (MenuManager.Instance != null) {