mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-02-10 12:39:53 +00:00
Merge branch 'dev' into echoes-of-the-eye
This commit is contained in:
commit
00aa0c8037
81
FizzyFacepunch/BidirectionalDictionary.cs
Normal file
81
FizzyFacepunch/BidirectionalDictionary.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class BidirectionalDictionary<T1, T2> : IEnumerable
|
||||
{
|
||||
private Dictionary<T1, T2> t1ToT2Dict = new Dictionary<T1, T2>();
|
||||
private Dictionary<T2, T1> t2ToT1Dict = new Dictionary<T2, T1>();
|
||||
|
||||
public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
|
||||
public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys;
|
||||
|
||||
public IEnumerator GetEnumerator() => t1ToT2Dict.GetEnumerator();
|
||||
|
||||
public int Count => t1ToT2Dict.Count;
|
||||
|
||||
public void Add(T1 key, T2 value)
|
||||
{
|
||||
t1ToT2Dict[key] = value;
|
||||
t2ToT1Dict[value] = key;
|
||||
}
|
||||
|
||||
public void Add(T2 key, T1 value)
|
||||
{
|
||||
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
|
||||
{
|
||||
t2ToT1Dict[key] = value;
|
||||
t1ToT2Dict[value] = key;
|
||||
}
|
||||
}
|
||||
|
||||
public T2 this[T1 key]
|
||||
{
|
||||
get => t1ToT2Dict[key];
|
||||
set
|
||||
{
|
||||
t1ToT2Dict[key] = value;
|
||||
t2ToT1Dict[value] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
FizzyFacepunch/FizzyConnectionManager.cs
Normal file
12
FizzyFacepunch/FizzyConnectionManager.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
|
||||
public class FizzyConnectionManager : ConnectionManager
|
||||
{
|
||||
public Action<IntPtr, int> ForwardMessage;
|
||||
|
||||
public override void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel)
|
||||
{
|
||||
ForwardMessage(data, size);
|
||||
}
|
||||
}
|
303
FizzyFacepunch/FizzyFacepunch.cs
Normal file
303
FizzyFacepunch/FizzyFacepunch.cs
Normal file
@ -0,0 +1,303 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
[HelpURL("https://github.com/Chykary/FizzyFacepunch")]
|
||||
public class FizzyFacepunch : Transport
|
||||
{
|
||||
private const string STEAM_SCHEME = "steam";
|
||||
|
||||
private static IClient client;
|
||||
private static IServer server;
|
||||
|
||||
[SerializeField]
|
||||
public P2PSend[] Channels = new P2PSend[2] { P2PSend.Reliable, P2PSend.UnreliableNoDelay };
|
||||
|
||||
[Tooltip("Timeout for connecting in seconds.")]
|
||||
public int Timeout = 25;
|
||||
[Tooltip("The Steam ID for your application.")]
|
||||
public string SteamAppID = "480";
|
||||
[Tooltip("Allow or disallow P2P connections to fall back to being relayed through the Steam servers if a direct connection or NAT-traversal cannot be established.")]
|
||||
public bool AllowSteamRelay = true;
|
||||
|
||||
[Tooltip("Use SteamSockets instead of the (deprecated) SteamNetworking. This will always use Relay.")]
|
||||
public bool UseNextGenSteamNetworking = true;
|
||||
|
||||
[Tooltip("Check this if you want the transport to initialise Facepunch.")]
|
||||
public bool InitFacepunch = true;
|
||||
|
||||
[Header("Info")]
|
||||
[Tooltip("This will display your Steam User ID when you start or connect to a server.")]
|
||||
public ulong SteamUserID;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
const string fileName = "steam_appid.txt";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
string content = File.ReadAllText(fileName);
|
||||
if (content != SteamAppID)
|
||||
{
|
||||
File.WriteAllText(fileName, SteamAppID.ToString());
|
||||
Debug.Log($"Updating {fileName}. Previous: {content}, new SteamAppID {SteamAppID}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText(fileName, SteamAppID.ToString());
|
||||
Debug.Log($"New {fileName} written with SteamAppID {SteamAppID}");
|
||||
}
|
||||
|
||||
Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for FizzySteamworks.");
|
||||
|
||||
if (InitFacepunch)
|
||||
{
|
||||
SteamClient.Init(uint.Parse(SteamAppID), true);
|
||||
}
|
||||
|
||||
Invoke(nameof(FetchSteamID), 1f);
|
||||
}
|
||||
|
||||
public string GetSteamID() => SteamClient.SteamId.ToString();
|
||||
|
||||
public override void ClientEarlyUpdate()
|
||||
{
|
||||
if (enabled && client != null && !client.Error)
|
||||
{
|
||||
client?.ReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ServerEarlyUpdate()
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
server?.ReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientLateUpdate()
|
||||
{
|
||||
if (enabled && client != null && !client.Error)
|
||||
{
|
||||
client?.FlushData();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ServerLateUpdate()
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
server?.FlushData();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ClientConnected() => ClientActive() && client.Connected;
|
||||
public override void ClientConnect(string address)
|
||||
{
|
||||
if (!SteamClient.IsValid)
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized. Client could not be started.");
|
||||
OnClientDisconnected.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
FetchSteamID();
|
||||
|
||||
if (ServerActive())
|
||||
{
|
||||
Debug.LogError("Transport already running as server!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ClientActive() || client.Error)
|
||||
{
|
||||
if (UseNextGenSteamNetworking)
|
||||
{
|
||||
Debug.Log($"Starting client [SteamSockets], target address {address}.");
|
||||
client = NextClient.CreateClient(this, address);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Starting client [DEPRECATED SteamNetworking], target address {address}. Relay enabled: {AllowSteamRelay}");
|
||||
SteamNetworking.AllowP2PPacketRelay(AllowSteamRelay);
|
||||
client = LegacyClient.CreateClient(this, address);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Client already running!");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientConnect(Uri uri)
|
||||
{
|
||||
if (uri.Scheme != STEAM_SCHEME)
|
||||
throw new ArgumentException($"Invalid url {uri}, use {STEAM_SCHEME}://SteamID instead", nameof(uri));
|
||||
|
||||
ClientConnect(uri.Host);
|
||||
}
|
||||
|
||||
public override void ClientSend(ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
byte[] data = new byte[segment.Count];
|
||||
Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count);
|
||||
client.Send(data, channelId);
|
||||
}
|
||||
|
||||
public override void ClientDisconnect()
|
||||
{
|
||||
if (ClientActive())
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
public bool ClientActive() => client != null;
|
||||
|
||||
|
||||
public override bool ServerActive() => server != null;
|
||||
public override void ServerStart()
|
||||
{
|
||||
if (!SteamClient.IsValid)
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized. Server could not be started.");
|
||||
return;
|
||||
}
|
||||
|
||||
FetchSteamID();
|
||||
|
||||
if (ClientActive())
|
||||
{
|
||||
Debug.LogError("Transport already running as client!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ServerActive())
|
||||
{
|
||||
if (UseNextGenSteamNetworking)
|
||||
{
|
||||
Debug.Log($"Starting server [SteamSockets].");
|
||||
server = NextServer.CreateServer(this, NetworkManager.singleton.maxConnections);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Starting server [DEPRECATED SteamNetworking]. Relay enabled: {AllowSteamRelay}");
|
||||
SteamNetworking.AllowP2PPacketRelay(AllowSteamRelay);
|
||||
server = LegacyServer.CreateServer(this, NetworkManager.singleton.maxConnections);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Server already started!");
|
||||
}
|
||||
}
|
||||
|
||||
public override Uri ServerUri()
|
||||
{
|
||||
var steamBuilder = new UriBuilder
|
||||
{
|
||||
Scheme = STEAM_SCHEME,
|
||||
Host = SteamClient.SteamId.Value.ToString()
|
||||
};
|
||||
|
||||
return steamBuilder.Uri;
|
||||
}
|
||||
|
||||
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
byte[] data = new byte[segment.Count];
|
||||
Array.Copy(segment.Array, segment.Offset, data, 0, segment.Count);
|
||||
server.Send(connectionId, data, channelId);
|
||||
}
|
||||
}
|
||||
public override void ServerDisconnect(int connectionId)
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
server.Disconnect(connectionId);
|
||||
}
|
||||
}
|
||||
public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty;
|
||||
public override void ServerStop()
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (server != null)
|
||||
{
|
||||
server.Shutdown();
|
||||
server = null;
|
||||
Debug.Log("Transport shut down - was server.");
|
||||
}
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
client.Disconnect();
|
||||
client = null;
|
||||
Debug.Log("Transport shut down - was client.");
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetMaxPacketSize(int channelId)
|
||||
{
|
||||
if (channelId >= Channels.Length)
|
||||
{
|
||||
Debug.LogError("Channel Id exceeded configured channels! Please configure more channels.");
|
||||
return 1200;
|
||||
}
|
||||
|
||||
switch (Channels[channelId])
|
||||
{
|
||||
case P2PSend.Unreliable:
|
||||
case P2PSend.UnreliableNoDelay:
|
||||
return 1200;
|
||||
case P2PSend.Reliable:
|
||||
case P2PSend.ReliableWithBuffering:
|
||||
return 1048576;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Available()
|
||||
{
|
||||
try
|
||||
{
|
||||
return SteamClient.IsValid;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void FetchSteamID()
|
||||
{
|
||||
if (SteamClient.IsValid)
|
||||
{
|
||||
if (UseNextGenSteamNetworking)
|
||||
{
|
||||
SteamNetworkingUtils.InitRelayNetworkAccess();
|
||||
}
|
||||
|
||||
SteamUserID = SteamClient.SteamId;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
9
FizzyFacepunch/FizzyFacepunch.csproj
Normal file
9
FizzyFacepunch/FizzyFacepunch.csproj
Normal file
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Facepunch.Steamworks" Version="2.3.3" />
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>$(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="../Mirror/*.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
13
FizzyFacepunch/FizzySocketManager.cs
Normal file
13
FizzyFacepunch/FizzySocketManager.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Steamworks;
|
||||
using Steamworks.Data;
|
||||
using System;
|
||||
|
||||
public class FizzySocketManager : 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);
|
||||
}
|
||||
}
|
12
FizzyFacepunch/IClient.cs
Normal file
12
FizzyFacepunch/IClient.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public interface IClient
|
||||
{
|
||||
bool Connected { get; }
|
||||
bool Error { get; }
|
||||
void ReceiveData();
|
||||
void Disconnect();
|
||||
void FlushData();
|
||||
void Send(byte[] data, int channelId);
|
||||
}
|
||||
}
|
12
FizzyFacepunch/IServer.cs
Normal file
12
FizzyFacepunch/IServer.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public interface IServer
|
||||
{
|
||||
void ReceiveData();
|
||||
void Send(int connectionId, byte[] data, int channelId);
|
||||
void Disconnect(int connectionId);
|
||||
void FlushData();
|
||||
string ServerGetClientAddress(int connectionId);
|
||||
void Shutdown();
|
||||
}
|
||||
}
|
164
FizzyFacepunch/LegacyClient.cs
Normal file
164
FizzyFacepunch/LegacyClient.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class LegacyClient : LegacyCommon, IClient
|
||||
{
|
||||
public bool Error { get; private set; }
|
||||
public bool Connected { get; private set; }
|
||||
|
||||
private event Action<byte[], int> OnReceivedData;
|
||||
private event Action OnConnected;
|
||||
private event Action OnDisconnected;
|
||||
|
||||
private TimeSpan ConnectionTimeout;
|
||||
|
||||
private SteamId hostSteamID = 0;
|
||||
private TaskCompletionSource<Task> connectedComplete;
|
||||
private CancellationTokenSource cancelToken;
|
||||
|
||||
private LegacyClient(FizzyFacepunch transport) : base(transport)
|
||||
{
|
||||
ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.Timeout));
|
||||
}
|
||||
|
||||
public static LegacyClient CreateClient(FizzyFacepunch transport, string host)
|
||||
{
|
||||
LegacyClient c = new LegacyClient(transport);
|
||||
|
||||
c.OnConnected += () => transport.OnClientConnected.Invoke();
|
||||
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
|
||||
c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel);
|
||||
|
||||
if (SteamClient.IsValid)
|
||||
{
|
||||
c.Connect(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized.");
|
||||
c.OnConnectionFailed(new SteamId());
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
private async void Connect(string host)
|
||||
{
|
||||
cancelToken = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
hostSteamID = ulong.Parse(host);
|
||||
connectedComplete = new TaskCompletionSource<Task>();
|
||||
|
||||
OnConnected += SetConnectedComplete;
|
||||
SendInternal(hostSteamID, InternalMessages.CONNECT);
|
||||
Task connectedCompleteTask = connectedComplete.Task;
|
||||
Task timeOutTask = Task.Delay(ConnectionTimeout, cancelToken.Token);
|
||||
|
||||
if (await Task.WhenAny(connectedCompleteTask, timeOutTask) != connectedCompleteTask)
|
||||
{
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
{
|
||||
Debug.LogError($"The connection attempt was cancelled.");
|
||||
}
|
||||
else if (timeOutTask.IsCompleted)
|
||||
{
|
||||
Debug.LogError($"Connection to {host} timed out.");
|
||||
}
|
||||
OnConnected -= SetConnectedComplete;
|
||||
Debug.LogError("Connection timed out.");
|
||||
OnConnectionFailed(hostSteamID);
|
||||
}
|
||||
|
||||
OnConnected -= SetConnectedComplete;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
Debug.LogError($"Connection string was not in the right format. Did you enter a SteamId?");
|
||||
Error = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message);
|
||||
Error = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Error)
|
||||
{
|
||||
OnConnectionFailed(new SteamId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
Debug.LogError("Sending Disconnect message");
|
||||
SendInternal(hostSteamID, InternalMessages.DISCONNECT);
|
||||
Dispose();
|
||||
cancelToken?.Cancel();
|
||||
|
||||
WaitForClose(hostSteamID);
|
||||
}
|
||||
|
||||
private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task);
|
||||
|
||||
protected override void OnReceiveData(byte[] data, SteamId clientSteamID, int channel)
|
||||
{
|
||||
if (clientSteamID != hostSteamID)
|
||||
{
|
||||
Debug.LogError("Received a message from an unknown");
|
||||
return;
|
||||
}
|
||||
|
||||
OnReceivedData.Invoke(data, channel);
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(SteamId id)
|
||||
{
|
||||
if (hostSteamID == id)
|
||||
{
|
||||
SteamNetworking.AcceptP2PSessionWithUser(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("P2P Acceptance Request from unknown host ID.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveInternalData(InternalMessages type, SteamId clientSteamID)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.ACCEPT_CONNECT:
|
||||
if (!Connected)
|
||||
{
|
||||
Connected = true;
|
||||
Debug.LogError("Connection established.");
|
||||
OnConnected.Invoke();
|
||||
}
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
if (Connected)
|
||||
{
|
||||
Connected = false;
|
||||
Debug.LogError("Disconnected.");
|
||||
OnDisconnected.Invoke();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.LogError("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int channelId) => Send(hostSteamID, data, channelId);
|
||||
protected override void OnConnectionFailed(SteamId remoteId) => OnDisconnected.Invoke();
|
||||
public void FlushData() { }
|
||||
}
|
||||
}
|
125
FizzyFacepunch/LegacyCommon.cs
Normal file
125
FizzyFacepunch/LegacyCommon.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public abstract class LegacyCommon
|
||||
{
|
||||
private P2PSend[] channels;
|
||||
private int internal_ch => channels.Length;
|
||||
|
||||
protected enum InternalMessages : byte
|
||||
{
|
||||
CONNECT,
|
||||
ACCEPT_CONNECT,
|
||||
DISCONNECT
|
||||
}
|
||||
|
||||
protected readonly FizzyFacepunch transport;
|
||||
|
||||
protected LegacyCommon(FizzyFacepunch transport)
|
||||
{
|
||||
channels = transport.Channels;
|
||||
|
||||
SteamNetworking.OnP2PSessionRequest += OnNewConnection;
|
||||
SteamNetworking.OnP2PConnectionFailed += OnConnectFail;
|
||||
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
protected void WaitForClose(SteamId cSteamID) => transport.StartCoroutine(DelayedClose(cSteamID));
|
||||
private IEnumerator DelayedClose(SteamId cSteamID)
|
||||
{
|
||||
yield return null;
|
||||
CloseP2PSessionWithUser(cSteamID);
|
||||
}
|
||||
|
||||
protected void Dispose()
|
||||
{
|
||||
SteamNetworking.OnP2PSessionRequest -= OnNewConnection;
|
||||
SteamNetworking.OnP2PConnectionFailed -= OnConnectFail;
|
||||
}
|
||||
|
||||
protected abstract void OnNewConnection(SteamId steamID);
|
||||
|
||||
private void OnConnectFail(SteamId id, P2PSessionError err)
|
||||
{
|
||||
OnConnectionFailed(id);
|
||||
CloseP2PSessionWithUser(id);
|
||||
|
||||
switch (err)
|
||||
{
|
||||
case P2PSessionError.NotRunningApp:
|
||||
throw new Exception("Connection failed: The target user is not running the same game.");
|
||||
case P2PSessionError.NoRightsToApp:
|
||||
throw new Exception("Connection failed: The local user doesn't own the app that is running.");
|
||||
case P2PSessionError.DestinationNotLoggedIn:
|
||||
throw new Exception("Connection failed: Target user isn't connected to Steam.");
|
||||
case P2PSessionError.Timeout:
|
||||
throw new Exception("Connection failed: The connection timed out because the target user didn't respond.");
|
||||
default:
|
||||
throw new Exception("Connection failed: Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
protected bool SendInternal(SteamId target, InternalMessages type) => SteamNetworking.SendP2PPacket(target, new byte[] { (byte)type }, 1, internal_ch);
|
||||
protected void Send(SteamId host, byte[] msgBuffer, int channel) => SteamNetworking.SendP2PPacket(host, msgBuffer, msgBuffer.Length, channel, channels[Mathf.Min(channel, channels.Length - 1)]);
|
||||
private bool Receive(out SteamId clientSteamID, out byte[] receiveBuffer, int channel)
|
||||
{
|
||||
if (SteamNetworking.IsP2PPacketAvailable(channel))
|
||||
{
|
||||
var data = SteamNetworking.ReadP2PPacket(channel);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
receiveBuffer = data.Value.Data;
|
||||
clientSteamID = data.Value.SteamId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
receiveBuffer = null;
|
||||
clientSteamID = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void CloseP2PSessionWithUser(SteamId clientSteamID) => SteamNetworking.CloseP2PSessionWithUser(clientSteamID);
|
||||
|
||||
public void ReceiveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (transport.enabled && Receive(out SteamId clientSteamID, out byte[] internalMessage, internal_ch))
|
||||
{
|
||||
if (internalMessage.Length == 1)
|
||||
{
|
||||
OnReceiveInternalData((InternalMessages)internalMessage[0], clientSteamID);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Incorrect package length on internal channel.");
|
||||
}
|
||||
}
|
||||
|
||||
for (int chNum = 0; chNum < channels.Length; chNum++)
|
||||
{
|
||||
while (transport.enabled && Receive(out SteamId clientSteamID, out byte[] receiveBuffer, chNum))
|
||||
{
|
||||
OnReceiveData(receiveBuffer, clientSteamID, chNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnReceiveInternalData(InternalMessages type, SteamId clientSteamID);
|
||||
protected abstract void OnReceiveData(byte[] data, SteamId clientSteamID, int channel);
|
||||
protected abstract void OnConnectionFailed(SteamId remoteId);
|
||||
}
|
||||
}
|
164
FizzyFacepunch/LegacyServer.cs
Normal file
164
FizzyFacepunch/LegacyServer.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class LegacyServer : LegacyCommon, IServer
|
||||
{
|
||||
private event Action<int> OnConnected;
|
||||
private event Action<int, byte[], int> OnReceivedData;
|
||||
private event Action<int> OnDisconnected;
|
||||
private event Action<int, Exception> OnReceivedError;
|
||||
|
||||
private BidirectionalDictionary<SteamId, int> steamToMirrorIds;
|
||||
private int maxConnections;
|
||||
private int nextConnectionID;
|
||||
|
||||
public static LegacyServer CreateServer(FizzyFacepunch transport, int maxConnections)
|
||||
{
|
||||
LegacyServer s = new LegacyServer(transport, maxConnections);
|
||||
|
||||
s.OnConnected += (id) => transport.OnServerConnected.Invoke(id);
|
||||
s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id);
|
||||
s.OnReceivedData += (id, data, channel) => transport.OnServerDataReceived.Invoke(id, new ArraySegment<byte>(data), channel);
|
||||
s.OnReceivedError += (id, exception) => transport.OnServerError.Invoke(id, exception);
|
||||
|
||||
SteamNetworking.OnP2PSessionRequest = (steamid) =>
|
||||
{
|
||||
Debug.LogError($"Incoming request from SteamId {steamid}.");
|
||||
SteamNetworking.AcceptP2PSessionWithUser(steamid);
|
||||
};
|
||||
|
||||
if (!SteamClient.IsValid)
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized.");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private LegacyServer(FizzyFacepunch transport, int maxConnections) : base(transport)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
steamToMirrorIds = new BidirectionalDictionary<SteamId, int>();
|
||||
nextConnectionID = 1;
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(SteamId id) => SteamNetworking.AcceptP2PSessionWithUser(id);
|
||||
|
||||
protected override void OnReceiveInternalData(InternalMessages type, SteamId clientSteamID)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.CONNECT:
|
||||
if (steamToMirrorIds.Count >= maxConnections)
|
||||
{
|
||||
SendInternal(clientSteamID, InternalMessages.DISCONNECT);
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(clientSteamID, InternalMessages.ACCEPT_CONNECT);
|
||||
|
||||
int connectionId = nextConnectionID++;
|
||||
steamToMirrorIds.Add(clientSteamID, connectionId);
|
||||
OnConnected.Invoke(connectionId);
|
||||
Debug.LogError($"Client with SteamID {clientSteamID} connected. Assigning connection id {connectionId}");
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
if (steamToMirrorIds.TryGetValue(clientSteamID, out int connId))
|
||||
{
|
||||
OnDisconnected.Invoke(connId);
|
||||
CloseP2PSessionWithUser(clientSteamID);
|
||||
steamToMirrorIds.Remove(clientSteamID);
|
||||
Debug.LogError($"Client with SteamID {clientSteamID} disconnected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown SteamID while receiving disconnect message."));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
Debug.LogError("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveData(byte[] data, SteamId clientSteamID, int channel)
|
||||
{
|
||||
if (steamToMirrorIds.TryGetValue(clientSteamID, out int connectionId))
|
||||
{
|
||||
OnReceivedData.Invoke(connectionId, data, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseP2PSessionWithUser(clientSteamID);
|
||||
Debug.LogError("Data received from steam client thats not known " + clientSteamID);
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown SteamID"));
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect(int connectionId)
|
||||
{
|
||||
if (steamToMirrorIds.TryGetValue(connectionId, out SteamId steamID))
|
||||
{
|
||||
SendInternal(steamID, InternalMessages.DISCONNECT);
|
||||
steamToMirrorIds.Remove(connectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
foreach (KeyValuePair<SteamId, int> client in steamToMirrorIds)
|
||||
{
|
||||
Disconnect(client.Value);
|
||||
WaitForClose(client.Key);
|
||||
}
|
||||
|
||||
SteamNetworking.OnP2PSessionRequest = null;
|
||||
Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void Send(int connectionId, byte[] data, int channelId)
|
||||
{
|
||||
if (steamToMirrorIds.TryGetValue(connectionId, out SteamId steamId))
|
||||
{
|
||||
Send(steamId, data, channelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Trying to send on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
}
|
||||
}
|
||||
|
||||
public string ServerGetClientAddress(int connectionId)
|
||||
{
|
||||
if (steamToMirrorIds.TryGetValue(connectionId, out SteamId steamId))
|
||||
{
|
||||
return steamId.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Trying to get info on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnConnectionFailed(SteamId remoteId)
|
||||
{
|
||||
int connectionId = steamToMirrorIds.TryGetValue(remoteId, out int connId) ? connId : nextConnectionID++;
|
||||
OnDisconnected.Invoke(connectionId);
|
||||
}
|
||||
|
||||
public void FlushData() { }
|
||||
}
|
||||
}
|
191
FizzyFacepunch/NextClient.cs
Normal file
191
FizzyFacepunch/NextClient.cs
Normal file
@ -0,0 +1,191 @@
|
||||
using Steamworks;
|
||||
using Steamworks.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class NextClient : NextCommon, IClient
|
||||
{
|
||||
public bool Connected { get; private set; }
|
||||
public bool Error { get; private set; }
|
||||
|
||||
private TimeSpan ConnectionTimeout;
|
||||
|
||||
private event Action<byte[], int> OnReceivedData;
|
||||
private event Action OnConnected;
|
||||
private event Action OnDisconnected;
|
||||
|
||||
private CancellationTokenSource cancelToken;
|
||||
private TaskCompletionSource<Task> connectedComplete;
|
||||
private SteamId hostSteamID = 0;
|
||||
private FizzyConnectionManager HostConnectionManager;
|
||||
private Connection HostConnection => HostConnectionManager.Connection;
|
||||
private List<Action> BufferedData;
|
||||
|
||||
private NextClient(FizzyFacepunch transport)
|
||||
{
|
||||
ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.Timeout));
|
||||
BufferedData = new List<Action>();
|
||||
}
|
||||
|
||||
public static NextClient CreateClient(FizzyFacepunch transport, string host)
|
||||
{
|
||||
NextClient c = new NextClient(transport);
|
||||
|
||||
c.OnConnected += () => transport.OnClientConnected.Invoke();
|
||||
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
|
||||
c.OnReceivedData += (data, ch) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), ch);
|
||||
|
||||
if (SteamClient.IsValid)
|
||||
{
|
||||
c.Connect(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized");
|
||||
c.OnConnectionFailed();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
private async void Connect(string host)
|
||||
{
|
||||
cancelToken = new CancellationTokenSource();
|
||||
SteamNetworkingSockets.OnConnectionStatusChanged += OnConnectionStatusChanged;
|
||||
|
||||
try
|
||||
{
|
||||
hostSteamID = UInt64.Parse(host);
|
||||
connectedComplete = new TaskCompletionSource<Task>();
|
||||
OnConnected += SetConnectedComplete;
|
||||
HostConnectionManager = SteamNetworkingSockets.ConnectRelay<FizzyConnectionManager>(hostSteamID);
|
||||
HostConnectionManager.ForwardMessage = OnMessageReceived;
|
||||
Task connectedCompleteTask = connectedComplete.Task;
|
||||
Task timeOutTask = Task.Delay(ConnectionTimeout, cancelToken.Token);
|
||||
|
||||
if (await Task.WhenAny(connectedCompleteTask, timeOutTask) != connectedCompleteTask)
|
||||
{
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
{
|
||||
Debug.LogError($"The connection attempt was cancelled.");
|
||||
}
|
||||
else if (timeOutTask.IsCompleted)
|
||||
{
|
||||
Debug.LogError($"Connection to {host} timed out.");
|
||||
}
|
||||
|
||||
OnConnected -= SetConnectedComplete;
|
||||
OnConnectionFailed();
|
||||
}
|
||||
|
||||
OnConnected -= SetConnectedComplete;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
Debug.LogError($"Connection string was not in the right format. Did you enter a SteamId?");
|
||||
Error = true;
|
||||
OnConnectionFailed();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message);
|
||||
Error = true;
|
||||
OnConnectionFailed();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Error)
|
||||
{
|
||||
Debug.LogError("Connection failed.");
|
||||
OnConnectionFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageReceived(IntPtr dataPtr, int size)
|
||||
{
|
||||
(byte[] data, int ch) = ProcessMessage(dataPtr, size);
|
||||
if (Connected)
|
||||
{
|
||||
OnReceivedData(data, ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferedData.Add(() => OnReceivedData(data, ch));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConnectionStatusChanged(Connection conn, ConnectionInfo info)
|
||||
{
|
||||
ulong clientSteamID = info.Identity.SteamId;
|
||||
if (info.State == ConnectionState.Connected)
|
||||
{
|
||||
Connected = true;
|
||||
OnConnected.Invoke();
|
||||
Debug.LogError("Connection established.");
|
||||
|
||||
if (BufferedData.Count > 0)
|
||||
{
|
||||
Debug.LogError($"{BufferedData.Count} received before connection was established. Processing now.");
|
||||
{
|
||||
foreach (Action a in BufferedData)
|
||||
{
|
||||
a();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (info.State == ConnectionState.ClosedByPeer)
|
||||
{
|
||||
Connected = false;
|
||||
OnDisconnected.Invoke();
|
||||
Debug.LogError("Disconnected.");
|
||||
conn.Close(false, 0, "Disconnected");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Connection state changed: {info.State.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
cancelToken?.Cancel();
|
||||
SteamNetworkingSockets.OnConnectionStatusChanged -= OnConnectionStatusChanged;
|
||||
|
||||
if (HostConnectionManager != null)
|
||||
{
|
||||
Debug.LogError("Sending Disconnect message");
|
||||
HostConnection.Close(false, 0, "Graceful disconnect");
|
||||
HostConnectionManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveData()
|
||||
{
|
||||
HostConnectionManager.Receive(MAX_MESSAGES);
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int channelId)
|
||||
{
|
||||
Result res = SendSocket(HostConnection, data, channelId);
|
||||
|
||||
if (res != Result.OK)
|
||||
{
|
||||
Debug.LogError($"Could not send: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task);
|
||||
private void OnConnectionFailed() => OnDisconnected.Invoke();
|
||||
public void FlushData()
|
||||
{
|
||||
HostConnection.Flush();
|
||||
}
|
||||
}
|
||||
}
|
38
FizzyFacepunch/NextCommon.cs
Normal file
38
FizzyFacepunch/NextCommon.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Mirror;
|
||||
using Steamworks;
|
||||
using Steamworks.Data;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class NextCommon
|
||||
{
|
||||
protected const int MAX_MESSAGES = 256;
|
||||
|
||||
protected Result SendSocket(Connection conn, byte[] data, int channelId)
|
||||
{
|
||||
Array.Resize(ref data, data.Length + 1);
|
||||
data[data.Length - 1] = (byte)channelId;
|
||||
|
||||
GCHandle pinnedArray = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
IntPtr pData = pinnedArray.AddrOfPinnedObject();
|
||||
SendType sendFlag = channelId == Channels.Unreliable ? SendType.Unreliable : SendType.Reliable;
|
||||
Result res = conn.SendMessage(pData, data.Length, sendFlag);
|
||||
if (res != Result.OK)
|
||||
{
|
||||
Debug.LogWarning($"Send issue: {res}");
|
||||
}
|
||||
|
||||
pinnedArray.Free();
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
190
FizzyFacepunch/NextServer.cs
Normal file
190
FizzyFacepunch/NextServer.cs
Normal file
@ -0,0 +1,190 @@
|
||||
using Steamworks;
|
||||
using Steamworks.Data;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.FizzySteam
|
||||
{
|
||||
public class NextServer : NextCommon, IServer
|
||||
{
|
||||
private event Action<int> OnConnected;
|
||||
private event Action<int, byte[], int> OnReceivedData;
|
||||
private event Action<int> OnDisconnected;
|
||||
private event Action<int, Exception> OnReceivedError;
|
||||
|
||||
private BidirectionalDictionary<Connection, int> connToMirrorID;
|
||||
private BidirectionalDictionary<SteamId, int> steamIDToMirrorID;
|
||||
private int maxConnections;
|
||||
private int nextConnectionID;
|
||||
|
||||
private FizzySocketManager listenSocket;
|
||||
|
||||
private NextServer(int maxConnections)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
connToMirrorID = new BidirectionalDictionary<Connection, int>();
|
||||
steamIDToMirrorID = new BidirectionalDictionary<SteamId, int>();
|
||||
nextConnectionID = 1;
|
||||
SteamNetworkingSockets.OnConnectionStatusChanged += OnConnectionStatusChanged;
|
||||
}
|
||||
|
||||
public static NextServer CreateServer(FizzyFacepunch transport, int maxConnections)
|
||||
{
|
||||
NextServer s = new NextServer(maxConnections);
|
||||
|
||||
s.OnConnected += (id) => transport.OnServerConnected.Invoke(id);
|
||||
s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id);
|
||||
s.OnReceivedData += (id, data, ch) => transport.OnServerDataReceived.Invoke(id, new ArraySegment<byte>(data), ch);
|
||||
s.OnReceivedError += (id, exception) => transport.OnServerError.Invoke(id, exception);
|
||||
|
||||
if (!SteamClient.IsValid)
|
||||
{
|
||||
Debug.LogError("SteamWorks not initialized.");
|
||||
}
|
||||
|
||||
s.Host();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private void Host()
|
||||
{
|
||||
listenSocket = SteamNetworkingSockets.CreateRelaySocket<FizzySocketManager>();
|
||||
listenSocket.ForwardMessage = OnMessageReceived;
|
||||
}
|
||||
|
||||
private void OnConnectionStatusChanged(Connection conn, ConnectionInfo info)
|
||||
{
|
||||
ulong clientSteamID = info.Identity.SteamId;
|
||||
if (info.State == ConnectionState.Connecting)
|
||||
{
|
||||
if (connToMirrorID.Count >= maxConnections)
|
||||
{
|
||||
Debug.LogError($"Incoming connection {clientSteamID} would exceed max connection count. Rejecting.");
|
||||
conn.Close(false, 0, "Max Connection Count");
|
||||
return;
|
||||
}
|
||||
|
||||
Result res;
|
||||
|
||||
if ((res = conn.Accept()) == Result.OK)
|
||||
{
|
||||
Debug.LogError($"Accepting connection {clientSteamID}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Connection {clientSteamID} could not be accepted: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
else if (info.State == ConnectionState.Connected)
|
||||
{
|
||||
int connectionId = nextConnectionID++;
|
||||
connToMirrorID.Add(conn, connectionId);
|
||||
steamIDToMirrorID.Add(clientSteamID, connectionId);
|
||||
OnConnected.Invoke(connectionId);
|
||||
Debug.LogError($"Client with SteamID {clientSteamID} connected. Assigning connection id {connectionId}");
|
||||
}
|
||||
else if (info.State == ConnectionState.ClosedByPeer)
|
||||
{
|
||||
if (connToMirrorID.TryGetValue(conn, out int connId))
|
||||
{
|
||||
InternalDisconnect(connId, conn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Connection {clientSteamID} state changed: {info.State.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
private void InternalDisconnect(int connId, Connection socket)
|
||||
{
|
||||
OnDisconnected.Invoke(connId);
|
||||
socket.Close(false, 0, "Graceful disconnect");
|
||||
connToMirrorID.Remove(connId);
|
||||
steamIDToMirrorID.Remove(connId);
|
||||
Debug.LogError($"Client with SteamID {connId} disconnected.");
|
||||
}
|
||||
|
||||
public void Disconnect(int connectionId)
|
||||
{
|
||||
if (connToMirrorID.TryGetValue(connectionId, out Connection conn))
|
||||
{
|
||||
Debug.LogError($"Connection id {connectionId} disconnected.");
|
||||
conn.Close(false, 0, "Disconnected by server");
|
||||
steamIDToMirrorID.Remove(connectionId);
|
||||
connToMirrorID.Remove(connectionId);
|
||||
OnDisconnected(connectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushData()
|
||||
{
|
||||
foreach (Connection conn in connToMirrorID.FirstTypes)
|
||||
{
|
||||
conn.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveData()
|
||||
{
|
||||
listenSocket.Receive(MAX_MESSAGES);
|
||||
}
|
||||
|
||||
private void OnMessageReceived(Connection conn, IntPtr dataPtr, int size)
|
||||
{
|
||||
(byte[] data, int ch) = ProcessMessage(dataPtr, size);
|
||||
OnReceivedData(connToMirrorID[conn], data, ch);
|
||||
}
|
||||
|
||||
public void Send(int connectionId, byte[] data, int channelId)
|
||||
{
|
||||
if (connToMirrorID.TryGetValue(connectionId, out Connection conn))
|
||||
{
|
||||
Result res = SendSocket(conn, data, channelId);
|
||||
|
||||
if (res == Result.NoConnection || res == Result.InvalidParam)
|
||||
{
|
||||
Debug.LogError($"Connection to {connectionId} was lost.");
|
||||
InternalDisconnect(connectionId, conn);
|
||||
}
|
||||
else if (res != Result.OK)
|
||||
{
|
||||
Debug.LogError($"Could not send: {res.ToString()}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Trying to send on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
}
|
||||
}
|
||||
|
||||
public string ServerGetClientAddress(int connectionId)
|
||||
{
|
||||
if (steamIDToMirrorID.TryGetValue(connectionId, out SteamId steamId))
|
||||
{
|
||||
return steamId.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Trying to get info on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
if (listenSocket != null)
|
||||
{
|
||||
SteamNetworkingSockets.OnConnectionStatusChanged -= OnConnectionStatusChanged;
|
||||
listenSocket.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
18
QSB.sln
18
QSB.sln
@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSB", "QSB\QSB.csproj", "{1F00090A-C697-4C55-B401-192F3CFB9DC2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QSB", "QSB\QSB.csproj", "{1F00090A-C697-4C55-B401-192F3CFB9DC2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSBTests", "QSBTests\QSBTests.csproj", "{2FE8256C-0CB6-48F1-A4C0-5FA18A1846A3}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QSBTests", "QSBTests\QSBTests.csproj", "{2FE8256C-0CB6-48F1-A4C0-5FA18A1846A3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2569F98D-F671-42AA-82DE-505B05CDCEF2}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
@ -14,7 +14,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MirrorWeaver", "MirrorWeaver\MirrorWeaver.csproj", "{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MirrorWeaver", "MirrorWeaver\MirrorWeaver.csproj", "{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FizzyFacepunch", "FizzyFacepunch\FizzyFacepunch.csproj", "{C533C95D-F634-4221-99F6-3E59C2A45F90}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSBPatcher", "QSBPatcher\QSBPatcher.csproj", "{86CE49AB-487A-466A-98F3-3DCAF9BE66D3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -34,6 +38,14 @@ Global
|
||||
{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA8A467E-15BA-456C-9034-6EB80BAF1FF9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C533C95D-F634-4221-99F6-3E59C2A45F90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C533C95D-F634-4221-99F6-3E59C2A45F90}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C533C95D-F634-4221-99F6-3E59C2A45F90}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C533C95D-F634-4221-99F6-3E59C2A45F90}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{86CE49AB-487A-466A-98F3-3DCAF9BE66D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{86CE49AB-487A-466A-98F3-3DCAF9BE66D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{86CE49AB-487A-466A-98F3-3DCAF9BE66D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{86CE49AB-487A-466A-98F3-3DCAF9BE66D3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -6,12 +6,15 @@ using UnityEngine;
|
||||
|
||||
namespace QSB.Anglerfish.Messages
|
||||
{
|
||||
public class AnglerChangeStateMessage : QSBEnumWorldObjectMessage<QSBAngler, AnglerfishController.AnglerState>
|
||||
/// <summary>
|
||||
/// angler state, target transform, and local disturbance pos
|
||||
/// </summary>
|
||||
public class AnglerDataMessage : QSBEnumWorldObjectMessage<QSBAngler, AnglerfishController.AnglerState>
|
||||
{
|
||||
private uint TargetId;
|
||||
private Vector3 LocalDisturbancePos;
|
||||
|
||||
public AnglerChangeStateMessage(QSBAngler qsbAngler)
|
||||
public AnglerDataMessage(QSBAngler qsbAngler)
|
||||
{
|
||||
Value = qsbAngler.AttachedObject._currentState;
|
||||
TargetId = TargetToId(qsbAngler.TargetTransform);
|
@ -39,7 +39,8 @@ namespace QSB.Anglerfish.Patches
|
||||
if (qsbAngler.TargetTransform != null && sectorDetector.GetAttachedOWRigidbody().transform == qsbAngler.TargetTransform)
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -62,7 +63,7 @@ namespace QSB.Anglerfish.Patches
|
||||
if ((__instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos) - __instance._anglerBody.GetPosition()).sqrMagnitude < __instance._arrivalDistance * __instance._arrivalDistance)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -71,7 +72,7 @@ namespace QSB.Anglerfish.Patches
|
||||
if (qsbAngler.TargetTransform == null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -79,7 +80,7 @@ namespace QSB.Anglerfish.Patches
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -90,7 +91,7 @@ namespace QSB.Anglerfish.Patches
|
||||
if (qsbAngler.TargetTransform == null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -124,7 +125,8 @@ namespace QSB.Anglerfish.Patches
|
||||
else
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -135,12 +137,12 @@ namespace QSB.Anglerfish.Patches
|
||||
if (qsbAngler.TargetTransform != null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -252,7 +254,7 @@ namespace QSB.Anglerfish.Patches
|
||||
{
|
||||
qsbAngler.TargetTransform = attachedOWRigidbody.transform;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -281,7 +283,7 @@ namespace QSB.Anglerfish.Patches
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
}
|
||||
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -293,7 +295,7 @@ namespace QSB.Anglerfish.Patches
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Investigating);
|
||||
}
|
||||
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -321,7 +323,7 @@ namespace QSB.Anglerfish.Patches
|
||||
qsbAngler.TargetTransform = caughtBody.transform;
|
||||
__instance._consumeStartTime = Time.time;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Consuming);
|
||||
qsbAngler.SendMessage(new AnglerChangeStateMessage(qsbAngler));
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -10,7 +10,6 @@ namespace QSB.Anglerfish.TransformSync
|
||||
{
|
||||
public class AnglerTransformSync : UnsectoredRigidbodySync
|
||||
{
|
||||
protected override bool IsReady => QSBWorldSync.AllObjectsAdded;
|
||||
protected override bool UseInterpolation => false;
|
||||
protected override bool OnlyApplyOnDeserialize => true;
|
||||
|
||||
@ -30,14 +29,6 @@ namespace QSB.Anglerfish.TransformSync
|
||||
{
|
||||
_instances.Remove(this);
|
||||
base.OnStopClient();
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
|
||||
protected override float SendInterval => 1;
|
||||
@ -61,15 +52,28 @@ namespace QSB.Anglerfish.TransformSync
|
||||
netIdentity.SendAuthQueueMessage(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
|
||||
base.Uninit();
|
||||
}
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Remove);
|
||||
|
||||
protected override void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsInitialized
|
||||
|| AttachedRigidbody == null
|
||||
|| AttachedRigidbody.IsSuspended())
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -82,7 +86,7 @@ namespace QSB.Anglerfish.TransformSync
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition()
|
||||
+ AttachedRigidbody.transform.TransformDirection(_qsbAngler.AttachedObject._mouthOffset), 3, Color.grey);
|
||||
|
||||
if (_qsbAngler.TargetTransform != null)
|
||||
if (_qsbAngler.TargetTransform)
|
||||
{
|
||||
Popcron.Gizmos.Line(_qsbAngler.TargetTransform.position, AttachedRigidbody.GetPosition(), Color.gray);
|
||||
Popcron.Gizmos.Line(_qsbAngler.TargetTransform.position, _qsbAngler.TargetTransform.position + _qsbAngler.TargetVelocity, Color.green);
|
||||
|
@ -1,5 +1,7 @@
|
||||
using Mirror;
|
||||
using QSB.Anglerfish.Messages;
|
||||
using QSB.Anglerfish.TransformSync;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
@ -8,6 +10,8 @@ namespace QSB.Anglerfish.WorldObjects
|
||||
{
|
||||
public class QSBAngler : WorldObject<AnglerfishController>
|
||||
{
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public AnglerTransformSync TransformSync;
|
||||
public Transform TargetTransform;
|
||||
public Vector3 TargetVelocity { get; private set; }
|
||||
@ -33,6 +37,14 @@ namespace QSB.Anglerfish.WorldObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (TransformSync.hasAuthority)
|
||||
{
|
||||
this.SendMessage(new AnglerDataMessage(this) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTargetVelocity()
|
||||
{
|
||||
if (TargetTransform == null)
|
||||
|
@ -1,9 +1,19 @@
|
||||
using QSB.WorldSync;
|
||||
using QSB.CampfireSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.CampfireSync.WorldObjects
|
||||
{
|
||||
public class QSBCampfire : WorldObject<Campfire>
|
||||
{
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
this.SendMessage(new CampfireStateMessage(GetState()) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public void StartRoasting()
|
||||
=> AttachedObject.StartRoasting();
|
||||
|
||||
@ -13,4 +23,4 @@ namespace QSB.CampfireSync.WorldObjects
|
||||
public void SetState(Campfire.State newState)
|
||||
=> AttachedObject.SetState(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace QSB.DeathSync
|
||||
|
||||
RespawnManager.Instance.TriggerRespawnMap();
|
||||
|
||||
var inSpace = PlayerTransformSync.LocalInstance.SectorSync.SectorList.Count == 0;
|
||||
var inSpace = PlayerTransformSync.LocalInstance.SectorDetector.SectorList.Count == 0;
|
||||
|
||||
if (inSpace)
|
||||
{
|
||||
|
@ -77,8 +77,6 @@ namespace QSB.ElevatorSync.WorldObjects
|
||||
_interactVolume.DisableInteraction();
|
||||
}
|
||||
|
||||
public override bool ShouldDisplayDebug() => base.ShouldDisplayDebug() && _elevatorTrigger;
|
||||
|
||||
public override void DisplayLines()
|
||||
{
|
||||
var boxShape = (BoxShape)_elevatorTrigger._shape;
|
||||
|
@ -16,22 +16,13 @@ namespace QSB.EyeOfTheUniverse.ForestOfGalaxies.Messages
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(_deathDelays.Count);
|
||||
foreach (var item in _deathDelays)
|
||||
{
|
||||
writer.Write(item);
|
||||
}
|
||||
writer.WriteList(_deathDelays);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
var length = reader.Read<int>();
|
||||
_deathDelays = new List<float>(length);
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
_deathDelays.Add(reader.Read<float>());
|
||||
}
|
||||
_deathDelays = reader.ReadList<float>();
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
@ -32,11 +32,11 @@ namespace QSB.EyeOfTheUniverse.ForestOfGalaxies.Patches
|
||||
}
|
||||
|
||||
if ((Locator.GetProbe() != null && Locator.GetProbe().IsAnchored())
|
||||
|| QSBPlayerManager.PlayerList.Where(x => x != QSBPlayerManager.LocalPlayer).Any(x => x.Probe != null && x.Probe.IsAnchored()))
|
||||
|| QSBPlayerManager.PlayerList.Where(x => !x.IsLocalPlayer).Any(x => x.Probe != null && x.Probe.IsAnchored()))
|
||||
{
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
{
|
||||
if (player == QSBPlayerManager.LocalPlayer
|
||||
if (player.IsLocalPlayer
|
||||
&& Locator.GetProbe() != null
|
||||
&& Locator.GetProbe().IsAnchored())
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace QSB.EyeOfTheUniverse.InstrumentSync.WorldObjects
|
||||
|
||||
foreach (var player in MaskManager.WentOnSolanumsWildRide)
|
||||
{
|
||||
player.DitheringAnimator.SetVisible(true, 0.5f);
|
||||
player.SetVisible(true, 2);
|
||||
}
|
||||
|
||||
maskZoneController._whiteSphere.SetActive(false);
|
||||
|
@ -32,7 +32,7 @@ namespace QSB.EyeOfTheUniverse.MaskSync
|
||||
_flickering = true;
|
||||
|
||||
// hide all players in shuttle
|
||||
QSBPlayerManager.PlayerList.Where(x => x.IsInEyeShuttle).ForEach(x => x.DitheringAnimator.SetVisibleImmediate(false));
|
||||
QSBPlayerManager.PlayerList.Where(x => x.IsInEyeShuttle).ForEach(x => x.SetVisible(false));
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Mirror;
|
||||
using QSB.GeyserSync.Messages;
|
||||
using QSB.GeyserSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
@ -13,6 +12,9 @@ namespace QSB.GeyserSync.WorldObjects
|
||||
AttachedObject.OnGeyserDeactivateEvent += () => HandleEvent(false);
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to) =>
|
||||
HandleEvent(AttachedObject._isActive);
|
||||
|
||||
private void HandleEvent(bool state)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
|
@ -7,7 +7,6 @@ namespace QSB.JellyfishSync.Messages
|
||||
{
|
||||
public JellyfishRisingMessage(bool isRising) => Value = isRising;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.IsRising = Value;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.SetIsRising(Value);
|
||||
}
|
||||
}
|
||||
|
@ -20,17 +20,16 @@ namespace QSB.JellyfishSync.Patches
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbJellyfish = __instance.GetWorldObject<QSBJellyfish>();
|
||||
|
||||
var sqrMagnitude = (__instance._jellyfishBody.GetPosition() - __instance._planetBody.GetPosition()).sqrMagnitude;
|
||||
if (qsbJellyfish.IsRising)
|
||||
if (__instance._isRising)
|
||||
{
|
||||
__instance._jellyfishBody.AddAcceleration(__instance.transform.up * __instance._upwardsAcceleration);
|
||||
if (sqrMagnitude > __instance._upperLimit * __instance._upperLimit)
|
||||
{
|
||||
qsbJellyfish.IsRising = false;
|
||||
|
||||
qsbJellyfish.SendMessage(new JellyfishRisingMessage(qsbJellyfish.IsRising));
|
||||
__instance._isRising = false;
|
||||
__instance._attractiveFluidVolume.SetVolumeActivation(true);
|
||||
__instance.GetWorldObject<QSBJellyfish>().SendMessage(new JellyfishRisingMessage(false));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -38,8 +37,9 @@ namespace QSB.JellyfishSync.Patches
|
||||
__instance._jellyfishBody.AddAcceleration(-__instance.transform.up * __instance._downwardsAcceleration);
|
||||
if (sqrMagnitude < __instance._lowerLimit * __instance._lowerLimit)
|
||||
{
|
||||
qsbJellyfish.IsRising = true;
|
||||
qsbJellyfish.SendMessage(new JellyfishRisingMessage(qsbJellyfish.IsRising));
|
||||
__instance._isRising = true;
|
||||
__instance._attractiveFluidVolume.SetVolumeActivation(false);
|
||||
__instance.GetWorldObject<QSBJellyfish>().SendMessage(new JellyfishRisingMessage(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Mirror;
|
||||
using QSB.AuthoritySync;
|
||||
using QSB.AuthoritySync;
|
||||
using QSB.JellyfishSync.WorldObjects;
|
||||
using QSB.Syncs.Unsectored.Rigidbodies;
|
||||
using QSB.Utility;
|
||||
@ -11,7 +10,6 @@ namespace QSB.JellyfishSync.TransformSync
|
||||
{
|
||||
public class JellyfishTransformSync : UnsectoredRigidbodySync
|
||||
{
|
||||
protected override bool IsReady => QSBWorldSync.AllObjectsAdded;
|
||||
protected override bool UseInterpolation => false;
|
||||
protected override bool OnlyApplyOnDeserialize => true;
|
||||
|
||||
@ -31,14 +29,6 @@ namespace QSB.JellyfishSync.TransformSync
|
||||
{
|
||||
_instances.Remove(this);
|
||||
base.OnStopClient();
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
|
||||
protected override float SendInterval => 10;
|
||||
@ -62,39 +52,25 @@ namespace QSB.JellyfishSync.TransformSync
|
||||
netIdentity.SendAuthQueueMessage(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
|
||||
base.Uninit();
|
||||
}
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Remove);
|
||||
|
||||
private bool _isRising;
|
||||
|
||||
protected override void Serialize(NetworkWriter writer, bool initialState)
|
||||
{
|
||||
base.Serialize(writer, initialState);
|
||||
|
||||
writer.Write(_isRising);
|
||||
}
|
||||
|
||||
protected override void Deserialize(NetworkReader reader, bool initialState)
|
||||
{
|
||||
base.Deserialize(reader, initialState);
|
||||
|
||||
_isRising = reader.ReadBool();
|
||||
}
|
||||
|
||||
protected override void GetFromAttached()
|
||||
{
|
||||
base.GetFromAttached();
|
||||
|
||||
_qsbJellyfish.Align = true;
|
||||
_isRising = _qsbJellyfish.IsRising;
|
||||
}
|
||||
|
||||
/// replacement using SetPosition/Rotation instead of Move
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
_qsbJellyfish.Align = false;
|
||||
_qsbJellyfish.IsRising = _isRising;
|
||||
|
||||
var pos = ReferenceTransform.FromRelPos(transform.position);
|
||||
AttachedRigidbody.SetPosition(pos);
|
||||
AttachedRigidbody.SetRotation(ReferenceTransform.FromRelRot(transform.rotation));
|
||||
@ -105,10 +81,9 @@ namespace QSB.JellyfishSync.TransformSync
|
||||
protected override void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsInitialized
|
||||
|| AttachedRigidbody == null
|
||||
|| ReferenceTransform == null
|
||||
|| AttachedRigidbody.IsSuspended())
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
using Mirror;
|
||||
using QSB.JellyfishSync.Messages;
|
||||
using QSB.JellyfishSync.TransformSync;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
@ -8,13 +10,12 @@ namespace QSB.JellyfishSync.WorldObjects
|
||||
{
|
||||
public class QSBJellyfish : WorldObject<JellyfishController>
|
||||
{
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public JellyfishTransformSync TransformSync;
|
||||
private AlignWithTargetBody _alignWithTargetBody;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_alignWithTargetBody = AttachedObject.GetRequiredComponent<AlignWithTargetBody>();
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
Object.Instantiate(QSBNetworkManager.singleton.JellyfishPrefab).SpawnWithServerAuthority();
|
||||
@ -32,24 +33,23 @@ namespace QSB.JellyfishSync.WorldObjects
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRising
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
get => AttachedObject._isRising;
|
||||
set
|
||||
if (TransformSync.hasAuthority)
|
||||
{
|
||||
if (AttachedObject._isRising == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject._isRising = value;
|
||||
AttachedObject._attractiveFluidVolume.SetVolumeActivation(!value);
|
||||
this.SendMessage(new JellyfishRisingMessage(AttachedObject._isRising) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public bool Align
|
||||
public void SetIsRising(bool value)
|
||||
{
|
||||
set => _alignWithTargetBody.enabled = value;
|
||||
if (AttachedObject._isRising == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject._isRising = value;
|
||||
AttachedObject._attractiveFluidVolume.SetVolumeActivation(!value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
using QSB.Messaging;
|
||||
using Mirror;
|
||||
using Mirror.FizzySteam;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Player.TransformSync;
|
||||
using QSB.SaveSync.Messages;
|
||||
using QSB.Utility;
|
||||
using Steamworks;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Button = UnityEngine.UI.Button;
|
||||
|
||||
namespace QSB.Menus
|
||||
{
|
||||
@ -17,7 +22,8 @@ namespace QSB.Menus
|
||||
private IMenuAPI MenuApi => QSBCore.MenuApi;
|
||||
|
||||
private PopupMenu IPPopup;
|
||||
private PopupMenu InfoPopup;
|
||||
private PopupMenu OneButtonInfoPopup;
|
||||
private PopupMenu TwoButtonInfoPopup;
|
||||
private bool _addedPauseLock;
|
||||
|
||||
// Pause menu only
|
||||
@ -36,6 +42,13 @@ namespace QSB.Menus
|
||||
private const int _ClientButtonIndex = 2;
|
||||
private const int _DisconnectIndex = 3;
|
||||
|
||||
private const string OpenString = "OPEN TO MULTIPLAYER";
|
||||
private const string ConnectString = "CONNECT TO MULTIPLAYER";
|
||||
private const string DisconnectString = "DISCONNECT";
|
||||
private const string StopHostingString = "STOP HOSTING";
|
||||
|
||||
private Action PopupOK;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Instance = this;
|
||||
@ -110,9 +123,9 @@ namespace QSB.Menus
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenInfoPopup(string message, string buttonText)
|
||||
private void OpenInfoPopup(string message, string okButtonText)
|
||||
{
|
||||
InfoPopup.SetUpPopup(message, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(buttonText), null, true, false);
|
||||
OneButtonInfoPopup.SetUpPopup(message, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(okButtonText), null, true, false);
|
||||
|
||||
OWTime.Pause(OWTime.PauseType.System);
|
||||
OWInput.ChangeInputMode(InputMode.Menu);
|
||||
@ -124,7 +137,24 @@ namespace QSB.Menus
|
||||
_addedPauseLock = true;
|
||||
}
|
||||
|
||||
InfoPopup.EnableMenu(true);
|
||||
OneButtonInfoPopup.EnableMenu(true);
|
||||
}
|
||||
|
||||
private void OpenInfoPopup(string message, string okButtonText, string cancelButtonText)
|
||||
{
|
||||
TwoButtonInfoPopup.SetUpPopup(message, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(okButtonText), new ScreenPrompt(cancelButtonText), true, true);
|
||||
|
||||
OWTime.Pause(OWTime.PauseType.System);
|
||||
OWInput.ChangeInputMode(InputMode.Menu);
|
||||
|
||||
var pauseCommandListener = Locator.GetPauseCommandListener();
|
||||
if (pauseCommandListener != null)
|
||||
{
|
||||
pauseCommandListener.AddPauseCommandLock();
|
||||
_addedPauseLock = true;
|
||||
}
|
||||
|
||||
TwoButtonInfoPopup.EnableMenu(true);
|
||||
}
|
||||
|
||||
private void OnCloseInfoPopup()
|
||||
@ -139,20 +169,26 @@ namespace QSB.Menus
|
||||
OWTime.Unpause(OWTime.PauseType.System);
|
||||
OWInput.RestorePreviousInputs();
|
||||
|
||||
if (QSBSceneManager.IsInUniverse)
|
||||
{
|
||||
LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f);
|
||||
}
|
||||
//if (QSBSceneManager.IsInUniverse)
|
||||
//{
|
||||
// LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f);
|
||||
//}
|
||||
PopupOK?.SafeInvoke();
|
||||
PopupOK = null;
|
||||
}
|
||||
|
||||
private void CreateCommonPopups()
|
||||
{
|
||||
IPPopup = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel");
|
||||
var text = QSBCore.UseKcpTransport ? "Public IP Address" : "Steam ID";
|
||||
IPPopup = MenuApi.MakeInputFieldPopup(text, text, "Connect", "Cancel");
|
||||
IPPopup.OnPopupConfirm += Connect;
|
||||
IPPopup.OnPopupValidate += Validate;
|
||||
|
||||
InfoPopup = MenuApi.MakeInfoPopup("", "");
|
||||
InfoPopup.OnDeactivateMenu += OnCloseInfoPopup;
|
||||
OneButtonInfoPopup = MenuApi.MakeInfoPopup("", "");
|
||||
OneButtonInfoPopup.OnDeactivateMenu += OnCloseInfoPopup;
|
||||
|
||||
TwoButtonInfoPopup = MenuApi.MakeTwoChoicePopup("", "", "");
|
||||
TwoButtonInfoPopup.OnDeactivateMenu += OnCloseInfoPopup;
|
||||
}
|
||||
|
||||
private void SetButtonActive(Button button, bool active)
|
||||
@ -174,13 +210,13 @@ namespace QSB.Menus
|
||||
{
|
||||
CreateCommonPopups();
|
||||
|
||||
HostButton = MenuApi.PauseMenu_MakeSimpleButton("OPEN TO MULTIPLAYER");
|
||||
HostButton = MenuApi.PauseMenu_MakeSimpleButton(OpenString);
|
||||
HostButton.onClick.AddListener(Host);
|
||||
|
||||
DisconnectPopup = MenuApi.MakeTwoChoicePopup("Are you sure you want to disconnect?\r\nThis will send you back to the main menu.", "YES", "NO");
|
||||
DisconnectPopup.OnPopupConfirm += Disconnect;
|
||||
|
||||
DisconnectButton = MenuApi.PauseMenu_MakeMenuOpenButton("DISCONNECT", DisconnectPopup);
|
||||
DisconnectButton = MenuApi.PauseMenu_MakeMenuOpenButton(DisconnectString, DisconnectPopup);
|
||||
|
||||
QuitButton = FindObjectOfType<PauseMenuManager>()._exitToMainMenuAction.gameObject;
|
||||
|
||||
@ -198,8 +234,8 @@ namespace QSB.Menus
|
||||
}
|
||||
|
||||
var text = QSBCore.IsHost
|
||||
? "STOP HOSTING"
|
||||
: "DISCONNECT";
|
||||
? StopHostingString
|
||||
: DisconnectString;
|
||||
DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent<Text>().text = text;
|
||||
|
||||
var popupText = QSBCore.IsHost
|
||||
@ -220,7 +256,7 @@ namespace QSB.Menus
|
||||
{
|
||||
CreateCommonPopups();
|
||||
|
||||
ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton("CONNECT TO MULTIPLAYER", _ClientButtonIndex, IPPopup);
|
||||
ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton(ConnectString, _ClientButtonIndex, IPPopup);
|
||||
_loadingText = ClientButton.transform.GetChild(0).GetChild(1).GetComponent<Text>();
|
||||
|
||||
ResumeGameButton = GameObject.Find("MainMenuLayoutGroup/Button-ResumeGame");
|
||||
@ -250,7 +286,7 @@ namespace QSB.Menus
|
||||
|
||||
if (QSBCore.DebugSettings.SkipTitleScreen)
|
||||
{
|
||||
Application.runInBackground = true;
|
||||
UnityEngine.Application.runInBackground = true;
|
||||
var titleScreenManager = FindObjectOfType<TitleScreenManager>();
|
||||
var titleScreenAnimation = titleScreenManager._cameraController;
|
||||
const float small = 1 / 1000f;
|
||||
@ -290,21 +326,37 @@ namespace QSB.Menus
|
||||
SetButtonActive(QuitButton, false);
|
||||
|
||||
var text = QSBCore.IsHost
|
||||
? "STOP HOSTING"
|
||||
: "DISCONNECT";
|
||||
? StopHostingString
|
||||
: DisconnectString;
|
||||
DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent<Text>().text = text;
|
||||
|
||||
var popupText = QSBCore.IsHost
|
||||
? "Are you sure you want to stop hosting?\r\nThis will disconnect all clients and send everyone back to the main menu."
|
||||
: "Are you sure you want to disconnect?\r\nThis will send you back to the main menu.";
|
||||
DisconnectPopup._labelText.text = popupText;
|
||||
|
||||
if (QSBCore.UseKcpTransport)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var steamId = ((FizzyFacepunch)Transport.activeTransport).GetSteamID();
|
||||
|
||||
PopupOK += () => GUIUtility.systemCopyBuffer = steamId;
|
||||
|
||||
OpenInfoPopup($"Hosting server.\r\nClients will connect using your steam id, which is :\r\n" +
|
||||
$"{steamId}\r\n" +
|
||||
$"Do you want to copy this to the clipboard?"
|
||||
, "YES"
|
||||
, "NO");
|
||||
}
|
||||
|
||||
private bool Validate()
|
||||
{
|
||||
var inputText = ((PopupInputMenu)IPPopup).GetInputText();
|
||||
var regex = new Regex(@"\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z");
|
||||
return inputText == "localhost" || regex.Match(inputText).Success;
|
||||
//var inputText = ((PopupInputMenu)IPPopup).GetInputText();
|
||||
//var regex = new Regex(@"\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z");
|
||||
//return inputText == "localhost" || regex.Match(inputText).Success;
|
||||
return ((PopupInputMenu)IPPopup).GetInputText() != "";
|
||||
}
|
||||
|
||||
private void Connect()
|
||||
@ -349,6 +401,15 @@ namespace QSB.Menus
|
||||
KickReason.None => "Kicked from server. No reason given.",
|
||||
_ => $"Kicked from server. KickReason:{reason}",
|
||||
};
|
||||
|
||||
PopupOK += () =>
|
||||
{
|
||||
if (QSBSceneManager.IsInUniverse)
|
||||
{
|
||||
LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f);
|
||||
}
|
||||
};
|
||||
|
||||
OpenInfoPopup(text, "OK");
|
||||
|
||||
SetButtonActive(DisconnectButton, false);
|
||||
@ -364,6 +425,14 @@ namespace QSB.Menus
|
||||
return;
|
||||
}
|
||||
|
||||
PopupOK += () =>
|
||||
{
|
||||
if (QSBSceneManager.IsInUniverse)
|
||||
{
|
||||
LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f);
|
||||
}
|
||||
};
|
||||
|
||||
OpenInfoPopup($"Client disconnected with error!\r\n{error}", "OK");
|
||||
|
||||
SetButtonActive(DisconnectButton, false);
|
||||
|
@ -20,7 +20,7 @@ namespace QSB.Messaging
|
||||
#region inner workings
|
||||
|
||||
internal static readonly Type[] _types;
|
||||
private static readonly Dictionary<Type, ushort> _typeToId = new();
|
||||
internal static readonly Dictionary<Type, ushort> _typeToId = new();
|
||||
|
||||
static QSBMessageManager()
|
||||
{
|
||||
@ -36,19 +36,18 @@ namespace QSB.Messaging
|
||||
public static void Init()
|
||||
{
|
||||
NetworkServer.RegisterHandler<Wrapper>((_, wrapper) => OnServerReceive(wrapper));
|
||||
NetworkClient.RegisterHandler<Wrapper>(wrapper => OnClientReceive(wrapper.Msg));
|
||||
NetworkClient.RegisterHandler<Wrapper>(wrapper => OnClientReceive(wrapper));
|
||||
}
|
||||
|
||||
private static void OnServerReceive(Wrapper wrapper)
|
||||
private static void OnServerReceive(QSBMessage msg)
|
||||
{
|
||||
var msg = wrapper.Msg;
|
||||
if (msg.To == uint.MaxValue)
|
||||
{
|
||||
NetworkServer.SendToAll(wrapper);
|
||||
NetworkServer.SendToAll<Wrapper>(msg);
|
||||
}
|
||||
else if (msg.To == 0)
|
||||
{
|
||||
NetworkServer.localConnection.Send(wrapper);
|
||||
NetworkServer.localConnection.Send<Wrapper>(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -59,7 +58,7 @@ namespace QSB.Messaging
|
||||
return;
|
||||
}
|
||||
|
||||
conn.Send(wrapper);
|
||||
conn.Send<Wrapper>(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +118,7 @@ namespace QSB.Messaging
|
||||
}
|
||||
|
||||
msg.From = QSBPlayerManager.LocalPlayerId;
|
||||
NetworkClient.Send(new Wrapper
|
||||
{
|
||||
Id = _typeToId[msg.GetType()],
|
||||
Msg = msg
|
||||
});
|
||||
NetworkClient.Send<Wrapper>(msg);
|
||||
}
|
||||
|
||||
public static void SendMessage<T, M>(this T worldObject, M msg)
|
||||
@ -137,25 +132,29 @@ namespace QSB.Messaging
|
||||
|
||||
internal struct Wrapper : NetworkMessage
|
||||
{
|
||||
internal ushort Id;
|
||||
internal QSBMessage Msg;
|
||||
public QSBMessage Msg;
|
||||
|
||||
public static implicit operator QSBMessage(Wrapper wrapper) => wrapper.Msg;
|
||||
public static implicit operator Wrapper(QSBMessage msg) => new() { Msg = msg };
|
||||
}
|
||||
|
||||
internal static class ReaderWriterExtensions
|
||||
{
|
||||
private static Wrapper ReadWrapper(this NetworkReader reader)
|
||||
private static QSBMessage ReadQSBMessage(this NetworkReader reader)
|
||||
{
|
||||
var wrapper = new Wrapper();
|
||||
wrapper.Id = reader.ReadUShort();
|
||||
wrapper.Msg = (QSBMessage)FormatterServices.GetUninitializedObject(QSBMessageManager._types[wrapper.Id]);
|
||||
wrapper.Msg.Deserialize(reader);
|
||||
return wrapper;
|
||||
var id = reader.ReadUShort();
|
||||
var type = QSBMessageManager._types[id];
|
||||
var msg = (QSBMessage)FormatterServices.GetUninitializedObject(type);
|
||||
msg.Deserialize(reader);
|
||||
return msg;
|
||||
}
|
||||
|
||||
private static void WriteWrapper(this NetworkWriter writer, Wrapper wrapper)
|
||||
private static void WriteQSBMessage(this NetworkWriter writer, QSBMessage msg)
|
||||
{
|
||||
writer.Write(wrapper.Id);
|
||||
wrapper.Msg.Serialize(writer);
|
||||
var type = msg.GetType();
|
||||
var id = QSBMessageManager._typeToId[type];
|
||||
writer.Write(id);
|
||||
msg.Serialize(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using QSB.WorldSync;
|
||||
using QSB.Messaging;
|
||||
using QSB.MeteorSync.Messages;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.MeteorSync.WorldObjects
|
||||
@ -20,6 +22,14 @@ namespace QSB.MeteorSync.WorldObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
this.SendMessage(new FragmentResyncMessage(this) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public DetachableFragment DetachableFragment;
|
||||
public bool IsDetached => DetachableFragment != null && DetachableFragment._isDetached;
|
||||
public bool IsThruWhiteHole => IsDetached && DetachableFragment._sector != null &&
|
||||
|
@ -11,7 +11,11 @@ namespace QSB.OrbSync.TransformSync
|
||||
{
|
||||
public class NomaiOrbTransformSync : UnsectoredTransformSync
|
||||
{
|
||||
protected override bool IsReady => QSBWorldSync.AllObjectsAdded;
|
||||
/// <summary>
|
||||
/// normally prints error when attached object is null.
|
||||
/// this overrides it so that doesn't happen, since the orb can be destroyed.
|
||||
/// </summary>
|
||||
protected override bool CheckValid() => _attachedBody && base.CheckValid();
|
||||
protected override bool UseInterpolation => true;
|
||||
protected override float DistanceLeeway => 1f;
|
||||
|
||||
@ -32,14 +36,6 @@ namespace QSB.OrbSync.TransformSync
|
||||
{
|
||||
_instances.Remove(this);
|
||||
base.OnStopClient();
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
_attachedBody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
_attachedBody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
@ -58,15 +54,6 @@ namespace QSB.OrbSync.TransformSync
|
||||
_attachedBody = AttachedTransform.GetAttachedOWRigidbody();
|
||||
SetReferenceTransform(_attachedBody.GetOrigParent());
|
||||
|
||||
/*
|
||||
if (_attachedBody.GetOrigParent() == Locator.GetRootTransform())
|
||||
{
|
||||
DebugLog.DebugWrite($"{LogName} with AttachedObject {AttachedObject.name} had it's original parent as SolarSystemRoot - Disabling...");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.RegisterAuthQueue();
|
||||
@ -77,6 +64,20 @@ namespace QSB.OrbSync.TransformSync
|
||||
netIdentity.SendAuthQueueMessage(_attachedBody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
_attachedBody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
_attachedBody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
_attachedBody = null;
|
||||
|
||||
base.Uninit();
|
||||
}
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.SendAuthQueueMessage(AuthQueueAction.Remove);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using Mirror;
|
||||
using QSB.Messaging;
|
||||
using QSB.OrbSync.Messages;
|
||||
using QSB.OrbSync.TransformSync;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
@ -8,6 +10,8 @@ namespace QSB.OrbSync.WorldObjects
|
||||
{
|
||||
public class QSBOrb : WorldObject<NomaiInterfaceOrb>
|
||||
{
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public NomaiOrbTransformSync TransformSync;
|
||||
|
||||
public override void Init()
|
||||
@ -29,6 +33,15 @@ namespace QSB.OrbSync.WorldObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (TransformSync.hasAuthority)
|
||||
{
|
||||
this.SendMessage(new OrbDragMessage(AttachedObject._isBeingDragged) { To = to });
|
||||
this.SendMessage(new OrbSlotMessage(AttachedObject._slots.IndexOf(AttachedObject._occupiedSlot)) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDragging(bool value)
|
||||
{
|
||||
if (value == AttachedObject._isBeingDragged)
|
||||
|
@ -1,27 +1,11 @@
|
||||
using OWML.Common;
|
||||
using QSB.CampfireSync.Messages;
|
||||
using QSB.CampfireSync.WorldObjects;
|
||||
using QSB.ClientServerStateSync;
|
||||
using QSB.ClientServerStateSync.Messages;
|
||||
using QSB.ConversationSync.Messages;
|
||||
using QSB.LogSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.MeteorSync.Messages;
|
||||
using QSB.MeteorSync.WorldObjects;
|
||||
using QSB.OrbSync.Messages;
|
||||
using QSB.OrbSync.WorldObjects;
|
||||
using QSB.QuantumSync.Messages;
|
||||
using QSB.QuantumSync.WorldObjects;
|
||||
using QSB.Tools.TranslatorTool.TranslationSync.Messages;
|
||||
using QSB.Tools.TranslatorTool.TranslationSync.WorldObjects;
|
||||
using QSB.TornadoSync.Messages;
|
||||
using QSB.TornadoSync.WorldObjects;
|
||||
using QSB.TriggerSync.Messages;
|
||||
using QSB.TriggerSync.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Player.Messages
|
||||
{
|
||||
@ -54,7 +38,11 @@ namespace QSB.Player.Messages
|
||||
{
|
||||
if (_waitingForEvent)
|
||||
{
|
||||
DebugLog.ToConsole($"Did not receive PlayerInformationEvent in time. Setting _waitingForEvent to false.", MessageType.Info);
|
||||
if (QSBPlayerManager.PlayerList.Count > 1)
|
||||
{
|
||||
DebugLog.ToConsole($"Did not receive PlayerInformationEvent in time. Setting _waitingForEvent to false.", MessageType.Info);
|
||||
}
|
||||
|
||||
_waitingForEvent = false;
|
||||
}
|
||||
}, 60);
|
||||
@ -70,7 +58,11 @@ namespace QSB.Player.Messages
|
||||
|
||||
if (QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
SendWorldObjectInfo();
|
||||
QSBWorldSync.DialogueConditions.ForEach(condition
|
||||
=> new DialogueConditionMessage(condition.Key, condition.Value) { To = From }.Send());
|
||||
|
||||
QSBWorldSync.ShipLogFacts.ForEach(fact
|
||||
=> new RevealFactMessage(fact.Id, fact.SaveGame, false) { To = From }.Send());
|
||||
}
|
||||
}
|
||||
// if client, send player and client states
|
||||
@ -81,72 +73,10 @@ namespace QSB.Player.Messages
|
||||
|
||||
if (QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
SendAuthorityObjectInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private void SendWorldObjectInfo()
|
||||
{
|
||||
QSBWorldSync.DialogueConditions.ForEach(condition
|
||||
=> new DialogueConditionMessage(condition.Key, condition.Value) { To = From }.Send());
|
||||
|
||||
QSBWorldSync.ShipLogFacts.ForEach(fact
|
||||
=> new RevealFactMessage(fact.Id, fact.SaveGame, false) { To = From }.Send());
|
||||
|
||||
foreach (var text in QSBWorldSync.GetWorldObjects<QSBNomaiText>())
|
||||
{
|
||||
text.GetTranslatedIds().ForEach(id =>
|
||||
text.SendMessage(new SetAsTranslatedMessage(id) { To = From }));
|
||||
}
|
||||
|
||||
QSBWorldSync.GetWorldObjects<IQSBQuantumObject>().ForEach(x =>
|
||||
{
|
||||
x.SendMessage(new QuantumAuthorityMessage(x.ControllingPlayer) { To = From });
|
||||
|
||||
if (x is QSBQuantumMoon qsbQuantumMoon)
|
||||
foreach (var worldObject in QSBWorldSync.GetWorldObjects())
|
||||
{
|
||||
var moon = qsbQuantumMoon.AttachedObject;
|
||||
var moonBody = moon._moonBody;
|
||||
var stateIndex = moon.GetStateIndex();
|
||||
var orbit = moon._orbits.First(y => y.GetStateIndex() == stateIndex);
|
||||
var orbitBody = orbit.GetAttachedOWRigidbody();
|
||||
var relPos = moonBody.GetWorldCenterOfMass() - orbitBody.GetWorldCenterOfMass();
|
||||
var relVel = moonBody.GetVelocity() - orbitBody.GetVelocity();
|
||||
var onUnitSphere = relPos.normalized;
|
||||
var perpendicular = Vector3.Cross(relPos, Vector3.up).normalized;
|
||||
var orbitAngle = (int)OWMath.WrapAngle(OWMath.Angle(perpendicular, relVel, relPos));
|
||||
|
||||
new MoonStateChangeMessage(stateIndex, onUnitSphere, orbitAngle) { To = From }.Send();
|
||||
worldObject.SendResyncInfo(From);
|
||||
}
|
||||
});
|
||||
|
||||
QSBWorldSync.GetWorldObjects<QSBCampfire>().ForEach(campfire
|
||||
=> campfire.SendMessage(new CampfireStateMessage(campfire.GetState()) { To = From }));
|
||||
|
||||
QSBWorldSync.GetWorldObjects<QSBFragment>().ForEach(fragment
|
||||
=> fragment.SendMessage(new FragmentResyncMessage(fragment) { To = From }));
|
||||
|
||||
QSBWorldSync.GetWorldObjects<QSBTornado>().ForEach(tornado
|
||||
=> tornado.SendMessage(new TornadoFormStateMessage(tornado.FormState) { To = From }));
|
||||
|
||||
QSBWorldSync.GetWorldObjects<IQSBTrigger>().ForEach(trigger
|
||||
=> trigger.SendMessage(new TriggerResyncMessage(trigger.Occupants) { To = From }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// send info for objects we have authority over
|
||||
/// </summary>
|
||||
private void SendAuthorityObjectInfo()
|
||||
{
|
||||
foreach (var qsbOrb in QSBWorldSync.GetWorldObjects<QSBOrb>())
|
||||
{
|
||||
if (!qsbOrb.TransformSync.hasAuthority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
qsbOrb.SendMessage(new OrbDragMessage(qsbOrb.AttachedObject._isBeingDragged) { To = From });
|
||||
qsbOrb.SendMessage(new OrbSlotMessage(qsbOrb.AttachedObject._slots.IndexOf(qsbOrb.AttachedObject._occupiedSlot)) { To = From });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,16 @@ namespace QSB.Player
|
||||
public ClientState State { get; set; }
|
||||
public EyeState EyeState { get; set; }
|
||||
public bool IsDead { get; set; }
|
||||
public bool Visible => DitheringAnimator != null && DitheringAnimator._visible;
|
||||
public bool Visible => IsLocalPlayer || !_ditheringAnimator || _ditheringAnimator._visible;
|
||||
public bool IsReady { get; set; }
|
||||
public bool IsInMoon { get; set; }
|
||||
public bool IsInShrine { get; set; }
|
||||
public bool IsInEyeShuttle { get; set; }
|
||||
public IQSBQuantumObject EntangledObject { get; set; }
|
||||
public QSBPlayerAudioController AudioController { get; set; }
|
||||
public DitheringAnimator DitheringAnimator { get; set; }
|
||||
internal DitheringAnimator _ditheringAnimator;
|
||||
|
||||
public bool IsLocalPlayer => TransformSync.isLocalPlayer;
|
||||
|
||||
// Body Objects
|
||||
public OWCamera Camera
|
||||
@ -136,7 +138,7 @@ namespace QSB.Player
|
||||
{
|
||||
get
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer != this)
|
||||
if (!IsLocalPlayer)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Tried to access local-only property LocalProbeLauncher in PlayerInfo for non local player!", MessageType.Warning);
|
||||
return null;
|
||||
@ -150,7 +152,7 @@ namespace QSB.Player
|
||||
{
|
||||
get
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer != this)
|
||||
if (!IsLocalPlayer)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Tried to access local-only property LocalFlashlight in PlayerInfo for non local player!", MessageType.Warning);
|
||||
return null;
|
||||
@ -164,7 +166,7 @@ namespace QSB.Player
|
||||
{
|
||||
get
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer != this)
|
||||
if (!IsLocalPlayer)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Tried to access local-only property LocalSignalscope in PlayerInfo for non local player!", MessageType.Warning);
|
||||
return null;
|
||||
@ -178,7 +180,7 @@ namespace QSB.Player
|
||||
{
|
||||
get
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer != this)
|
||||
if (!IsLocalPlayer)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Tried to access local-only property LocalTranslator in PlayerInfo for non local player!", MessageType.Warning);
|
||||
return null;
|
||||
@ -257,5 +259,28 @@ namespace QSB.Player
|
||||
|
||||
return tool;
|
||||
}
|
||||
|
||||
public void SetVisible(bool visible, float seconds = 0)
|
||||
{
|
||||
if (IsLocalPlayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_ditheringAnimator)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {PlayerId}.DitheringAnimator is null!", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seconds == 0)
|
||||
{
|
||||
_ditheringAnimator.SetVisibleImmediate(visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ditheringAnimator.SetVisible(visible, 1 / seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +88,10 @@ namespace QSB.Player
|
||||
=> new(Locator.GetFlashlight(), PlayerList.Where(x => x.FlashLight != null).Select(x => x.FlashLight));
|
||||
|
||||
public static void ShowAllPlayers()
|
||||
=> PlayerList.Where(x => x != LocalPlayer && x.DitheringAnimator != null).ForEach(x => x.DitheringAnimator.SetVisible(true, 0.5f));
|
||||
=> PlayerList.ForEach(x => x.SetVisible(true, 2));
|
||||
|
||||
public static void HideAllPlayers()
|
||||
=> PlayerList.Where(x => x != LocalPlayer && x.DitheringAnimator != null).ForEach(x => x.DitheringAnimator.SetVisible(false, 0.5f));
|
||||
=> PlayerList.ForEach(x => x.SetVisible(false, 2));
|
||||
|
||||
public static PlayerInfo GetClosestPlayerToWorldPoint(Vector3 worldPoint, bool includeLocalPlayer) => includeLocalPlayer
|
||||
? GetClosestPlayerToWorldPoint(PlayerList, worldPoint)
|
||||
|
@ -54,28 +54,6 @@ namespace QSB.Player.TransformSync
|
||||
|
||||
public override void OnStartLocalPlayer() => LocalInstance = this;
|
||||
|
||||
protected override void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse)
|
||||
{
|
||||
base.OnSceneLoaded(oldScene, newScene, isInUniverse);
|
||||
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
Player.IsReady = false;
|
||||
new PlayerReadyMessage(false).Send();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
Player.IsReady = true;
|
||||
new PlayerReadyMessage(true).Send();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
// TODO : Maybe move this to a leave event...? Would ensure everything could finish up before removing the player
|
||||
@ -86,9 +64,20 @@ namespace QSB.Player.TransformSync
|
||||
DebugLog.DebugWrite($"Remove Player : id<{Player.PlayerId}>", MessageType.Info);
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
Player.IsReady = false;
|
||||
new PlayerReadyMessage(false).Send();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Transform InitLocalTransform()
|
||||
{
|
||||
SectorSync.Init(Locator.GetPlayerSectorDetector(), TargetType.Player);
|
||||
SectorDetector.Init(Locator.GetPlayerSectorDetector(), TargetType.Player);
|
||||
|
||||
// player body
|
||||
var player = Locator.GetPlayerTransform();
|
||||
@ -112,6 +101,9 @@ namespace QSB.Player.TransformSync
|
||||
_visibleStickPivot = pivot;
|
||||
_visibleStickTip = pivot.Find("Stick_Tip");
|
||||
|
||||
Player.IsReady = true;
|
||||
new PlayerReadyMessage(true).Send();
|
||||
|
||||
new RequestStateResyncMessage().Send();
|
||||
|
||||
return player;
|
||||
@ -160,10 +152,10 @@ namespace QSB.Player.TransformSync
|
||||
|
||||
REMOTE_Player_Body.AddComponent<PlayerHUDMarker>().Init(Player);
|
||||
REMOTE_Player_Body.AddComponent<PlayerMapMarker>().PlayerName = Player.Name;
|
||||
Player.DitheringAnimator = REMOTE_Player_Body.AddComponent<DitheringAnimator>();
|
||||
Player._ditheringAnimator = REMOTE_Player_Body.AddComponent<DitheringAnimator>();
|
||||
// get inactive renderers too
|
||||
QSBCore.UnityEvents.FireOnNextUpdate(() =>
|
||||
Player.DitheringAnimator._renderers = Player.DitheringAnimator
|
||||
Player._ditheringAnimator._renderers = Player._ditheringAnimator
|
||||
.GetComponentsInChildren<Renderer>(true)
|
||||
.Select(x => x.gameObject.GetAddComponent<OWRenderer>())
|
||||
.ToArray());
|
||||
@ -261,8 +253,9 @@ namespace QSB.Player.TransformSync
|
||||
protected override void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsInitialized
|
||||
|| ReferenceTransform == null)
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -280,9 +273,8 @@ namespace QSB.Player.TransformSync
|
||||
Popcron.Gizmos.Cube(_visibleCameraRoot.position, _visibleCameraRoot.rotation, Vector3.one / 4, Color.grey);
|
||||
}
|
||||
|
||||
protected override bool IsReady
|
||||
=> AttachedTransform != null
|
||||
|| Locator.GetPlayerTransform() != null;
|
||||
protected override bool CheckReady() => base.CheckReady()
|
||||
&& (Locator.GetPlayerTransform() || AttachedTransform);
|
||||
|
||||
public static PlayerTransformSync LocalInstance { get; private set; }
|
||||
|
||||
|
@ -675,7 +675,7 @@ namespace QSB.PoolSync
|
||||
|
||||
public void OnRemovePlayer(PlayerInfo player)
|
||||
{
|
||||
if (player == QSBPlayerManager.LocalPlayer)
|
||||
if (player.IsLocalPlayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -69,6 +69,8 @@ copy /y "$(OwmlDir)\OWML.Abstractions.dll" "$(UnityAssetsDir)"
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.125" IncludeAssets="compile" />
|
||||
<Reference Include="..\Mirror\*.dll" />
|
||||
<ProjectReference Include="..\FizzyFacepunch\FizzyFacepunch.csproj" />
|
||||
<ProjectReference Include="..\QSBPatcher\QSBPatcher.csproj" />
|
||||
<ProjectReference Include="..\MirrorWeaver\MirrorWeaver.csproj" ReferenceOutputAssembly="false" />
|
||||
<PackageReference Include="HarmonyX" Version="2.8.0" IncludeAssets="compile" />
|
||||
<PackageReference Include="OWML" Version="2.3.1" IncludeAssets="compile" />
|
||||
|
@ -45,7 +45,15 @@ namespace QSB
|
||||
public static IModHelper Helper { get; private set; }
|
||||
public static IModUnityEvents UnityEvents => Helper.Events.Unity;
|
||||
public static string DefaultServerIP { get; private set; }
|
||||
public static int Port { get; private set; }
|
||||
public static bool UseKcpTransport => DebugSettings.UseKcpTransport;
|
||||
public static int OverrideAppId => DebugSettings.OverrideAppId;
|
||||
public static bool DebugMode => DebugSettings.DebugMode;
|
||||
public static bool ShowLinesInDebug => DebugMode && DebugSettings.DrawLines;
|
||||
public static bool ShowQuantumVisibilityObjects => DebugMode && DebugSettings.ShowQuantumVisibilityObjects;
|
||||
public static bool ShowDebugLabels => DebugMode && DebugSettings.ShowDebugLabels;
|
||||
public static bool AvoidTimeSync => DebugMode && DebugSettings.AvoidTimeSync;
|
||||
public static bool SkipTitleScreen => DebugMode && DebugSettings.SkipTitleScreen;
|
||||
public static bool GreySkybox => DebugMode && DebugSettings.GreySkybox;
|
||||
public static AssetBundle NetworkAssetBundle { get; internal set; }
|
||||
public static AssetBundle InstrumentAssetBundle { get; private set; }
|
||||
public static AssetBundle ConversationAssetBundle { get; private set; }
|
||||
@ -134,10 +142,6 @@ namespace QSB
|
||||
if (type == QSBPatchTypes.OnClientConnect)
|
||||
{
|
||||
Application.runInBackground = true;
|
||||
if (Locator.GetSceneMenuManager() != null && Locator.GetSceneMenuManager().pauseMenu.IsOpen())
|
||||
{
|
||||
Locator.GetSceneMenuManager().pauseMenu._pauseMenu.EnableMenu(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,17 +156,11 @@ namespace QSB
|
||||
public override void Configure(IModConfig config)
|
||||
{
|
||||
DefaultServerIP = config.GetSettingsValue<string>("defaultServerIP");
|
||||
Port = config.GetSettingsValue<int>("port");
|
||||
if (QSBNetworkManager.singleton != null)
|
||||
{
|
||||
QSBNetworkManager.singleton.Port = Port;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Keyboard.current[Key.LeftArrow].isPressed && Keyboard.current[Key.RightArrow].isPressed &&
|
||||
(Keyboard.current[Key.LeftArrow].wasPressedThisFrame || Keyboard.current[Key.RightArrow].wasPressedThisFrame))
|
||||
if (Keyboard.current[Key.Q].isPressed && Keyboard.current[Key.D].wasPressedThisFrame)
|
||||
{
|
||||
DebugSettings.DebugMode = !DebugSettings.DebugMode;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Mirror;
|
||||
using Mirror.FizzySteam;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using QSB.Anglerfish.TransformSync;
|
||||
@ -47,10 +48,6 @@ namespace QSB
|
||||
private GameObject _probePrefab;
|
||||
private bool _everConnected;
|
||||
|
||||
public int Port
|
||||
{
|
||||
set => ((kcp2k.KcpTransport)transport).Port = (ushort)value;
|
||||
}
|
||||
private string _lastTransportError;
|
||||
internal bool _intentionalDisconnect;
|
||||
|
||||
@ -64,7 +61,23 @@ namespace QSB
|
||||
.Where(x => x.GetCustomAttribute<RuntimeInitializeOnLoadMethodAttribute>() != null)
|
||||
.ForEach(x => x.Invoke(null, null));
|
||||
|
||||
transport = gameObject.AddComponent<kcp2k.KcpTransport>();
|
||||
gameObject.SetActive(false);
|
||||
|
||||
if (QSBCore.UseKcpTransport)
|
||||
{
|
||||
transport = gameObject.AddComponent<kcp2k.KcpTransport>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var fizzy = gameObject.AddComponent<FizzyFacepunch>();
|
||||
fizzy.SteamAppID = QSBCore.OverrideAppId == -1
|
||||
? "753640"
|
||||
: $"{QSBCore.OverrideAppId}";
|
||||
transport = fizzy;
|
||||
}
|
||||
|
||||
gameObject.SetActive(true);
|
||||
|
||||
base.Awake();
|
||||
|
||||
InitPlayerName();
|
||||
@ -144,7 +157,6 @@ namespace QSB
|
||||
private void ConfigureNetworkManager()
|
||||
{
|
||||
networkAddress = QSBCore.DefaultServerIP;
|
||||
Port = QSBCore.Port;
|
||||
maxConnections = MaxConnections;
|
||||
|
||||
kcp2k.Log.Info = s => DebugLog.DebugWrite("[KCP] " + s);
|
||||
|
@ -1,7 +1,33 @@
|
||||
namespace QSB.QuantumSync.WorldObjects
|
||||
using QSB.Messaging;
|
||||
using QSB.QuantumSync.Messages;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.QuantumSync.WorldObjects
|
||||
{
|
||||
internal class QSBQuantumMoon : QSBQuantumObject<QuantumMoon>
|
||||
{
|
||||
protected override bool HostControls => true;
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
base.SendResyncInfo(to);
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
var moon = AttachedObject;
|
||||
var moonBody = moon._moonBody;
|
||||
var stateIndex = moon.GetStateIndex();
|
||||
var orbit = moon._orbits.First(y => y.GetStateIndex() == stateIndex);
|
||||
var orbitBody = orbit.GetAttachedOWRigidbody();
|
||||
var relPos = moonBody.GetWorldCenterOfMass() - orbitBody.GetWorldCenterOfMass();
|
||||
var relVel = moonBody.GetVelocity() - orbitBody.GetVelocity();
|
||||
var onUnitSphere = relPos.normalized;
|
||||
var perpendicular = Vector3.Cross(relPos, Vector3.up).normalized;
|
||||
var orbitAngle = (int)OWMath.WrapAngle(OWMath.Angle(perpendicular, relVel, relPos));
|
||||
|
||||
new MoonStateChangeMessage(stateIndex, onUnitSphere, orbitAngle) { To = to }.Send();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace QSB.QuantumSync.WorldObjects
|
||||
{
|
||||
@ -78,6 +77,14 @@ namespace QSB.QuantumSync.WorldObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
((IQSBQuantumObject)this).SendMessage(new QuantumAuthorityMessage(ControllingPlayer) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public List<Shape> GetAttachedShapes()
|
||||
{
|
||||
if (AttachedObject == null)
|
||||
|
@ -153,14 +153,7 @@ namespace QSB.RespawnSync
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.DitheringAnimator != null)
|
||||
{
|
||||
player.DitheringAnimator.SetVisible(false, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {player.PlayerId}.DitheringAnimator is null!", OWML.Common.MessageType.Warning);
|
||||
}
|
||||
player.SetVisible(false, 1);
|
||||
}
|
||||
|
||||
public void OnPlayerRespawn(PlayerInfo player)
|
||||
@ -176,14 +169,7 @@ namespace QSB.RespawnSync
|
||||
_playersPendingRespawn.Remove(player);
|
||||
UpdateRespawnNotification();
|
||||
|
||||
if (player.DitheringAnimator != null)
|
||||
{
|
||||
player.DitheringAnimator.SetVisible(true, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {player.PlayerId}.DitheringAnimator is null!", OWML.Common.MessageType.Warning);
|
||||
}
|
||||
player.SetVisible(true, 1);
|
||||
}
|
||||
|
||||
public void RespawnSomePlayer()
|
||||
|
@ -40,11 +40,7 @@ namespace QSB.SaveSync.Messages
|
||||
writer.Write(LaunchCodesGiven);
|
||||
writer.Write(LoopCount);
|
||||
|
||||
writer.Write(KnownFrequencies.Length);
|
||||
foreach (var item in KnownFrequencies)
|
||||
{
|
||||
writer.Write(item);
|
||||
}
|
||||
writer.WriteArray(KnownFrequencies);
|
||||
|
||||
writer.Write(KnownSignals.Count);
|
||||
foreach (var (name, discovered) in KnownSignals)
|
||||
@ -62,12 +58,7 @@ namespace QSB.SaveSync.Messages
|
||||
LaunchCodesGiven = reader.Read<bool>();
|
||||
LoopCount = reader.Read<int>();
|
||||
|
||||
var frequenciesLength = reader.Read<int>();
|
||||
KnownFrequencies = new bool[frequenciesLength];
|
||||
for (var i = 0; i < frequenciesLength; i++)
|
||||
{
|
||||
KnownFrequencies[i] = reader.Read<bool>();
|
||||
}
|
||||
KnownFrequencies = reader.ReadArray<bool>();
|
||||
|
||||
var signalsLength = reader.Read<int>();
|
||||
KnownSignals = new Dictionary<int, bool>(signalsLength);
|
||||
|
@ -9,36 +9,23 @@ using UnityEngine;
|
||||
|
||||
namespace QSB.SectorSync
|
||||
{
|
||||
public class SectorSync : MonoBehaviour
|
||||
public class QSBSectorDetector : MonoBehaviour
|
||||
{
|
||||
public bool IsReady { get; private set; }
|
||||
public readonly List<QSBSector> SectorList = new();
|
||||
|
||||
private SectorDetector _sectorDetector;
|
||||
private TargetType _targetType;
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_sectorDetector != null)
|
||||
{
|
||||
_sectorDetector.OnEnterSector -= AddSector;
|
||||
_sectorDetector.OnExitSector -= RemoveSector;
|
||||
}
|
||||
|
||||
IsReady = false;
|
||||
}
|
||||
|
||||
public void Init(SectorDetector detector, TargetType type)
|
||||
{
|
||||
if (_sectorDetector != null)
|
||||
if (_sectorDetector)
|
||||
{
|
||||
_sectorDetector.OnEnterSector -= AddSector;
|
||||
_sectorDetector.OnExitSector -= RemoveSector;
|
||||
return;
|
||||
}
|
||||
|
||||
if (detector == null)
|
||||
if (!detector)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to init SectorSync with null SectorDetector.", MessageType.Error);
|
||||
DebugLog.ToConsole("Error - Trying to init QSBSectorDetector with null SectorDetector!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -46,15 +33,34 @@ namespace QSB.SectorSync
|
||||
_sectorDetector.OnEnterSector += AddSector;
|
||||
_sectorDetector.OnExitSector += RemoveSector;
|
||||
|
||||
SectorList.Clear();
|
||||
_sectorDetector._sectorList.ForEach(AddSector);
|
||||
|
||||
_targetType = type;
|
||||
IsReady = true;
|
||||
}
|
||||
|
||||
public void Uninit()
|
||||
{
|
||||
if (!_sectorDetector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sectorDetector.OnEnterSector -= AddSector;
|
||||
_sectorDetector.OnExitSector -= RemoveSector;
|
||||
_sectorDetector = null;
|
||||
|
||||
SectorList.Clear();
|
||||
}
|
||||
|
||||
private void AddSector(Sector sector)
|
||||
{
|
||||
if (!sector)
|
||||
{
|
||||
// wtf
|
||||
DebugLog.ToConsole($"Warning - Trying to add {sector.name} for {gameObject.name}, but it is null", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var worldObject = sector.GetWorldObject<QSBSector>();
|
||||
if (worldObject == null)
|
||||
{
|
||||
@ -73,6 +79,13 @@ namespace QSB.SectorSync
|
||||
|
||||
private void RemoveSector(Sector sector)
|
||||
{
|
||||
if (!sector)
|
||||
{
|
||||
// wtf
|
||||
DebugLog.ToConsole($"Warning - Trying to remove {sector.name} for {gameObject.name}, but it is null", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var worldObject = sector.GetWorldObject<QSBSector>();
|
||||
if (worldObject == null)
|
||||
{
|
||||
@ -89,25 +102,11 @@ namespace QSB.SectorSync
|
||||
SectorList.Remove(worldObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called only by the sector manager
|
||||
/// </summary>
|
||||
public QSBSector GetClosestSector()
|
||||
{
|
||||
if (QSBSectorManager.Instance == null || !QSBSectorManager.Instance.IsReady)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!IsReady)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Tried to use GetClosestSector() before this SectorSync is ready. Stacktrace:\r\n{Environment.StackTrace}", MessageType.Warning);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_sectorDetector == null)
|
||||
{
|
||||
IsReady = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
var inASector = SectorList.Any(x => x.ShouldSyncTo(_targetType));
|
||||
|
||||
var listToCheck = inASector
|
||||
@ -156,12 +155,9 @@ namespace QSB.SectorSync
|
||||
{
|
||||
// TODO : make this work for other stuff, not just shaped triggervolumes
|
||||
var trigger = sector.AttachedObject.GetTriggerVolume();
|
||||
if (trigger != null)
|
||||
if (trigger && trigger.GetShape())
|
||||
{
|
||||
if (trigger.GetShape() != null)
|
||||
{
|
||||
return trigger.GetShape().CalcWorldBounds().radius;
|
||||
}
|
||||
return trigger.GetShape().CalcWorldBounds().radius;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
@ -170,7 +166,7 @@ namespace QSB.SectorSync
|
||||
private static float GetRelativeVelocity(QSBSector sector, OWRigidbody rigidbody)
|
||||
{
|
||||
var sectorRigidBody = sector.AttachedObject.GetOWRigidbody();
|
||||
if (sectorRigidBody != null && rigidbody != null)
|
||||
if (sectorRigidBody && rigidbody)
|
||||
{
|
||||
var relativeVelocity = sectorRigidBody.GetRelativeVelocity(rigidbody);
|
||||
return relativeVelocity.sqrMagnitude;
|
@ -14,10 +14,10 @@ namespace QSB.SectorSync
|
||||
public override WorldObjectType WorldObjectType => WorldObjectType.Both;
|
||||
|
||||
public static QSBSectorManager Instance { get; private set; }
|
||||
public bool IsReady { get; private set; }
|
||||
private bool _isReady;
|
||||
public readonly List<QSBSector> FakeSectors = new();
|
||||
|
||||
public readonly List<BaseSectoredSync> TransformSyncs = new();
|
||||
public readonly List<BaseSectoredSync> SectoredSyncs = new();
|
||||
|
||||
private const float UpdateInterval = 0.4f;
|
||||
private float _timer = UpdateInterval;
|
||||
@ -36,28 +36,33 @@ namespace QSB.SectorSync
|
||||
|
||||
public void UpdateReferenceSectors()
|
||||
{
|
||||
if (!Instance.IsReady || !QSBWorldSync.AllObjectsReady)
|
||||
if (!Instance._isReady || !QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var sync in TransformSyncs)
|
||||
foreach (var sync in SectoredSyncs)
|
||||
{
|
||||
if (sync.AttachedTransform == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sync.hasAuthority
|
||||
&& sync.AttachedTransform.gameObject.activeInHierarchy
|
||||
&& sync.IsInitialized
|
||||
&& sync.SectorSync.IsReady)
|
||||
&& sync.IsValid
|
||||
&& sync.AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
UpdateReferenceSector(sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateReferenceSector(BaseSectoredSync sync)
|
||||
{
|
||||
var closestSector = sync.SectorDetector.GetClosestSector();
|
||||
if (closestSector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sync.SetReferenceSector(closestSector);
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
@ -84,21 +89,10 @@ namespace QSB.SectorSync
|
||||
}
|
||||
|
||||
QSBWorldSync.Init<QSBSector, Sector>();
|
||||
IsReady = QSBWorldSync.GetWorldObjects<QSBSector>().Any();
|
||||
_isReady = QSBWorldSync.GetWorldObjects<QSBSector>().Any();
|
||||
}
|
||||
|
||||
public override void UnbuildWorldObjects() =>
|
||||
IsReady = false;
|
||||
|
||||
private static void UpdateReferenceSector(BaseSectoredSync transformSync)
|
||||
{
|
||||
var closestSector = transformSync.SectorSync.GetClosestSector();
|
||||
if (closestSector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
transformSync.SetReferenceSector(closestSector);
|
||||
}
|
||||
_isReady = false;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ namespace QSB.ShipSync.TransformSync
|
||||
private const int ForcePositionAfterUpdates = 50;
|
||||
private int _updateCount;
|
||||
|
||||
protected override bool IsReady
|
||||
=> Locator.GetShipBody() != null;
|
||||
protected override bool CheckReady() => base.CheckReady()
|
||||
&& Locator.GetShipBody();
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
@ -23,7 +23,7 @@ namespace QSB.ShipSync.TransformSync
|
||||
|
||||
protected override OWRigidbody InitAttachedRigidbody()
|
||||
{
|
||||
SectorSync.Init(Locator.GetShipDetector().GetComponent<SectorDetector>(), TargetType.Ship);
|
||||
SectorDetector.Init(Locator.GetShipDetector().GetComponent<SectorDetector>(), TargetType.Ship);
|
||||
return Locator.GetShipBody();
|
||||
}
|
||||
|
||||
@ -31,8 +31,7 @@ namespace QSB.ShipSync.TransformSync
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
ApplyToSector();
|
||||
|
||||
if (ReferenceTransform == null || transform.position == Vector3.zero)
|
||||
if (!ReferenceTransform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -7,30 +7,32 @@ namespace QSB.Syncs.Sectored
|
||||
{
|
||||
public abstract class BaseSectoredSync : SyncBase
|
||||
{
|
||||
protected override bool AllowNullReferenceTransform => true;
|
||||
protected sealed override bool AllowNullReferenceTransform => true;
|
||||
|
||||
public QSBSector ReferenceSector { get; private set; }
|
||||
public SectorSync.SectorSync SectorSync { get; private set; }
|
||||
public QSBSectorDetector SectorDetector { get; private set; }
|
||||
|
||||
private int _sectorId = -1;
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
SectorSync = gameObject.AddComponent<SectorSync.SectorSync>();
|
||||
QSBSectorManager.Instance.TransformSyncs.Add(this);
|
||||
SectorDetector = gameObject.AddComponent<QSBSectorDetector>();
|
||||
QSBSectorManager.Instance.SectoredSyncs.Add(this);
|
||||
base.OnStartClient();
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
base.OnStopClient();
|
||||
QSBSectorManager.Instance.TransformSyncs.Remove(this);
|
||||
Destroy(SectorSync);
|
||||
QSBSectorManager.Instance.SectoredSyncs.Remove(this);
|
||||
Destroy(SectorDetector);
|
||||
}
|
||||
|
||||
protected override void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse)
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.OnSceneLoaded(oldScene, newScene, isInUniverse);
|
||||
base.Uninit();
|
||||
|
||||
SectorDetector.Uninit();
|
||||
SetReferenceSector(null);
|
||||
}
|
||||
|
||||
@ -48,14 +50,7 @@ namespace QSB.Syncs.Sectored
|
||||
|
||||
protected void GetFromSector()
|
||||
{
|
||||
if (ReferenceSector != null)
|
||||
{
|
||||
_sectorId = ReferenceSector.ObjectId;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sectorId = -1;
|
||||
}
|
||||
_sectorId = ReferenceSector?.ObjectId ?? -1;
|
||||
}
|
||||
|
||||
protected void ApplyToSector()
|
||||
|
@ -20,7 +20,7 @@ namespace QSB.Syncs.Sectored.Rigidbodies
|
||||
|
||||
protected abstract OWRigidbody InitAttachedRigidbody();
|
||||
|
||||
protected override Transform InitAttachedTransform()
|
||||
protected sealed override Transform InitAttachedTransform()
|
||||
{
|
||||
AttachedRigidbody = InitAttachedRigidbody();
|
||||
return AttachedRigidbody.transform;
|
||||
@ -47,31 +47,30 @@ namespace QSB.Syncs.Sectored.Rigidbodies
|
||||
_relativeAngularVelocity = reader.ReadVector3();
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
AttachedRigidbody = null;
|
||||
}
|
||||
|
||||
protected override void GetFromAttached()
|
||||
{
|
||||
GetFromSector();
|
||||
if (!ReferenceTransform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceTransform != null)
|
||||
{
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedRigidbody.GetPosition());
|
||||
transform.rotation = ReferenceTransform.ToRelRot(AttachedRigidbody.GetRotation());
|
||||
_relativeVelocity = ReferenceTransform.GetAttachedOWRigidbody().ToRelVel(AttachedRigidbody.GetVelocity(), AttachedRigidbody.GetPosition());
|
||||
_relativeAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().ToRelAngVel(AttachedRigidbody.GetAngularVelocity());
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = Vector3.zero;
|
||||
transform.rotation = Quaternion.identity;
|
||||
_relativeVelocity = Vector3.zero;
|
||||
_relativeAngularVelocity = Vector3.zero;
|
||||
}
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedRigidbody.GetPosition());
|
||||
transform.rotation = ReferenceTransform.ToRelRot(AttachedRigidbody.GetRotation());
|
||||
_relativeVelocity = ReferenceTransform.GetAttachedOWRigidbody().ToRelVel(AttachedRigidbody.GetVelocity(), AttachedRigidbody.GetPosition());
|
||||
_relativeAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().ToRelAngVel(AttachedRigidbody.GetAngularVelocity());
|
||||
}
|
||||
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
ApplyToSector();
|
||||
|
||||
if (ReferenceTransform == null || transform.position == Vector3.zero)
|
||||
if (!ReferenceTransform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Mirror;
|
||||
using QSB.Utility;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Syncs.Sectored.Transforms
|
||||
@ -9,52 +8,54 @@ namespace QSB.Syncs.Sectored.Transforms
|
||||
protected abstract Transform InitLocalTransform();
|
||||
protected abstract Transform InitRemoteTransform();
|
||||
|
||||
protected override Transform InitAttachedTransform()
|
||||
protected sealed override Transform InitAttachedTransform()
|
||||
=> hasAuthority ? InitLocalTransform() : InitRemoteTransform();
|
||||
|
||||
protected override void Deserialize(NetworkReader reader, bool initialState)
|
||||
{
|
||||
base.Deserialize(reader, initialState);
|
||||
|
||||
if (transform.position == Vector3.zero)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetFromAttached()
|
||||
{
|
||||
GetFromSector();
|
||||
if (!ReferenceTransform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceTransform != null)
|
||||
{
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedTransform.position);
|
||||
transform.rotation = ReferenceTransform.ToRelRot(AttachedTransform.rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = Vector3.zero;
|
||||
transform.rotation = Quaternion.identity;
|
||||
}
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedTransform.position);
|
||||
transform.rotation = ReferenceTransform.ToRelRot(AttachedTransform.rotation);
|
||||
}
|
||||
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
ApplyToSector();
|
||||
|
||||
if (ReferenceTransform == null || transform.position == Vector3.zero)
|
||||
if (!ReferenceTransform)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UseInterpolation)
|
||||
if (IsPlayerObject)
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(SmoothPosition);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(SmoothRotation);
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedTransform.localPosition = SmoothPosition;
|
||||
AttachedTransform.localRotation = SmoothRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.localPosition = transform.position;
|
||||
AttachedTransform.localRotation = transform.rotation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(transform.position);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(SmoothPosition);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(SmoothRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(transform.position);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ using UnityEngine;
|
||||
namespace QSB.Syncs
|
||||
{
|
||||
/*
|
||||
* Rewrite number : 10
|
||||
* Rewrite number : 11
|
||||
* God has cursed me for my hubris, and my work is never finished.
|
||||
*/
|
||||
|
||||
@ -27,50 +27,85 @@ namespace QSB.Syncs
|
||||
{
|
||||
DebugLog.ToConsole($"Error - trying to get SyncBase.Player for {netId} before Start has been called! "
|
||||
+ "this really should not be happening!\n"
|
||||
+ $"{Environment.StackTrace}",
|
||||
+ Environment.StackTrace,
|
||||
MessageType.Error);
|
||||
}
|
||||
|
||||
return _player;
|
||||
}
|
||||
private set => _player = value;
|
||||
}
|
||||
private PlayerInfo _player;
|
||||
|
||||
private bool _baseIsReady
|
||||
private bool IsInitialized;
|
||||
|
||||
protected virtual bool CheckReady()
|
||||
{
|
||||
get
|
||||
if (netId is uint.MaxValue or 0)
|
||||
{
|
||||
if (netId is uint.MaxValue or 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QSBWorldSync.AllObjectsAdded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsPlayerObject)
|
||||
{
|
||||
if (_player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isLocalPlayer && !Player.IsReady)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QSBWorldSync.AllObjectsAdded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsPlayerObject)
|
||||
{
|
||||
if (_player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isLocalPlayer && !_player.IsReady)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// can be true with null reference transform. <br/>
|
||||
/// can be true with inactive attached object.
|
||||
/// </summary>
|
||||
public bool IsValid { get; private set; }
|
||||
|
||||
protected virtual bool CheckValid()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AttachedTransform)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - AttachedObject {LogName} is null!", MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AllowInactiveAttachedObject && !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AllowNullReferenceTransform && !ReferenceTransform)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {LogName}'s ReferenceTransform is null.", MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceTransform == Locator.GetRootTransform())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract bool IsReady { get; }
|
||||
protected abstract bool UseInterpolation { get; }
|
||||
protected virtual bool AllowDisabledAttachedObject => false;
|
||||
protected virtual bool AllowInactiveAttachedObject => false;
|
||||
protected abstract bool AllowNullReferenceTransform { get; }
|
||||
protected virtual bool IsPlayerObject => false;
|
||||
protected virtual bool OnlyApplyOnDeserialize => false;
|
||||
@ -78,15 +113,17 @@ namespace QSB.Syncs
|
||||
public Transform AttachedTransform { get; private set; }
|
||||
public Transform ReferenceTransform { get; private set; }
|
||||
|
||||
public string LogName => (IsPlayerObject ? $"{Player.PlayerId}." : string.Empty) + $"{netId}:{GetType().Name}";
|
||||
public string Name => AttachedTransform ? AttachedTransform.name : "<NullObject!>";
|
||||
public string LogName => (IsPlayerObject ? $"{Player.PlayerId}." : string.Empty)
|
||||
+ $"{netId}:{GetType().Name} ({Name})";
|
||||
|
||||
protected virtual float DistanceLeeway => 5f;
|
||||
private float _previousDistance;
|
||||
protected const float SmoothTime = 0.1f;
|
||||
private Vector3 _positionSmoothVelocity;
|
||||
private Quaternion _rotationSmoothVelocity;
|
||||
public bool IsInitialized { get; private set; }
|
||||
protected Vector3 SmoothPosition;
|
||||
protected Quaternion SmoothRotation;
|
||||
protected Vector3 SmoothPosition { get; private set; }
|
||||
protected Quaternion SmoothRotation { get; private set; }
|
||||
|
||||
protected abstract Transform InitAttachedTransform();
|
||||
protected abstract void GetFromAttached();
|
||||
@ -98,7 +135,7 @@ namespace QSB.Syncs
|
||||
{
|
||||
// get player objects spawned before this object (or is this one)
|
||||
// and use the closest one
|
||||
Player = QSBPlayerManager.PlayerList
|
||||
_player = QSBPlayerManager.PlayerList
|
||||
.Where(x => x.PlayerId <= netId)
|
||||
.OrderBy(x => x.PlayerId).Last();
|
||||
}
|
||||
@ -109,31 +146,39 @@ namespace QSB.Syncs
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
if (IsPlayerObject && !hasAuthority && AttachedTransform != null)
|
||||
{
|
||||
Destroy(AttachedTransform.gameObject);
|
||||
}
|
||||
|
||||
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
|
||||
if (IsInitialized)
|
||||
{
|
||||
Uninit();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse)
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
Uninit();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
if (!QSBSceneManager.IsInUniverse)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {LogName} is being init-ed when not in the universe!", MessageType.Error);
|
||||
}
|
||||
|
||||
if (IsPlayerObject && !hasAuthority && AttachedTransform != null)
|
||||
{
|
||||
Destroy(AttachedTransform.gameObject);
|
||||
}
|
||||
|
||||
AttachedTransform = InitAttachedTransform();
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
protected virtual void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) => IsInitialized = false;
|
||||
protected virtual void Uninit()
|
||||
{
|
||||
if (IsPlayerObject && !hasAuthority && AttachedTransform)
|
||||
{
|
||||
Destroy(AttachedTransform.gameObject);
|
||||
}
|
||||
|
||||
AttachedTransform = null;
|
||||
ReferenceTransform = null;
|
||||
IsInitialized = false;
|
||||
IsValid = false;
|
||||
}
|
||||
|
||||
private bool _shouldApply;
|
||||
|
||||
@ -148,58 +193,30 @@ namespace QSB.Syncs
|
||||
|
||||
protected sealed override void Update()
|
||||
{
|
||||
if (!IsInitialized && IsReady && _baseIsReady)
|
||||
if (!IsInitialized && CheckReady())
|
||||
{
|
||||
try
|
||||
{
|
||||
Init();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugLog.ToConsole($"Exception when initializing {name} : {ex}", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
Init();
|
||||
}
|
||||
else if (IsInitialized && (!IsReady || !_baseIsReady))
|
||||
else if (IsInitialized && !CheckReady())
|
||||
{
|
||||
IsInitialized = false;
|
||||
Uninit();
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsInitialized)
|
||||
IsValid = CheckValid();
|
||||
if (!IsValid)
|
||||
{
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttachedTransform == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - AttachedObject {LogName} is null.", MessageType.Warning);
|
||||
IsInitialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AttachedTransform.gameObject.activeInHierarchy && !AllowDisabledAttachedObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceTransform == null && !AllowNullReferenceTransform)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {LogName}'s ReferenceTransform is null. AttachedObject:{AttachedTransform.name}", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceTransform != null && ReferenceTransform.position == Vector3.zero && ReferenceTransform != Locator.GetRootTransform())
|
||||
if (ReferenceTransform && ReferenceTransform.position == Vector3.zero)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {LogName}'s ReferenceTransform is at (0,0,0). ReferenceTransform:{ReferenceTransform.name}, AttachedObject:{AttachedTransform.name}", MessageType.Warning);
|
||||
}
|
||||
|
||||
if (ReferenceTransform == Locator.GetRootTransform())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UseInterpolation)
|
||||
if (!hasAuthority && UseInterpolation)
|
||||
{
|
||||
SmoothPosition = SmartSmoothDamp(SmoothPosition, transform.position);
|
||||
SmoothRotation = QuaternionHelper.SmoothDamp(SmoothRotation, transform.rotation, ref _rotationSmoothVelocity, SmoothTime);
|
||||
@ -240,24 +257,28 @@ namespace QSB.Syncs
|
||||
|
||||
ReferenceTransform = referenceTransform;
|
||||
|
||||
if (hasAuthority)
|
||||
if (!hasAuthority && UseInterpolation)
|
||||
{
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedTransform.position);
|
||||
transform.rotation = ReferenceTransform.ToRelRot(AttachedTransform.rotation);
|
||||
}
|
||||
else if (UseInterpolation)
|
||||
{
|
||||
SmoothPosition = ReferenceTransform.ToRelPos(AttachedTransform.position);
|
||||
SmoothRotation = ReferenceTransform.ToRelRot(AttachedTransform.rotation);
|
||||
if (IsPlayerObject)
|
||||
{
|
||||
AttachedTransform.SetParent(ReferenceTransform, true);
|
||||
SmoothPosition = AttachedTransform.localPosition;
|
||||
SmoothRotation = AttachedTransform.localRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
SmoothPosition = ReferenceTransform.ToRelPos(AttachedTransform.position);
|
||||
SmoothRotation = ReferenceTransform.ToRelRot(AttachedTransform.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsInitialized
|
||||
|| AttachedTransform == null
|
||||
|| ReferenceTransform == null)
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -278,16 +299,16 @@ namespace QSB.Syncs
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.ShowDebugLabels ||
|
||||
Event.current.type != EventType.Repaint)
|
||||
if (!QSBCore.DebugSettings.ShowDebugLabels
|
||||
|| Event.current.type != EventType.Repaint
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttachedTransform != null)
|
||||
{
|
||||
DebugGUI.DrawLabel(AttachedTransform.transform, LogName);
|
||||
}
|
||||
DebugGUI.DrawLabel(AttachedTransform.transform, LogName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
{
|
||||
public abstract class BaseUnsectoredSync : SyncBase
|
||||
{
|
||||
protected override bool AllowNullReferenceTransform => false;
|
||||
protected sealed override bool AllowNullReferenceTransform => false;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace QSB.Syncs.Unsectored.Rigidbodies
|
||||
|
||||
protected abstract OWRigidbody InitAttachedRigidbody();
|
||||
|
||||
protected override Transform InitAttachedTransform()
|
||||
protected sealed override Transform InitAttachedTransform()
|
||||
{
|
||||
AttachedRigidbody = InitAttachedRigidbody();
|
||||
return AttachedRigidbody.transform;
|
||||
@ -47,6 +47,12 @@ namespace QSB.Syncs.Unsectored.Rigidbodies
|
||||
_relativeAngularVelocity = reader.ReadVector3();
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
AttachedRigidbody = null;
|
||||
}
|
||||
|
||||
protected override void GetFromAttached()
|
||||
{
|
||||
transform.position = ReferenceTransform.ToRelPos(AttachedRigidbody.GetPosition());
|
||||
|
@ -9,7 +9,7 @@ namespace QSB.Syncs.Unsectored.Transforms
|
||||
protected abstract Transform InitLocalTransform();
|
||||
protected abstract Transform InitRemoteTransform();
|
||||
|
||||
protected override Transform InitAttachedTransform()
|
||||
protected sealed override Transform InitAttachedTransform()
|
||||
=> hasAuthority ? InitLocalTransform() : InitRemoteTransform();
|
||||
|
||||
protected override void Serialize(NetworkWriter writer, bool initialState)
|
||||
@ -34,15 +34,31 @@ namespace QSB.Syncs.Unsectored.Transforms
|
||||
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
if (UseInterpolation)
|
||||
if (IsPlayerObject)
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(SmoothPosition);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(SmoothRotation);
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedTransform.localPosition = SmoothPosition;
|
||||
AttachedTransform.localRotation = SmoothRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.localPosition = transform.position;
|
||||
AttachedTransform.localRotation = transform.rotation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(transform.position);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(SmoothPosition);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(SmoothRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedTransform.position = ReferenceTransform.FromRelPos(transform.position);
|
||||
AttachedTransform.rotation = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using QSB.Tools.ProbeLauncherTool.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.Tools.ProbeLauncherTool
|
||||
{
|
||||
@ -8,6 +9,12 @@ namespace QSB.Tools.ProbeLauncherTool
|
||||
public override WorldObjectType WorldObjectType => WorldObjectType.Both;
|
||||
|
||||
public override void BuildWorldObjects(OWScene scene)
|
||||
=> QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(typeof(PlayerProbeLauncher));
|
||||
{
|
||||
QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(typeof(PlayerProbeLauncher));
|
||||
QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(new[]
|
||||
{
|
||||
QSBWorldSync.GetUnityObjects<ShipCockpitController>().First().GetShipProbeLauncher()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ namespace QSB.Tools.ProbeTool.TransformSync
|
||||
{
|
||||
protected override float DistanceLeeway => 10f;
|
||||
protected override bool UseInterpolation => true;
|
||||
protected override bool AllowDisabledAttachedObject => true;
|
||||
protected override bool AllowInactiveAttachedObject => true;
|
||||
protected override bool IsPlayerObject => true;
|
||||
|
||||
public static PlayerProbeSync LocalInstance { get; private set; }
|
||||
@ -20,12 +20,12 @@ namespace QSB.Tools.ProbeTool.TransformSync
|
||||
|
||||
protected override Transform InitLocalTransform()
|
||||
{
|
||||
SectorSync.Init(Locator.GetProbe().GetSectorDetector(), TargetType.Probe);
|
||||
SectorDetector.Init(Locator.GetProbe().GetSectorDetector(), TargetType.Probe);
|
||||
|
||||
var body = Locator.GetProbe().transform;
|
||||
Player.ProbeBody = body.gameObject;
|
||||
|
||||
if (Player.Body == null)
|
||||
if (!Player.Body)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Player.Body is null!", MessageType.Warning);
|
||||
return null;
|
||||
@ -44,7 +44,7 @@ namespace QSB.Tools.ProbeTool.TransformSync
|
||||
{
|
||||
var probe = Locator.GetProbe().transform;
|
||||
|
||||
if (probe == null)
|
||||
if (!probe)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Probe is null!", MessageType.Error);
|
||||
return default;
|
||||
@ -65,35 +65,27 @@ namespace QSB.Tools.ProbeTool.TransformSync
|
||||
|
||||
protected override void GetFromAttached()
|
||||
{
|
||||
if (AttachedTransform.gameObject.activeInHierarchy)
|
||||
if (!AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
base.GetFromAttached();
|
||||
return;
|
||||
}
|
||||
var probeBody = Locator.GetProbe().GetOWRigidbody();
|
||||
if (!probeBody)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Could not find OWRigidbody of local probe.", MessageType.Warning);
|
||||
}
|
||||
|
||||
var probeOWRigidbody = Locator.GetProbe().GetOWRigidbody();
|
||||
if (probeOWRigidbody == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Could not find OWRigidbody of local probe.", MessageType.Warning);
|
||||
}
|
||||
var probeLauncher = Player.LocalProbeLauncher;
|
||||
// TODO : make this sync to the *active* probe launcher's _launcherTransform
|
||||
var launcherTransform = probeLauncher._launcherTransform;
|
||||
probeBody.SetPosition(launcherTransform.position);
|
||||
probeBody.SetRotation(launcherTransform.rotation);
|
||||
|
||||
var probeLauncher = Player.LocalProbeLauncher;
|
||||
// TODO : make this sync to the *active* probe launcher's _launcherTransform
|
||||
var launcherTransform = probeLauncher._launcherTransform;
|
||||
probeOWRigidbody.SetPosition(launcherTransform.position);
|
||||
probeOWRigidbody.SetRotation(launcherTransform.rotation);
|
||||
SetReferenceSector(Player.TransformSync.ReferenceSector);
|
||||
}
|
||||
|
||||
base.GetFromAttached();
|
||||
|
||||
var currentReferenceSector = ReferenceSector;
|
||||
var playerReferenceSector = Player.TransformSync.ReferenceSector;
|
||||
|
||||
if (currentReferenceSector != playerReferenceSector)
|
||||
{
|
||||
SetReferenceSector(playerReferenceSector);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsReady => AttachedTransform != null || Locator.GetProbe() != null;
|
||||
protected override bool CheckReady() => base.CheckReady()
|
||||
&& (Locator.GetProbe() || AttachedTransform);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
using QSB.WorldSync;
|
||||
using QSB.Messaging;
|
||||
using QSB.Tools.TranslatorTool.TranslationSync.Messages;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -7,6 +10,15 @@ namespace QSB.Tools.TranslatorTool.TranslationSync.WorldObjects
|
||||
{
|
||||
internal class QSBNomaiText : WorldObject<NomaiText>
|
||||
{
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
GetTranslatedIds().ForEach(id =>
|
||||
this.SendMessage(new SetAsTranslatedMessage(id) { To = to }));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAsTranslated(int id) => AttachedObject.SetAsTranslated(id);
|
||||
|
||||
public IEnumerable<int> GetTranslatedIds()
|
||||
|
@ -13,7 +13,8 @@ namespace QSB.TornadoSync.TransformSync
|
||||
{
|
||||
public class OccasionalTransformSync : UnsectoredRigidbodySync
|
||||
{
|
||||
protected override bool IsReady => QSBWorldSync.AllObjectsReady
|
||||
protected override bool CheckReady() => base.CheckReady()
|
||||
&& QSBWorldSync.AllObjectsReady
|
||||
&& CenterOfTheUniverse.s_rigidbodies.IsInRange(_bodyIndex)
|
||||
&& CenterOfTheUniverse.s_rigidbodies.IsInRange(_refBodyIndex);
|
||||
protected override bool UseInterpolation => false;
|
||||
|
@ -1,9 +1,19 @@
|
||||
using QSB.WorldSync;
|
||||
using QSB.Messaging;
|
||||
using QSB.TornadoSync.Messages;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.TornadoSync.WorldObjects
|
||||
{
|
||||
public class QSBTornado : WorldObject<TornadoController>
|
||||
{
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
this.SendMessage(new TornadoFormStateMessage(FormState) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
public bool FormState
|
||||
{
|
||||
get => AttachedObject._tornadoRoot.activeSelf // forming or formed or collapsing
|
||||
|
@ -21,18 +21,13 @@ namespace QSB.TriggerSync.Messages
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(_playerIds.Length);
|
||||
_playerIds.ForEach(writer.Write);
|
||||
writer.WriteArray(_playerIds);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
_playerIds = new uint[reader.ReadInt()];
|
||||
for (var i = 0; i < _playerIds.Length; i++)
|
||||
{
|
||||
_playerIds[i] = reader.ReadUInt();
|
||||
}
|
||||
_playerIds = reader.ReadArray<uint>();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
|
@ -21,7 +21,7 @@ namespace QSB.TriggerSync.WorldObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (player == QSBPlayerManager.LocalPlayer)
|
||||
if (player.IsLocalPlayer)
|
||||
{
|
||||
AttachedObject.OnEntry -= OnEnterEvent;
|
||||
|
||||
@ -41,7 +41,7 @@ namespace QSB.TriggerSync.WorldObjects
|
||||
}
|
||||
else
|
||||
{
|
||||
player.DitheringAnimator.SetVisible(false, 3);
|
||||
player.SetVisible(false, .3f);
|
||||
}
|
||||
|
||||
if (Occupants.Count == QSBPlayerManager.PlayerList.Count)
|
||||
|
@ -49,6 +49,14 @@ namespace QSB.TriggerSync.WorldObjects
|
||||
QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
|
||||
}
|
||||
|
||||
public override void SendResyncInfo(uint to)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
((IQSBTrigger)this).SendMessage(new TriggerResyncMessage(Occupants) { To = to });
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnEnterEvent(GameObject hitObj)
|
||||
{
|
||||
if (hitObj.CompareTag(CompareTag))
|
||||
|
@ -35,6 +35,11 @@ namespace QSB.Utility
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!Keyboard.current[Key.Q].isPressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1 - Warp to first non local player
|
||||
* 2 - Set time flowing
|
||||
@ -45,12 +50,12 @@ namespace QSB.Utility
|
||||
* 7 - Warp to vessel
|
||||
* 8 - Place warp core into vessel
|
||||
* 9 - Load eye scene
|
||||
* 0 - Die
|
||||
* 0 -
|
||||
*/
|
||||
|
||||
if (Keyboard.current[Key.Numpad1].wasPressedThisFrame)
|
||||
{
|
||||
var otherPlayer = QSBPlayerManager.PlayerList.FirstOrDefault(x => x != QSBPlayerManager.LocalPlayer);
|
||||
var otherPlayer = QSBPlayerManager.PlayerList.FirstOrDefault(x => !x.IsLocalPlayer);
|
||||
if (otherPlayer != null)
|
||||
{
|
||||
new DebugRequestTeleportInfoMessage(otherPlayer.PlayerId).Send();
|
||||
@ -109,11 +114,6 @@ namespace QSB.Utility
|
||||
LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, true, LoadManager.FadeType.ToWhite);
|
||||
}
|
||||
}
|
||||
|
||||
if (Keyboard.current[Key.Numpad0].wasPressedThisFrame)
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,12 @@ namespace QSB.Utility
|
||||
{
|
||||
public class DebugSettings
|
||||
{
|
||||
[JsonProperty("useKcpTransport")]
|
||||
public bool UseKcpTransport { get; set; }
|
||||
|
||||
[JsonProperty("overrideAppId")]
|
||||
public int OverrideAppId { get; set; }
|
||||
|
||||
[JsonProperty("debugMode")]
|
||||
public bool DebugMode { get; set; }
|
||||
|
||||
|
@ -51,7 +51,7 @@ namespace QSB.Utility
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
DebugLog.ToConsole($"Error invoking delegate! Message : {ex.InnerException!.Message} Stack Trace : {ex.InnerException.StackTrace}", MessageType.Error);
|
||||
DebugLog.ToConsole($"Error invoking delegate! {ex.InnerException}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,7 @@ namespace QSB.WorldSync
|
||||
bool ShouldDisplayDebug();
|
||||
string ReturnLabel();
|
||||
void DisplayLines();
|
||||
|
||||
void SendResyncInfo(uint to);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace QSB.WorldSync
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugLog.ToConsole($"Exception - Exception when trying to build WorldObjects of manager {manager.GetType().Name} : {ex.Message} Stacktrace :\r\n{ex.StackTrace}", MessageType.Error);
|
||||
DebugLog.ToConsole($"Exception - Exception when trying to build WorldObjects of manager {manager.GetType().Name} : {ex}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,15 +78,12 @@ namespace QSB.WorldSync
|
||||
|
||||
public static void RemoveWorldObjects()
|
||||
{
|
||||
if (!AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameReset();
|
||||
|
||||
AllObjectsAdded = false;
|
||||
AllObjectsReady = false;
|
||||
_numManagersReadying = 0;
|
||||
_numObjectsReadying = 0;
|
||||
|
||||
foreach (var item in WorldObjects)
|
||||
{
|
||||
@ -96,7 +93,7 @@ namespace QSB.WorldSync
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Exception in OnRemoval() for {item.GetType()}. Message : {e.Message}, Stack trace : {e.StackTrace}", MessageType.Error);
|
||||
DebugLog.ToConsole($"Error - Exception in OnRemoval() for {item.GetType()}. {e}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using QSB.Player;
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.WorldSync
|
||||
{
|
||||
@ -8,16 +7,18 @@ namespace QSB.WorldSync
|
||||
{
|
||||
public int ObjectId { get; init; }
|
||||
public T AttachedObject { get; init; }
|
||||
public string Name => AttachedObject == null ? "<NullObject!>" : AttachedObject.name;
|
||||
public string LogName => $"{QSBPlayerManager.LocalPlayerId}.{ObjectId}:{GetType().Name} ({Name})";
|
||||
public string Name => AttachedObject ? AttachedObject.name : "<NullObject!>";
|
||||
public string LogName => $"{ObjectId}:{GetType().Name} ({Name})";
|
||||
|
||||
public virtual void Init() { }
|
||||
public virtual void OnRemoval() { }
|
||||
public MonoBehaviour ReturnObject() => AttachedObject;
|
||||
public virtual bool ShouldDisplayDebug() => AttachedObject != null && AttachedObject.gameObject.activeInHierarchy;
|
||||
public virtual bool ShouldDisplayDebug() => QSBWorldSync.AllObjectsReady && AttachedObject && AttachedObject.gameObject.activeInHierarchy;
|
||||
public virtual string ReturnLabel() => LogName;
|
||||
public virtual void DisplayLines() { }
|
||||
|
||||
public virtual void SendResyncInfo(uint to) { }
|
||||
|
||||
/// indicates that this won't become ready immediately
|
||||
protected void StartDelayedReady() => QSBWorldSync._numObjectsReadying++;
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
{
|
||||
"useKcpTransport": true,
|
||||
"overrideAppId": -1,
|
||||
"debugMode": true,
|
||||
"debugGui": true,
|
||||
"drawLines": false,
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"settings": {
|
||||
"defaultServerIP": "localhost",
|
||||
"port": 7777
|
||||
"defaultServerIP": "localhost"
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"filename": "QSB.dll",
|
||||
"patcher": "QSBPatcher.exe",
|
||||
"author": "Nebula, John, Alek, & Rai",
|
||||
"name": "Quantum Space Buddies",
|
||||
"warning": {
|
||||
@ -7,7 +8,7 @@
|
||||
"body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications.\n- Make sure you have forwarded/opened the correct ports. (See the GitHub readme.)"
|
||||
},
|
||||
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
|
||||
"version": "0.15.1",
|
||||
"version": "0.16.0",
|
||||
"owmlVersion": "2.3.1",
|
||||
"dependencies": [ "_nebula.MenuFramework" ]
|
||||
}
|
||||
|
49
QSBPatcher/PatchSteamFiles.cs
Normal file
49
QSBPatcher/PatchSteamFiles.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace QSBPatcher
|
||||
{
|
||||
public static class PatchSteamFiles
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var basePath = args.Length > 0 ? args[0] : ".";
|
||||
var gamePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
var dataPath = GetDataPath(gamePath);
|
||||
var pluginsPath = Path.Combine(dataPath, @"Plugins\x86_64");
|
||||
|
||||
var dir = new DirectoryInfo(Path.Combine(basePath, "SteamAPI"));
|
||||
var files = dir.GetFiles();
|
||||
foreach (var file in files)
|
||||
{
|
||||
var tempPath = Path.Combine(pluginsPath, file.Name);
|
||||
file.CopyTo(tempPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetExecutableName(string gamePath)
|
||||
{
|
||||
var executableNames = new[] { "Outer Wilds.exe", "OuterWilds.exe" };
|
||||
foreach (var executableName in executableNames)
|
||||
{
|
||||
var executablePath = Path.Combine(gamePath, executableName);
|
||||
if (File.Exists(executablePath))
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(executablePath);
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Outer Wilds exe file not found in {gamePath}");
|
||||
}
|
||||
|
||||
private static string GetDataDirectoryName()
|
||||
{
|
||||
var gamePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
return $"{GetExecutableName(gamePath)}_Data";
|
||||
}
|
||||
|
||||
private static string GetDataPath(string gamePath)
|
||||
=> Path.Combine(gamePath, $"{GetDataDirectoryName()}");
|
||||
}
|
||||
}
|
10
QSBPatcher/QSBPatcher.csproj
Normal file
10
QSBPatcher/QSBPatcher.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Update="SteamAPI\*">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
QSBPatcher/SteamAPI/steam_api64.dll
Normal file
BIN
QSBPatcher/SteamAPI/steam_api64.dll
Normal file
Binary file not shown.
121
README.md
121
README.md
@ -7,14 +7,12 @@
|
||||
![GitHub release (latest by date)](https://img.shields.io/github/downloads/misternebula/quantum-space-buddies/latest/total?style=flat-square)
|
||||
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/misternebula/quantum-space-buddies/dev?label=last%20commit%20to%20dev&style=flat-square)
|
||||
|
||||
Quantum Space Buddies (QSB) is a multiplayer mod for Outer Wilds. The mod uses the OWML mod loader and customized UNET code (internally referred to as QNet or QuantumUNET) for networking.
|
||||
Quantum Space Buddies (QSB) is a multiplayer mod for Outer Wilds. The mod uses the OWML mod loader and Mirror for networking.
|
||||
|
||||
# Spoilers within!
|
||||
Spoilers within!
|
||||
|
||||
## License
|
||||
|
||||
QNet code adapted in part from Unity Technologies' UNET.
|
||||
|
||||
Copyright (C) 2020 - 2021 :
|
||||
- Henry Pointer (_nebula or misternebula)
|
||||
- Will Corby (JohnCorby)
|
||||
@ -49,66 +47,88 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
- Extract the `QSB` directory to the `OWML/Mods` directory;
|
||||
- Run `OWML.Launcher.exe` to start the game.
|
||||
|
||||
## Playing as a client
|
||||
## Hosting / Connecting
|
||||
|
||||
- Run the game.
|
||||
- On the title menu, select "CONNECT TO MULTIPLAYER".
|
||||
- Enter the public IP address of the host.
|
||||
- Hit connect, and pray.
|
||||
### If you own Outer Wilds on Steam
|
||||
|
||||
## Playing as a host
|
||||
Make sure you are logged into Steam before hosting/connecting.
|
||||
|
||||
- Open port `7777` on your router.
|
||||
- Run the game.
|
||||
- On the pause menu, select "OPEN TO MULTIPLAYER".
|
||||
- Give your external IPv4 address to your clients ([like what you see here](http://whatismyip.host/)).
|
||||
#### Connecting to a server
|
||||
|
||||
- On the title screen, click the option `CONNECT TO MULTIPLAYER`.
|
||||
- Enter the SteamID of the person you are trying to connect to.
|
||||
- Enjoy!
|
||||
|
||||
#### Hosting a server
|
||||
|
||||
- Enter a game. This can be a new expedition or an existing save file.
|
||||
- On the pause screen, click the option `OPEN TO MULTIPLAYER`.
|
||||
- Share your SteamID with the people who want to connect.
|
||||
- Enjoy!
|
||||
|
||||
### If you do not own Outer Wilds on Steam
|
||||
|
||||
QSB uses Steamworks to simplify connecting and hosting. If you do not own Outer Wilds on steam, you will not be able to use this.
|
||||
|
||||
There are several ways around this :
|
||||
- Change the "appIdOverride" option in `debugsettings.json` to an AppID that you own on Steam. The most common id to use is 480, as Spacewar is an app everyone owns by default. You will then be able to connect and host as detailed in the above section.
|
||||
- If that doesn't work, enable the "useKcpTransport" option in `debugsettings.json`. This will force QSB to use the KCP transport, which means you will have to port forward and all that fun stuff. To connect/host, follow the below instructions.
|
||||
|
||||
#### Connecting to a server with KCP transport
|
||||
|
||||
- On the title screen, click the option `CONNECT TO MULTIPLAYER`.
|
||||
- Enter the public IP address of the person you are trying to connect to.
|
||||
- Enjoy!
|
||||
|
||||
#### Hosting a server with KCP transport
|
||||
|
||||
- Port forward port 7777 with UDP/TCP.
|
||||
- Make sure your firewall isn't blocking the connections, you've port forwarded the entire route to your NAT (if using multiple routers), etc. There are many guides on port forwarding online, so check those if you need help.
|
||||
- Enter a game. This can be a new expedition or an existing save file.
|
||||
- On the pause screen, click the option `OPEN TO MULTIPLAYER`.
|
||||
- Share your public IP address with the people who want to connect.
|
||||
- Enjoy!
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
#### Requirements
|
||||
### Requirements
|
||||
- Steam account.
|
||||
- Latest version of OWML.
|
||||
- Latest version of Mod Manager. (If using)
|
||||
- Latest version of Outer Wilds. (Epic version preferred, as Steam version is untestable. **We cannot guarantee QSB, or OWML, will work on cracked/pirated versions of Outer Wilds. Do not come asking us for help when using pirated versions.**)
|
||||
- Latest version of Outer Wilds. **We cannot guarantee QSB, or OWML, will work on cracked/pirated versions of Outer Wilds. Do not come asking us for help when using pirated versions.**
|
||||
- Fast and stable internet connection, upload and download.
|
||||
- Above minimum Outer Wilds system requirements.
|
||||
- Knowledge on port forwarding and router/network configuration. We can be tech support for the mod, not your router or computer.
|
||||
|
||||
#### Compatibility with other mods
|
||||
### How complete is this mod? How far through the game can I play?
|
||||
|
||||
| Area of the game | Working |
|
||||
| ------------- | ------------- |
|
||||
| Base game | :heavy_check_mark: |
|
||||
| Echoes of the Eye | :x: |
|
||||
|
||||
### Compatibility with other mods
|
||||
TL;DR - Don't use any mods with QSB that aren't marked as QSB compatible.
|
||||
|
||||
QSB relies on exact orders of objects found using Resources.FindObjectsOfTypeAll to sync objects, so any mod that changes the hierarchy at all risks breaking QSB. Also, QSB relies on certain game events being called when things happen in-game. Any mod that makes these things happen without calling the correct events will break QSB. Some mods will work fine and have been tested, like CrouchMod. Others may only work partly, like EnableDebugMode and TAICheat.
|
||||
|
||||
#### Will you make this compatible with NomaiVR?
|
||||
### Will you make this compatible with NomaiVR?
|
||||
|
||||
Short answer : No.
|
||||
|
||||
Long answer : Pay me enough money, and maybe I'll consider it.
|
||||
|
||||
#### Why can't a Steam game connect to an Epic game, and vice versa? Do you hate Steam/Epic?
|
||||
### Why can't a Steam game connect to an Epic game, and vice versa? Do you hate Steam/Epic?
|
||||
|
||||
QSB is incompatible between game vendors because of how it works at a base level. Not because I dislike Steam or Epic.
|
||||
|
||||
Technical explanation : QSB relies on the orders of lists returned by certain Unity methods to be the same on all clients. For Unity objects, these are (probably) ordered by AssetID or InstanceID. These IDs are different across different game builds. The Epic and Steam versions are different builds. Therefore, the lists are ordered differently and everything breaks.
|
||||
|
||||
#### Why can't I connect to a server?
|
||||
##### For the host :
|
||||
- Open port 7777 TCP and UDP on your router. If access the internet through multiple layers of routers, the port will need to be opened on every router.
|
||||
- Open port 7777 TCP and UDP in and out on your firewall. Some AVs might block you editing firewall settings, so check with your specific software.
|
||||
- Make sure you are giving your public IPv4 address to your clients.
|
||||
##### For the client :
|
||||
- Open port 7777 TCP and UDP in and out on your firewall. Some AVs might block you editing firewall settings, so check with your specific software.
|
||||
- Sometimes, it has helped to change your network profile to "private".
|
||||
- Make sure you are putting the right address into the address box.
|
||||
### Why do I keep getting thrown around the ship?
|
||||
|
||||
If nothing here works, many people have got QSB working through programs such as Hamachi. Also make sure you are not running through a VPN while trying to connect.
|
||||
|
||||
Note - _nebula has no idea how Hamachi works and has never used it, so don't ask them for help setting it up! As said before, we are tech support for the mod. If you cannot connect to someone, or someone cannot connect to you, **that is not QSB's fault.**
|
||||
|
||||
#### Why do I keep getting thrown around the ship?
|
||||
Boring boring physics stuff. The velocity of the ship is synced, as well as the angular velocity. However, this velocity is not also applied to the player. (Or it is sometimes. I don't 100% know.) This means the ship will accelerate, leaving the player "behind". Which makes you fly into the walls alot.
|
||||
So really there's nothing we can do about this. I disabled damage by impact inside the ship, so if you die inside the ship while it is flying then that is a bug.
|
||||
|
||||
#### What's the difference between QSB and Outer Wilds Online?
|
||||
### What's the difference between QSB and Outer Wilds Online?
|
||||
|
||||
TL;DR - QSB is multiplayer co-op, Outer Wilds Online is multiplayer not not co-op.
|
||||
|
||||
@ -116,7 +136,7 @@ QSB is a fully synced game. The other players are actually there in the world, a
|
||||
|
||||
Outer Wilds Online is easier to set up, but much more basic in its features. The other players cannot affect your game, and do not contribute to anything in your save. The loop is entirely per-player.
|
||||
|
||||
#### Why would someone make this mod? Seems like a lot of effort for no reward.
|
||||
### Why would someone make this mod? Seems like a lot of effort for no reward.
|
||||
|
||||
Good question.
|
||||
|
||||
@ -140,18 +160,6 @@ To fix the references, right click "References" in the Solution Explorer > "Add
|
||||
|
||||
After doing this, the project references should be working.
|
||||
|
||||
The build pipeline (in post-build events):
|
||||
|
||||
- Build QuantumUNET.
|
||||
- Copy built `QuantumUNET.dll` to mod folder.
|
||||
- Build QSB.
|
||||
- Copy `default-config.json` to mod folder.
|
||||
- Copy AssetBundles to mod folder.
|
||||
- Copy `manifest.json` to mod folder.
|
||||
- Copy built `QSB.dll` into mod folder.
|
||||
- Build QSBTests.
|
||||
- Use `dotnet test` to run QSBTests on QSB project.
|
||||
|
||||
If Visual Studio isn't able to automatically copy the files, you'll have to copy the built dlls manually to OWML.
|
||||
|
||||
It is recommended to use the Epic version of Outer Wilds, as you cannot run multiple versions of the Steam version.
|
||||
@ -170,7 +178,7 @@ It is also recommended to lower all graphics settings to minimum, be in windowed
|
||||
### Authors
|
||||
|
||||
- [\_nebula](https://github.com/misternebula) - Developer of v0.3 onwards
|
||||
- [JohnCorby](https://github.com/JohnCorby) - Co-developer of 0.13.0 onwards.
|
||||
- [JohnCorby](https://github.com/JohnCorby) - Co-developer of v0.13.0 onwards.
|
||||
- [AmazingAlek](https://github.com/amazingalek) - Developer of v0.1.0 - v0.7.1.
|
||||
- [Raicuparta](https://github.com/Raicuparta) - Developer of v0.1.0 - v0.2.0.
|
||||
|
||||
@ -180,10 +188,17 @@ It is also recommended to lower all graphics settings to minimum, be in windowed
|
||||
- [Chris Yeninas](https://github.com/PhantomGamers)
|
||||
|
||||
### Special Thanks
|
||||
- Thanks to Logan Ver Hoef for help with the game code.
|
||||
- Thanks to all the people in the Outer Wilds Discord for helping in public tests.
|
||||
- Special thanks (and apologies) to all the people in the #modding channel, which I (_nebula) have been using as a virtual [rubber duck.](https://en.wikipedia.org/wiki/Rubber_duck_debugging)
|
||||
- Thanks to Logan Ver Hoef for help with the game code, and for helping make the damn game in the first place.
|
||||
- Thanks to all the people who helped in public tests.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [OWML](https://github.com/amazingalek/owml)
|
||||
- [Mirror](https://mirror-networking.com/)
|
||||
- [FizzyFacepunch](https://github.com/Chykary/FizzyFacepunch)
|
||||
- [HarmonyX](https://github.com/BepInEx/HarmonyX)
|
||||
- [Mono.Cecil](https://github.com/jbevain/cecil)
|
||||
|
||||
## Help / Discuss development / Whatever
|
||||
|
||||
[Join the Outer Wilds Modding Discord](https://discord.gg/9vE5aHxcF9), we have a nice `#mod-support` channel for any mod help, and a other channels to discuss modding!
|
||||
[Join the Outer Wilds Modding Discord](https://discord.gg/9vE5aHxcF9), we have a nice `#qsb-bugs-and-questions` channel for support, and other channels to discuss modding!
|
||||
|
Loading…
x
Reference in New Issue
Block a user