fishy facepunch

This commit is contained in:
sebastianhampel1 2023-05-31 12:47:21 -04:00
parent 12779859cd
commit 6a28112faa
338 changed files with 2161 additions and 58 deletions

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1c10e8242689baa4795de57f9ef42ec3
guid: 4bbdfd51d4aa3a44ea6f20e7a150ed80
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -29,6 +29,7 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4393252310584637084}
m_Father: {fileID: 0}
@ -46,12 +47,13 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3}
m_Name:
m_EditorClassIdentifier:
_logging: {fileID: 0}
_spawnablePrefabs: {fileID: 11400000, guid: ec64eb18c93ab344892891f33edbf82a, type: 2}
_refreshDefaultPrefabs: 0
_runInBackground: 1
_dontDestroyOnLoad: 1
_objectPool: {fileID: 0}
_persistence: 0
_logging: {fileID: 0}
_spawnablePrefabs: {fileID: 11400000, guid: ec64eb18c93ab344892891f33edbf82a, type: 2}
--- !u!114 &934570884
MonoBehaviour:
m_ObjectHideFlags: 0
@ -64,6 +66,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3}
m_Name:
m_EditorClassIdentifier:
_useNetworkLod: 0
_levelOfDetailDistances: []
_updateHostVisibility: 1
_defaultConditions:
- {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2}
--- !u!114 &7443408887813606060
@ -88,113 +93,91 @@ PrefabInstance:
m_Modification:
m_TransformParent: {fileID: 7443408887813606049}
m_Modifications:
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_Pivot.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_Pivot.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4393252310969058995, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
- target: {fileID: 4393252310969058995, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
propertyPath: m_Name
value: NetworkHudCanvas
objectReference: {fileID: 0}
@ -202,7 +185,6 @@ PrefabInstance:
m_SourcePrefab: {fileID: 100100000, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
--- !u!224 &4393252310584637084 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0,
type: 3}
m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3}
m_PrefabInstance: {fileID: 2130063410}
m_PrefabAsset: {fileID: 0}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c11e7223b052ece4cbb66d2cf012cb04
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
2.1.1
- Added deinitializer to FishyFacepunch.
2.1.0
- Fixed ClientId 32767 not found error when being used with Multipass.
2.0.0
- FishNetworking 2.0.0 support.
1.3.1
- FishNetworking 1.3.1 Support
1.3.0
- Removed obsolete methods
- FishNetworking 1.3.0 Support
1.2.0
- Removed GC (Full Credits to Punfish - Thank you)
- Removed obsolete method
1.1.1
- FishNetworking 0.1.5.Nightly.10 Support
1.1.0
- Support for client hosted games (needs two separated steam accounts, to test) - Thank to Punfish
- Organized folder structure
- Removed ChannelData
1.0.0
- Initial release.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cf7010bd3b524434c8cefe4534247875
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fc9c8228eea0edc4eb3428086998d364
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,92 @@
using System.Collections;
using System.Collections.Generic;
namespace FishyFacepunch
{
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);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 336587b490f535a4aa3b1618c9ebac55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,126 @@
#if !FishyFacepunch
using FishNet.Transporting;
using FishyFacepunch.Server;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace FishyFacepunch.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));
packet.Dispose();
}
}
/// <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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 112bec8b5281832468d85543d0b6e31c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,238 @@
#if !FishyFacepunch
using FishNet.Managing.Logging;
using FishNet.Transporting;
using Steamworks;
using Steamworks.Data;
using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace FishyFacepunch.Client
{
public class ClientSocket : CommonSocket
{
#region Private.
/// <summary>
/// SteamId for host.
/// </summary>
private SteamId _hostSteamID = 0;
/// <summary>
/// Socket to use.
/// </summary>
private Connection HostConnection => HostConnectionManager.Connection;
/// <summary>
/// Use the internal connection manager from steam.
/// </summary>
private FishyConnectionManager HostConnectionManager;
/// <summary>
/// Task used to check for timeout.
/// </summary>
private CancellationTokenSource cancelToken;
private TaskCompletionSource<Task> connectedComplete;
private TimeSpan ConnectionTimeout;
/// <summary>
/// Task used to check for timeout.
/// </summary>
private bool _Error = false;
#endregion
/// <summary>
/// Initializes this for use.
/// </summary>
/// <param name="t"></param>
internal override void Initialize(Transport t)
{
base.Initialize(t);
}
/// <summary>
/// Starts the client connection.
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="channelsCount"></param>
/// <param name="pollTime"></param>
internal async void StartConnection(string address, ushort port)
{
cancelToken = new CancellationTokenSource();
SteamNetworkingSockets.OnConnectionStatusChanged += OnConnectionStatusChanged;
ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, base.Transport.GetTimeout(false)));
SetLocalConnectionState(LocalConnectionState.Starting, false);
try
{
if (SteamClient.IsValid)
{
connectedComplete = new TaskCompletionSource<Task>();
if (!IsValidAddress(address))
{
_hostSteamID = UInt64.Parse(address);
HostConnectionManager = SteamNetworkingSockets.ConnectRelay<FishyConnectionManager>(_hostSteamID);
}
else
{
HostConnectionManager = SteamNetworkingSockets.ConnectNormal<FishyConnectionManager>(NetAddress.From(address, port));
}
HostConnectionManager.ForwardMessage = OnMessageReceived;
Task connectedCompleteTask = connectedComplete.Task;
Task timeOutTask = Task.Delay(ConnectionTimeout, cancelToken.Token);
if (await Task.WhenAny(connectedCompleteTask, timeOutTask) != connectedCompleteTask)
{
if (cancelToken.IsCancellationRequested)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError($"The connection attempt was cancelled.");
}
else if (timeOutTask.IsCompleted)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError($"Connection to {address} timed out.");
StopConnection();
}
SetLocalConnectionState(LocalConnectionState.Stopped, false);
}
}
else
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError("SteamWorks not initialized");
SetLocalConnectionState(LocalConnectionState.Stopped, false);
}
}
catch (FormatException)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError($"Connection string was not in the right format. Did you enter a SteamId?");
SetLocalConnectionState(LocalConnectionState.Stopped, false);
_Error = true;
}
catch (Exception ex)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError(ex.Message);
SetLocalConnectionState(LocalConnectionState.Stopped, false);
_Error = true;
}
finally
{
if (_Error)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Error))
Debug.LogError("Connection failed.");
SetLocalConnectionState(LocalConnectionState.Stopped, false);
}
}
}
/// <summary>
/// Called when local connection state changes.
/// </summary>
private void OnConnectionStatusChanged(Connection conn, ConnectionInfo info)
{
if (info.State == ConnectionState.Connected)
{
SetLocalConnectionState(LocalConnectionState.Started, false);
connectedComplete.SetResult(connectedComplete.Task);
}
else if (info.State == ConnectionState.ClosedByPeer || info.State == ConnectionState.ProblemDetectedLocally)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Connection was closed by peer, {info.EndReason}");
StopConnection();
}
else
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Connection state changed: {info.State.ToString()} - {info.EndReason}");
}
}
/// <summary>
/// Stops the local socket.
/// </summary>
internal bool StopConnection()
{
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped || base.GetLocalConnectionState() == LocalConnectionState.Stopping)
return false;
SetLocalConnectionState(LocalConnectionState.Stopping, false);
cancelToken?.Cancel();
//Reset callback.
SteamNetworkingSockets.OnConnectionStatusChanged -= OnConnectionStatusChanged;
if (HostConnectionManager != null)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log("Sending Disconnect message");
HostConnection.Close(false, 0, "Graceful disconnect");
HostConnectionManager = null;
}
SetLocalConnectionState(LocalConnectionState.Stopped, false);
return true;
}
/// <summary>
/// Iterations data received.
/// </summary>
internal void IterateIncoming()
{
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
return;
HostConnectionManager.Receive(MAX_MESSAGES);
}
private void OnMessageReceived(IntPtr dataPtr, int size)
{
(byte[] data, int ch) = ProcessMessage(dataPtr, size);
base.Transport.HandleClientReceivedDataArgs(new ClientReceivedDataArgs(new ArraySegment<byte>(data), (Channel)ch, 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;
Result res = base.Send(HostConnection, segment, channelId);
if (res == Result.NoConnection || res == Result.InvalidParam)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Connection to server was lost.");
StopConnection();
}
else if (res != Result.OK)
{
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;
HostConnection.Flush();
}
}
}
#endif // !DISABLESTEAMWORKS

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5c258174868ee54cb570bef71c82404
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,177 @@
#if !FishyFacepunch
using FishNet.Managing.Logging;
using FishNet.Transporting;
using Steamworks;
using Steamworks.Data;
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;
using UnityEngine;
namespace FishyFacepunch
{
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 asServer)
{
//If state hasn't changed.
if (connectionState == _connectionState)
return;
_connectionState = connectionState;
if (asServer)
Transport.HandleServerConnectionState(new ServerConnectionStateArgs(connectionState, Transport.Index));
else
Transport.HandleClientConnectionState(new ClientConnectionStateArgs(connectionState, Transport.Index));
}
#endregion
#region Protected.
/// <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
internal void ClearQueue(Queue<LocalPacket> lpq)
{
while (lpq.Count > 0)
{
LocalPacket lp = lpq.Dequeue();
lp.Dispose();
}
}
/// <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>
/// Check if this is a valid address to start a p2p or c2s session.
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
protected bool IsValidAddress(string address)
{
//If address is required then make sure it can be parsed.
if (!string.IsNullOrEmpty(address))
{
if (!IPAddress.TryParse(address, out IPAddress result))
{
return false;
}
else
{
return true;
}
}
else
{
return false;
}
}
/// <summary>
/// Sends data over the steamConnection.
/// </summary>
/// <param name="steamConnection"></param>
/// <param name="segment"></param>
/// <param name="channelId"></param>
/// <returns></returns>
protected Result Send(Connection conn, 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;
SendType sendFlag = (channelId == (byte)Channel.Unreliable) ? SendType.Unreliable : SendType.Reliable;
Result result = conn.SendMessage(pData, segment.Count, sendFlag);
if (result != Result.OK)
{
if (Transport.NetworkManager.CanLog(LoggingType.Warning))
Debug.LogWarning($"Send issue: {result}");
}
pinnedArray.Free();
return result;
}
/// <summary>
/// Returns a message from the steam network.
/// </summary>
/// <param name="ptr"></param>
/// <param name="buffer"></param>
/// <returns></returns>
protected (byte[], int) ProcessMessage(IntPtr ptrs, int size)
{
byte[] managedArray = new byte[size];
Marshal.Copy(ptrs, managedArray, 0, size);
int channel = managedArray[managedArray.Length - 1];
Array.Resize(ref managedArray, managedArray.Length - 1);
return (managedArray, channel);
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3bc85d49720cf5844bf19af677e4f5f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using Steamworks;
using System;
public class FishyConnectionManager : ConnectionManager
{
public Action<IntPtr, int> ForwardMessage;
public override void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel)
{
ForwardMessage(data, size);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4200e3c1bd9ce214baf0cc323ef78429
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using Steamworks;
using Steamworks.Data;
using System;
public class FishySocketManager : SocketManager
{
public Action<Connection, IntPtr, int> ForwardMessage;
public override void OnMessage(Connection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel)
{
ForwardMessage(connection, data, size);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1180bddcdfeaad44e80261cedd2b806d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using FishNet.Utility.Performance;
using System;
namespace FishyFacepunch
{
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;
}
public void Dispose()
{
if (Data != null)
ByteArrayPool.Store(Data);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1e0f33bdfade44489a2decb96d23c72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,425 @@
#if !FishyFacepunch
using FishNet.Managing.Logging;
using FishNet.Transporting;
using FishyFacepunch.Client;
using Steamworks;
using Steamworks.Data;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FishyFacepunch.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<Connection, int> _steamConnections = new BidirectionalDictionary<Connection, int>();
/// <summary>
/// SteamIds for ConnectionIds.
/// </summary>
private BidirectionalDictionary<SteamId, int> _steamIds = new BidirectionalDictionary<SteamId, 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 FishySocketManager _socket;
/// <summary>
/// ConnectionIds which can be reused.
/// </summary>
private Queue<int> _cachedConnectionIds = new Queue<int>();
/// <summary>
/// Contains state of the client host. True is started, false is stopped.
/// </summary>
private bool _clientHostStarted = false;
/// <summary>
/// Packets received from local client.
/// </summary>
private Queue<LocalPacket> _clientHostIncoming = new Queue<LocalPacket>();
/// <summary>
/// Socket for client host. Will be null if not being used.
/// </summary>
private ClientHostSocket _clientHost;
#endregion
/// <summary>
/// Initializes this for use.
/// </summary>
/// <param name="t"></param>
internal override void Initialize(Transport t)
{
base.Initialize(t);
}
/// <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 == default)
base.SetLocalConnectionState(LocalConnectionState.Stopped, true);
}
/// <summary>
/// Starts the server.
/// </summary>
internal bool StartConnection(string address, ushort port, int maximumClients)
{
SteamNetworkingSockets.OnConnectionStatusChanged += OnRemoteConnectionState;
SetMaximumClients(maximumClients);
_nextConnectionId = 0;
_cachedConnectionIds.Clear();
base.SetLocalConnectionState(LocalConnectionState.Starting, true);
if (_socket != null)
{
_socket?.Close();
_socket = default;
}
#if UNITY_SERVER
_socket = SteamNetworkingSockets.CreateNormalSocket<FishySocketManager>(NetAddress.From(address, port));
#else
_socket = SteamNetworkingSockets.CreateRelaySocket<FishySocketManager>();
#endif
_socket.ForwardMessage = OnMessageReceived;
base.SetLocalConnectionState(LocalConnectionState.Started, true);
return true;
}
/// <summary>
/// Stops the local socket.
/// </summary>
internal bool StopConnection()
{
if (base.GetLocalConnectionState() == LocalConnectionState.Stopped)
return false;
base.SetLocalConnectionState(LocalConnectionState.Stopping, true);
if (_socket != null)
{
SteamNetworkingSockets.OnConnectionStatusChanged -= OnRemoteConnectionState;
_socket?.Close();
_socket = default;
}
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 == FishyFacepunch.CLIENT_HOST_ID)
{
if (_clientHost != null)
{
_clientHost.StopConnection();
return true;
}
else
{
return false;
}
}
else
{
if (_steamConnections.Second.TryGetValue(connectionId, out Connection 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, Connection socket)
{
socket.Close(false, 0, "Graceful disconnect");
_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>
private void OnRemoteConnectionState(Connection conn, ConnectionInfo info)
{
ulong clientSteamID = info.Identity.SteamId;
if (info.State == ConnectionState.Connecting)
{
if (_steamConnections.Count >= _maximumClients)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Incoming connection {clientSteamID} was rejected because would exceed the maximum connection count.");
conn.Close(false, 0, "Max Connection Count");
return;
}
Result res;
if ((res = conn.Accept()) == Result.OK)
{
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 (info.State == ConnectionState.Connected)
{
int connectionId = (_cachedConnectionIds.Count > 0) ? _cachedConnectionIds.Dequeue() : _nextConnectionId++;
_steamConnections.Add(conn, connectionId);
_steamIds.Add(clientSteamID, 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 (info.State == ConnectionState.ClosedByPeer || info.State == ConnectionState.ProblemDetectedLocally)
{
if (_steamConnections.TryGetValue(conn, out int connId))
{
StopConnection(connId, conn);
}
}
else
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Connection {clientSteamID} state changed: {info.State.ToString()}");
}
}
/// <summary>
/// Allows for Outgoing queue to be iterated.
/// </summary>
internal void IterateOutgoing()
{
if (base.GetLocalConnectionState() != LocalConnectionState.Started)
return;
foreach (Connection conn in _steamConnections.FirstTypes)
{
conn.Flush();
}
}
/// <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, FishyFacepunch.CLIENT_HOST_ID, Transport.Index));
packet.Dispose();
}
_socket.Receive(MAX_MESSAGES);
}
private void OnMessageReceived(Connection conn, IntPtr dataPtr, int size)
{
(byte[] data, int ch) = ProcessMessage(dataPtr, size);
base.Transport.HandleServerReceivedDataArgs(new ServerReceivedDataArgs(new ArraySegment<byte>(data), (Channel)ch, _steamConnections[conn], 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 == FishyFacepunch.CLIENT_HOST_ID)
{
if (_clientHost != null)
{
LocalPacket packet = new LocalPacket(segment, channelId);
_clientHost.ReceivedFromLocalServer(packet);
}
return;
}
if (_steamConnections.TryGetValue(connectionId, out Connection steamConn))
{
Result res = base.Send(steamConn, segment, channelId);
if (res == Result.NoConnection || res == Result.InvalidParam)
{
if (base.Transport.NetworkManager.CanLog(LoggingType.Common))
Debug.Log($"Connection to {connectionId} was lost.");
StopConnection(connectionId, steamConn);
}
else if (res != Result.OK)
{
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 SteamId 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>
/// <param name="value"></param>
internal void SetMaximumClients(int value)
{
_maximumClients = Math.Min(value, FishyFacepunch.CLIENT_HOST_ID - 1);
}
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 stops.
/// </summary>
internal void OnClientHostState(bool started)
{
_clientHostStarted = started;
FishyFacepunch ff = (FishyFacepunch)base.Transport;
SteamId steamId = new SteamId()
{
Value = ff.LocalUserSteamID
};
//If not started flush incoming from local client.
if (!started && _clientHostStarted)
{
base.ClearQueue(_clientHostIncoming);
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, FishyFacepunch.CLIENT_HOST_ID, Transport.Index));
_steamIds.Remove(steamId);
}
//If started.
else if (started)
{
_steamIds[steamId] = FishyFacepunch.CLIENT_HOST_ID;
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Started, FishyFacepunch.CLIENT_HOST_ID, Transport.Index));
}
_clientHostStarted = started;
}
/// <summary>
/// Queues a received packet from the local client.
/// </summary>
internal void ReceivedFromClientHost(LocalPacket packet)
{
if (!_clientHostStarted)
{
packet.Dispose();
return;
}
_clientHostIncoming.Enqueue(packet);
}
#endregion
}
}
#endif // !DISABLESTEAMWORKS

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0f5d2e6e0da6a244a3fa589e4f6bba2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,518 @@
#if !FishyFacepunch
using FishNet.Managing;
using FishNet.Transporting;
using Steamworks;
using System;
using UnityEngine;
namespace FishyFacepunch
{
public class FishyFacepunch : Transport
{
~FishyFacepunch()
{
Shutdown();
}
#region Public.
[System.NonSerialized]
public ulong LocalUserSteamID;
#endregion
#region Serialized.
/// <summary>
/// Steam application Id.
/// </summary>
[Tooltip("Steam application Id.")]
[SerializeField]
private uint _steamAppID = 480;
[Header("Server")]
/// <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 = 27015;
/// <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 = 16;
[Header("Client")]
/// <summary>
/// Address client should connect to.
/// </summary>
[Tooltip("Address client should connect to.")]
[SerializeField]
private string _clientAddress = string.Empty;
[Tooltip("Timeout for connecting in seconds.")]
[SerializeField]
private int _timeout = 25;
#endregion
#region Private.
/// <summary>
/// MTUs for each channel.
/// </summary>
private int[] _mtus;
/// <summary>
/// Client for the transport.
/// </summary>
private Client.ClientSocket _client = new Client.ClientSocket();
/// <summary>
/// Client when acting as host.
/// </summary>
private Client.ClientHostSocket _clientHost = new Client.ClientHostSocket();
/// <summary>
/// Server for the transport.
/// </summary>
private Server.ServerSocket _server = new Server.ServerSocket();
#endregion
#region Const.
/// <summary>
/// Id to use for client when acting as host.
/// </summary>
internal const int CLIENT_HOST_ID = short.MaxValue;
#endregion
#region Initialization and Unity.
public override void Initialize(NetworkManager networkManager, int transportIndex)
{
base.Initialize(networkManager, transportIndex);
CreateChannelData();
#if !UNITY_SERVER
SteamClient.Init(_steamAppID, true);
SteamNetworking.AllowP2PPacketRelay(true);
#endif
_clientHost.Initialize(this);
_client.Initialize(this);
_server.Initialize(this);
}
private void OnDestroy()
{
Shutdown();
}
private void Update()
{
_clientHost.CheckSetStarted();
}
#endregion
#region Setup.
/// <summary>
/// Creates ChannelData for the transport.
/// </summary>
private void CreateChannelData()
{
_mtus = new int[2]
{
1048576,
1200
};
}
/// <summary>
/// Tries to initialize steam network access.
/// </summary>
private void InitializeRelayNetworkAccess()
{
#if !UNITY_SERVER
SteamNetworkingUtils.InitRelayNetworkAccess();
LocalUserSteamID = Steamworks.SteamClient.SteamId.Value;
#endif
}
#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)
{
_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;
}
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;
}
/// <summary>
/// Returns the adjusted timeout as float
/// </summary>
/// <param name="asServer"></param>
public override float GetTimeout(bool asServer)
{
return _timeout;
}
#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)
{
Debug.Log("StartConnection fishy server: " + 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()
{
bool clientRunning = false;
#if !UNITY_SERVER
if (!SteamClient.IsValid)
{
Debug.LogError("Steam Facepunch not initialized. Server could not be started.");
return false;
}
//if (_client.GetLocalConnectionState() != LocalConnectionState.Stopped)
//{
// Debug.LogError("Server cannot run while client is running.");
// return false;
//}
clientRunning = (_client.GetLocalConnectionState() != LocalConnectionState.Stopped);
/* If remote _client is running then stop it
* and start the client host variant. */
if (clientRunning)
_client.StopConnection();
#endif
_server.ResetInvalidSocket();
if (_server.GetLocalConnectionState() != LocalConnectionState.Stopped)
{
Debug.LogError("Server is already running.");
return false;
}
InitializeRelayNetworkAccess();
bool result = _server.StartConnection(_serverBindAddress, _port, _maximumClients);
//If need to restart client.
if (result && clientRunning)
StartConnection(false);
return result;
}
/// <summary>
/// Stops server.
/// </summary>
private bool StopServer()
{
return _server.StopConnection();
}
/// <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 (!SteamClient.IsValid)
{
Debug.LogError("Steam Facepunch not initialized. Client could not be started.");
return false;
}
//If not acting as a host.
if (_server.GetLocalConnectionState() == LocalConnectionState.Stopped)
{
if (_client.GetLocalConnectionState() != LocalConnectionState.Stopped)
{
Debug.LogError("Client is already running.");
return false;
}
//Stop client host if running.
if (_clientHost.GetLocalConnectionState() != LocalConnectionState.Stopped)
_clientHost.StopConnection();
//Initialize.
InitializeRelayNetworkAccess();
_client.StartConnection(address, _port);
}
//Acting as host.
else
{
_clientHost.StartConnection(_server);
}
return true;
}
/// <summary>
/// Stops the client.
/// </summary>
private bool StopClient()
{
bool result = false;
result |= _client.StopConnection();
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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9da90c29836670c49809661c5c09cd5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,144 @@
using FishNet;
using FishNet.Transporting;
using Steamworks;
using System;
using System.Net;
using UnityEngine;
public class SteamManager : MonoBehaviour
{
#region Serialized.
[Header("Steam Settings")]
/// <summary>
/// Steam application Id.
/// </summary>
[Tooltip("Steam application Id.")]
[SerializeField]
private uint _steamAppID = 480;
/// <summary>
/// SSteam Mod Directory.
/// </summary>
[Tooltip("Steam Mod Directory.")]
[SerializeField]
private string _modDir = string.Empty;
/// <summary>
/// Steam Game Description.
/// </summary>
[Tooltip("Steam Game Description.")]
[SerializeField]
private string _gameDesc = string.Empty;
/// <summary>
/// Steam version.
/// </summary>
[Tooltip("Steam version.")]
[SerializeField]
private string _version = string.Empty;
[Header("Server Settings")]
/// <summary>
/// Servername.
/// </summary>
[Tooltip("Server Name")]
[SerializeField]
private string _serverName = string.Empty;
/// <summary>
/// Server Password.
/// </summary>
[Tooltip("Server Password.")]
[SerializeField]
private string _password = string.Empty;
/// <summary>
/// Steam Server Query Port.
/// </summary>
[Tooltip("Server Query Port.")]
[SerializeField]
private ushort _queryPort = 27016;
/// <summary>
/// Server VAC Secure.
/// </summary>
[Tooltip("Server VAC Secure.")]
[SerializeField]
private bool _vac = true;
/// <summary>
/// Server as Dedicated Server.
/// </summary>
[Tooltip("Server as Dedicated Server.")]
[SerializeField]
private bool _ds = true;
#endregion
void Start()
{
#if UNITY_SERVER
InstanceFinder.ServerManager.StartConnection();
InstanceFinder.ServerManager.OnServerConnectionState += OnServerConnectionState;
#endif
}
private void OnServerConnectionState(FishNet.Transporting.ServerConnectionStateArgs state)
{
if (state.ConnectionState == FishNet.Transporting.LocalConnectionState.Started)
{
var serverInit = new SteamServerInit(_modDir, _gameDesc)
{
IpAddress = IPAddress.Parse(InstanceFinder.TransportManager.Transport.GetServerBindAddress(IPAddressType.IPv4)),
GamePort = InstanceFinder.TransportManager.Transport.GetPort(),
QueryPort = _queryPort,
Secure = _vac,
DedicatedServer = _ds,
VersionString = _version,
};
serverInit.WithRandomSteamPort();
try
{
SteamServer.Init(1280590, serverInit, true);
SteamServer.ServerName = _serverName;
SteamServer.MaxPlayers = InstanceFinder.TransportManager.Transport.GetMaximumClients();
SteamServer.Passworded = !string.IsNullOrEmpty(_password);
SteamServer.DedicatedServer = _ds;
SteamServer.AutomaticHeartbeats = true;
SteamServer.LogOnAnonymous();
SteamServer.OnSteamServersConnected += OnSteamServersConnected;
SteamServer.OnSteamServersDisconnected += OnSteamServersDisconnected;
SteamServer.OnSteamServerConnectFailure += OnSteamServerConnectFailure;
if (!SteamServer.IsValid)
{
Debug.LogWarning("Couldn't initialize server");
return;
}
}
catch (Exception ex)
{
Debug.LogError("Couldn't initialize Steam server (" + ex.Message + ")");
Application.Quit();
}
}
}
private void OnSteamServersConnected()
{
Debug.Log("Dedicated Server connected to Steam successfully");
}
private void OnSteamServerConnectFailure(Result result, bool stilltrying)
{
Debug.Log("Dedicated Server failed to connect to Steam");
}
private void OnSteamServersDisconnected(Result result)
{
Debug.Log("Dedicated Server got logged out of Steam");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b5f291225ca7c4459871c80128b5172
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
2.1.1

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1db9d679a4e8b8d4c9d5ab6cb2d3ad0f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -230,7 +230,7 @@ Material:
- _StencilWriteMaskDepth: 8
- _StencilWriteMaskDistortionVec: 4
- _StencilWriteMaskGBuffer: 14
- _StencilWriteMaskMV: 40
- _StencilWriteMaskMV: 42
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0

View File

@ -206,7 +206,7 @@ Material:
- _StencilWriteMask: 6
- _StencilWriteMaskDepth: 8
- _StencilWriteMaskGBuffer: 14
- _StencilWriteMaskMV: 40
- _StencilWriteMaskMV: 42
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0

View File

@ -206,7 +206,7 @@ Material:
- _StencilWriteMask: 6
- _StencilWriteMaskDepth: 8
- _StencilWriteMaskGBuffer: 14
- _StencilWriteMaskMV: 40
- _StencilWriteMaskMV: 42
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 464 B

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 670 B

View File

Before

Width:  |  Height:  |  Size: 978 B

After

Width:  |  Height:  |  Size: 978 B

Some files were not shown because too many files have changed in this diff Show More