mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-29 18:32:45 +00:00
add steam
This commit is contained in:
parent
b4a6280e3d
commit
ca99454139
88
FizzySteamworks/BidirectionalDictionary.cs
Normal file
88
FizzySteamworks/BidirectionalDictionary.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mirror.FizzySteam;
|
||||
|
||||
public class BidirectionalDictionary<T1, T2> : IEnumerable
|
||||
{
|
||||
private Dictionary<T1, T2> t1ToT2Dict = new Dictionary<T1, T2>();
|
||||
private Dictionary<T2, T1> t2ToT1Dict = new Dictionary<T2, T1>();
|
||||
|
||||
public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
|
||||
public IEnumerable<T2> 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);
|
||||
}
|
||||
}
|
||||
}
|
228
FizzySteamworks/FizzySteamworks.cs
Normal file
228
FizzySteamworks/FizzySteamworks.cs
Normal file
@ -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<byte> 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<byte> 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();
|
||||
}
|
32
FizzySteamworks/FizzySteamworks.csproj
Normal file
32
FizzySteamworks/FizzySteamworks.csproj
Normal file
@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<RootNamespace>Mirror.FizzySteam</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="com.rlabrecque.steamworks.net">
|
||||
<HintPath>F:\Steam\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\com.rlabrecque.steamworks.net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="kcp2k">
|
||||
<HintPath>..\Mirror\kcp2k.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mirror">
|
||||
<HintPath>..\Mirror\Mirror.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mirror.Components">
|
||||
<HintPath>..\Mirror\Mirror.Components.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mirror.Transports">
|
||||
<HintPath>..\Mirror\Mirror.Transports.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Telepathy">
|
||||
<HintPath>..\Mirror\Telepathy.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>F:\Steam\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
222
FizzySteamworks/SteamClient.cs
Normal file
222
FizzySteamworks/SteamClient.cs
Normal file
@ -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<byte[], int> OnReceivedData;
|
||||
private event Action OnConnected;
|
||||
private event Action OnDisconnected;
|
||||
private Callback<SteamNetConnectionStatusChangedCallback_t> c_onConnectionChange = null;
|
||||
|
||||
private CancellationTokenSource cancelToken;
|
||||
private TaskCompletionSource<Task> connectedComplete;
|
||||
private CSteamID hostSteamID = CSteamID.Nil;
|
||||
private HSteamNetConnection HostConnection;
|
||||
private List<Action> BufferedData;
|
||||
|
||||
private SteamClient(FizzySteamworks transport)
|
||||
{
|
||||
ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.Timeout));
|
||||
BufferedData = new List<Action>();
|
||||
}
|
||||
|
||||
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<byte>(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<SteamNetConnectionStatusChangedCallback_t>.Create(OnConnectionStatusChanged);
|
||||
|
||||
try
|
||||
{
|
||||
hostSteamID = new CSteamID(ulong.Parse(host));
|
||||
connectedComplete = new TaskCompletionSource<Task>();
|
||||
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);
|
||||
}
|
||||
}
|
42
FizzySteamworks/SteamCommon.cs
Normal file
42
FizzySteamworks/SteamCommon.cs
Normal file
@ -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<SteamNetworkingMessage_t>(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);
|
||||
}
|
||||
}
|
||||
}
|
208
FizzySteamworks/SteamServer.cs
Normal file
208
FizzySteamworks/SteamServer.cs
Normal file
@ -0,0 +1,208 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class SteamServer : SteamCommon
|
||||
{
|
||||
private event Action<int> OnConnected;
|
||||
private event Action<int, byte[], int> OnReceivedData;
|
||||
private event Action<int> OnDisconnected;
|
||||
private event Action<int, TransportError, string> OnReceivedError;
|
||||
|
||||
private BidirectionalDictionary<HSteamNetConnection, int> connToMirrorID;
|
||||
private BidirectionalDictionary<CSteamID, int> steamIDToMirrorID;
|
||||
private int maxConnections;
|
||||
private int nextConnectionID;
|
||||
|
||||
private HSteamListenSocket listenSocket;
|
||||
|
||||
private Callback<SteamNetConnectionStatusChangedCallback_t> c_onConnectionChange = null;
|
||||
|
||||
private SteamServer(int maxConnections)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
connToMirrorID = new BidirectionalDictionary<HSteamNetConnection, int>();
|
||||
steamIDToMirrorID = new BidirectionalDictionary<CSteamID, int>();
|
||||
nextConnectionID = 1;
|
||||
c_onConnectionChange = Callback<SteamNetConnectionStatusChangedCallback_t>.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<byte>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
QSB.sln
6
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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -72,6 +72,7 @@
|
||||
<PackageReference Include="OWML" Version="2.9.0" IncludeAssets="compile" />
|
||||
<Reference Include="..\Mirror\*.dll" />
|
||||
<Reference Include="..\UniTask\*.dll" />
|
||||
<ProjectReference Include="..\FizzySteamworks\FizzySteamworks.csproj" />
|
||||
<ProjectReference Include="..\MirrorWeaver\MirrorWeaver.csproj" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -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()
|
||||
|
@ -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<kcp2k.KcpTransport>();
|
||||
}
|
||||
|
||||
transport = QSBCore.UseKcpTransport ? _kcpTransport : null;
|
||||
{
|
||||
_steamTransport = gameObject.AddComponent<FizzySteamworks>();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user