Moved a couple of folders and wrote some code
This commit is contained in:
8
Assets/Packages/FishNet/Plugins/CodeAnalysis.meta
Normal file
8
Assets/Packages/FishNet/Plugins/CodeAnalysis.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90f5de37ae0e3184fb0d662879ba060b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,72 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1907658b89c1bbe42a0063df40b7ca24
|
||||
labels:
|
||||
- RoslynAnalyzer
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints:
|
||||
- UNITY_2020_3_OR_NEWER
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 620557a5e202e644cb322b8fcc9422ea
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
21
Assets/Packages/FishNet/Plugins/CodeAnalysis/LICENSE.txt
Normal file
21
Assets/Packages/FishNet/Plugins/CodeAnalysis/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Abdelfattah-Radwan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93ef12b9e040fa8429d9ef686212ed4e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
2
Assets/Packages/FishNet/Plugins/CodeAnalysis/README.txt
Normal file
2
Assets/Packages/FishNet/Plugins/CodeAnalysis/README.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Git URL:
|
||||
https://github.com/Abdelfattah-Radwan/FishNet.CodeAnalysis
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 835336ee0aec7ef41a1cfda40886f443
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Packages/FishNet/Plugins/FishySteamworks.meta
Normal file
8
Assets/Packages/FishNet/Plugins/FishySteamworks.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a1aa3332a3dba24cbfab110df5ec883
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,59 @@
|
||||
2.1.2
|
||||
- Added deinitializer to FishySteamworks.
|
||||
|
||||
2.1.1
|
||||
- Updated SetServerBindAddress signature to Fish-Networking 2.0.0.
|
||||
|
||||
2.1.0
|
||||
- Increased version to match other projects with Fish-Networking 2.0.0 support.
|
||||
- Fixed ClientId 32767 not found error when being used with Multipass.
|
||||
|
||||
1.7.0
|
||||
- Fish-Networking 2.0.0 Support
|
||||
|
||||
1.6.0
|
||||
- GetConnectionAddress now works for clientHost as well.
|
||||
- Changed from ConcurrentQueue to Queue.
|
||||
- Shutting down the socket properly recycles pending packets now.
|
||||
|
||||
1.5.0
|
||||
- Fish-Networking 1.3.1 Multipass transport support.
|
||||
|
||||
1.4.4
|
||||
- Removed obsolete method.
|
||||
|
||||
1.4.3
|
||||
- Added a few sanity checks.
|
||||
|
||||
1.4.2
|
||||
- Fish-Networking 0.1.5.Nightly.10 Support
|
||||
|
||||
1.4.1
|
||||
- Fixed server incorrectly rejecting connections due to maximum clients met.
|
||||
- Fixed client giving false connection started response.
|
||||
- Added support for Steamworks.Net 20.0.0.
|
||||
|
||||
1.4.0
|
||||
- Added client-host support.
|
||||
- Organized folder structure.
|
||||
- Removed ChannelData, it's not needed.
|
||||
|
||||
1.3.2
|
||||
- Fixed HUD displaying incorrect states when connecting server.
|
||||
|
||||
1.3.1
|
||||
- Fixed an allocation.
|
||||
|
||||
1.3.0
|
||||
- Moved to it's own git. https://github.com/FirstGearGames/FishySteamworks/
|
||||
|
||||
1.2.0
|
||||
- Support for Steamworks 15.0.1 changes.
|
||||
|
||||
1.1.0
|
||||
- Made changes to function with Heathen Engineering - Steamworks v2: Foundation, Steamworks v2: Complete
|
||||
https://assetstore.unity.com/packages/tools/integration/steamworks-v2-foundation-186949
|
||||
https://assetstore.unity.com/packages/tools/integration/steamworks-v2-complete-190316
|
||||
|
||||
1.0.0
|
||||
- Initial release.
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34f427edf65a7b845917c09d7dd5a4d9
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d2f070096038814b95de884c97d7432
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,92 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishySteamworks
|
||||
{
|
||||
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 Dictionary<T1, T2> First => t1ToT2Dict;
|
||||
public Dictionary<T2, T1> Second => t2ToT1Dict;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03b938ae217476545845d400f70e90ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,130 @@
|
||||
#if !FISHYSTEAMWORKS
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility.Performance;
|
||||
using FishySteamworks.Server;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishySteamworks.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a fake client connection to interact with the ServerSocket when acting as host.
|
||||
/// </summary>
|
||||
public class ClientHostSocket : CommonSocket
|
||||
{
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Socket for the server.
|
||||
/// </summary>
|
||||
private ServerSocket _server;
|
||||
/// <summary>
|
||||
/// Incomimg data.
|
||||
/// </summary>
|
||||
private Queue<LocalPacket> _incoming = new Queue<LocalPacket>();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Checks to set localCLient started.
|
||||
/// </summary>
|
||||
internal void CheckSetStarted()
|
||||
{
|
||||
//Check to set as started.
|
||||
if (_server != null && base.GetLocalConnectionState() == LocalConnectionState.Starting)
|
||||
{
|
||||
if (_server.GetLocalConnectionState() == LocalConnectionState.Started)
|
||||
SetLocalConnectionState(LocalConnectionState.Started, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Starts the client connection.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="channelsCount"></param>
|
||||
/// <param name="pollTime"></param>
|
||||
internal bool StartConnection(ServerSocket serverSocket)
|
||||
{
|
||||
_server = serverSocket;
|
||||
_server.SetClientHostSocket(this);
|
||||
if (_server.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return false;
|
||||
|
||||
SetLocalConnectionState(LocalConnectionState.Starting, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new connection state.
|
||||
/// </summary>
|
||||
protected override void SetLocalConnectionState(LocalConnectionState connectionState, bool server)
|
||||
{
|
||||
base.SetLocalConnectionState(connectionState, server);
|
||||
if (connectionState == LocalConnectionState.Started)
|
||||
_server.OnClientHostState(true);
|
||||
else
|
||||
_server.OnClientHostState(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local socket.
|
||||
/// </summary>
|
||||
internal bool StopConnection()
|
||||
{
|
||||
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped || base.GetLocalConnectionState() == LocalConnectionState.Stopping)
|
||||
return false;
|
||||
|
||||
base.ClearQueue(_incoming);
|
||||
//Immediately set stopped since no real connection exists.
|
||||
SetLocalConnectionState(LocalConnectionState.Stopping, false);
|
||||
SetLocalConnectionState(LocalConnectionState.Stopped, false);
|
||||
_server.SetClientHostSocket(null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterations data received.
|
||||
/// </summary>
|
||||
internal void IterateIncoming()
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
while (_incoming.Count > 0)
|
||||
{
|
||||
LocalPacket packet = _incoming.Dequeue();
|
||||
ArraySegment<byte> segment = new ArraySegment<byte>(packet.Data, 0, packet.Length);
|
||||
base.Transport.HandleClientReceivedDataArgs(new ClientReceivedDataArgs(segment, (Channel)packet.Channel, Transport.Index));
|
||||
ByteArrayPool.Store(packet.Data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server sends the local client data.
|
||||
/// </summary>
|
||||
internal void ReceivedFromLocalServer(LocalPacket packet)
|
||||
{
|
||||
_incoming.Enqueue(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues data to be sent to server.
|
||||
/// </summary>
|
||||
internal void SendToServer(byte channelId, ArraySegment<byte> segment)
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
if (_server.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
LocalPacket packet = new LocalPacket(segment, channelId);
|
||||
_server.ReceivedFromClientHost(packet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif // !DISABLESTEAMWORKS
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26f811c782a153c40bf204a24f016233
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,230 @@
|
||||
#if !FISHYSTEAMWORKS
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Transporting;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishySteamworks.Client
|
||||
{
|
||||
public class ClientSocket : CommonSocket
|
||||
{
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Called when local connection state changes.
|
||||
/// </summary>
|
||||
private Callback<SteamNetConnectionStatusChangedCallback_t> _onLocalConnectionStateCallback = null;
|
||||
/// <summary>
|
||||
/// SteamId for host.
|
||||
/// </summary>
|
||||
private CSteamID _hostSteamID = CSteamID.Nil;
|
||||
/// <summary>
|
||||
/// Socket to use.
|
||||
/// </summary>
|
||||
private HSteamNetConnection _socket;
|
||||
/// <summary>
|
||||
/// Thread used to check for timeout.
|
||||
/// </summary>
|
||||
private Thread _timeoutThread = null;
|
||||
/// <summary>
|
||||
/// When connect should timeout in unscaled time.
|
||||
/// </summary>
|
||||
private float _connectTimeout = -1f;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
/// <summary>
|
||||
/// Maximum time to wait before a timeout occurs when trying ot connect.
|
||||
/// </summary>
|
||||
private const float CONNECT_TIMEOUT_DURATION = 8000;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Checks of a connect attempt should time out.
|
||||
/// </summary>
|
||||
private void CheckTimeout()
|
||||
{
|
||||
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
do
|
||||
{
|
||||
//Timeout occurred.
|
||||
if ((sw.ElapsedMilliseconds / 1000) > _connectTimeout)
|
||||
StopConnection();
|
||||
|
||||
Thread.Sleep(50);
|
||||
} while (base.GetLocalConnectionState() == LocalConnectionState.Starting);
|
||||
sw.Stop();
|
||||
|
||||
//If here then the thread no longer needs to run. Can abort itself.
|
||||
_timeoutThread.Abort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the client connection.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="channelsCount"></param>
|
||||
/// <param name="pollTime"></param>
|
||||
internal bool StartConnection(string address, ushort port, bool peerToPeer)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_onLocalConnectionStateCallback == null)
|
||||
_onLocalConnectionStateCallback = Callback<SteamNetConnectionStatusChangedCallback_t>.Create(OnLocalConnectionState);
|
||||
|
||||
base.PeerToPeer = peerToPeer;
|
||||
//If address is required then make sure it can be parsed.
|
||||
byte[] ip = (!peerToPeer) ? base.GetIPBytes(address) : null;
|
||||
if (!peerToPeer && ip == null)
|
||||
{
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
base.SetLocalConnectionState(LocalConnectionState.Starting, false);
|
||||
_connectTimeout = Time.unscaledTime + CONNECT_TIMEOUT_DURATION;
|
||||
_timeoutThread = new Thread(CheckTimeout);
|
||||
_timeoutThread.Start();
|
||||
_hostSteamID = new CSteamID(UInt64.Parse(address));
|
||||
|
||||
SteamNetworkingIdentity smi = new SteamNetworkingIdentity();
|
||||
smi.SetSteamID(_hostSteamID);
|
||||
SteamNetworkingConfigValue_t[] options = new SteamNetworkingConfigValue_t[] { };
|
||||
if (base.PeerToPeer)
|
||||
{
|
||||
_socket = SteamNetworkingSockets.ConnectP2P(ref smi, 0, options.Length, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
SteamNetworkingIPAddr addr = new SteamNetworkingIPAddr();
|
||||
addr.Clear();
|
||||
addr.SetIPv6(ip, port);
|
||||
_socket = SteamNetworkingSockets.ConnectByIPAddress(ref addr, 0, options);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when local connection state changes.
|
||||
/// </summary>
|
||||
private void OnLocalConnectionState(SteamNetConnectionStatusChangedCallback_t args)
|
||||
{
|
||||
if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected)
|
||||
{
|
||||
base.SetLocalConnectionState(LocalConnectionState.Started, false);
|
||||
}
|
||||
else if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer || args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection was closed by peer, {args.m_info.m_szEndDebug}");
|
||||
StopConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection state changed: {args.m_info.m_eState.ToString()} - {args.m_info.m_szEndDebug}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local socket.
|
||||
/// </summary>
|
||||
internal bool StopConnection()
|
||||
{
|
||||
//Manually abort thread to close it down quicker.
|
||||
if (_timeoutThread != null && _timeoutThread.IsAlive)
|
||||
_timeoutThread.Abort();
|
||||
|
||||
/* Try to close the socket before exiting early
|
||||
* We never want to leave sockets open. */
|
||||
if (_socket != HSteamNetConnection.Invalid)
|
||||
{
|
||||
//Reset callback.
|
||||
if (_onLocalConnectionStateCallback != null)
|
||||
{
|
||||
_onLocalConnectionStateCallback.Dispose();
|
||||
_onLocalConnectionStateCallback = null;
|
||||
}
|
||||
|
||||
SteamNetworkingSockets.CloseConnection(_socket, 0, string.Empty, false);
|
||||
_socket = HSteamNetConnection.Invalid;
|
||||
}
|
||||
|
||||
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped || base.GetLocalConnectionState() == LocalConnectionState.Stopping)
|
||||
return false;
|
||||
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopping, false);
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterations data received.
|
||||
/// </summary>
|
||||
internal void IterateIncoming()
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
int messageCount = SteamNetworkingSockets.ReceiveMessagesOnConnection(_socket, base.MessagePointers, MAX_MESSAGES);
|
||||
if (messageCount > 0)
|
||||
{
|
||||
for (int i = 0; i < messageCount; i++)
|
||||
{
|
||||
base.GetMessage(base.MessagePointers[i], InboundBuffer, out ArraySegment<byte> segment, out byte channel);
|
||||
base.Transport.HandleClientReceivedDataArgs(new ClientReceivedDataArgs(segment, (Channel)channel, Transport.Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues data to be sent to server.
|
||||
/// </summary>
|
||||
/// <param name="channelId"></param>
|
||||
/// <param name="segment"></param>
|
||||
internal void SendToServer(byte channelId, ArraySegment<byte> segment)
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
EResult res = base.Send(_socket, segment, channelId);
|
||||
if (res == EResult.k_EResultNoConnection || res == EResult.k_EResultInvalidParam)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection to server was lost.");
|
||||
StopConnection();
|
||||
}
|
||||
else if (res != EResult.k_EResultOK)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"Could not send: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends queued data to server.
|
||||
/// </summary>
|
||||
internal void IterateOutgoing()
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
SteamNetworkingSockets.FlushMessagesOnConnection(_socket);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // !DISABLESTEAMWORKS
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 414566e956be2e44c88936767a5075a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,210 @@
|
||||
#if !FISHYSTEAMWORKS
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility.Performance;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishySteamworks
|
||||
{
|
||||
|
||||
public abstract class CommonSocket
|
||||
{
|
||||
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// Current ConnectionState.
|
||||
/// </summary>
|
||||
private LocalConnectionState _connectionState = LocalConnectionState.Stopped;
|
||||
/// <summary>
|
||||
/// Returns the current ConnectionState.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal LocalConnectionState GetLocalConnectionState()
|
||||
{
|
||||
return _connectionState;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets a new connection state.
|
||||
/// </summary>
|
||||
/// <param name="connectionState"></param>
|
||||
protected virtual void SetLocalConnectionState(LocalConnectionState connectionState, bool server)
|
||||
{
|
||||
//If state hasn't changed.
|
||||
if (connectionState == _connectionState)
|
||||
return;
|
||||
|
||||
_connectionState = connectionState;
|
||||
|
||||
if (server)
|
||||
Transport.HandleServerConnectionState(new ServerConnectionStateArgs(connectionState, Transport.Index));
|
||||
else
|
||||
Transport.HandleClientConnectionState(new ClientConnectionStateArgs(connectionState, Transport.Index));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected.
|
||||
/// <summary>
|
||||
/// True if using PeerToPeer.
|
||||
/// </summary>
|
||||
protected bool PeerToPeer = false;
|
||||
/// <summary>
|
||||
/// Transport controlling this socket.
|
||||
/// </summary>
|
||||
protected Transport Transport = null;
|
||||
/// <summary>
|
||||
/// Pointers for received messages per connection.
|
||||
/// </summary>
|
||||
protected IntPtr[] MessagePointers = new IntPtr[MAX_MESSAGES];
|
||||
/// <summary>
|
||||
/// Buffer used to receive data.
|
||||
/// </summary>
|
||||
protected byte[] InboundBuffer = null;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
/// <summary>
|
||||
/// Maximum number of messages which can be received per connection.
|
||||
/// </summary>
|
||||
protected const int MAX_MESSAGES = 256;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this for use.
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
internal virtual void Initialize(Transport t)
|
||||
{
|
||||
Transport = t;
|
||||
//Get whichever channel has max MTU and resize buffer.
|
||||
int maxMTU = Transport.GetMTU(0);
|
||||
maxMTU = Math.Max(maxMTU, Transport.GetMTU(1));
|
||||
InboundBuffer = new byte[maxMTU];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets bytes for address.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
protected byte[] GetIPBytes(string address)
|
||||
{
|
||||
//If address is required then make sure it can be parsed.
|
||||
if (!string.IsNullOrEmpty(address))
|
||||
{
|
||||
if (!IPAddress.TryParse(address, out IPAddress result))
|
||||
{
|
||||
if (Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"Could not parse address {address} to IPAddress.");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return result.GetAddressBytes();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends data over the steamConnection.
|
||||
/// </summary>
|
||||
/// <param name="steamConnection"></param>
|
||||
/// <param name="segment"></param>
|
||||
/// <param name="channelId"></param>
|
||||
/// <returns></returns>
|
||||
protected EResult Send(HSteamNetConnection steamConnection, ArraySegment<byte> segment, byte channelId)
|
||||
{
|
||||
/* Have to resize array to include channel index
|
||||
* if array isn't large enough to fit it. This is because
|
||||
* we don't know what channel data comes in on so
|
||||
* the channel has to be packed into the data sent.
|
||||
* Odds of the array having to resize are extremely low
|
||||
* so while this is not ideal, it's still very low risk. */
|
||||
if ((segment.Array.Length - 1) <= (segment.Offset + segment.Count))
|
||||
{
|
||||
byte[] arr = segment.Array;
|
||||
Array.Resize(ref arr, arr.Length + 1);
|
||||
arr[arr.Length - 1] = channelId;
|
||||
}
|
||||
//If large enough just increase the segment and set the channel byte.
|
||||
else
|
||||
{
|
||||
segment.Array[segment.Offset + segment.Count] = channelId;
|
||||
}
|
||||
//Make a new segment so count is right.
|
||||
segment = new ArraySegment<byte>(segment.Array, segment.Offset, segment.Count + 1);
|
||||
|
||||
GCHandle pinnedArray = GCHandle.Alloc(segment.Array, GCHandleType.Pinned);
|
||||
IntPtr pData = pinnedArray.AddrOfPinnedObject() + segment.Offset;
|
||||
|
||||
int sendFlag = (channelId == (byte)Channel.Unreliable) ? Constants.k_nSteamNetworkingSend_Unreliable : Constants.k_nSteamNetworkingSend_Reliable;
|
||||
#if UNITY_SERVER
|
||||
EResult result = SteamGameServerNetworkingSockets.SendMessageToConnection(steamConnection, pData, (uint)segment.Count, sendFlag, out long _);
|
||||
#else
|
||||
EResult result = SteamNetworkingSockets.SendMessageToConnection(steamConnection, pData, (uint)segment.Count, sendFlag, out long _);
|
||||
#endif
|
||||
if (result != EResult.k_EResultOK)
|
||||
{
|
||||
if (Transport.NetworkManager.CanLog(LoggingType.Warning))
|
||||
Debug.LogWarning($"Send issue: {result}");
|
||||
}
|
||||
|
||||
pinnedArray.Free();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a queue.
|
||||
/// </summary>
|
||||
/// <param name="queue"></param>
|
||||
internal void ClearQueue(ConcurrentQueue<LocalPacket> queue)
|
||||
{
|
||||
while (queue.TryDequeue(out LocalPacket p))
|
||||
ByteArrayPool.Store(p.Data);
|
||||
}
|
||||
/// <summary>
|
||||
/// Clears a queue.
|
||||
/// </summary>
|
||||
/// <param name="queue"></param>
|
||||
internal void ClearQueue(Queue<LocalPacket> queue)
|
||||
{
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
LocalPacket p = queue.Dequeue();
|
||||
ByteArrayPool.Store(p.Data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a message from the steam network.
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
protected void GetMessage(IntPtr ptr, byte[] buffer, out ArraySegment<byte> segment, out byte channel)
|
||||
{
|
||||
SteamNetworkingMessage_t data = Marshal.PtrToStructure<SteamNetworkingMessage_t>(ptr);
|
||||
|
||||
int packetLength = data.m_cbSize;
|
||||
Marshal.Copy(data.m_pData, buffer, 0, packetLength);
|
||||
|
||||
//data.Release();
|
||||
SteamNetworkingMessage_t.Release(ptr);
|
||||
//Channel will be at the end of the packet.
|
||||
channel = buffer[packetLength - 1];
|
||||
//Set segment to length - 1 to exclude channel.
|
||||
segment = new ArraySegment<byte>(buffer, 0, packetLength - 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b4d1ee96e33fa9428f01142ed427231
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,21 @@
|
||||
|
||||
using FishNet.Utility.Performance;
|
||||
using System;
|
||||
|
||||
namespace FishySteamworks
|
||||
{
|
||||
internal struct LocalPacket
|
||||
{
|
||||
public byte[] Data;
|
||||
public int Length;
|
||||
public byte Channel;
|
||||
public LocalPacket(ArraySegment<byte> data, byte channel)
|
||||
{
|
||||
Data = ByteArrayPool.Retrieve(data.Count);
|
||||
Length = data.Count;
|
||||
Buffer.BlockCopy(data.Array, data.Offset, Data, 0, Length);
|
||||
Channel = channel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9af11b60772c62749919bedb1b39e754
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,473 @@
|
||||
#if !FISHYSTEAMWORKS
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Transporting;
|
||||
using FishySteamworks.Client;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishySteamworks.Server
|
||||
{
|
||||
public class ServerSocket : CommonSocket
|
||||
{
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// Gets the current ConnectionState of a remote client on the server.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
|
||||
internal RemoteConnectionState GetConnectionState(int connectionId)
|
||||
{
|
||||
//Remote clients can only have Started or Stopped states since we cannot know in between.
|
||||
if (_steamConnections.Second.ContainsKey(connectionId))
|
||||
return RemoteConnectionState.Started;
|
||||
else
|
||||
return RemoteConnectionState.Stopped;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// SteamConnections for ConnectionIds.
|
||||
/// </summary>
|
||||
private BidirectionalDictionary<HSteamNetConnection, int> _steamConnections = new BidirectionalDictionary<HSteamNetConnection, int>();
|
||||
/// <summary>
|
||||
/// SteamIds for ConnectionIds.
|
||||
/// </summary>
|
||||
private BidirectionalDictionary<CSteamID, int> _steamIds = new BidirectionalDictionary<CSteamID, int>();
|
||||
/// <summary>
|
||||
/// Maximum number of remote connections.
|
||||
/// </summary>
|
||||
private int _maximumClients;
|
||||
/// <summary>
|
||||
/// Next Id to use for a connection.
|
||||
/// </summary>
|
||||
private int _nextConnectionId;
|
||||
/// <summary>
|
||||
/// Socket for the connection.
|
||||
/// </summary>
|
||||
private HSteamListenSocket _socket = new HSteamListenSocket(0);
|
||||
/// <summary>
|
||||
/// Packets received from local client.
|
||||
/// </summary>
|
||||
private Queue<LocalPacket> _clientHostIncoming = new Queue<LocalPacket>();
|
||||
/// <summary>
|
||||
/// Contains state of the client host. True is started, false is stopped.
|
||||
/// </summary>
|
||||
private bool _clientHostStarted = false;
|
||||
/// <summary>
|
||||
/// Called when a remote connection state changes.
|
||||
/// </summary>
|
||||
private Steamworks.Callback<SteamNetConnectionStatusChangedCallback_t> _onRemoteConnectionStateCallback;
|
||||
/// <summary>
|
||||
/// ConnectionIds which can be reused.
|
||||
/// </summary>
|
||||
private Queue<int> _cachedConnectionIds = new Queue<int>();
|
||||
/// <summary>
|
||||
/// Socket for client host. Will be null if not being used.
|
||||
/// </summary>
|
||||
private ClientHostSocket _clientHost;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Resets the socket if invalid.
|
||||
/// </summary>
|
||||
internal void ResetInvalidSocket()
|
||||
{
|
||||
/* Force connection state to stopped if listener is invalid.
|
||||
* Not sure if steam may change this internally so better
|
||||
* safe than sorry and check before trying to connect
|
||||
* rather than being stuck in the incorrect state. */
|
||||
if (_socket == HSteamListenSocket.Invalid)
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, true);
|
||||
}
|
||||
/// <summary>
|
||||
/// Starts the server.
|
||||
/// </summary>
|
||||
internal bool StartConnection(string address, ushort port, int maximumClients, bool peerToPeer)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_onRemoteConnectionStateCallback == null)
|
||||
_onRemoteConnectionStateCallback = Steamworks.Callback<SteamNetConnectionStatusChangedCallback_t>.Create(OnRemoteConnectionState);
|
||||
|
||||
base.PeerToPeer = peerToPeer;
|
||||
|
||||
//If address is required then make sure it can be parsed.
|
||||
byte[] ip = (!peerToPeer) ? base.GetIPBytes(address) : null;
|
||||
|
||||
base.PeerToPeer = peerToPeer;
|
||||
SetMaximumClients(maximumClients);
|
||||
_nextConnectionId = 0;
|
||||
_cachedConnectionIds.Clear();
|
||||
|
||||
base.SetLocalConnectionState(LocalConnectionState.Starting, true);
|
||||
SteamNetworkingConfigValue_t[] options = new SteamNetworkingConfigValue_t[] { };
|
||||
|
||||
if (base.PeerToPeer)
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
_socket = SteamGameServerNetworkingSockets.CreateListenSocketP2P(0, options.Length, options);
|
||||
#else
|
||||
_socket = SteamNetworkingSockets.CreateListenSocketP2P(0, options.Length, options);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
SteamNetworkingIPAddr addr = new SteamNetworkingIPAddr();
|
||||
addr.Clear();
|
||||
if (ip != null)
|
||||
addr.SetIPv6(ip, port);
|
||||
#if UNITY_SERVER
|
||||
_socket = SteamGameServerNetworkingSockets.CreateListenSocketIP(ref addr, 0, options);
|
||||
#else
|
||||
_socket = SteamNetworkingSockets.CreateListenSocketIP(ref addr, 0, options);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
base.SetLocalConnectionState(LocalConnectionState.Started, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local socket.
|
||||
/// </summary>
|
||||
internal bool StopConnection()
|
||||
{
|
||||
/* Try to close the socket before exiting early
|
||||
* We never want to leave sockets open. */
|
||||
if (_socket != HSteamListenSocket.Invalid)
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
SteamGameServerNetworkingSockets.CloseListenSocket(_socket);
|
||||
#else
|
||||
SteamNetworkingSockets.CloseListenSocket(_socket);
|
||||
#endif
|
||||
if (_onRemoteConnectionStateCallback != null)
|
||||
{
|
||||
_onRemoteConnectionStateCallback.Dispose();
|
||||
_onRemoteConnectionStateCallback = null;
|
||||
}
|
||||
_socket = HSteamListenSocket.Invalid;
|
||||
}
|
||||
|
||||
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped)
|
||||
return false;
|
||||
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopping, true);
|
||||
base.SetLocalConnectionState(LocalConnectionState.Stopped, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a remote client from the server, disconnecting the client.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
|
||||
internal bool StopConnection(int connectionId)
|
||||
{
|
||||
if (connectionId == FishySteamworks.CLIENT_HOST_ID)
|
||||
{
|
||||
if (_clientHost != null)
|
||||
{
|
||||
_clientHost.StopConnection();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
//Remote client.
|
||||
else
|
||||
{
|
||||
if (_steamConnections.Second.TryGetValue(connectionId, out HSteamNetConnection steamConn))
|
||||
{
|
||||
return StopConnection(connectionId, steamConn);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"Steam connection not found for connectionId {connectionId}.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Stops a remote client from the server, disconnecting the client.
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="socket"></param>
|
||||
private bool StopConnection(int connectionId, HSteamNetConnection socket)
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
SteamGameServerNetworkingSockets.CloseConnection(socket, 0, string.Empty, false);
|
||||
#else
|
||||
SteamNetworkingSockets.CloseConnection(socket, 0, string.Empty, false);
|
||||
#endif
|
||||
_steamConnections.Remove(connectionId);
|
||||
_steamIds.Remove(connectionId);
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Client with ConnectionID {connectionId} disconnected.");
|
||||
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, connectionId, Transport.Index));
|
||||
_cachedConnectionIds.Enqueue(connectionId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a remote connection state changes.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void OnRemoteConnectionState(SteamNetConnectionStatusChangedCallback_t args)
|
||||
{
|
||||
ulong clientSteamID = args.m_info.m_identityRemote.GetSteamID64();
|
||||
if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connecting)
|
||||
{
|
||||
if (_steamConnections.Count >= GetMaximumClients())
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Incoming connection {clientSteamID} was rejected because would exceed the maximum connection count.");
|
||||
#if UNITY_SERVER
|
||||
SteamGameServerNetworkingSockets.CloseConnection(args.m_hConn, 0, "Max Connection Count", false);
|
||||
#else
|
||||
SteamNetworkingSockets.CloseConnection(args.m_hConn, 0, "Max Connection Count", false);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_SERVER
|
||||
EResult res = SteamGameServerNetworkingSockets.AcceptConnection(args.m_hConn);
|
||||
#else
|
||||
EResult res = SteamNetworkingSockets.AcceptConnection(args.m_hConn);
|
||||
#endif
|
||||
if (res == EResult.k_EResultOK)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Accepting connection {clientSteamID}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection {clientSteamID} could not be accepted: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
else if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected)
|
||||
{
|
||||
int connectionId = (_cachedConnectionIds.Count > 0) ? _cachedConnectionIds.Dequeue() : _nextConnectionId++;
|
||||
_steamConnections.Add(args.m_hConn, connectionId);
|
||||
_steamIds.Add(args.m_info.m_identityRemote.GetSteamID(), connectionId);
|
||||
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Client with SteamID {clientSteamID} connected. Assigning connection id {connectionId}");
|
||||
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Started, connectionId, Transport.Index));
|
||||
}
|
||||
else if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer || args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
|
||||
{
|
||||
if (_steamConnections.TryGetValue(args.m_hConn, out int connId))
|
||||
{
|
||||
StopConnection(connId, args.m_hConn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection {clientSteamID} state changed: {args.m_info.m_eState.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allows for Outgoing queue to be iterated.
|
||||
/// </summary>
|
||||
internal void IterateOutgoing()
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
foreach (HSteamNetConnection conn in _steamConnections.FirstTypes)
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
SteamGameServerNetworkingSockets.FlushMessagesOnConnection(conn);
|
||||
#else
|
||||
SteamNetworkingSockets.FlushMessagesOnConnection(conn);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates the Incoming queue.
|
||||
/// </summary>
|
||||
/// <param name="transport"></param>
|
||||
internal void IterateIncoming()
|
||||
{
|
||||
//Stopped or trying to stop.
|
||||
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped || base.GetLocalConnectionState() == LocalConnectionState.Stopping)
|
||||
return;
|
||||
|
||||
//Iterate local client packets first.
|
||||
while (_clientHostIncoming.Count > 0)
|
||||
{
|
||||
LocalPacket packet = _clientHostIncoming.Dequeue();
|
||||
ArraySegment<byte> segment = new ArraySegment<byte>(packet.Data, 0, packet.Length);
|
||||
base.Transport.HandleServerReceivedDataArgs(new ServerReceivedDataArgs(segment, (Channel)packet.Channel, FishySteamworks.CLIENT_HOST_ID, Transport.Index));
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<HSteamNetConnection, int> item in _steamConnections.First)
|
||||
{
|
||||
HSteamNetConnection steamNetConn = item.Key;
|
||||
int connectionId = item.Value;
|
||||
|
||||
int messageCount;
|
||||
#if UNITY_SERVER
|
||||
messageCount = SteamGameServerNetworkingSockets.ReceiveMessagesOnConnection(steamNetConn, base.MessagePointers, MAX_MESSAGES);
|
||||
#else
|
||||
messageCount = SteamNetworkingSockets.ReceiveMessagesOnConnection(steamNetConn, base.MessagePointers, MAX_MESSAGES);
|
||||
#endif
|
||||
if (messageCount > 0)
|
||||
{
|
||||
for (int i = 0; i < messageCount; i++)
|
||||
{
|
||||
base.GetMessage(base.MessagePointers[i], InboundBuffer, out ArraySegment<byte> segment, out byte channel);
|
||||
base.Transport.HandleServerReceivedDataArgs(new ServerReceivedDataArgs(segment, (Channel)channel, connectionId, Transport.Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends data to a client.
|
||||
/// </summary>
|
||||
/// <param name="channelId"></param>
|
||||
/// <param name="segment"></param>
|
||||
/// <param name="connectionId"></param>
|
||||
internal void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
|
||||
{
|
||||
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
//Check if sending local client first, send and exit if so.
|
||||
if (connectionId == FishySteamworks.CLIENT_HOST_ID)
|
||||
{
|
||||
if (_clientHost != null)
|
||||
{
|
||||
LocalPacket packet = new LocalPacket(segment, channelId);
|
||||
_clientHost.ReceivedFromLocalServer(packet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_steamConnections.TryGetValue(connectionId, out HSteamNetConnection steamConn))
|
||||
{
|
||||
EResult res = base.Send(steamConn, segment, channelId);
|
||||
|
||||
if (res == EResult.k_EResultNoConnection || res == EResult.k_EResultInvalidParam)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
|
||||
Debug.Log($"Connection to {connectionId} was lost.");
|
||||
StopConnection(connectionId, steamConn);
|
||||
}
|
||||
else if (res != EResult.k_EResultOK)
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"Could not send: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"ConnectionId {connectionId} does not exist, data will not be sent.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of a remote connection Id.
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
internal string GetConnectionAddress(int connectionId)
|
||||
{
|
||||
if (_steamIds.TryGetValue(connectionId, out CSteamID steamId))
|
||||
{
|
||||
return steamId.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"ConnectionId {connectionId} is invalid; address cannot be returned.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
|
||||
/// </summary>
|
||||
internal void SetMaximumClients(int value)
|
||||
{
|
||||
_maximumClients = Math.Min(value, FishySteamworks.CLIENT_HOST_ID - 1);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns maximum number of allowed clients.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal int GetMaximumClients()
|
||||
{
|
||||
return _maximumClients;
|
||||
}
|
||||
|
||||
#region ClientHost (local client).
|
||||
/// <summary>
|
||||
/// Sets ClientHost value.
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
internal void SetClientHostSocket(ClientHostSocket socket)
|
||||
{
|
||||
_clientHost = socket;
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when the local client state changes.
|
||||
/// </summary>
|
||||
internal void OnClientHostState(bool started)
|
||||
{
|
||||
FishySteamworks fs = (FishySteamworks)base.Transport;
|
||||
CSteamID steamId = new CSteamID(fs.LocalUserSteamID);
|
||||
|
||||
//If not started but was previously flush incoming from local client.
|
||||
if (!started && _clientHostStarted)
|
||||
{
|
||||
base.ClearQueue(_clientHostIncoming);
|
||||
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, FishySteamworks.CLIENT_HOST_ID, Transport.Index));
|
||||
_steamIds.Remove(steamId);
|
||||
}
|
||||
//If started.
|
||||
else if (started)
|
||||
{
|
||||
_steamIds[steamId] = FishySteamworks.CLIENT_HOST_ID;
|
||||
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Started, FishySteamworks.CLIENT_HOST_ID, Transport.Index));
|
||||
}
|
||||
|
||||
_clientHostStarted = started;
|
||||
}
|
||||
/// <summary>
|
||||
/// Queues a received packet from the local client.
|
||||
/// </summary>
|
||||
internal void ReceivedFromClientHost(LocalPacket packet)
|
||||
{
|
||||
if (!_clientHostStarted)
|
||||
return;
|
||||
|
||||
_clientHostIncoming.Enqueue(packet);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif // !DISABLESTEAMWORKS
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 764a7199db8c90246820be2df11ee41d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,585 @@
|
||||
#if !FISHYSTEAMWORKS
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Transporting;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishySteamworks
|
||||
{
|
||||
public class FishySteamworks : Transport
|
||||
{
|
||||
~FishySteamworks()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// The SteamId for the local user after connecting to or starting the server. This is populated automatically.
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
public ulong LocalUserSteamID = 0;
|
||||
#endregion
|
||||
|
||||
#region Serialized.
|
||||
/// <summary>
|
||||
/// Steam application Id.
|
||||
/// </summary>
|
||||
[Tooltip("Steam application Id.")]
|
||||
[SerializeField]
|
||||
private ulong _steamAppID = 480;
|
||||
/// <summary>
|
||||
/// Address server should bind to.
|
||||
/// </summary>
|
||||
[Tooltip("Address server should bind to.")]
|
||||
[SerializeField]
|
||||
private string _serverBindAddress = string.Empty;
|
||||
/// <summary>
|
||||
/// Port to use.
|
||||
/// </summary>
|
||||
[Tooltip("Port to use.")]
|
||||
[SerializeField]
|
||||
private ushort _port = 7770;
|
||||
/// <summary>
|
||||
/// Maximum number of players which may be connected at once.
|
||||
/// </summary>
|
||||
[Tooltip("Maximum number of players which may be connected at once.")]
|
||||
[Range(1, ushort.MaxValue)]
|
||||
[SerializeField]
|
||||
private ushort _maximumClients = 9001;
|
||||
/// <summary>
|
||||
/// True if using peer to peer socket.
|
||||
/// </summary>
|
||||
[Tooltip("True if using peer to peer socket.")]
|
||||
[SerializeField]
|
||||
private bool _peerToPeer = false;
|
||||
/// <summary>
|
||||
/// Address client should connect to.
|
||||
/// </summary>
|
||||
[Tooltip("Address client should connect to.")]
|
||||
[SerializeField]
|
||||
private string _clientAddress = string.Empty;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// MTUs for each channel.
|
||||
/// </summary>
|
||||
private int[] _mtus;
|
||||
/// <summary>
|
||||
/// Client when acting as client only.
|
||||
/// </summary>
|
||||
private Client.ClientSocket _client;
|
||||
/// <summary>
|
||||
/// Client when acting as host.
|
||||
/// </summary>
|
||||
private Client.ClientHostSocket _clientHost;
|
||||
/// <summary>
|
||||
/// Server for the transport.
|
||||
/// </summary>
|
||||
private Server.ServerSocket _server;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
/// <summary>
|
||||
/// Id to use for client when acting as host.
|
||||
/// </summary>
|
||||
internal const int CLIENT_HOST_ID = short.MaxValue;
|
||||
#endregion
|
||||
|
||||
public override void Initialize(NetworkManager networkManager, int transportIndex)
|
||||
{
|
||||
base.Initialize(networkManager, transportIndex);
|
||||
|
||||
_client = new Client.ClientSocket();
|
||||
_clientHost = new Client.ClientHostSocket();
|
||||
_server = new Server.ServerSocket();
|
||||
|
||||
CreateChannelData();
|
||||
WriteSteamAppId();
|
||||
_client.Initialize(this);
|
||||
_clientHost.Initialize(this);
|
||||
_server.Initialize(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_clientHost.CheckSetStarted();
|
||||
}
|
||||
|
||||
#region Setup.
|
||||
/// <summary>
|
||||
/// Creates ChannelData for the transport.
|
||||
/// </summary>
|
||||
private void CreateChannelData()
|
||||
{
|
||||
_mtus = new int[2]
|
||||
{
|
||||
1048576,
|
||||
1200
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Writes SteamAppId to file.
|
||||
/// </summary>
|
||||
private void WriteSteamAppId()
|
||||
{
|
||||
string fileName = "steam_appid.txt";
|
||||
string appIdText = _steamAppID.ToString();
|
||||
try
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
string content = File.ReadAllText(fileName);
|
||||
if (content != appIdText)
|
||||
{
|
||||
File.WriteAllText(fileName, appIdText);
|
||||
Debug.Log($"SteamId has been updated from {content} to {appIdText} within {fileName}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText(fileName, appIdText);
|
||||
Debug.Log($"SteamId {appIdText} has been set within {fileName}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"There was an exception when trying to write {appIdText} to {fileName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to initialize steam network access.
|
||||
/// </summary>
|
||||
private bool InitializeRelayNetworkAccess()
|
||||
{
|
||||
try
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
SteamGameServerNetworkingUtils.InitRelayNetworkAccess();
|
||||
#else
|
||||
SteamNetworkingUtils.InitRelayNetworkAccess();
|
||||
if (IsNetworkAccessAvailable())
|
||||
LocalUserSteamID = SteamUser.GetSteamID().m_SteamID;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if network access is available.
|
||||
/// </summary>
|
||||
public bool IsNetworkAccessAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
#if UNITY_SERVER
|
||||
InteropHelp.TestIfAvailableGameServer();
|
||||
#else
|
||||
InteropHelp.TestIfAvailableClient();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ConnectionStates.
|
||||
/// <summary>
|
||||
/// Gets the IP address of a remote connection Id.
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetConnectionAddress(int connectionId)
|
||||
{
|
||||
return _server.GetConnectionAddress(connectionId);
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when a connection state changes for the local client.
|
||||
/// </summary>
|
||||
public override event Action<ClientConnectionStateArgs> OnClientConnectionState;
|
||||
/// <summary>
|
||||
/// Called when a connection state changes for the local server.
|
||||
/// </summary>
|
||||
public override event Action<ServerConnectionStateArgs> OnServerConnectionState;
|
||||
/// <summary>
|
||||
/// Called when a connection state changes for a remote client.
|
||||
/// </summary>
|
||||
public override event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
|
||||
/// <summary>
|
||||
/// Gets the current local ConnectionState.
|
||||
/// </summary>
|
||||
/// <param name="server">True if getting ConnectionState for the server.</param>
|
||||
public override LocalConnectionState GetConnectionState(bool server)
|
||||
{
|
||||
if (server)
|
||||
return _server.GetLocalConnectionState();
|
||||
else
|
||||
return _client.GetLocalConnectionState();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current ConnectionState of a remote client on the server.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
|
||||
public override RemoteConnectionState GetConnectionState(int connectionId)
|
||||
{
|
||||
return _server.GetConnectionState(connectionId);
|
||||
}
|
||||
/// <summary>
|
||||
/// Handles a ConnectionStateArgs for the local client.
|
||||
/// </summary>
|
||||
/// <param name="connectionStateArgs"></param>
|
||||
public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs)
|
||||
{
|
||||
OnClientConnectionState?.Invoke(connectionStateArgs);
|
||||
}
|
||||
/// <summary>
|
||||
/// Handles a ConnectionStateArgs for the local server.
|
||||
/// </summary>
|
||||
/// <param name="connectionStateArgs"></param>
|
||||
public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs)
|
||||
{
|
||||
OnServerConnectionState?.Invoke(connectionStateArgs);
|
||||
}
|
||||
/// <summary>
|
||||
/// Handles a ConnectionStateArgs for a remote client.
|
||||
/// </summary>
|
||||
/// <param name="connectionStateArgs"></param>
|
||||
public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs)
|
||||
{
|
||||
OnRemoteConnectionState?.Invoke(connectionStateArgs);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Iterating.
|
||||
/// <summary>
|
||||
/// Processes data received by the socket.
|
||||
/// </summary>
|
||||
/// <param name="server">True to process data received on the server.</param>
|
||||
public override void IterateIncoming(bool server)
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
_server.IterateIncoming();
|
||||
}
|
||||
else
|
||||
{
|
||||
_client.IterateIncoming();
|
||||
_clientHost.IterateIncoming();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes data to be sent by the socket.
|
||||
/// </summary>
|
||||
/// <param name="server">True to process data received on the server.</param>
|
||||
public override void IterateOutgoing(bool server)
|
||||
{
|
||||
if (server)
|
||||
_server.IterateOutgoing();
|
||||
else
|
||||
_client.IterateOutgoing();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReceivedData.
|
||||
/// <summary>
|
||||
/// Called when client receives data.
|
||||
/// </summary>
|
||||
public override event Action<ClientReceivedDataArgs> OnClientReceivedData;
|
||||
/// <summary>
|
||||
/// Handles a ClientReceivedDataArgs.
|
||||
/// </summary>
|
||||
/// <param name="receivedDataArgs"></param>
|
||||
public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs)
|
||||
{
|
||||
OnClientReceivedData?.Invoke(receivedDataArgs);
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when server receives data.
|
||||
/// </summary>
|
||||
public override event Action<ServerReceivedDataArgs> OnServerReceivedData;
|
||||
/// <summary>
|
||||
/// Handles a ClientReceivedDataArgs.
|
||||
/// </summary>
|
||||
/// <param name="receivedDataArgs"></param>
|
||||
public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs)
|
||||
{
|
||||
OnServerReceivedData?.Invoke(receivedDataArgs);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Sending.
|
||||
/// <summary>
|
||||
/// Sends to the server or all clients.
|
||||
/// </summary>
|
||||
/// <param name="channelId">Channel to use.</param>
|
||||
/// /// <param name="segment">Data to send.</param>
|
||||
public override void SendToServer(byte channelId, ArraySegment<byte> segment)
|
||||
{
|
||||
_client.SendToServer(channelId, segment);
|
||||
_clientHost.SendToServer(channelId, segment);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sends data to a client.
|
||||
/// </summary>
|
||||
/// <param name="channelId"></param>
|
||||
/// <param name="segment"></param>
|
||||
/// <param name="connectionId"></param>
|
||||
public override void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
|
||||
{
|
||||
//uint tick = BitConverter.ToUInt32(segment.Array, 0);
|
||||
//ushort val = BitConverter.ToUInt16(segment.Array, 4);
|
||||
//Debug.Log(tick + ", " + val);
|
||||
_server.SendToClient(channelId, segment, connectionId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Configuration.
|
||||
/// <summary>
|
||||
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetMaximumClients()
|
||||
{
|
||||
return _server.GetMaximumClients();
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public override void SetMaximumClients(int value)
|
||||
{
|
||||
_server.SetMaximumClients(value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets which address the client will connect to.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
public override void SetClientAddress(string address)
|
||||
{
|
||||
_clientAddress = address;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets which address the server will bind to.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
public override void SetServerBindAddress(string address, IPAddressType addressType)
|
||||
{
|
||||
_serverBindAddress = address;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets which port to use.
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
public override void SetPort(ushort port)
|
||||
{
|
||||
_port = port;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Start and stop.
|
||||
/// <summary>
|
||||
/// Starts the local server or client using configured settings.
|
||||
/// </summary>
|
||||
/// <param name="server">True to start server.</param>
|
||||
public override bool StartConnection(bool server)
|
||||
{
|
||||
if (server)
|
||||
return StartServer();
|
||||
else
|
||||
return StartClient(_clientAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local server or client.
|
||||
/// </summary>
|
||||
/// <param name="server">True to stop server.</param>
|
||||
public override bool StopConnection(bool server)
|
||||
{
|
||||
if (server)
|
||||
return StopServer();
|
||||
else
|
||||
return StopClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a remote client from the server, disconnecting the client.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
|
||||
/// <param name="immediately">True to abrutly stp the client socket without waiting socket thread.</param>
|
||||
public override bool StopConnection(int connectionId, bool immediately)
|
||||
{
|
||||
return StopClient(connectionId, immediately);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops both client and server.
|
||||
/// </summary>
|
||||
public override void Shutdown()
|
||||
{
|
||||
//Stops client then server connections.
|
||||
StopConnection(false);
|
||||
StopConnection(true);
|
||||
}
|
||||
|
||||
#region Privates.
|
||||
/// <summary>
|
||||
/// Starts server.
|
||||
/// </summary>
|
||||
/// <returns>True if there were no blocks. A true response does not promise a socket will or has connected.</returns>
|
||||
private bool StartServer()
|
||||
{
|
||||
if (!InitializeRelayNetworkAccess())
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"RelayNetworkAccess could not be initialized.");
|
||||
return false;
|
||||
}
|
||||
if (!IsNetworkAccessAvailable())
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError("Server network access is not available.");
|
||||
return false;
|
||||
}
|
||||
_server.ResetInvalidSocket();
|
||||
if (_server.GetLocalConnectionState() != LocalConnectionState.Stopped)
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError("Server is already running.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool clientRunning = (_client.GetLocalConnectionState() != LocalConnectionState.Stopped);
|
||||
/* If remote _client is running then stop it
|
||||
* and start the client host variant. */
|
||||
if (clientRunning)
|
||||
_client.StopConnection();
|
||||
|
||||
bool result = _server.StartConnection(_serverBindAddress, _port, _maximumClients, _peerToPeer);
|
||||
//If need to restart client.
|
||||
if (result && clientRunning)
|
||||
StartConnection(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops server.
|
||||
/// </summary>
|
||||
private bool StopServer()
|
||||
{
|
||||
if (_server != null)
|
||||
return _server.StopConnection();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the client.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns>True if there were no blocks. A true response does not promise a socket will or has connected.</returns>
|
||||
private bool StartClient(string address)
|
||||
{
|
||||
//If not acting as a host.
|
||||
if (_server.GetLocalConnectionState() == LocalConnectionState.Stopped)
|
||||
{
|
||||
if (_client.GetLocalConnectionState() != LocalConnectionState.Stopped)
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError("Client is already running.");
|
||||
return false;
|
||||
}
|
||||
//Stop client host if running.
|
||||
if (_clientHost.GetLocalConnectionState() != LocalConnectionState.Stopped)
|
||||
_clientHost.StopConnection();
|
||||
//Initialize.
|
||||
if (!InitializeRelayNetworkAccess())
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError($"RelayNetworkAccess could not be initialized.");
|
||||
return false;
|
||||
}
|
||||
if (!IsNetworkAccessAvailable())
|
||||
{
|
||||
if (NetworkManager.CanLog(LoggingType.Error))
|
||||
Debug.LogError("Client network access is not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//SetUserSteamID();
|
||||
_client.StartConnection(address, _port, _peerToPeer);
|
||||
}
|
||||
//Acting as host.
|
||||
else
|
||||
{
|
||||
_clientHost.StartConnection(_server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the client.
|
||||
/// </summary>
|
||||
private bool StopClient()
|
||||
{
|
||||
bool result = false;
|
||||
if (_client != null)
|
||||
result |= _client.StopConnection();
|
||||
if (_clientHost != null)
|
||||
result |= _clientHost.StopConnection();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a remote client on the server.
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="immediately">True to abrutly stp the client socket without waiting socket thread.</param>
|
||||
private bool StopClient(int connectionId, bool immediately)
|
||||
{
|
||||
return _server.StopConnection(connectionId);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Channels.
|
||||
/// <summary>
|
||||
/// Gets the MTU for a channel. This should take header size into consideration.
|
||||
/// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190.
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetMTU(byte channel)
|
||||
{
|
||||
if (channel >= _mtus.Length)
|
||||
{
|
||||
Debug.LogError($"Channel {channel} is out of bounds.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _mtus[channel];
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
#endif // !DISABLESTEAMWORKS
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 400639878008206409ec440eb16c3007
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9c34549a246d47439b106bb23f71dff
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1 @@
|
||||
2.1.2
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df96c994e41a8f9498b5c3313e5b4540
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user