mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-27 12:35:28 +00:00
solution-wide: file-scoped namespaces
This commit is contained in:
parent
b99dfaa510
commit
e4da50a656
@ -50,82 +50,81 @@ using System.Collections.Generic;
|
||||
/// MIT License
|
||||
/// </summary>
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public class BidirectionalDictionary<T1, T2> : IEnumerable
|
||||
{
|
||||
public class BidirectionalDictionary<T1, T2> : IEnumerable
|
||||
private Dictionary<T1, T2> t1ToT2Dict = new();
|
||||
private Dictionary<T2, T1> t2ToT1Dict = new();
|
||||
|
||||
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)
|
||||
{
|
||||
private Dictionary<T1, T2> t1ToT2Dict = new();
|
||||
private Dictionary<T2, T1> t2ToT1Dict = new();
|
||||
t1ToT2Dict[key] = value;
|
||||
t2ToT1Dict[value] = key;
|
||||
}
|
||||
|
||||
public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
|
||||
public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys;
|
||||
public void Add(T2 key, T1 value)
|
||||
{
|
||||
t2ToT1Dict[key] = value;
|
||||
t1ToT2Dict[value] = key;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator() => t1ToT2Dict.GetEnumerator();
|
||||
public T2 Get(T1 key) => t1ToT2Dict[key];
|
||||
|
||||
public int Count => t1ToT2Dict.Count;
|
||||
public T1 Get(T2 key) => t2ToT1Dict[key];
|
||||
|
||||
public void Add(T1 key, T2 value)
|
||||
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))
|
||||
{
|
||||
t1ToT2Dict[key] = value;
|
||||
t2ToT1Dict[value] = key;
|
||||
var val = t1ToT2Dict[key];
|
||||
t1ToT2Dict.Remove(key);
|
||||
t2ToT1Dict.Remove(val);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(T2 key, T1 value)
|
||||
public void Remove(T2 key)
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
var 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 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)
|
||||
public T2 this[T1 key]
|
||||
{
|
||||
get => t1ToT2Dict[key];
|
||||
set
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
var val = t1ToT2Dict[key];
|
||||
t1ToT2Dict.Remove(key);
|
||||
t2ToT1Dict.Remove(val);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(T2 key)
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
var 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;
|
||||
}
|
||||
t1ToT2Dict[key] = value;
|
||||
t2ToT1Dict[value] = key;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,193 +5,192 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public class Client : Common
|
||||
{
|
||||
public class Client : Common
|
||||
public SocketId socketId;
|
||||
public ProductUserId serverId;
|
||||
|
||||
public bool Connected { get; private set; }
|
||||
public bool Error { get; private set; }
|
||||
|
||||
private event Action<byte[], int> OnReceivedData;
|
||||
private event Action OnConnected;
|
||||
public event Action OnDisconnected;
|
||||
private Action<string> SetTransportError;
|
||||
|
||||
private TimeSpan ConnectionTimeout;
|
||||
|
||||
public bool isConnecting = false;
|
||||
public string hostAddress = "";
|
||||
private ProductUserId hostProductId = null;
|
||||
private TaskCompletionSource<Task> connectedComplete;
|
||||
private CancellationTokenSource cancelToken;
|
||||
|
||||
private Client(EosTransport transport) : base(transport) => ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.timeout));
|
||||
|
||||
public static Client CreateClient(EosTransport transport, string host)
|
||||
{
|
||||
public SocketId socketId;
|
||||
public ProductUserId serverId;
|
||||
var c = new Client(transport);
|
||||
|
||||
public bool Connected { get; private set; }
|
||||
public bool Error { get; private set; }
|
||||
c.hostAddress = host;
|
||||
c.socketId = new SocketId() { SocketName = RandomString.Generate(20) };
|
||||
|
||||
private event Action<byte[], int> OnReceivedData;
|
||||
private event Action OnConnected;
|
||||
public event Action OnDisconnected;
|
||||
private Action<string> SetTransportError;
|
||||
c.OnConnected += () => transport.OnClientConnected.Invoke();
|
||||
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
|
||||
c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel);
|
||||
c.SetTransportError = transport.SetTransportError;
|
||||
|
||||
private TimeSpan ConnectionTimeout;
|
||||
|
||||
public bool isConnecting = false;
|
||||
public string hostAddress = "";
|
||||
private ProductUserId hostProductId = null;
|
||||
private TaskCompletionSource<Task> connectedComplete;
|
||||
private CancellationTokenSource cancelToken;
|
||||
|
||||
private Client(EosTransport transport) : base(transport) => ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.timeout));
|
||||
|
||||
public static Client CreateClient(EosTransport transport, string host)
|
||||
{
|
||||
var c = new Client(transport);
|
||||
|
||||
c.hostAddress = host;
|
||||
c.socketId = new SocketId() { SocketName = RandomString.Generate(20) };
|
||||
|
||||
c.OnConnected += () => transport.OnClientConnected.Invoke();
|
||||
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
|
||||
c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel);
|
||||
c.SetTransportError = transport.SetTransportError;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public async void Connect(string host)
|
||||
{
|
||||
cancelToken = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
hostProductId = ProductUserId.FromString(host);
|
||||
serverId = hostProductId;
|
||||
connectedComplete = new TaskCompletionSource<Task>();
|
||||
|
||||
OnConnected += SetConnectedComplete;
|
||||
|
||||
SendInternal(hostProductId, socketId, InternalMessages.CONNECT);
|
||||
|
||||
Task connectedCompleteTask = connectedComplete.Task;
|
||||
|
||||
if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout /*, cancelToken.Token*/)) != connectedCompleteTask)
|
||||
{
|
||||
SetTransportError($"Connection to {host} timed out.");
|
||||
Debug.LogError($"Connection to {host} timed out.");
|
||||
OnConnected -= SetConnectedComplete;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
|
||||
OnConnected -= SetConnectedComplete;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
SetTransportError("Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Debug.LogError($"Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetTransportError(ex.Message);
|
||||
Debug.LogError(ex.Message);
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Error)
|
||||
{
|
||||
OnConnectionFailed(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (serverId != null)
|
||||
{
|
||||
CloseP2PSessionWithUser(serverId, socketId);
|
||||
|
||||
serverId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(hostProductId, socketId, InternalMessages.DISCONNECT);
|
||||
|
||||
Dispose();
|
||||
cancelToken?.Cancel();
|
||||
|
||||
WaitForClose(hostProductId, socketId);
|
||||
}
|
||||
|
||||
private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task);
|
||||
|
||||
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientUserId != hostProductId)
|
||||
{
|
||||
Debug.LogError("Received a message from an unknown");
|
||||
return;
|
||||
}
|
||||
|
||||
OnReceivedData.Invoke(data, channel);
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadSockets.Contains(result.SocketId.SocketName))
|
||||
{
|
||||
Debug.LogError("Received incoming connection request from dead socket");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hostProductId == result.RemoteUserId)
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().AcceptConnection(
|
||||
new AcceptConnectionOptions()
|
||||
{
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
RemoteUserId = result.RemoteUserId,
|
||||
SocketId = result.SocketId
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("P2P Acceptance Request from unknown host ID.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.ACCEPT_CONNECT:
|
||||
Connected = true;
|
||||
OnConnected.Invoke();
|
||||
Debug.Log("Connection established.");
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
SetTransportError("host disconnected");
|
||||
Connected = false;
|
||||
Debug.Log("Disconnected.");
|
||||
|
||||
OnDisconnected.Invoke();
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int channelId) => Send(hostProductId, socketId, data, (byte)channelId);
|
||||
|
||||
protected override void OnConnectionFailed(ProductUserId remoteId) => OnDisconnected.Invoke();
|
||||
public void EosNotInitialized() => OnDisconnected.Invoke();
|
||||
return c;
|
||||
}
|
||||
|
||||
public async void Connect(string host)
|
||||
{
|
||||
cancelToken = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
hostProductId = ProductUserId.FromString(host);
|
||||
serverId = hostProductId;
|
||||
connectedComplete = new TaskCompletionSource<Task>();
|
||||
|
||||
OnConnected += SetConnectedComplete;
|
||||
|
||||
SendInternal(hostProductId, socketId, InternalMessages.CONNECT);
|
||||
|
||||
Task connectedCompleteTask = connectedComplete.Task;
|
||||
|
||||
if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout /*, cancelToken.Token*/)) != connectedCompleteTask)
|
||||
{
|
||||
SetTransportError($"Connection to {host} timed out.");
|
||||
Debug.LogError($"Connection to {host} timed out.");
|
||||
OnConnected -= SetConnectedComplete;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
|
||||
OnConnected -= SetConnectedComplete;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
SetTransportError("Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Debug.LogError($"Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetTransportError(ex.Message);
|
||||
Debug.LogError(ex.Message);
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Error)
|
||||
{
|
||||
OnConnectionFailed(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (serverId != null)
|
||||
{
|
||||
CloseP2PSessionWithUser(serverId, socketId);
|
||||
|
||||
serverId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(hostProductId, socketId, InternalMessages.DISCONNECT);
|
||||
|
||||
Dispose();
|
||||
cancelToken?.Cancel();
|
||||
|
||||
WaitForClose(hostProductId, socketId);
|
||||
}
|
||||
|
||||
private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task);
|
||||
|
||||
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientUserId != hostProductId)
|
||||
{
|
||||
Debug.LogError("Received a message from an unknown");
|
||||
return;
|
||||
}
|
||||
|
||||
OnReceivedData.Invoke(data, channel);
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadSockets.Contains(result.SocketId.SocketName))
|
||||
{
|
||||
Debug.LogError("Received incoming connection request from dead socket");
|
||||
return;
|
||||
}
|
||||
|
||||
if (hostProductId == result.RemoteUserId)
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().AcceptConnection(
|
||||
new AcceptConnectionOptions()
|
||||
{
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
RemoteUserId = result.RemoteUserId,
|
||||
SocketId = result.SocketId
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("P2P Acceptance Request from unknown host ID.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.ACCEPT_CONNECT:
|
||||
Connected = true;
|
||||
OnConnected.Invoke();
|
||||
Debug.Log("Connection established.");
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
SetTransportError("host disconnected");
|
||||
Connected = false;
|
||||
Debug.Log("Disconnected.");
|
||||
|
||||
OnDisconnected.Invoke();
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int channelId) => Send(hostProductId, socketId, data, (byte)channelId);
|
||||
|
||||
protected override void OnConnectionFailed(ProductUserId remoteId) => OnDisconnected.Invoke();
|
||||
public void EosNotInitialized() => OnDisconnected.Invoke();
|
||||
}
|
@ -5,331 +5,330 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public abstract class Common
|
||||
{
|
||||
public abstract class Common
|
||||
private PacketReliability[] channels;
|
||||
private int internal_ch => channels.Length;
|
||||
|
||||
protected enum InternalMessages : byte
|
||||
{
|
||||
private PacketReliability[] channels;
|
||||
private int internal_ch => channels.Length;
|
||||
CONNECT,
|
||||
ACCEPT_CONNECT,
|
||||
DISCONNECT
|
||||
}
|
||||
|
||||
protected enum InternalMessages : byte
|
||||
protected struct PacketKey
|
||||
{
|
||||
public ProductUserId productUserId;
|
||||
public byte channel;
|
||||
}
|
||||
|
||||
private OnIncomingConnectionRequestCallback OnIncomingConnectionRequest;
|
||||
private ulong incomingNotificationId = 0;
|
||||
private OnRemoteConnectionClosedCallback OnRemoteConnectionClosed;
|
||||
private ulong outgoingNotificationId = 0;
|
||||
|
||||
protected readonly EosTransport transport;
|
||||
|
||||
protected List<string> deadSockets;
|
||||
public bool ignoreAllMessages = false;
|
||||
|
||||
// Mapping from PacketKey to a List of Packet Lists
|
||||
protected Dictionary<PacketKey, List<List<Packet>>> incomingPackets = new();
|
||||
|
||||
protected Common(EosTransport transport)
|
||||
{
|
||||
channels = transport.Channels;
|
||||
|
||||
deadSockets = new List<string>();
|
||||
|
||||
var addNotifyPeerConnectionRequestOptions = new AddNotifyPeerConnectionRequestOptions();
|
||||
addNotifyPeerConnectionRequestOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
|
||||
addNotifyPeerConnectionRequestOptions.SocketId = null;
|
||||
|
||||
OnIncomingConnectionRequest += OnNewConnection;
|
||||
OnRemoteConnectionClosed += OnConnectFail;
|
||||
|
||||
incomingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionRequest(addNotifyPeerConnectionRequestOptions,
|
||||
null, OnIncomingConnectionRequest);
|
||||
|
||||
var addNotifyPeerConnectionClosedOptions = new AddNotifyPeerConnectionClosedOptions();
|
||||
addNotifyPeerConnectionClosedOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
|
||||
addNotifyPeerConnectionClosedOptions.SocketId = null;
|
||||
|
||||
outgoingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionClosed(addNotifyPeerConnectionClosedOptions,
|
||||
null, OnRemoteConnectionClosed);
|
||||
|
||||
if (outgoingNotificationId == 0 || incomingNotificationId == 0)
|
||||
{
|
||||
CONNECT,
|
||||
ACCEPT_CONNECT,
|
||||
DISCONNECT
|
||||
Debug.LogError("Couldn't bind notifications with P2P interface");
|
||||
}
|
||||
|
||||
protected struct PacketKey
|
||||
incomingPackets = new Dictionary<PacketKey, List<List<Packet>>>();
|
||||
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
protected void Dispose()
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionRequest(incomingNotificationId);
|
||||
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionClosed(outgoingNotificationId);
|
||||
|
||||
transport.ResetIgnoreMessagesAtStartUpTimer();
|
||||
}
|
||||
|
||||
protected abstract void OnNewConnection(OnIncomingConnectionRequestInfo result);
|
||||
|
||||
private void OnConnectFail(OnRemoteConnectionClosedInfo result)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
public ProductUserId productUserId;
|
||||
public byte channel;
|
||||
return;
|
||||
}
|
||||
|
||||
private OnIncomingConnectionRequestCallback OnIncomingConnectionRequest;
|
||||
private ulong incomingNotificationId = 0;
|
||||
private OnRemoteConnectionClosedCallback OnRemoteConnectionClosed;
|
||||
private ulong outgoingNotificationId = 0;
|
||||
OnConnectionFailed(result.RemoteUserId);
|
||||
|
||||
protected readonly EosTransport transport;
|
||||
|
||||
protected List<string> deadSockets;
|
||||
public bool ignoreAllMessages = false;
|
||||
|
||||
// Mapping from PacketKey to a List of Packet Lists
|
||||
protected Dictionary<PacketKey, List<List<Packet>>> incomingPackets = new();
|
||||
|
||||
protected Common(EosTransport transport)
|
||||
switch (result.Reason)
|
||||
{
|
||||
channels = transport.Channels;
|
||||
case ConnectionClosedReason.ClosedByLocalUser:
|
||||
throw new Exception("Connection cLosed: The Connection was gracecfully closed by the local user.");
|
||||
case ConnectionClosedReason.ClosedByPeer:
|
||||
throw new Exception("Connection closed: The connection was gracefully closed by remote user.");
|
||||
case ConnectionClosedReason.ConnectionClosed:
|
||||
throw new Exception("Connection closed: The connection was unexpectedly closed.");
|
||||
case ConnectionClosedReason.ConnectionFailed:
|
||||
throw new Exception("Connection failed: Failled to establish connection.");
|
||||
case ConnectionClosedReason.InvalidData:
|
||||
throw new Exception("Connection failed: The remote user sent us invalid data..");
|
||||
case ConnectionClosedReason.InvalidMessage:
|
||||
throw new Exception("Connection failed: The remote user sent us an invalid message.");
|
||||
case ConnectionClosedReason.NegotiationFailed:
|
||||
throw new Exception("Connection failed: Negotiation failed.");
|
||||
case ConnectionClosedReason.TimedOut:
|
||||
throw new Exception("Connection failed: Timeout.");
|
||||
case ConnectionClosedReason.TooManyConnections:
|
||||
throw new Exception("Connection failed: Too many connections.");
|
||||
case ConnectionClosedReason.UnexpectedError:
|
||||
throw new Exception("Unexpected Error, connection will be closed");
|
||||
case ConnectionClosedReason.Unknown:
|
||||
default:
|
||||
throw new Exception("Unknown Error, connection has been closed.");
|
||||
}
|
||||
}
|
||||
|
||||
deadSockets = new List<string>();
|
||||
protected void SendInternal(ProductUserId target, SocketId socketId, InternalMessages type)
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions()
|
||||
{
|
||||
AllowDelayedDelivery = true,
|
||||
Channel = (byte)internal_ch,
|
||||
Data = new byte[] { (byte)type },
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
Reliability = PacketReliability.ReliableOrdered,
|
||||
RemoteUserId = target,
|
||||
SocketId = socketId
|
||||
});
|
||||
}
|
||||
|
||||
var addNotifyPeerConnectionRequestOptions = new AddNotifyPeerConnectionRequestOptions();
|
||||
addNotifyPeerConnectionRequestOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
|
||||
addNotifyPeerConnectionRequestOptions.SocketId = null;
|
||||
protected void Send(ProductUserId host, SocketId socketId, byte[] msgBuffer, byte channel)
|
||||
{
|
||||
var result = EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions()
|
||||
{
|
||||
AllowDelayedDelivery = true,
|
||||
Channel = channel,
|
||||
Data = msgBuffer,
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
Reliability = channels[channel],
|
||||
RemoteUserId = host,
|
||||
SocketId = socketId
|
||||
});
|
||||
|
||||
OnIncomingConnectionRequest += OnNewConnection;
|
||||
OnRemoteConnectionClosed += OnConnectFail;
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Debug.LogError("Send failed " + result);
|
||||
}
|
||||
}
|
||||
|
||||
incomingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionRequest(addNotifyPeerConnectionRequestOptions,
|
||||
null, OnIncomingConnectionRequest);
|
||||
private bool Receive(out ProductUserId clientProductUserId, out SocketId socketId, out byte[] receiveBuffer, byte channel)
|
||||
{
|
||||
var result = EOSSDKComponent.GetP2PInterface().ReceivePacket(new ReceivePacketOptions()
|
||||
{
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
MaxDataSizeBytes = P2PInterface.MaxPacketSize,
|
||||
RequestedChannel = channel
|
||||
}, out clientProductUserId, out socketId, out channel, out receiveBuffer);
|
||||
|
||||
var addNotifyPeerConnectionClosedOptions = new AddNotifyPeerConnectionClosedOptions();
|
||||
addNotifyPeerConnectionClosedOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
|
||||
addNotifyPeerConnectionClosedOptions.SocketId = null;
|
||||
|
||||
outgoingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionClosed(addNotifyPeerConnectionClosedOptions,
|
||||
null, OnRemoteConnectionClosed);
|
||||
|
||||
if (outgoingNotificationId == 0 || incomingNotificationId == 0)
|
||||
{
|
||||
Debug.LogError("Couldn't bind notifications with P2P interface");
|
||||
}
|
||||
|
||||
incomingPackets = new Dictionary<PacketKey, List<List<Packet>>>();
|
||||
|
||||
this.transport = transport;
|
||||
if (result == Result.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void Dispose()
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionRequest(incomingNotificationId);
|
||||
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionClosed(outgoingNotificationId);
|
||||
receiveBuffer = null;
|
||||
clientProductUserId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
transport.ResetIgnoreMessagesAtStartUpTimer();
|
||||
protected virtual void CloseP2PSessionWithUser(ProductUserId clientUserID, SocketId socketId)
|
||||
{
|
||||
if (socketId == null)
|
||||
{
|
||||
Debug.LogWarning("Socket ID == null | " + ignoreAllMessages);
|
||||
return;
|
||||
}
|
||||
|
||||
protected abstract void OnNewConnection(OnIncomingConnectionRequestInfo result);
|
||||
|
||||
private void OnConnectFail(OnRemoteConnectionClosedInfo result)
|
||||
if (deadSockets == null)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnConnectionFailed(result.RemoteUserId);
|
||||
|
||||
switch (result.Reason)
|
||||
{
|
||||
case ConnectionClosedReason.ClosedByLocalUser:
|
||||
throw new Exception("Connection cLosed: The Connection was gracecfully closed by the local user.");
|
||||
case ConnectionClosedReason.ClosedByPeer:
|
||||
throw new Exception("Connection closed: The connection was gracefully closed by remote user.");
|
||||
case ConnectionClosedReason.ConnectionClosed:
|
||||
throw new Exception("Connection closed: The connection was unexpectedly closed.");
|
||||
case ConnectionClosedReason.ConnectionFailed:
|
||||
throw new Exception("Connection failed: Failled to establish connection.");
|
||||
case ConnectionClosedReason.InvalidData:
|
||||
throw new Exception("Connection failed: The remote user sent us invalid data..");
|
||||
case ConnectionClosedReason.InvalidMessage:
|
||||
throw new Exception("Connection failed: The remote user sent us an invalid message.");
|
||||
case ConnectionClosedReason.NegotiationFailed:
|
||||
throw new Exception("Connection failed: Negotiation failed.");
|
||||
case ConnectionClosedReason.TimedOut:
|
||||
throw new Exception("Connection failed: Timeout.");
|
||||
case ConnectionClosedReason.TooManyConnections:
|
||||
throw new Exception("Connection failed: Too many connections.");
|
||||
case ConnectionClosedReason.UnexpectedError:
|
||||
throw new Exception("Unexpected Error, connection will be closed");
|
||||
case ConnectionClosedReason.Unknown:
|
||||
default:
|
||||
throw new Exception("Unknown Error, connection has been closed.");
|
||||
}
|
||||
Debug.LogWarning("DeadSockets == null");
|
||||
return;
|
||||
}
|
||||
|
||||
protected void SendInternal(ProductUserId target, SocketId socketId, InternalMessages type)
|
||||
if (deadSockets.Contains(socketId.SocketName))
|
||||
{
|
||||
EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions()
|
||||
{
|
||||
AllowDelayedDelivery = true,
|
||||
Channel = (byte)internal_ch,
|
||||
Data = new byte[] { (byte)type },
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
Reliability = PacketReliability.ReliableOrdered,
|
||||
RemoteUserId = target,
|
||||
SocketId = socketId
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
protected void Send(ProductUserId host, SocketId socketId, byte[] msgBuffer, byte channel)
|
||||
else
|
||||
{
|
||||
var result = EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions()
|
||||
{
|
||||
AllowDelayedDelivery = true,
|
||||
Channel = channel,
|
||||
Data = msgBuffer,
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
Reliability = channels[channel],
|
||||
RemoteUserId = host,
|
||||
SocketId = socketId
|
||||
});
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Debug.LogError("Send failed " + result);
|
||||
}
|
||||
deadSockets.Add(socketId.SocketName);
|
||||
}
|
||||
}
|
||||
|
||||
private bool Receive(out ProductUserId clientProductUserId, out SocketId socketId, out byte[] receiveBuffer, byte channel)
|
||||
protected void WaitForClose(ProductUserId clientUserID, SocketId socketId) => transport.StartCoroutine(DelayedClose(clientUserID, socketId));
|
||||
|
||||
private IEnumerator DelayedClose(ProductUserId clientUserID, SocketId socketId)
|
||||
{
|
||||
yield return null;
|
||||
CloseP2PSessionWithUser(clientUserID, socketId);
|
||||
}
|
||||
|
||||
public void ReceiveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = EOSSDKComponent.GetP2PInterface().ReceivePacket(new ReceivePacketOptions()
|
||||
// Internal Channel, no fragmentation here
|
||||
var socketId = new SocketId();
|
||||
while (transport.enabled && Receive(out var clientUserID, out socketId, out var internalMessage, (byte)internal_ch))
|
||||
{
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
MaxDataSizeBytes = P2PInterface.MaxPacketSize,
|
||||
RequestedChannel = channel
|
||||
}, out clientProductUserId, out socketId, out channel, out receiveBuffer);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
receiveBuffer = null;
|
||||
clientProductUserId = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void CloseP2PSessionWithUser(ProductUserId clientUserID, SocketId socketId)
|
||||
{
|
||||
if (socketId == null)
|
||||
{
|
||||
Debug.LogWarning("Socket ID == null | " + ignoreAllMessages);
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadSockets == null)
|
||||
{
|
||||
Debug.LogWarning("DeadSockets == null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadSockets.Contains(socketId.SocketName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
deadSockets.Add(socketId.SocketName);
|
||||
}
|
||||
}
|
||||
|
||||
protected void WaitForClose(ProductUserId clientUserID, SocketId socketId) => transport.StartCoroutine(DelayedClose(clientUserID, socketId));
|
||||
|
||||
private IEnumerator DelayedClose(ProductUserId clientUserID, SocketId socketId)
|
||||
{
|
||||
yield return null;
|
||||
CloseP2PSessionWithUser(clientUserID, socketId);
|
||||
}
|
||||
|
||||
public void ReceiveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Internal Channel, no fragmentation here
|
||||
var socketId = new SocketId();
|
||||
while (transport.enabled && Receive(out var clientUserID, out socketId, out var internalMessage, (byte)internal_ch))
|
||||
if (internalMessage.Length == 1)
|
||||
{
|
||||
if (internalMessage.Length == 1)
|
||||
OnReceiveInternalData((InternalMessages)internalMessage[0], clientUserID, socketId);
|
||||
return; // Wait one frame
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Incorrect package length on internal channel.");
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new packet at the correct location in the incoming queue
|
||||
for (var chNum = 0; chNum < channels.Length; chNum++)
|
||||
{
|
||||
while (transport.enabled && Receive(out var clientUserID, out socketId, out var receiveBuffer, (byte)chNum))
|
||||
{
|
||||
var incomingPacketKey = new PacketKey();
|
||||
incomingPacketKey.productUserId = clientUserID;
|
||||
incomingPacketKey.channel = (byte)chNum;
|
||||
|
||||
var packet = new Packet();
|
||||
packet.FromBytes(receiveBuffer);
|
||||
|
||||
if (!incomingPackets.ContainsKey(incomingPacketKey))
|
||||
{
|
||||
OnReceiveInternalData((InternalMessages)internalMessage[0], clientUserID, socketId);
|
||||
return; // Wait one frame
|
||||
incomingPackets.Add(incomingPacketKey, new List<List<Packet>>());
|
||||
}
|
||||
|
||||
var packetListIndex = incomingPackets[incomingPacketKey].Count;
|
||||
for (var i = 0; i < incomingPackets[incomingPacketKey].Count; i++)
|
||||
{
|
||||
if (incomingPackets[incomingPacketKey][i][0].id == packet.id)
|
||||
{
|
||||
packetListIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetListIndex == incomingPackets[incomingPacketKey].Count)
|
||||
{
|
||||
incomingPackets[incomingPacketKey].Add(new List<Packet>());
|
||||
}
|
||||
|
||||
var insertionIndex = -1;
|
||||
|
||||
for (var i = 0; i < incomingPackets[incomingPacketKey][packetListIndex].Count; i++)
|
||||
{
|
||||
if (incomingPackets[incomingPacketKey][packetListIndex][i].fragment > packet.fragment)
|
||||
{
|
||||
insertionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (insertionIndex >= 0)
|
||||
{
|
||||
incomingPackets[incomingPacketKey][packetListIndex].Insert(insertionIndex, packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Incorrect package length on internal channel.");
|
||||
incomingPackets[incomingPacketKey][packetListIndex].Add(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new packet at the correct location in the incoming queue
|
||||
for (var chNum = 0; chNum < channels.Length; chNum++)
|
||||
// Find fully received packets
|
||||
var emptyPacketLists = new List<List<Packet>>();
|
||||
foreach (var keyValuePair in incomingPackets)
|
||||
{
|
||||
for (var packetList = 0; packetList < keyValuePair.Value.Count; packetList++)
|
||||
{
|
||||
while (transport.enabled && Receive(out var clientUserID, out socketId, out var receiveBuffer, (byte)chNum))
|
||||
var packetReady = true;
|
||||
var packetLength = 0;
|
||||
for (var packet = 0; packet < keyValuePair.Value[packetList].Count; packet++)
|
||||
{
|
||||
var incomingPacketKey = new PacketKey();
|
||||
incomingPacketKey.productUserId = clientUserID;
|
||||
incomingPacketKey.channel = (byte)chNum;
|
||||
|
||||
var packet = new Packet();
|
||||
packet.FromBytes(receiveBuffer);
|
||||
|
||||
if (!incomingPackets.ContainsKey(incomingPacketKey))
|
||||
var tempPacket = keyValuePair.Value[packetList][packet];
|
||||
if (tempPacket.fragment != packet || packet == keyValuePair.Value[packetList].Count - 1 && tempPacket.moreFragments)
|
||||
{
|
||||
incomingPackets.Add(incomingPacketKey, new List<List<Packet>>());
|
||||
}
|
||||
|
||||
var packetListIndex = incomingPackets[incomingPacketKey].Count;
|
||||
for (var i = 0; i < incomingPackets[incomingPacketKey].Count; i++)
|
||||
{
|
||||
if (incomingPackets[incomingPacketKey][i][0].id == packet.id)
|
||||
{
|
||||
packetListIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packetListIndex == incomingPackets[incomingPacketKey].Count)
|
||||
{
|
||||
incomingPackets[incomingPacketKey].Add(new List<Packet>());
|
||||
}
|
||||
|
||||
var insertionIndex = -1;
|
||||
|
||||
for (var i = 0; i < incomingPackets[incomingPacketKey][packetListIndex].Count; i++)
|
||||
{
|
||||
if (incomingPackets[incomingPacketKey][packetListIndex][i].fragment > packet.fragment)
|
||||
{
|
||||
insertionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (insertionIndex >= 0)
|
||||
{
|
||||
incomingPackets[incomingPacketKey][packetListIndex].Insert(insertionIndex, packet);
|
||||
packetReady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
incomingPackets[incomingPacketKey][packetListIndex].Add(packet);
|
||||
packetLength += tempPacket.data.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find fully received packets
|
||||
var emptyPacketLists = new List<List<Packet>>();
|
||||
foreach (var keyValuePair in incomingPackets)
|
||||
{
|
||||
for (var packetList = 0; packetList < keyValuePair.Value.Count; packetList++)
|
||||
if (packetReady)
|
||||
{
|
||||
var packetReady = true;
|
||||
var packetLength = 0;
|
||||
var data = new byte[packetLength];
|
||||
var dataIndex = 0;
|
||||
|
||||
for (var packet = 0; packet < keyValuePair.Value[packetList].Count; packet++)
|
||||
{
|
||||
var tempPacket = keyValuePair.Value[packetList][packet];
|
||||
if (tempPacket.fragment != packet || packet == keyValuePair.Value[packetList].Count - 1 && tempPacket.moreFragments)
|
||||
{
|
||||
packetReady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
packetLength += tempPacket.data.Length;
|
||||
}
|
||||
Array.Copy(keyValuePair.Value[packetList][packet].data, 0, data, dataIndex, keyValuePair.Value[packetList][packet].data.Length);
|
||||
dataIndex += keyValuePair.Value[packetList][packet].data.Length;
|
||||
}
|
||||
|
||||
if (packetReady)
|
||||
{
|
||||
var data = new byte[packetLength];
|
||||
var dataIndex = 0;
|
||||
OnReceiveData(data, keyValuePair.Key.productUserId, keyValuePair.Key.channel);
|
||||
|
||||
for (var packet = 0; packet < keyValuePair.Value[packetList].Count; packet++)
|
||||
{
|
||||
Array.Copy(keyValuePair.Value[packetList][packet].data, 0, data, dataIndex, keyValuePair.Value[packetList][packet].data.Length);
|
||||
dataIndex += keyValuePair.Value[packetList][packet].data.Length;
|
||||
}
|
||||
|
||||
OnReceiveData(data, keyValuePair.Key.productUserId, keyValuePair.Key.channel);
|
||||
|
||||
//keyValuePair.Value[packetList].Clear();
|
||||
emptyPacketLists.Add(keyValuePair.Value[packetList]);
|
||||
}
|
||||
//keyValuePair.Value[packetList].Clear();
|
||||
emptyPacketLists.Add(keyValuePair.Value[packetList]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < emptyPacketLists.Count; i++)
|
||||
{
|
||||
keyValuePair.Value.Remove(emptyPacketLists[i]);
|
||||
}
|
||||
|
||||
emptyPacketLists.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
|
||||
for (var i = 0; i < emptyPacketLists.Count; i++)
|
||||
{
|
||||
keyValuePair.Value.Remove(emptyPacketLists[i]);
|
||||
}
|
||||
|
||||
emptyPacketLists.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserID, SocketId socketId);
|
||||
protected abstract void OnReceiveData(byte[] data, ProductUserId clientUserID, int channel);
|
||||
protected abstract void OnConnectionFailed(ProductUserId remoteId);
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserID, SocketId socketId);
|
||||
protected abstract void OnReceiveData(byte[] data, ProductUserId clientUserID, int channel);
|
||||
protected abstract void OnConnectionFailed(ProductUserId remoteId);
|
||||
}
|
@ -10,120 +10,120 @@ using UnityEngine;
|
||||
/// after releasing the SDK the game has to be restarted in order to initialize the SDK again.
|
||||
/// In the unity editor the OnDestroy function will not run so that we dont have to restart the editor after play.
|
||||
/// </summary>
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
[DefaultExecutionOrder(-32000)]
|
||||
public class EOSSDKComponent : MonoBehaviour
|
||||
{
|
||||
[DefaultExecutionOrder(-32000)]
|
||||
public class EOSSDKComponent : MonoBehaviour
|
||||
// Unity Inspector shown variables
|
||||
|
||||
[SerializeField]
|
||||
public EosApiKey apiKeys;
|
||||
|
||||
[Header("User Login")]
|
||||
public bool authInterfaceLogin = false;
|
||||
|
||||
public Epic.OnlineServices.Auth.LoginCredentialType authInterfaceCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
|
||||
public uint devAuthToolPort = 7878;
|
||||
public string devAuthToolCredentialName = "";
|
||||
public ExternalCredentialType connectInterfaceCredentialType = ExternalCredentialType.DeviceidAccessToken;
|
||||
public string deviceModel = "PC Windows 64bit";
|
||||
[SerializeField] private string displayName = "User";
|
||||
|
||||
public static string DisplayName
|
||||
{
|
||||
// Unity Inspector shown variables
|
||||
get => Instance.displayName;
|
||||
set => Instance.displayName = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
public EosApiKey apiKeys;
|
||||
[Header("Misc")]
|
||||
public LogLevel epicLoggerLevel = LogLevel.Error;
|
||||
|
||||
[Header("User Login")]
|
||||
public bool authInterfaceLogin = false;
|
||||
[SerializeField]
|
||||
public bool collectPlayerMetrics = true;
|
||||
|
||||
public Epic.OnlineServices.Auth.LoginCredentialType authInterfaceCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
|
||||
public uint devAuthToolPort = 7878;
|
||||
public string devAuthToolCredentialName = "";
|
||||
public ExternalCredentialType connectInterfaceCredentialType = ExternalCredentialType.DeviceidAccessToken;
|
||||
public string deviceModel = "PC Windows 64bit";
|
||||
[SerializeField] private string displayName = "User";
|
||||
public static bool CollectPlayerMetrics => Instance.collectPlayerMetrics;
|
||||
|
||||
public static string DisplayName
|
||||
public bool checkForEpicLauncherAndRestart = false;
|
||||
public bool delayedInitialization = false;
|
||||
public float platformTickIntervalInSeconds = 0.0f;
|
||||
private float platformTickTimer = 0f;
|
||||
public uint tickBudgetInMilliseconds = 0;
|
||||
|
||||
// End Unity Inspector shown variables
|
||||
|
||||
private ulong authExpirationHandle;
|
||||
|
||||
private string authInterfaceLoginCredentialId = null;
|
||||
public static void SetAuthInterfaceLoginCredentialId(string credentialId) => Instance.authInterfaceLoginCredentialId = credentialId;
|
||||
private string authInterfaceCredentialToken = null;
|
||||
public static void SetAuthInterfaceCredentialToken(string credentialToken) => Instance.authInterfaceCredentialToken = credentialToken;
|
||||
private string connectInterfaceCredentialToken = null;
|
||||
public static void SetConnectInterfaceCredentialToken(string credentialToken) => Instance.connectInterfaceCredentialToken = credentialToken;
|
||||
|
||||
private PlatformInterface EOS;
|
||||
|
||||
// Interfaces
|
||||
public static Epic.OnlineServices.Achievements.AchievementsInterface GetAchievementsInterface() => Instance.EOS.GetAchievementsInterface();
|
||||
public static Epic.OnlineServices.Auth.AuthInterface GetAuthInterface() => Instance.EOS.GetAuthInterface();
|
||||
public static Epic.OnlineServices.Connect.ConnectInterface GetConnectInterface() => Instance.EOS.GetConnectInterface();
|
||||
public static Epic.OnlineServices.Ecom.EcomInterface GetEcomInterface() => Instance.EOS.GetEcomInterface();
|
||||
public static Epic.OnlineServices.Friends.FriendsInterface GetFriendsInterface() => Instance.EOS.GetFriendsInterface();
|
||||
public static Epic.OnlineServices.Leaderboards.LeaderboardsInterface GetLeaderboardsInterface() => Instance.EOS.GetLeaderboardsInterface();
|
||||
public static Epic.OnlineServices.Lobby.LobbyInterface GetLobbyInterface() => Instance.EOS.GetLobbyInterface();
|
||||
public static Epic.OnlineServices.Metrics.MetricsInterface GetMetricsInterface() => Instance.EOS.GetMetricsInterface(); // Handled by the transport automatically, only use this interface if Mirror is not used for singleplayer
|
||||
public static Epic.OnlineServices.Mods.ModsInterface GetModsInterface() => Instance.EOS.GetModsInterface();
|
||||
public static Epic.OnlineServices.P2P.P2PInterface GetP2PInterface() => Instance.EOS.GetP2PInterface();
|
||||
public static Epic.OnlineServices.PlayerDataStorage.PlayerDataStorageInterface GetPlayerDataStorageInterface() => Instance.EOS.GetPlayerDataStorageInterface();
|
||||
public static Epic.OnlineServices.Presence.PresenceInterface GetPresenceInterface() => Instance.EOS.GetPresenceInterface();
|
||||
public static Epic.OnlineServices.Sessions.SessionsInterface GetSessionsInterface() => Instance.EOS.GetSessionsInterface();
|
||||
public static Epic.OnlineServices.TitleStorage.TitleStorageInterface GetTitleStorageInterface() => Instance.EOS.GetTitleStorageInterface();
|
||||
public static Epic.OnlineServices.UI.UIInterface GetUIInterface() => Instance.EOS.GetUIInterface();
|
||||
public static Epic.OnlineServices.UserInfo.UserInfoInterface GetUserInfoInterface() => Instance.EOS.GetUserInfoInterface();
|
||||
|
||||
protected EpicAccountId localUserAccountId;
|
||||
public static EpicAccountId LocalUserAccountId => Instance.localUserAccountId;
|
||||
|
||||
protected string localUserAccountIdString;
|
||||
public static string LocalUserAccountIdString => Instance.localUserAccountIdString;
|
||||
|
||||
protected ProductUserId localUserProductId;
|
||||
public static ProductUserId LocalUserProductId => Instance.localUserProductId;
|
||||
|
||||
protected string localUserProductIdString;
|
||||
public static string LocalUserProductIdString => Instance.localUserProductIdString;
|
||||
|
||||
protected bool initialized;
|
||||
public static bool Initialized => Instance.initialized;
|
||||
|
||||
protected bool isConnecting;
|
||||
public static bool IsConnecting => Instance.isConnecting;
|
||||
|
||||
protected static EOSSDKComponent instance;
|
||||
|
||||
protected static EOSSDKComponent Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
get => Instance.displayName;
|
||||
set => Instance.displayName = value;
|
||||
}
|
||||
|
||||
[Header("Misc")]
|
||||
public LogLevel epicLoggerLevel = LogLevel.Error;
|
||||
|
||||
[SerializeField]
|
||||
public bool collectPlayerMetrics = true;
|
||||
|
||||
public static bool CollectPlayerMetrics => Instance.collectPlayerMetrics;
|
||||
|
||||
public bool checkForEpicLauncherAndRestart = false;
|
||||
public bool delayedInitialization = false;
|
||||
public float platformTickIntervalInSeconds = 0.0f;
|
||||
private float platformTickTimer = 0f;
|
||||
public uint tickBudgetInMilliseconds = 0;
|
||||
|
||||
// End Unity Inspector shown variables
|
||||
|
||||
private ulong authExpirationHandle;
|
||||
|
||||
private string authInterfaceLoginCredentialId = null;
|
||||
public static void SetAuthInterfaceLoginCredentialId(string credentialId) => Instance.authInterfaceLoginCredentialId = credentialId;
|
||||
private string authInterfaceCredentialToken = null;
|
||||
public static void SetAuthInterfaceCredentialToken(string credentialToken) => Instance.authInterfaceCredentialToken = credentialToken;
|
||||
private string connectInterfaceCredentialToken = null;
|
||||
public static void SetConnectInterfaceCredentialToken(string credentialToken) => Instance.connectInterfaceCredentialToken = credentialToken;
|
||||
|
||||
private PlatformInterface EOS;
|
||||
|
||||
// Interfaces
|
||||
public static Epic.OnlineServices.Achievements.AchievementsInterface GetAchievementsInterface() => Instance.EOS.GetAchievementsInterface();
|
||||
public static Epic.OnlineServices.Auth.AuthInterface GetAuthInterface() => Instance.EOS.GetAuthInterface();
|
||||
public static Epic.OnlineServices.Connect.ConnectInterface GetConnectInterface() => Instance.EOS.GetConnectInterface();
|
||||
public static Epic.OnlineServices.Ecom.EcomInterface GetEcomInterface() => Instance.EOS.GetEcomInterface();
|
||||
public static Epic.OnlineServices.Friends.FriendsInterface GetFriendsInterface() => Instance.EOS.GetFriendsInterface();
|
||||
public static Epic.OnlineServices.Leaderboards.LeaderboardsInterface GetLeaderboardsInterface() => Instance.EOS.GetLeaderboardsInterface();
|
||||
public static Epic.OnlineServices.Lobby.LobbyInterface GetLobbyInterface() => Instance.EOS.GetLobbyInterface();
|
||||
public static Epic.OnlineServices.Metrics.MetricsInterface GetMetricsInterface() => Instance.EOS.GetMetricsInterface(); // Handled by the transport automatically, only use this interface if Mirror is not used for singleplayer
|
||||
public static Epic.OnlineServices.Mods.ModsInterface GetModsInterface() => Instance.EOS.GetModsInterface();
|
||||
public static Epic.OnlineServices.P2P.P2PInterface GetP2PInterface() => Instance.EOS.GetP2PInterface();
|
||||
public static Epic.OnlineServices.PlayerDataStorage.PlayerDataStorageInterface GetPlayerDataStorageInterface() => Instance.EOS.GetPlayerDataStorageInterface();
|
||||
public static Epic.OnlineServices.Presence.PresenceInterface GetPresenceInterface() => Instance.EOS.GetPresenceInterface();
|
||||
public static Epic.OnlineServices.Sessions.SessionsInterface GetSessionsInterface() => Instance.EOS.GetSessionsInterface();
|
||||
public static Epic.OnlineServices.TitleStorage.TitleStorageInterface GetTitleStorageInterface() => Instance.EOS.GetTitleStorageInterface();
|
||||
public static Epic.OnlineServices.UI.UIInterface GetUIInterface() => Instance.EOS.GetUIInterface();
|
||||
public static Epic.OnlineServices.UserInfo.UserInfoInterface GetUserInfoInterface() => Instance.EOS.GetUserInfoInterface();
|
||||
|
||||
protected EpicAccountId localUserAccountId;
|
||||
public static EpicAccountId LocalUserAccountId => Instance.localUserAccountId;
|
||||
|
||||
protected string localUserAccountIdString;
|
||||
public static string LocalUserAccountIdString => Instance.localUserAccountIdString;
|
||||
|
||||
protected ProductUserId localUserProductId;
|
||||
public static ProductUserId LocalUserProductId => Instance.localUserProductId;
|
||||
|
||||
protected string localUserProductIdString;
|
||||
public static string LocalUserProductIdString => Instance.localUserProductIdString;
|
||||
|
||||
protected bool initialized;
|
||||
public static bool Initialized => Instance.initialized;
|
||||
|
||||
protected bool isConnecting;
|
||||
public static bool IsConnecting => Instance.isConnecting;
|
||||
|
||||
protected static EOSSDKComponent instance;
|
||||
|
||||
protected static EOSSDKComponent Instance
|
||||
{
|
||||
get
|
||||
if (instance == null)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
return new GameObject("EOSSDKComponent").AddComponent<EOSSDKComponent>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
return new GameObject("EOSSDKComponent").AddComponent<EOSSDKComponent>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
instance.platformTickTimer -= Time.deltaTime;
|
||||
instance.EOS.Tick();
|
||||
}
|
||||
public static void Tick()
|
||||
{
|
||||
instance.platformTickTimer -= Time.deltaTime;
|
||||
instance.EOS.Tick();
|
||||
}
|
||||
|
||||
// If we're in editor, we should dynamically load and unload the SDK between play sessions.
|
||||
// This allows us to initialize the SDK each time the game is run in editor.
|
||||
// If we're in editor, we should dynamically load and unload the SDK between play sessions.
|
||||
// This allows us to initialize the SDK each time the game is run in editor.
|
||||
#if UNITY_EDITOR_WIN
|
||||
[DllImport("Kernel32.dll")]
|
||||
private static extern IntPtr LoadLibrary(string lpLibFileName);
|
||||
@ -163,27 +163,27 @@ namespace EpicTransport
|
||||
private IntPtr libraryPointer;
|
||||
#endif
|
||||
|
||||
private void Awake()
|
||||
private void Awake()
|
||||
{
|
||||
// Initialize Java version of the SDK with a reference to the VM with JNI
|
||||
// See https://eoshelp.epicgames.com/s/question/0D54z00006ufJBNCA2/cant-get-createdeviceid-to-work-in-unity-android-c-sdk?language=en_US
|
||||
if (Application.platform == RuntimePlatform.Android)
|
||||
{
|
||||
// Initialize Java version of the SDK with a reference to the VM with JNI
|
||||
// See https://eoshelp.epicgames.com/s/question/0D54z00006ufJBNCA2/cant-get-createdeviceid-to-work-in-unity-android-c-sdk?language=en_US
|
||||
if (Application.platform == RuntimePlatform.Android)
|
||||
{
|
||||
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
||||
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
|
||||
var context = activity.Call<AndroidJavaObject>("getApplicationContext");
|
||||
var EOS_SDK_JAVA = new AndroidJavaClass("com.epicgames.mobile.eossdk.EOSSDK");
|
||||
EOS_SDK_JAVA.CallStatic("init", context);
|
||||
}
|
||||
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
||||
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
|
||||
var context = activity.Call<AndroidJavaObject>("getApplicationContext");
|
||||
var EOS_SDK_JAVA = new AndroidJavaClass("com.epicgames.mobile.eossdk.EOSSDK");
|
||||
EOS_SDK_JAVA.CallStatic("init", context);
|
||||
}
|
||||
|
||||
// Prevent multiple instances
|
||||
if (instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
// Prevent multiple instances
|
||||
if (instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
instance = this;
|
||||
instance = this;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var libraryPath = "Assets/Mirror/Runtime/Transport/EpicOnlineTransport/EOSSDK/" + Config.LibraryName;
|
||||
@ -196,260 +196,260 @@ namespace EpicTransport
|
||||
Bindings.Hook(libraryPointer, GetProcAddress);
|
||||
#endif
|
||||
|
||||
if (!delayedInitialization)
|
||||
if (!delayedInitialization)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
protected void InitializeImplementation()
|
||||
{
|
||||
isConnecting = true;
|
||||
|
||||
var initializeOptions = new InitializeOptions()
|
||||
{
|
||||
ProductName = apiKeys.epicProductName,
|
||||
ProductVersion = apiKeys.epicProductVersion
|
||||
};
|
||||
|
||||
var initializeResult = PlatformInterface.Initialize(initializeOptions);
|
||||
|
||||
// This code is called each time the game is run in the editor, so we catch the case where the SDK has already been initialized in the editor.
|
||||
var isAlreadyConfiguredInEditor = Application.isEditor && initializeResult == Result.AlreadyConfigured;
|
||||
if (initializeResult != Result.Success && !isAlreadyConfiguredInEditor)
|
||||
{
|
||||
throw new System.Exception("Failed to initialize platform: " + initializeResult);
|
||||
}
|
||||
|
||||
// The SDK outputs lots of information that is useful for debugging.
|
||||
// Make sure to set up the logging interface as early as possible: after initializing.
|
||||
LoggingInterface.SetLogLevel(LogCategory.AllCategories, epicLoggerLevel);
|
||||
LoggingInterface.SetCallback(message => Logger.EpicDebugLog(message));
|
||||
|
||||
var options = new Options()
|
||||
{
|
||||
ProductId = apiKeys.epicProductId,
|
||||
SandboxId = apiKeys.epicSandboxId,
|
||||
DeploymentId = apiKeys.epicDeploymentId,
|
||||
ClientCredentials = new ClientCredentials()
|
||||
{
|
||||
Initialize();
|
||||
ClientId = apiKeys.epicClientId,
|
||||
ClientSecret = apiKeys.epicClientSecret
|
||||
},
|
||||
TickBudgetInMilliseconds = tickBudgetInMilliseconds
|
||||
};
|
||||
|
||||
EOS = PlatformInterface.Create(options);
|
||||
if (EOS == null)
|
||||
{
|
||||
throw new System.Exception("Failed to create platform");
|
||||
}
|
||||
|
||||
if (checkForEpicLauncherAndRestart)
|
||||
{
|
||||
var result = EOS.CheckForLauncherAndRestart();
|
||||
|
||||
// If not started through epic launcher the app will be restarted and we can quit
|
||||
if (result != Result.NoChange)
|
||||
{
|
||||
// Log error if launcher check failed, but still quit to prevent hacking
|
||||
if (result == Result.UnexpectedError)
|
||||
{
|
||||
Debug.LogError("Unexpected Error while checking if app was started through epic launcher");
|
||||
}
|
||||
|
||||
Application.Quit();
|
||||
}
|
||||
}
|
||||
|
||||
protected void InitializeImplementation()
|
||||
// If we use the Auth interface then only login into the Connect interface after finishing the auth interface login
|
||||
// If we don't use the Auth interface we can directly login to the Connect interface
|
||||
if (authInterfaceLogin)
|
||||
{
|
||||
isConnecting = true;
|
||||
|
||||
var initializeOptions = new InitializeOptions()
|
||||
if (authInterfaceCredentialType == Epic.OnlineServices.Auth.LoginCredentialType.Developer)
|
||||
{
|
||||
ProductName = apiKeys.epicProductName,
|
||||
ProductVersion = apiKeys.epicProductVersion
|
||||
};
|
||||
|
||||
var initializeResult = PlatformInterface.Initialize(initializeOptions);
|
||||
|
||||
// This code is called each time the game is run in the editor, so we catch the case where the SDK has already been initialized in the editor.
|
||||
var isAlreadyConfiguredInEditor = Application.isEditor && initializeResult == Result.AlreadyConfigured;
|
||||
if (initializeResult != Result.Success && !isAlreadyConfiguredInEditor)
|
||||
{
|
||||
throw new System.Exception("Failed to initialize platform: " + initializeResult);
|
||||
authInterfaceLoginCredentialId = "localhost:" + devAuthToolPort;
|
||||
authInterfaceCredentialToken = devAuthToolCredentialName;
|
||||
}
|
||||
|
||||
// The SDK outputs lots of information that is useful for debugging.
|
||||
// Make sure to set up the logging interface as early as possible: after initializing.
|
||||
LoggingInterface.SetLogLevel(LogCategory.AllCategories, epicLoggerLevel);
|
||||
LoggingInterface.SetCallback(message => Logger.EpicDebugLog(message));
|
||||
|
||||
var options = new Options()
|
||||
// Login to Auth Interface
|
||||
var loginOptions = new Epic.OnlineServices.Auth.LoginOptions()
|
||||
{
|
||||
ProductId = apiKeys.epicProductId,
|
||||
SandboxId = apiKeys.epicSandboxId,
|
||||
DeploymentId = apiKeys.epicDeploymentId,
|
||||
ClientCredentials = new ClientCredentials()
|
||||
Credentials = new Epic.OnlineServices.Auth.Credentials()
|
||||
{
|
||||
ClientId = apiKeys.epicClientId,
|
||||
ClientSecret = apiKeys.epicClientSecret
|
||||
Type = authInterfaceCredentialType,
|
||||
Id = authInterfaceLoginCredentialId,
|
||||
Token = authInterfaceCredentialToken
|
||||
},
|
||||
TickBudgetInMilliseconds = tickBudgetInMilliseconds
|
||||
ScopeFlags = Epic.OnlineServices.Auth.AuthScopeFlags.BasicProfile | Epic.OnlineServices.Auth.AuthScopeFlags.FriendsList | Epic.OnlineServices.Auth.AuthScopeFlags.Presence
|
||||
};
|
||||
|
||||
EOS = PlatformInterface.Create(options);
|
||||
if (EOS == null)
|
||||
EOS.GetAuthInterface().Login(loginOptions, null, OnAuthInterfaceLogin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Login to Connect Interface
|
||||
if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
||||
{
|
||||
throw new System.Exception("Failed to create platform");
|
||||
}
|
||||
|
||||
if (checkForEpicLauncherAndRestart)
|
||||
{
|
||||
var result = EOS.CheckForLauncherAndRestart();
|
||||
|
||||
// If not started through epic launcher the app will be restarted and we can quit
|
||||
if (result != Result.NoChange)
|
||||
{
|
||||
// Log error if launcher check failed, but still quit to prevent hacking
|
||||
if (result == Result.UnexpectedError)
|
||||
{
|
||||
Debug.LogError("Unexpected Error while checking if app was started through epic launcher");
|
||||
}
|
||||
|
||||
Application.Quit();
|
||||
}
|
||||
}
|
||||
|
||||
// If we use the Auth interface then only login into the Connect interface after finishing the auth interface login
|
||||
// If we don't use the Auth interface we can directly login to the Connect interface
|
||||
if (authInterfaceLogin)
|
||||
{
|
||||
if (authInterfaceCredentialType == Epic.OnlineServices.Auth.LoginCredentialType.Developer)
|
||||
{
|
||||
authInterfaceLoginCredentialId = "localhost:" + devAuthToolPort;
|
||||
authInterfaceCredentialToken = devAuthToolCredentialName;
|
||||
}
|
||||
|
||||
// Login to Auth Interface
|
||||
var loginOptions = new Epic.OnlineServices.Auth.LoginOptions()
|
||||
{
|
||||
Credentials = new Epic.OnlineServices.Auth.Credentials()
|
||||
{
|
||||
Type = authInterfaceCredentialType,
|
||||
Id = authInterfaceLoginCredentialId,
|
||||
Token = authInterfaceCredentialToken
|
||||
},
|
||||
ScopeFlags = Epic.OnlineServices.Auth.AuthScopeFlags.BasicProfile | Epic.OnlineServices.Auth.AuthScopeFlags.FriendsList | Epic.OnlineServices.Auth.AuthScopeFlags.Presence
|
||||
};
|
||||
|
||||
EOS.GetAuthInterface().Login(loginOptions, null, OnAuthInterfaceLogin);
|
||||
var createDeviceIdOptions = new Epic.OnlineServices.Connect.CreateDeviceIdOptions();
|
||||
createDeviceIdOptions.DeviceModel = deviceModel;
|
||||
EOS.GetConnectInterface().CreateDeviceId(createDeviceIdOptions, null, OnCreateDeviceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Login to Connect Interface
|
||||
if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
||||
{
|
||||
var createDeviceIdOptions = new Epic.OnlineServices.Connect.CreateDeviceIdOptions();
|
||||
createDeviceIdOptions.DeviceModel = deviceModel;
|
||||
EOS.GetConnectInterface().CreateDeviceId(createDeviceIdOptions, null, OnCreateDeviceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Instance.initialized || Instance.isConnecting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Instance.InitializeImplementation();
|
||||
}
|
||||
|
||||
private void OnAuthInterfaceLogin(Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
Debug.Log("Auth Interface Login succeeded");
|
||||
|
||||
string accountIdString;
|
||||
var result = loginCallbackInfo.LocalUserId.ToString(out accountIdString);
|
||||
if (Result.Success == result)
|
||||
{
|
||||
Debug.Log("EOS User ID:" + accountIdString);
|
||||
|
||||
localUserAccountIdString = accountIdString;
|
||||
localUserAccountId = loginCallbackInfo.LocalUserId;
|
||||
}
|
||||
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
||||
{
|
||||
Debug.Log("Login returned " + loginCallbackInfo.ResultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Instance.initialized || Instance.isConnecting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnCreateDeviceId(Epic.OnlineServices.Connect.CreateDeviceIdCallbackInfo createDeviceIdCallbackInfo)
|
||||
Instance.InitializeImplementation();
|
||||
}
|
||||
|
||||
private void OnAuthInterfaceLogin(Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
if (createDeviceIdCallbackInfo.ResultCode == Result.Success || createDeviceIdCallbackInfo.ResultCode == Result.DuplicateNotAllowed)
|
||||
{
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(createDeviceIdCallbackInfo.ResultCode))
|
||||
{
|
||||
Debug.Log("Device ID creation returned " + createDeviceIdCallbackInfo.ResultCode);
|
||||
}
|
||||
}
|
||||
Debug.Log("Auth Interface Login succeeded");
|
||||
|
||||
private void ConnectInterfaceLogin()
|
||||
{
|
||||
var loginOptions = new Epic.OnlineServices.Connect.LoginOptions();
|
||||
|
||||
if (connectInterfaceCredentialType == ExternalCredentialType.Epic)
|
||||
string accountIdString;
|
||||
var result = loginCallbackInfo.LocalUserId.ToString(out accountIdString);
|
||||
if (Result.Success == result)
|
||||
{
|
||||
Epic.OnlineServices.Auth.Token token;
|
||||
var result = EOS.GetAuthInterface().CopyUserAuthToken(new Epic.OnlineServices.Auth.CopyUserAuthTokenOptions(), localUserAccountId, out token);
|
||||
Debug.Log("EOS User ID:" + accountIdString);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
connectInterfaceCredentialToken = token.AccessToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Failed to retrieve User Auth Token");
|
||||
}
|
||||
}
|
||||
else if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
||||
{
|
||||
loginOptions.UserLoginInfo = new Epic.OnlineServices.Connect.UserLoginInfo();
|
||||
loginOptions.UserLoginInfo.DisplayName = displayName;
|
||||
localUserAccountIdString = accountIdString;
|
||||
localUserAccountId = loginCallbackInfo.LocalUserId;
|
||||
}
|
||||
|
||||
loginOptions.Credentials = new Epic.OnlineServices.Connect.Credentials();
|
||||
loginOptions.Credentials.Type = connectInterfaceCredentialType;
|
||||
loginOptions.Credentials.Token = connectInterfaceCredentialToken;
|
||||
|
||||
EOS.GetConnectInterface().Login(loginOptions, null, OnConnectInterfaceLogin);
|
||||
}
|
||||
|
||||
private void OnConnectInterfaceLogin(Epic.OnlineServices.Connect.LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
Debug.Log("Connect Interface Login succeeded");
|
||||
|
||||
string productIdString;
|
||||
var result = loginCallbackInfo.LocalUserId.ToString(out productIdString);
|
||||
if (Result.Success == result)
|
||||
{
|
||||
Debug.Log("EOS User Product ID:" + productIdString);
|
||||
|
||||
localUserProductIdString = productIdString;
|
||||
localUserProductId = loginCallbackInfo.LocalUserId;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
isConnecting = false;
|
||||
|
||||
var authExpirationOptions = new Epic.OnlineServices.Connect.AddNotifyAuthExpirationOptions();
|
||||
authExpirationHandle = EOS.GetConnectInterface().AddNotifyAuthExpiration(authExpirationOptions, null, OnAuthExpiration);
|
||||
}
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
||||
{
|
||||
Debug.Log("Login returned " + loginCallbackInfo.ResultCode + "\nRetrying...");
|
||||
EOS.GetConnectInterface().CreateUser(new Epic.OnlineServices.Connect.CreateUserOptions() { ContinuanceToken = loginCallbackInfo.ContinuanceToken }, null, (Epic.OnlineServices.Connect.CreateUserCallbackInfo cb) =>
|
||||
{
|
||||
if (cb.ResultCode != Result.Success)
|
||||
{
|
||||
Debug.Log(cb.ResultCode);
|
||||
return;
|
||||
}
|
||||
|
||||
localUserProductId = cb.LocalUserId;
|
||||
ConnectInterfaceLogin();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAuthExpiration(Epic.OnlineServices.Connect.AuthExpirationCallbackInfo authExpirationCallbackInfo)
|
||||
{
|
||||
Debug.Log("AuthExpiration callback");
|
||||
EOS.GetConnectInterface().RemoveNotifyAuthExpiration(authExpirationHandle);
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
|
||||
// Calling tick on a regular interval is required for callbacks to work.
|
||||
private void LateUpdate()
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
||||
{
|
||||
if (EOS != null)
|
||||
{
|
||||
platformTickTimer += Time.deltaTime;
|
||||
Debug.Log("Login returned " + loginCallbackInfo.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (platformTickTimer >= platformTickIntervalInSeconds)
|
||||
{
|
||||
platformTickTimer = 0;
|
||||
EOS.Tick();
|
||||
}
|
||||
private void OnCreateDeviceId(Epic.OnlineServices.Connect.CreateDeviceIdCallbackInfo createDeviceIdCallbackInfo)
|
||||
{
|
||||
if (createDeviceIdCallbackInfo.ResultCode == Result.Success || createDeviceIdCallbackInfo.ResultCode == Result.DuplicateNotAllowed)
|
||||
{
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(createDeviceIdCallbackInfo.ResultCode))
|
||||
{
|
||||
Debug.Log("Device ID creation returned " + createDeviceIdCallbackInfo.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectInterfaceLogin()
|
||||
{
|
||||
var loginOptions = new Epic.OnlineServices.Connect.LoginOptions();
|
||||
|
||||
if (connectInterfaceCredentialType == ExternalCredentialType.Epic)
|
||||
{
|
||||
Epic.OnlineServices.Auth.Token token;
|
||||
var result = EOS.GetAuthInterface().CopyUserAuthToken(new Epic.OnlineServices.Auth.CopyUserAuthTokenOptions(), localUserAccountId, out token);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
connectInterfaceCredentialToken = token.AccessToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Failed to retrieve User Auth Token");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
else if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
||||
{
|
||||
if (EOS != null)
|
||||
loginOptions.UserLoginInfo = new Epic.OnlineServices.Connect.UserLoginInfo();
|
||||
loginOptions.UserLoginInfo.DisplayName = displayName;
|
||||
}
|
||||
|
||||
loginOptions.Credentials = new Epic.OnlineServices.Connect.Credentials();
|
||||
loginOptions.Credentials.Type = connectInterfaceCredentialType;
|
||||
loginOptions.Credentials.Token = connectInterfaceCredentialToken;
|
||||
|
||||
EOS.GetConnectInterface().Login(loginOptions, null, OnConnectInterfaceLogin);
|
||||
}
|
||||
|
||||
private void OnConnectInterfaceLogin(Epic.OnlineServices.Connect.LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
Debug.Log("Connect Interface Login succeeded");
|
||||
|
||||
string productIdString;
|
||||
var result = loginCallbackInfo.LocalUserId.ToString(out productIdString);
|
||||
if (Result.Success == result)
|
||||
{
|
||||
EOS.Release();
|
||||
EOS = null;
|
||||
PlatformInterface.Shutdown();
|
||||
Debug.Log("EOS User Product ID:" + productIdString);
|
||||
|
||||
localUserProductIdString = productIdString;
|
||||
localUserProductId = loginCallbackInfo.LocalUserId;
|
||||
}
|
||||
|
||||
// Unhook the library in the editor, this makes it possible to load the library again after stopping to play
|
||||
initialized = true;
|
||||
isConnecting = false;
|
||||
|
||||
var authExpirationOptions = new Epic.OnlineServices.Connect.AddNotifyAuthExpirationOptions();
|
||||
authExpirationHandle = EOS.GetConnectInterface().AddNotifyAuthExpiration(authExpirationOptions, null, OnAuthExpiration);
|
||||
}
|
||||
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
||||
{
|
||||
Debug.Log("Login returned " + loginCallbackInfo.ResultCode + "\nRetrying...");
|
||||
EOS.GetConnectInterface().CreateUser(new Epic.OnlineServices.Connect.CreateUserOptions() { ContinuanceToken = loginCallbackInfo.ContinuanceToken }, null, (Epic.OnlineServices.Connect.CreateUserCallbackInfo cb) =>
|
||||
{
|
||||
if (cb.ResultCode != Result.Success)
|
||||
{
|
||||
Debug.Log(cb.ResultCode);
|
||||
return;
|
||||
}
|
||||
|
||||
localUserProductId = cb.LocalUserId;
|
||||
ConnectInterfaceLogin();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAuthExpiration(Epic.OnlineServices.Connect.AuthExpirationCallbackInfo authExpirationCallbackInfo)
|
||||
{
|
||||
Debug.Log("AuthExpiration callback");
|
||||
EOS.GetConnectInterface().RemoveNotifyAuthExpiration(authExpirationHandle);
|
||||
ConnectInterfaceLogin();
|
||||
}
|
||||
|
||||
// Calling tick on a regular interval is required for callbacks to work.
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (EOS != null)
|
||||
{
|
||||
platformTickTimer += Time.deltaTime;
|
||||
|
||||
if (platformTickTimer >= platformTickIntervalInSeconds)
|
||||
{
|
||||
platformTickTimer = 0;
|
||||
EOS.Tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
if (EOS != null)
|
||||
{
|
||||
EOS.Release();
|
||||
EOS = null;
|
||||
PlatformInterface.Shutdown();
|
||||
}
|
||||
|
||||
// Unhook the library in the editor, this makes it possible to load the library again after stopping to play
|
||||
#if UNITY_EDITOR
|
||||
if (libraryPointer != IntPtr.Zero) {
|
||||
Bindings.Unhook();
|
||||
@ -460,6 +460,5 @@ namespace EpicTransport
|
||||
libraryPointer = IntPtr.Zero;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -6,395 +6,394 @@ using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
/// <summary>
|
||||
/// EOS Transport following the Mirror transport standard
|
||||
/// </summary>
|
||||
public class EosTransport : Transport
|
||||
{
|
||||
/// <summary>
|
||||
/// EOS Transport following the Mirror transport standard
|
||||
/// </summary>
|
||||
public class EosTransport : Transport
|
||||
private const string EPIC_SCHEME = "epic";
|
||||
|
||||
private Client client;
|
||||
private Server server;
|
||||
|
||||
private Common activeNode;
|
||||
|
||||
[SerializeField]
|
||||
public PacketReliability[] Channels = new PacketReliability[2] { PacketReliability.ReliableOrdered, PacketReliability.UnreliableUnordered };
|
||||
|
||||
[Tooltip("Timeout for connecting in seconds.")]
|
||||
public int timeout = 25;
|
||||
|
||||
[Tooltip("The max fragments used in fragmentation before throwing an error.")]
|
||||
public int maxFragments = 55;
|
||||
|
||||
public float ignoreCachedMessagesAtStartUpInSeconds = 2.0f;
|
||||
private float ignoreCachedMessagesTimer = 0.0f;
|
||||
|
||||
public RelayControl relayControl = RelayControl.AllowRelays;
|
||||
|
||||
[Header("Info")]
|
||||
[Tooltip("This will display your Epic Account ID when you start or connect to a server.")]
|
||||
public ProductUserId productUserId;
|
||||
|
||||
private int packetId = 0;
|
||||
|
||||
public Action<string> SetTransportError;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
private const string EPIC_SCHEME = "epic";
|
||||
Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for EOS Transport.");
|
||||
Debug.Assert(Channels.Length < byte.MaxValue, "Too many channels configured for EOS Transport");
|
||||
|
||||
private Client client;
|
||||
private Server server;
|
||||
|
||||
private Common activeNode;
|
||||
|
||||
[SerializeField]
|
||||
public PacketReliability[] Channels = new PacketReliability[2] { PacketReliability.ReliableOrdered, PacketReliability.UnreliableUnordered };
|
||||
|
||||
[Tooltip("Timeout for connecting in seconds.")]
|
||||
public int timeout = 25;
|
||||
|
||||
[Tooltip("The max fragments used in fragmentation before throwing an error.")]
|
||||
public int maxFragments = 55;
|
||||
|
||||
public float ignoreCachedMessagesAtStartUpInSeconds = 2.0f;
|
||||
private float ignoreCachedMessagesTimer = 0.0f;
|
||||
|
||||
public RelayControl relayControl = RelayControl.AllowRelays;
|
||||
|
||||
[Header("Info")]
|
||||
[Tooltip("This will display your Epic Account ID when you start or connect to a server.")]
|
||||
public ProductUserId productUserId;
|
||||
|
||||
private int packetId = 0;
|
||||
|
||||
public Action<string> SetTransportError;
|
||||
|
||||
private void Awake()
|
||||
if (Channels[0] != PacketReliability.ReliableOrdered)
|
||||
{
|
||||
Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for EOS Transport.");
|
||||
Debug.Assert(Channels.Length < byte.MaxValue, "Too many channels configured for EOS Transport");
|
||||
|
||||
if (Channels[0] != PacketReliability.ReliableOrdered)
|
||||
{
|
||||
Debug.LogWarning("EOS Transport Channel[0] is not ReliableOrdered, Mirror expects Channel 0 to be ReliableOrdered, only change this if you know what you are doing.");
|
||||
}
|
||||
|
||||
if (Channels[1] != PacketReliability.UnreliableUnordered)
|
||||
{
|
||||
Debug.LogWarning("EOS Transport Channel[1] is not UnreliableUnordered, Mirror expects Channel 1 to be UnreliableUnordered, only change this if you know what you are doing.");
|
||||
}
|
||||
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
StartCoroutine("ChangeRelayStatus");
|
||||
Debug.LogWarning("EOS Transport Channel[0] is not ReliableOrdered, Mirror expects Channel 0 to be ReliableOrdered, only change this if you know what you are doing.");
|
||||
}
|
||||
|
||||
public override void ClientEarlyUpdate()
|
||||
if (Channels[1] != PacketReliability.UnreliableUnordered)
|
||||
{
|
||||
EOSSDKComponent.Tick();
|
||||
|
||||
if (activeNode != null)
|
||||
{
|
||||
ignoreCachedMessagesTimer += Time.deltaTime;
|
||||
|
||||
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds)
|
||||
{
|
||||
activeNode.ignoreAllMessages = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeNode.ignoreAllMessages = false;
|
||||
|
||||
if (client != null && !client.isConnecting)
|
||||
{
|
||||
if (EOSSDKComponent.Initialized)
|
||||
{
|
||||
client.Connect(client.hostAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("EOS not initialized");
|
||||
client.EosNotInitialized();
|
||||
}
|
||||
|
||||
client.isConnecting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
activeNode?.ReceiveData();
|
||||
}
|
||||
Debug.LogWarning("EOS Transport Channel[1] is not UnreliableUnordered, Mirror expects Channel 1 to be UnreliableUnordered, only change this if you know what you are doing.");
|
||||
}
|
||||
|
||||
public override void ClientLateUpdate() { }
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
StartCoroutine("ChangeRelayStatus");
|
||||
}
|
||||
|
||||
public override void ServerEarlyUpdate()
|
||||
public override void ClientEarlyUpdate()
|
||||
{
|
||||
EOSSDKComponent.Tick();
|
||||
|
||||
if (activeNode != null)
|
||||
{
|
||||
EOSSDKComponent.Tick();
|
||||
ignoreCachedMessagesTimer += Time.deltaTime;
|
||||
|
||||
if (activeNode != null)
|
||||
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds)
|
||||
{
|
||||
ignoreCachedMessagesTimer += Time.deltaTime;
|
||||
|
||||
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds)
|
||||
{
|
||||
activeNode.ignoreAllMessages = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeNode.ignoreAllMessages = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
activeNode?.ReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ServerLateUpdate() { }
|
||||
|
||||
public override bool ClientConnected() => ClientActive() && client.Connected;
|
||||
|
||||
public override void ClientConnect(string address)
|
||||
{
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.LogError("EOS not initialized. Client could not be started.");
|
||||
OnClientDisconnected.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
|
||||
if (ServerActive())
|
||||
{
|
||||
Debug.LogError("Transport already running as server!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ClientActive() || client.Error)
|
||||
{
|
||||
Debug.Log($"Starting client, target address {address}.");
|
||||
|
||||
client = Client.CreateClient(this, address);
|
||||
activeNode = client;
|
||||
|
||||
if (EOSSDKComponent.CollectPlayerMetrics)
|
||||
{
|
||||
// Start Metrics colletion session
|
||||
var sessionOptions = new BeginPlayerSessionOptions();
|
||||
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
sessionOptions.ControllerType = UserControllerType.Unknown;
|
||||
sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
|
||||
sessionOptions.GameSessionId = null;
|
||||
sessionOptions.ServerIp = null;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
Debug.Log("Started Metric Session");
|
||||
}
|
||||
}
|
||||
activeNode.ignoreAllMessages = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Client already running!");
|
||||
}
|
||||
}
|
||||
activeNode.ignoreAllMessages = false;
|
||||
|
||||
public override void ClientConnect(Uri uri)
|
||||
{
|
||||
if (uri.Scheme != EPIC_SCHEME)
|
||||
{
|
||||
throw new ArgumentException($"Invalid url {uri}, use {EPIC_SCHEME}://EpicAccountId instead", nameof(uri));
|
||||
}
|
||||
|
||||
ClientConnect(uri.Host);
|
||||
}
|
||||
|
||||
public override void ClientSend(ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
Send(channelId, segment);
|
||||
}
|
||||
|
||||
public override void ClientDisconnect()
|
||||
{
|
||||
if (ClientActive())
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ClientActive() => client != null;
|
||||
|
||||
public override bool ServerActive() => server != null;
|
||||
|
||||
public override void ServerStart()
|
||||
{
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.LogError("EOS not initialized. Server could not be started.");
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
|
||||
if (ClientActive())
|
||||
{
|
||||
Debug.LogError("Transport already running as client!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ServerActive())
|
||||
{
|
||||
Debug.Log("Starting server.");
|
||||
|
||||
server = Server.CreateServer(this, NetworkManager.singleton.maxConnections);
|
||||
activeNode = server;
|
||||
|
||||
if (EOSSDKComponent.CollectPlayerMetrics)
|
||||
if (client != null && !client.isConnecting)
|
||||
{
|
||||
// Start Metrics colletion session
|
||||
var sessionOptions = new BeginPlayerSessionOptions();
|
||||
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
sessionOptions.ControllerType = UserControllerType.Unknown;
|
||||
sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
|
||||
sessionOptions.GameSessionId = null;
|
||||
sessionOptions.ServerIp = null;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
|
||||
|
||||
if (result == Result.Success)
|
||||
if (EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.Log("Started Metric Session");
|
||||
client.Connect(client.hostAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("EOS not initialized");
|
||||
client.EosNotInitialized();
|
||||
}
|
||||
|
||||
client.isConnecting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
activeNode?.ReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientLateUpdate() { }
|
||||
|
||||
public override void ServerEarlyUpdate()
|
||||
{
|
||||
EOSSDKComponent.Tick();
|
||||
|
||||
if (activeNode != null)
|
||||
{
|
||||
ignoreCachedMessagesTimer += Time.deltaTime;
|
||||
|
||||
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds)
|
||||
{
|
||||
activeNode.ignoreAllMessages = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Server already started!");
|
||||
activeNode.ignoreAllMessages = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override Uri ServerUri()
|
||||
if (enabled)
|
||||
{
|
||||
var epicBuilder = new UriBuilder
|
||||
{
|
||||
Scheme = EPIC_SCHEME,
|
||||
Host = EOSSDKComponent.LocalUserProductIdString
|
||||
};
|
||||
activeNode?.ReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
return epicBuilder.Uri;
|
||||
public override void ServerLateUpdate() { }
|
||||
|
||||
public override bool ClientConnected() => ClientActive() && client.Connected;
|
||||
|
||||
public override void ClientConnect(string address)
|
||||
{
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.LogError("EOS not initialized. Client could not be started.");
|
||||
OnClientDisconnected.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
|
||||
if (ServerActive())
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
Send(channelId, segment, connectionId);
|
||||
}
|
||||
Debug.LogError("Transport already running as server!");
|
||||
return;
|
||||
}
|
||||
|
||||
public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId);
|
||||
public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty;
|
||||
|
||||
public override void ServerStop()
|
||||
if (!ClientActive() || client.Error)
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
Debug.Log($"Starting client, target address {address}.");
|
||||
|
||||
private void Send(int channelId, ArraySegment<byte> segment, int connectionId = int.MinValue)
|
||||
{
|
||||
var packets = GetPacketArray(channelId, segment);
|
||||
client = Client.CreateClient(this, address);
|
||||
activeNode = client;
|
||||
|
||||
for (var i = 0; i < packets.Length; i++)
|
||||
{
|
||||
if (connectionId == int.MinValue)
|
||||
{
|
||||
client.Send(packets[i].ToBytes(), channelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
server.SendAll(connectionId, packets[i].ToBytes(), channelId);
|
||||
}
|
||||
}
|
||||
|
||||
packetId++;
|
||||
}
|
||||
|
||||
private Packet[] GetPacketArray(int channelId, ArraySegment<byte> segment)
|
||||
{
|
||||
var packetCount = Mathf.CeilToInt((float)segment.Count / (float)GetMaxSinglePacketSize(channelId));
|
||||
var packets = new Packet[packetCount];
|
||||
|
||||
for (var i = 0; i < segment.Count; i += GetMaxSinglePacketSize(channelId))
|
||||
{
|
||||
var fragment = i / GetMaxSinglePacketSize(channelId);
|
||||
|
||||
packets[fragment] = new Packet();
|
||||
packets[fragment].id = packetId;
|
||||
packets[fragment].fragment = fragment;
|
||||
packets[fragment].moreFragments = segment.Count - i > GetMaxSinglePacketSize(channelId);
|
||||
packets[fragment].data = new byte[segment.Count - i > GetMaxSinglePacketSize(channelId) ? GetMaxSinglePacketSize(channelId) : segment.Count - i];
|
||||
Array.Copy(segment.Array, i, packets[fragment].data, 0, packets[fragment].data.Length);
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (EOSSDKComponent.CollectPlayerMetrics)
|
||||
{
|
||||
// Stop Metrics collection session
|
||||
var endSessionOptions = new EndPlayerSessionOptions();
|
||||
endSessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().EndPlayerSession(endSessionOptions);
|
||||
// Start Metrics colletion session
|
||||
var sessionOptions = new BeginPlayerSessionOptions();
|
||||
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
sessionOptions.ControllerType = UserControllerType.Unknown;
|
||||
sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
|
||||
sessionOptions.GameSessionId = null;
|
||||
sessionOptions.ServerIp = null;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
Debug.LogError("Stopped Metric Session");
|
||||
Debug.Log("Started Metric Session");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Client already running!");
|
||||
}
|
||||
}
|
||||
|
||||
server?.Shutdown();
|
||||
client?.Disconnect();
|
||||
|
||||
server = null;
|
||||
client = null;
|
||||
activeNode = null;
|
||||
Debug.Log("Transport shut down.");
|
||||
public override void ClientConnect(Uri uri)
|
||||
{
|
||||
if (uri.Scheme != EPIC_SCHEME)
|
||||
{
|
||||
throw new ArgumentException($"Invalid url {uri}, use {EPIC_SCHEME}://EpicAccountId instead", nameof(uri));
|
||||
}
|
||||
|
||||
public int GetMaxSinglePacketSize(int channelId) => P2PInterface.MaxPacketSize - 10; // 1159 bytes, we need to remove 10 bytes for the packet header (id (4 bytes) + fragment (4 bytes) + more fragments (1 byte))
|
||||
ClientConnect(uri.Host);
|
||||
}
|
||||
|
||||
public override int GetMaxPacketSize(int channelId) => P2PInterface.MaxPacketSize * maxFragments;
|
||||
public override void ClientSend(ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
Send(channelId, segment);
|
||||
}
|
||||
|
||||
public override int GetBatchThreshold(int channelId) => P2PInterface.MaxPacketSize; // Use P2PInterface.MaxPacketSize as everything above will get fragmentated and will be counter effective to batching
|
||||
|
||||
public override bool Available()
|
||||
public override void ClientDisconnect()
|
||||
{
|
||||
if (ClientActive())
|
||||
{
|
||||
try
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ClientActive() => client != null;
|
||||
|
||||
public override bool ServerActive() => server != null;
|
||||
|
||||
public override void ServerStart()
|
||||
{
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.LogError("EOS not initialized. Server could not be started.");
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine("FetchEpicAccountId");
|
||||
|
||||
if (ClientActive())
|
||||
{
|
||||
Debug.LogError("Transport already running as client!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ServerActive())
|
||||
{
|
||||
Debug.Log("Starting server.");
|
||||
|
||||
server = Server.CreateServer(this, NetworkManager.singleton.maxConnections);
|
||||
activeNode = server;
|
||||
|
||||
if (EOSSDKComponent.CollectPlayerMetrics)
|
||||
{
|
||||
return EOSSDKComponent.Initialized;
|
||||
// Start Metrics colletion session
|
||||
var sessionOptions = new BeginPlayerSessionOptions();
|
||||
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
sessionOptions.ControllerType = UserControllerType.Unknown;
|
||||
sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
|
||||
sessionOptions.GameSessionId = null;
|
||||
sessionOptions.ServerIp = null;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
Debug.Log("Started Metric Session");
|
||||
}
|
||||
}
|
||||
catch
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Server already started!");
|
||||
}
|
||||
}
|
||||
|
||||
public override Uri ServerUri()
|
||||
{
|
||||
var epicBuilder = new UriBuilder
|
||||
{
|
||||
Scheme = EPIC_SCHEME,
|
||||
Host = EOSSDKComponent.LocalUserProductIdString
|
||||
};
|
||||
|
||||
return epicBuilder.Uri;
|
||||
}
|
||||
|
||||
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
Send(channelId, segment, connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId);
|
||||
public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty;
|
||||
|
||||
public override void ServerStop()
|
||||
{
|
||||
if (ServerActive())
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void Send(int channelId, ArraySegment<byte> segment, int connectionId = int.MinValue)
|
||||
{
|
||||
var packets = GetPacketArray(channelId, segment);
|
||||
|
||||
for (var i = 0; i < packets.Length; i++)
|
||||
{
|
||||
if (connectionId == int.MinValue)
|
||||
{
|
||||
return false;
|
||||
client.Send(packets[i].ToBytes(), channelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
server.SendAll(connectionId, packets[i].ToBytes(), channelId);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FetchEpicAccountId()
|
||||
{
|
||||
while (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
packetId++;
|
||||
}
|
||||
|
||||
productUserId = EOSSDKComponent.LocalUserProductId;
|
||||
private Packet[] GetPacketArray(int channelId, ArraySegment<byte> segment)
|
||||
{
|
||||
var packetCount = Mathf.CeilToInt((float)segment.Count / (float)GetMaxSinglePacketSize(channelId));
|
||||
var packets = new Packet[packetCount];
|
||||
|
||||
for (var i = 0; i < segment.Count; i += GetMaxSinglePacketSize(channelId))
|
||||
{
|
||||
var fragment = i / GetMaxSinglePacketSize(channelId);
|
||||
|
||||
packets[fragment] = new Packet();
|
||||
packets[fragment].id = packetId;
|
||||
packets[fragment].fragment = fragment;
|
||||
packets[fragment].moreFragments = segment.Count - i > GetMaxSinglePacketSize(channelId);
|
||||
packets[fragment].data = new byte[segment.Count - i > GetMaxSinglePacketSize(channelId) ? GetMaxSinglePacketSize(channelId) : segment.Count - i];
|
||||
Array.Copy(segment.Array, i, packets[fragment].data, 0, packets[fragment].data.Length);
|
||||
}
|
||||
|
||||
private IEnumerator ChangeRelayStatus()
|
||||
return packets;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (EOSSDKComponent.CollectPlayerMetrics)
|
||||
{
|
||||
while (!EOSSDKComponent.Initialized)
|
||||
// Stop Metrics collection session
|
||||
var endSessionOptions = new EndPlayerSessionOptions();
|
||||
endSessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
|
||||
var result = EOSSDKComponent.GetMetricsInterface().EndPlayerSession(endSessionOptions);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
yield return null;
|
||||
Debug.LogError("Stopped Metric Session");
|
||||
}
|
||||
|
||||
var setRelayControlOptions = new SetRelayControlOptions();
|
||||
setRelayControlOptions.RelayControl = relayControl;
|
||||
|
||||
EOSSDKComponent.GetP2PInterface().SetRelayControl(setRelayControlOptions);
|
||||
}
|
||||
|
||||
public void ResetIgnoreMessagesAtStartUpTimer()
|
||||
server?.Shutdown();
|
||||
client?.Disconnect();
|
||||
|
||||
server = null;
|
||||
client = null;
|
||||
activeNode = null;
|
||||
Debug.Log("Transport shut down.");
|
||||
}
|
||||
|
||||
public int GetMaxSinglePacketSize(int channelId) => P2PInterface.MaxPacketSize - 10; // 1159 bytes, we need to remove 10 bytes for the packet header (id (4 bytes) + fragment (4 bytes) + more fragments (1 byte))
|
||||
|
||||
public override int GetMaxPacketSize(int channelId) => P2PInterface.MaxPacketSize * maxFragments;
|
||||
|
||||
public override int GetBatchThreshold(int channelId) => P2PInterface.MaxPacketSize; // Use P2PInterface.MaxPacketSize as everything above will get fragmentated and will be counter effective to batching
|
||||
|
||||
public override bool Available()
|
||||
{
|
||||
try
|
||||
{
|
||||
ignoreCachedMessagesTimer = 0;
|
||||
return EOSSDKComponent.Initialized;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FetchEpicAccountId()
|
||||
{
|
||||
while (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
productUserId = EOSSDKComponent.LocalUserProductId;
|
||||
}
|
||||
|
||||
private IEnumerator ChangeRelayStatus()
|
||||
{
|
||||
while (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
if (activeNode != null)
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
var setRelayControlOptions = new SetRelayControlOptions();
|
||||
setRelayControlOptions.RelayControl = relayControl;
|
||||
|
||||
EOSSDKComponent.GetP2PInterface().SetRelayControl(setRelayControlOptions);
|
||||
}
|
||||
|
||||
public void ResetIgnoreMessagesAtStartUpTimer()
|
||||
{
|
||||
ignoreCachedMessagesTimer = 0;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (activeNode != null)
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,30 +2,29 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
public static class Logger
|
||||
public static void EpicDebugLog(LogMessage message)
|
||||
{
|
||||
public static void EpicDebugLog(LogMessage message)
|
||||
switch (message.Level)
|
||||
{
|
||||
switch (message.Level)
|
||||
{
|
||||
case LogLevel.Info:
|
||||
Debug.Log($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
Debug.LogError($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
Debug.LogWarning($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Fatal:
|
||||
Debug.LogException(new Exception($"Epic Manager: Category - {message.Category} Message - {message.Message}"));
|
||||
break;
|
||||
default:
|
||||
Debug.Log($"Epic Manager: Unknown log processing. Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
}
|
||||
case LogLevel.Info:
|
||||
Debug.Log($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
Debug.LogError($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
Debug.LogWarning($"Epic Manager: Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
case LogLevel.Fatal:
|
||||
Debug.LogException(new Exception($"Epic Manager: Category - {message.Category} Message - {message.Message}"));
|
||||
break;
|
||||
default:
|
||||
Debug.Log($"Epic Manager: Unknown log processing. Category - {message.Category} Message - {message.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public struct Packet
|
||||
{
|
||||
public struct Packet
|
||||
public const int headerSize = sizeof(uint) + sizeof(uint) + 1;
|
||||
public int size => headerSize + data.Length;
|
||||
|
||||
// header
|
||||
public int id;
|
||||
public int fragment;
|
||||
public bool moreFragments;
|
||||
|
||||
// body
|
||||
public byte[] data;
|
||||
|
||||
public byte[] ToBytes()
|
||||
{
|
||||
public const int headerSize = sizeof(uint) + sizeof(uint) + 1;
|
||||
public int size => headerSize + data.Length;
|
||||
var array = new byte[size];
|
||||
|
||||
// header
|
||||
public int id;
|
||||
public int fragment;
|
||||
public bool moreFragments;
|
||||
// Copy id
|
||||
array[0] = (byte)id;
|
||||
array[1] = (byte)(id >> 8);
|
||||
array[2] = (byte)(id >> 0x10);
|
||||
array[3] = (byte)(id >> 0x18);
|
||||
|
||||
// body
|
||||
public byte[] data;
|
||||
// Copy fragment
|
||||
array[4] = (byte)fragment;
|
||||
array[5] = (byte)(fragment >> 8);
|
||||
array[6] = (byte)(fragment >> 0x10);
|
||||
array[7] = (byte)(fragment >> 0x18);
|
||||
|
||||
public byte[] ToBytes()
|
||||
{
|
||||
var array = new byte[size];
|
||||
array[8] = moreFragments ? (byte)1 : (byte)0;
|
||||
|
||||
// Copy id
|
||||
array[0] = (byte)id;
|
||||
array[1] = (byte)(id >> 8);
|
||||
array[2] = (byte)(id >> 0x10);
|
||||
array[3] = (byte)(id >> 0x18);
|
||||
Array.Copy(data, 0, array, 9, data.Length);
|
||||
|
||||
// Copy fragment
|
||||
array[4] = (byte)fragment;
|
||||
array[5] = (byte)(fragment >> 8);
|
||||
array[6] = (byte)(fragment >> 0x10);
|
||||
array[7] = (byte)(fragment >> 0x18);
|
||||
return array;
|
||||
}
|
||||
|
||||
array[8] = moreFragments ? (byte)1 : (byte)0;
|
||||
public void FromBytes(byte[] array)
|
||||
{
|
||||
id = BitConverter.ToInt32(array, 0);
|
||||
fragment = BitConverter.ToInt32(array, 4);
|
||||
moreFragments = array[8] == 1;
|
||||
|
||||
Array.Copy(data, 0, array, 9, data.Length);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public void FromBytes(byte[] array)
|
||||
{
|
||||
id = BitConverter.ToInt32(array, 0);
|
||||
fragment = BitConverter.ToInt32(array, 4);
|
||||
moreFragments = array[8] == 1;
|
||||
|
||||
data = new byte[array.Length - 9];
|
||||
Array.Copy(array, 9, data, 0, data.Length);
|
||||
}
|
||||
data = new byte[array.Length - 9];
|
||||
Array.Copy(array, 9, data, 0, data.Length);
|
||||
}
|
||||
}
|
@ -4,218 +4,217 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EpicTransport
|
||||
namespace EpicTransport;
|
||||
|
||||
public class Server : Common
|
||||
{
|
||||
public class Server : Common
|
||||
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<ProductUserId, int> epicToMirrorIds;
|
||||
private Dictionary<ProductUserId, SocketId> epicToSocketIds;
|
||||
private int maxConnections;
|
||||
private int nextConnectionID;
|
||||
|
||||
public static Server CreateServer(EosTransport transport, int maxConnections)
|
||||
{
|
||||
private event Action<int> OnConnected;
|
||||
private event Action<int, byte[], int> OnReceivedData;
|
||||
private event Action<int> OnDisconnected;
|
||||
private event Action<int, Exception> OnReceivedError;
|
||||
var s = new Server(transport, maxConnections);
|
||||
|
||||
private BidirectionalDictionary<ProductUserId, int> epicToMirrorIds;
|
||||
private Dictionary<ProductUserId, SocketId> epicToSocketIds;
|
||||
private int maxConnections;
|
||||
private int nextConnectionID;
|
||||
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);
|
||||
|
||||
public static Server CreateServer(EosTransport transport, int maxConnections)
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
var s = new Server(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);
|
||||
|
||||
if (!EOSSDKComponent.Initialized)
|
||||
{
|
||||
Debug.LogError("EOS not initialized.");
|
||||
}
|
||||
|
||||
return s;
|
||||
Debug.LogError("EOS not initialized.");
|
||||
}
|
||||
|
||||
private Server(EosTransport transport, int maxConnections) : base(transport)
|
||||
return s;
|
||||
}
|
||||
|
||||
private Server(EosTransport transport, int maxConnections) : base(transport)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
epicToMirrorIds = new BidirectionalDictionary<ProductUserId, int>();
|
||||
epicToSocketIds = new Dictionary<ProductUserId, SocketId>();
|
||||
nextConnectionID = 1;
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
this.maxConnections = maxConnections;
|
||||
epicToMirrorIds = new BidirectionalDictionary<ProductUserId, int>();
|
||||
epicToSocketIds = new Dictionary<ProductUserId, SocketId>();
|
||||
nextConnectionID = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result)
|
||||
if (deadSockets.Contains(result.SocketId.SocketName))
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Debug.LogError("Received incoming connection request from dead socket");
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadSockets.Contains(result.SocketId.SocketName))
|
||||
EOSSDKComponent.GetP2PInterface().AcceptConnection(
|
||||
new AcceptConnectionOptions()
|
||||
{
|
||||
Debug.LogError("Received incoming connection request from dead socket");
|
||||
return;
|
||||
}
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
RemoteUserId = result.RemoteUserId,
|
||||
SocketId = result.SocketId
|
||||
});
|
||||
}
|
||||
|
||||
EOSSDKComponent.GetP2PInterface().AcceptConnection(
|
||||
new AcceptConnectionOptions()
|
||||
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.CONNECT:
|
||||
if (epicToMirrorIds.Count >= maxConnections)
|
||||
{
|
||||
LocalUserId = EOSSDKComponent.LocalUserProductId,
|
||||
RemoteUserId = result.RemoteUserId,
|
||||
SocketId = result.SocketId
|
||||
});
|
||||
}
|
||||
Debug.LogError("Reached max connections");
|
||||
//CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
SendInternal(clientUserId, socketId, InternalMessages.DISCONNECT);
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SendInternal(clientUserId, socketId, InternalMessages.ACCEPT_CONNECT);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case InternalMessages.CONNECT:
|
||||
if (epicToMirrorIds.Count >= maxConnections)
|
||||
{
|
||||
Debug.LogError("Reached max connections");
|
||||
//CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
SendInternal(clientUserId, socketId, InternalMessages.DISCONNECT);
|
||||
return;
|
||||
}
|
||||
var connectionId = nextConnectionID++;
|
||||
epicToMirrorIds.Add(clientUserId, connectionId);
|
||||
epicToSocketIds.Add(clientUserId, socketId);
|
||||
OnConnected.Invoke(connectionId);
|
||||
|
||||
SendInternal(clientUserId, socketId, InternalMessages.ACCEPT_CONNECT);
|
||||
string clientUserIdString;
|
||||
clientUserId.ToString(out clientUserIdString);
|
||||
Debug.Log($"Client with Product User ID {clientUserIdString} connected. Assigning connection id {connectionId}");
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
if (epicToMirrorIds.TryGetValue(clientUserId, out var connId))
|
||||
{
|
||||
OnDisconnected.Invoke(connId);
|
||||
//CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
epicToMirrorIds.Remove(clientUserId);
|
||||
epicToSocketIds.Remove(clientUserId);
|
||||
Debug.Log($"Client with Product User ID {clientUserId} disconnected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown Product User ID"));
|
||||
}
|
||||
|
||||
var connectionId = nextConnectionID++;
|
||||
epicToMirrorIds.Add(clientUserId, connectionId);
|
||||
epicToSocketIds.Add(clientUserId, socketId);
|
||||
OnConnected.Invoke(connectionId);
|
||||
|
||||
string clientUserIdString;
|
||||
clientUserId.ToString(out clientUserIdString);
|
||||
Debug.Log($"Client with Product User ID {clientUserIdString} connected. Assigning connection id {connectionId}");
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
if (epicToMirrorIds.TryGetValue(clientUserId, out var connId))
|
||||
{
|
||||
OnDisconnected.Invoke(connId);
|
||||
//CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
epicToMirrorIds.Remove(clientUserId);
|
||||
epicToSocketIds.Remove(clientUserId);
|
||||
Debug.Log($"Client with Product User ID {clientUserId} disconnected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown Product User ID"));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (epicToMirrorIds.TryGetValue(clientUserId, out var connectionId))
|
||||
{
|
||||
OnReceivedData.Invoke(connectionId, data, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(clientUserId, out socketId);
|
||||
CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
|
||||
string productId;
|
||||
clientUserId.ToString(out productId);
|
||||
|
||||
Debug.LogError("Data received from epic client thats not known " + productId);
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown product ID"));
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect(int connectionId)
|
||||
{
|
||||
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(userId, out socketId);
|
||||
SendInternal(userId, socketId, InternalMessages.DISCONNECT);
|
||||
epicToMirrorIds.Remove(userId);
|
||||
epicToSocketIds.Remove(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
foreach (KeyValuePair<ProductUserId, int> client in epicToMirrorIds)
|
||||
{
|
||||
Disconnect(client.Value);
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(client.Key, out socketId);
|
||||
WaitForClose(client.Key, socketId);
|
||||
}
|
||||
|
||||
ignoreAllMessages = true;
|
||||
ReceiveData();
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void SendAll(int connectionId, byte[] data, int channelId)
|
||||
{
|
||||
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(userId, out socketId);
|
||||
Send(userId, socketId, data, (byte)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 (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
string userIdString;
|
||||
userId.ToString(out userIdString);
|
||||
return userIdString;
|
||||
}
|
||||
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(ProductUserId remoteId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var connectionId = epicToMirrorIds.TryGetValue(remoteId, out var connId) ? connId : nextConnectionID++;
|
||||
OnDisconnected.Invoke(connectionId);
|
||||
|
||||
Debug.LogError("Connection Failed, removing user");
|
||||
epicToMirrorIds.Remove(remoteId);
|
||||
epicToSocketIds.Remove(remoteId);
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Received unknown message type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (epicToMirrorIds.TryGetValue(clientUserId, out var connectionId))
|
||||
{
|
||||
OnReceivedData.Invoke(connectionId, data, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(clientUserId, out socketId);
|
||||
CloseP2PSessionWithUser(clientUserId, socketId);
|
||||
|
||||
string productId;
|
||||
clientUserId.ToString(out productId);
|
||||
|
||||
Debug.LogError("Data received from epic client thats not known " + productId);
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown product ID"));
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect(int connectionId)
|
||||
{
|
||||
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(userId, out socketId);
|
||||
SendInternal(userId, socketId, InternalMessages.DISCONNECT);
|
||||
epicToMirrorIds.Remove(userId);
|
||||
epicToSocketIds.Remove(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
foreach (KeyValuePair<ProductUserId, int> client in epicToMirrorIds)
|
||||
{
|
||||
Disconnect(client.Value);
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(client.Key, out socketId);
|
||||
WaitForClose(client.Key, socketId);
|
||||
}
|
||||
|
||||
ignoreAllMessages = true;
|
||||
ReceiveData();
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void SendAll(int connectionId, byte[] data, int channelId)
|
||||
{
|
||||
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
SocketId socketId;
|
||||
epicToSocketIds.TryGetValue(userId, out socketId);
|
||||
Send(userId, socketId, data, (byte)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 (epicToMirrorIds.TryGetValue(connectionId, out var userId))
|
||||
{
|
||||
string userIdString;
|
||||
userId.ToString(out userIdString);
|
||||
return userIdString;
|
||||
}
|
||||
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(ProductUserId remoteId)
|
||||
{
|
||||
if (ignoreAllMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var connectionId = epicToMirrorIds.TryGetValue(remoteId, out var connId) ? connId : nextConnectionID++;
|
||||
OnDisconnected.Invoke(connectionId);
|
||||
|
||||
Debug.LogError("Connection Failed, removing user");
|
||||
epicToMirrorIds.Remove(remoteId);
|
||||
epicToSocketIds.Remove(remoteId);
|
||||
}
|
||||
}
|
@ -1,53 +1,52 @@
|
||||
using Epic.OnlineServices;
|
||||
using Epic.OnlineServices.Ecom;
|
||||
|
||||
namespace EpicRerouter.ExeSide
|
||||
namespace EpicRerouter.ExeSide;
|
||||
|
||||
public static class EpicEntitlementRetriever
|
||||
{
|
||||
public static class EpicEntitlementRetriever
|
||||
private const string _eosDlcItemID = "49a9ac61fe464cbf8c8c73f46b3f1133";
|
||||
|
||||
private static EcomInterface _ecomInterface;
|
||||
private static OwnershipStatus _epicDlcOwnershipStatus;
|
||||
private static bool _epicResultReceived;
|
||||
|
||||
public static void Init() =>
|
||||
EpicPlatformManager.OnAuthSuccess += EOSQueryOwnership;
|
||||
|
||||
public static void Uninit() =>
|
||||
EpicPlatformManager.OnAuthSuccess -= EOSQueryOwnership;
|
||||
|
||||
public static EntitlementsManager.AsyncOwnershipStatus GetOwnershipStatus()
|
||||
{
|
||||
private const string _eosDlcItemID = "49a9ac61fe464cbf8c8c73f46b3f1133";
|
||||
|
||||
private static EcomInterface _ecomInterface;
|
||||
private static OwnershipStatus _epicDlcOwnershipStatus;
|
||||
private static bool _epicResultReceived;
|
||||
|
||||
public static void Init() =>
|
||||
EpicPlatformManager.OnAuthSuccess += EOSQueryOwnership;
|
||||
|
||||
public static void Uninit() =>
|
||||
EpicPlatformManager.OnAuthSuccess -= EOSQueryOwnership;
|
||||
|
||||
public static EntitlementsManager.AsyncOwnershipStatus GetOwnershipStatus()
|
||||
if (!_epicResultReceived)
|
||||
{
|
||||
if (!_epicResultReceived)
|
||||
{
|
||||
return EntitlementsManager.AsyncOwnershipStatus.NotReady;
|
||||
}
|
||||
|
||||
return _epicDlcOwnershipStatus == OwnershipStatus.Owned ?
|
||||
EntitlementsManager.AsyncOwnershipStatus.Owned : EntitlementsManager.AsyncOwnershipStatus.NotOwned;
|
||||
return EntitlementsManager.AsyncOwnershipStatus.NotReady;
|
||||
}
|
||||
|
||||
private static void EOSQueryOwnership()
|
||||
{
|
||||
Program.Log("[EOS] querying DLC ownership");
|
||||
_ecomInterface = EpicPlatformManager.PlatformInterface.GetEcomInterface();
|
||||
var queryOwnershipOptions = new QueryOwnershipOptions
|
||||
{
|
||||
LocalUserId = EpicPlatformManager.LocalUserId,
|
||||
CatalogItemIds = new[] { _eosDlcItemID }
|
||||
};
|
||||
_ecomInterface.QueryOwnership(queryOwnershipOptions, null, OnEOSQueryOwnershipComplete);
|
||||
}
|
||||
return _epicDlcOwnershipStatus == OwnershipStatus.Owned ?
|
||||
EntitlementsManager.AsyncOwnershipStatus.Owned : EntitlementsManager.AsyncOwnershipStatus.NotOwned;
|
||||
}
|
||||
|
||||
private static void OnEOSQueryOwnershipComplete(QueryOwnershipCallbackInfo data)
|
||||
private static void EOSQueryOwnership()
|
||||
{
|
||||
Program.Log("[EOS] querying DLC ownership");
|
||||
_ecomInterface = EpicPlatformManager.PlatformInterface.GetEcomInterface();
|
||||
var queryOwnershipOptions = new QueryOwnershipOptions
|
||||
{
|
||||
if (data.ResultCode == Result.Success)
|
||||
{
|
||||
_epicDlcOwnershipStatus = data.ItemOwnership[0].OwnershipStatus;
|
||||
_epicResultReceived = true;
|
||||
Program.Log($"[EOS] Query DLC ownership complete: {_epicDlcOwnershipStatus}");
|
||||
}
|
||||
LocalUserId = EpicPlatformManager.LocalUserId,
|
||||
CatalogItemIds = new[] { _eosDlcItemID }
|
||||
};
|
||||
_ecomInterface.QueryOwnership(queryOwnershipOptions, null, OnEOSQueryOwnershipComplete);
|
||||
}
|
||||
|
||||
private static void OnEOSQueryOwnershipComplete(QueryOwnershipCallbackInfo data)
|
||||
{
|
||||
if (data.ResultCode == Result.Success)
|
||||
{
|
||||
_epicDlcOwnershipStatus = data.ItemOwnership[0].OwnershipStatus;
|
||||
_epicResultReceived = true;
|
||||
Program.Log($"[EOS] Query DLC ownership complete: {_epicDlcOwnershipStatus}");
|
||||
}
|
||||
}
|
||||
}
|
@ -4,138 +4,137 @@ using Epic.OnlineServices.Platform;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace EpicRerouter.ExeSide
|
||||
namespace EpicRerouter.ExeSide;
|
||||
|
||||
public static class EpicPlatformManager
|
||||
{
|
||||
public static class EpicPlatformManager
|
||||
private const string _eosProductID = "prod-starfish";
|
||||
private const string _eosSandboxID = "starfish";
|
||||
private const string _eosDeploymentID = "e176ecc84fbc4dd8934664684f44dc71";
|
||||
private const string _eosClientID = "5c553c6accee4111bc8ea3a3ae52229b";
|
||||
private const string _eosClientSecret = "k87Nfp75BzPref4nJFnnbNjYXQQR";
|
||||
private const float _tickInterval = 0.1f;
|
||||
|
||||
public static PlatformInterface PlatformInterface;
|
||||
public static EpicAccountId LocalUserId;
|
||||
|
||||
public static OWEvent OnAuthSuccess = new(1);
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
private const string _eosProductID = "prod-starfish";
|
||||
private const string _eosSandboxID = "starfish";
|
||||
private const string _eosDeploymentID = "e176ecc84fbc4dd8934664684f44dc71";
|
||||
private const string _eosClientID = "5c553c6accee4111bc8ea3a3ae52229b";
|
||||
private const string _eosClientSecret = "k87Nfp75BzPref4nJFnnbNjYXQQR";
|
||||
private const float _tickInterval = 0.1f;
|
||||
|
||||
public static PlatformInterface PlatformInterface;
|
||||
public static EpicAccountId LocalUserId;
|
||||
|
||||
public static OWEvent OnAuthSuccess = new(1);
|
||||
|
||||
public static void Init()
|
||||
if (PlatformInterface == null)
|
||||
{
|
||||
if (PlatformInterface == null)
|
||||
try
|
||||
{
|
||||
try
|
||||
InitPlatform();
|
||||
}
|
||||
catch (EOSInitializeException ex)
|
||||
{
|
||||
if (ex.Result == Result.AlreadyConfigured)
|
||||
{
|
||||
InitPlatform();
|
||||
}
|
||||
catch (EOSInitializeException ex)
|
||||
{
|
||||
if (ex.Result == Result.AlreadyConfigured)
|
||||
{
|
||||
throw new Exception("[EOS] platform already configured!");
|
||||
}
|
||||
throw new Exception("[EOS] platform already configured!");
|
||||
}
|
||||
}
|
||||
|
||||
Auth();
|
||||
}
|
||||
|
||||
public static void Tick()
|
||||
Auth();
|
||||
}
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
PlatformInterface.Tick();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(_tickInterval));
|
||||
}
|
||||
|
||||
public static void Uninit()
|
||||
{
|
||||
PlatformInterface.Release();
|
||||
PlatformInterface = null;
|
||||
PlatformInterface.Shutdown();
|
||||
}
|
||||
|
||||
private static void InitPlatform()
|
||||
{
|
||||
var result = PlatformInterface.Initialize(new InitializeOptions
|
||||
{
|
||||
PlatformInterface.Tick();
|
||||
Thread.Sleep(TimeSpan.FromSeconds(_tickInterval));
|
||||
ProductName = Program.ProductName,
|
||||
ProductVersion = Program.Version
|
||||
});
|
||||
if (result != Result.Success)
|
||||
{
|
||||
throw new EOSInitializeException("Failed to initialize Epic Online Services platform: ", result);
|
||||
}
|
||||
|
||||
public static void Uninit()
|
||||
var options = new Options
|
||||
{
|
||||
PlatformInterface.Release();
|
||||
PlatformInterface = null;
|
||||
PlatformInterface.Shutdown();
|
||||
}
|
||||
|
||||
private static void InitPlatform()
|
||||
{
|
||||
var result = PlatformInterface.Initialize(new InitializeOptions
|
||||
ProductId = _eosProductID,
|
||||
SandboxId = _eosSandboxID,
|
||||
ClientCredentials = new ClientCredentials
|
||||
{
|
||||
ProductName = Program.ProductName,
|
||||
ProductVersion = Program.Version
|
||||
});
|
||||
if (result != Result.Success)
|
||||
ClientId = _eosClientID,
|
||||
ClientSecret = _eosClientSecret
|
||||
},
|
||||
DeploymentId = _eosDeploymentID
|
||||
};
|
||||
PlatformInterface = PlatformInterface.Create(options);
|
||||
Program.Log("[EOS] Platform interface has been created");
|
||||
}
|
||||
|
||||
private static void Auth()
|
||||
{
|
||||
Program.Log("[EOS] Authenticating...");
|
||||
var loginOptions = new LoginOptions
|
||||
{
|
||||
Credentials = new Credentials
|
||||
{
|
||||
throw new EOSInitializeException("Failed to initialize Epic Online Services platform: ", result);
|
||||
Type = LoginCredentialType.ExchangeCode,
|
||||
Id = null,
|
||||
Token = GetPasswordFromCommandLine()
|
||||
},
|
||||
ScopeFlags = 0
|
||||
};
|
||||
if (PlatformInterface == null)
|
||||
{
|
||||
throw new Exception("[EOS] Platform interface is null!");
|
||||
}
|
||||
|
||||
PlatformInterface.GetAuthInterface().Login(loginOptions, null, OnLogin);
|
||||
}
|
||||
|
||||
private static string GetPasswordFromCommandLine()
|
||||
{
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
foreach (var arg in commandLineArgs)
|
||||
{
|
||||
if (arg.Contains("AUTH_PASSWORD"))
|
||||
{
|
||||
return arg.Split('=')[1];
|
||||
}
|
||||
|
||||
var options = new Options
|
||||
{
|
||||
ProductId = _eosProductID,
|
||||
SandboxId = _eosSandboxID,
|
||||
ClientCredentials = new ClientCredentials
|
||||
{
|
||||
ClientId = _eosClientID,
|
||||
ClientSecret = _eosClientSecret
|
||||
},
|
||||
DeploymentId = _eosDeploymentID
|
||||
};
|
||||
PlatformInterface = PlatformInterface.Create(options);
|
||||
Program.Log("[EOS] Platform interface has been created");
|
||||
}
|
||||
|
||||
private static void Auth()
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void OnLogin(LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
Program.Log("[EOS] Authenticating...");
|
||||
var loginOptions = new LoginOptions
|
||||
{
|
||||
Credentials = new Credentials
|
||||
{
|
||||
Type = LoginCredentialType.ExchangeCode,
|
||||
Id = null,
|
||||
Token = GetPasswordFromCommandLine()
|
||||
},
|
||||
ScopeFlags = 0
|
||||
};
|
||||
if (PlatformInterface == null)
|
||||
{
|
||||
throw new Exception("[EOS] Platform interface is null!");
|
||||
}
|
||||
|
||||
PlatformInterface.GetAuthInterface().Login(loginOptions, null, OnLogin);
|
||||
LocalUserId = loginCallbackInfo.LocalUserId;
|
||||
LocalUserId.ToString(out var s);
|
||||
Program.Log($"[EOS SDK] login success! user ID: {s}");
|
||||
OnAuthSuccess.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
private static string GetPasswordFromCommandLine()
|
||||
{
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
foreach (var arg in commandLineArgs)
|
||||
{
|
||||
if (arg.Contains("AUTH_PASSWORD"))
|
||||
{
|
||||
return arg.Split('=')[1];
|
||||
}
|
||||
}
|
||||
throw new Exception("[EOS SDK] Login failed");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private class EOSInitializeException : Exception
|
||||
{
|
||||
public readonly Result Result;
|
||||
|
||||
private static void OnLogin(LoginCallbackInfo loginCallbackInfo)
|
||||
{
|
||||
if (loginCallbackInfo.ResultCode == Result.Success)
|
||||
{
|
||||
LocalUserId = loginCallbackInfo.LocalUserId;
|
||||
LocalUserId.ToString(out var s);
|
||||
Program.Log($"[EOS SDK] login success! user ID: {s}");
|
||||
OnAuthSuccess.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("[EOS SDK] Login failed");
|
||||
}
|
||||
|
||||
private class EOSInitializeException : Exception
|
||||
{
|
||||
public readonly Result Result;
|
||||
|
||||
public EOSInitializeException(string msg, Result initResult) :
|
||||
base(msg) =>
|
||||
Result = initResult;
|
||||
}
|
||||
public EOSInitializeException(string msg, Result initResult) :
|
||||
base(msg) =>
|
||||
Result = initResult;
|
||||
}
|
||||
}
|
@ -3,55 +3,54 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EpicRerouter.ExeSide
|
||||
namespace EpicRerouter.ExeSide;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static class Program
|
||||
public static string ProductName;
|
||||
public static string Version;
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
public static string ProductName;
|
||||
public static string Version;
|
||||
ProductName = args[0];
|
||||
Log($"product name = {ProductName}");
|
||||
Version = args[1];
|
||||
Log($"version = {Version}");
|
||||
var managedDir = args[2];
|
||||
Log($"managed dir = {managedDir}");
|
||||
var gameArgs = args.Skip(3).ToArray();
|
||||
Log($"game args = {string.Join(", ", gameArgs)}");
|
||||
|
||||
private static void Main(string[] args)
|
||||
AppDomain.CurrentDomain.AssemblyResolve += (_, e) =>
|
||||
{
|
||||
ProductName = args[0];
|
||||
Log($"product name = {ProductName}");
|
||||
Version = args[1];
|
||||
Log($"version = {Version}");
|
||||
var managedDir = args[2];
|
||||
Log($"managed dir = {managedDir}");
|
||||
var gameArgs = args.Skip(3).ToArray();
|
||||
Log($"game args = {string.Join(", ", gameArgs)}");
|
||||
var name = new AssemblyName(e.Name).Name + ".dll";
|
||||
var path = Path.Combine(managedDir, name);
|
||||
return File.Exists(path) ? Assembly.LoadFile(path) : null;
|
||||
};
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += (_, e) =>
|
||||
{
|
||||
var name = new AssemblyName(e.Name).Name + ".dll";
|
||||
var path = Path.Combine(managedDir, name);
|
||||
return File.Exists(path) ? Assembly.LoadFile(path) : null;
|
||||
};
|
||||
|
||||
Go();
|
||||
}
|
||||
|
||||
private static void Go()
|
||||
{
|
||||
try
|
||||
{
|
||||
EpicPlatformManager.Init();
|
||||
EpicEntitlementRetriever.Init();
|
||||
|
||||
while (EpicEntitlementRetriever.GetOwnershipStatus() == EntitlementsManager.AsyncOwnershipStatus.NotReady)
|
||||
{
|
||||
EpicPlatformManager.Tick();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EpicEntitlementRetriever.Uninit();
|
||||
EpicPlatformManager.Uninit();
|
||||
|
||||
Environment.Exit((int)EpicEntitlementRetriever.GetOwnershipStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(object msg) => Console.Error.WriteLine(msg);
|
||||
Go();
|
||||
}
|
||||
|
||||
private static void Go()
|
||||
{
|
||||
try
|
||||
{
|
||||
EpicPlatformManager.Init();
|
||||
EpicEntitlementRetriever.Init();
|
||||
|
||||
while (EpicEntitlementRetriever.GetOwnershipStatus() == EntitlementsManager.AsyncOwnershipStatus.NotReady)
|
||||
{
|
||||
EpicPlatformManager.Tick();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EpicEntitlementRetriever.Uninit();
|
||||
EpicPlatformManager.Uninit();
|
||||
|
||||
Environment.Exit((int)EpicEntitlementRetriever.GetOwnershipStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(object msg) => Console.Error.WriteLine(msg);
|
||||
}
|
@ -7,63 +7,62 @@ using System.Reflection;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace EpicRerouter.ModSide
|
||||
namespace EpicRerouter.ModSide;
|
||||
|
||||
public static class Interop
|
||||
{
|
||||
public static class Interop
|
||||
public static EntitlementsManager.AsyncOwnershipStatus OwnershipStatus = EntitlementsManager.AsyncOwnershipStatus.NotReady;
|
||||
|
||||
public static void Go()
|
||||
{
|
||||
public static EntitlementsManager.AsyncOwnershipStatus OwnershipStatus = EntitlementsManager.AsyncOwnershipStatus.NotReady;
|
||||
|
||||
public static void Go()
|
||||
if (typeof(EpicPlatformManager).GetField("_platformInterface", BindingFlags.NonPublic | BindingFlags.Instance) == null)
|
||||
{
|
||||
if (typeof(EpicPlatformManager).GetField("_platformInterface", BindingFlags.NonPublic | BindingFlags.Instance) == null)
|
||||
{
|
||||
Log("not epic. don't reroute");
|
||||
return;
|
||||
}
|
||||
|
||||
Log("go");
|
||||
|
||||
Patches.Apply();
|
||||
|
||||
var processPath = Path.Combine(
|
||||
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||
"EpicRerouter.exe"
|
||||
);
|
||||
Log($"process path = {processPath}");
|
||||
var gamePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(EpicPlatformManager).Assembly.Location)!, ".."));
|
||||
Log($"game path = {gamePath}");
|
||||
var workingDirectory = Path.Combine(gamePath, "Plugins", "x86_64");
|
||||
Log($"working dir = {workingDirectory}");
|
||||
var args = new[]
|
||||
{
|
||||
Application.productName,
|
||||
Application.version,
|
||||
Path.Combine(gamePath, "Managed")
|
||||
};
|
||||
Log($"args = {args.Join()}");
|
||||
var gameArgs = Environment.GetCommandLineArgs();
|
||||
Log($"game args = {gameArgs.Join()}");
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = processPath,
|
||||
WorkingDirectory = workingDirectory,
|
||||
Arguments = args
|
||||
.Concat(gameArgs)
|
||||
.Join(x => $"\"{x}\"", " "),
|
||||
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
});
|
||||
process!.WaitForExit();
|
||||
OwnershipStatus = (EntitlementsManager.AsyncOwnershipStatus)process.ExitCode;
|
||||
Log($"ownership status = {OwnershipStatus}");
|
||||
|
||||
Log($"output:\n{process.StandardOutput.ReadToEnd()}");
|
||||
Log($"error:\n{process.StandardError.ReadToEnd()}");
|
||||
Log("not epic. don't reroute");
|
||||
return;
|
||||
}
|
||||
|
||||
public static void Log(object msg) => Debug.Log($"[EpicRerouter] {msg}");
|
||||
Log("go");
|
||||
|
||||
Patches.Apply();
|
||||
|
||||
var processPath = Path.Combine(
|
||||
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||
"EpicRerouter.exe"
|
||||
);
|
||||
Log($"process path = {processPath}");
|
||||
var gamePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(EpicPlatformManager).Assembly.Location)!, ".."));
|
||||
Log($"game path = {gamePath}");
|
||||
var workingDirectory = Path.Combine(gamePath, "Plugins", "x86_64");
|
||||
Log($"working dir = {workingDirectory}");
|
||||
var args = new[]
|
||||
{
|
||||
Application.productName,
|
||||
Application.version,
|
||||
Path.Combine(gamePath, "Managed")
|
||||
};
|
||||
Log($"args = {args.Join()}");
|
||||
var gameArgs = Environment.GetCommandLineArgs();
|
||||
Log($"game args = {gameArgs.Join()}");
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = processPath,
|
||||
WorkingDirectory = workingDirectory,
|
||||
Arguments = args
|
||||
.Concat(gameArgs)
|
||||
.Join(x => $"\"{x}\"", " "),
|
||||
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
});
|
||||
process!.WaitForExit();
|
||||
OwnershipStatus = (EntitlementsManager.AsyncOwnershipStatus)process.ExitCode;
|
||||
Log($"ownership status = {OwnershipStatus}");
|
||||
|
||||
Log($"output:\n{process.StandardOutput.ReadToEnd()}");
|
||||
Log($"error:\n{process.StandardError.ReadToEnd()}");
|
||||
}
|
||||
|
||||
public static void Log(object msg) => Debug.Log($"[EpicRerouter] {msg}");
|
||||
}
|
@ -2,65 +2,64 @@
|
||||
using UnityEngine;
|
||||
using static EntitlementsManager;
|
||||
|
||||
namespace EpicRerouter.ModSide
|
||||
namespace EpicRerouter.ModSide;
|
||||
|
||||
[HarmonyPatch(typeof(EpicPlatformManager))]
|
||||
public static class Patches
|
||||
{
|
||||
[HarmonyPatch(typeof(EpicPlatformManager))]
|
||||
public static class Patches
|
||||
public static void Apply()
|
||||
{
|
||||
public static void Apply()
|
||||
var harmony = new Harmony(typeof(Patches).FullName);
|
||||
harmony.PatchAll(typeof(EntitlementsManagerPatches));
|
||||
harmony.PatchAll(typeof(EpicPlatformManagerPatches));
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EntitlementsManager))]
|
||||
private static class EntitlementsManagerPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.InitializeOnAwake))]
|
||||
private static bool InitializeOnAwake(EntitlementsManager __instance)
|
||||
{
|
||||
var harmony = new Harmony(typeof(Patches).FullName);
|
||||
harmony.PatchAll(typeof(EntitlementsManagerPatches));
|
||||
harmony.PatchAll(typeof(EpicPlatformManagerPatches));
|
||||
Object.Destroy(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EntitlementsManager))]
|
||||
private static class EntitlementsManagerPatches
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.Start))]
|
||||
private static bool Start() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.OnDestroy))]
|
||||
private static bool OnDestroy() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.IsDlcOwned))]
|
||||
private static bool IsDlcOwned(out AsyncOwnershipStatus __result)
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.InitializeOnAwake))]
|
||||
private static bool InitializeOnAwake(EntitlementsManager __instance)
|
||||
{
|
||||
Object.Destroy(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.Start))]
|
||||
private static bool Start() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.OnDestroy))]
|
||||
private static bool OnDestroy() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(EntitlementsManager.IsDlcOwned))]
|
||||
private static bool IsDlcOwned(out AsyncOwnershipStatus __result)
|
||||
{
|
||||
__result = Interop.OwnershipStatus;
|
||||
Interop.Log($"ownership status = {__result}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EpicPlatformManager))]
|
||||
private static class EpicPlatformManagerPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("Awake")]
|
||||
private static bool Awake(EpicPlatformManager __instance)
|
||||
{
|
||||
Object.Destroy(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("Start")]
|
||||
private static bool Start() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("OnDestroy")]
|
||||
private static bool OnDestroy() => false;
|
||||
__result = Interop.OwnershipStatus;
|
||||
Interop.Log($"ownership status = {__result}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EpicPlatformManager))]
|
||||
private static class EpicPlatformManagerPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("Awake")]
|
||||
private static bool Awake(EpicPlatformManager __instance)
|
||||
{
|
||||
Object.Destroy(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("Start")]
|
||||
private static bool Start() => false;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch("OnDestroy")]
|
||||
private static bool OnDestroy() => false;
|
||||
}
|
||||
}
|
@ -1,41 +1,40 @@
|
||||
using Mono.Cecil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MirrorWeaver
|
||||
namespace MirrorWeaver;
|
||||
|
||||
public static class ImprovedExtensions
|
||||
{
|
||||
public static class ImprovedExtensions
|
||||
/// <summary>
|
||||
/// filters ONLY public fields instead of all non-private <br/>
|
||||
/// replaces generic parameter fields with their corresponding argument
|
||||
/// </summary>
|
||||
public static IEnumerable<FieldDefinition> FindAllPublicFields_Improved(this TypeReference tr)
|
||||
{
|
||||
/// <summary>
|
||||
/// filters ONLY public fields instead of all non-private <br/>
|
||||
/// replaces generic parameter fields with their corresponding argument
|
||||
/// </summary>
|
||||
public static IEnumerable<FieldDefinition> FindAllPublicFields_Improved(this TypeReference tr)
|
||||
while (tr != null)
|
||||
{
|
||||
while (tr != null)
|
||||
var td = tr.Resolve();
|
||||
foreach (var fd in td.Fields)
|
||||
{
|
||||
var td = tr.Resolve();
|
||||
foreach (var fd in td.Fields)
|
||||
if (fd.IsStatic || !fd.IsPublic)
|
||||
{
|
||||
if (fd.IsStatic || !fd.IsPublic)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd.IsNotSerialized)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd.FieldType is GenericParameter gp && gp.Owner == td)
|
||||
{
|
||||
fd.FieldType = ((GenericInstanceType)tr).GenericArguments[gp.Position];
|
||||
}
|
||||
|
||||
yield return fd;
|
||||
continue;
|
||||
}
|
||||
|
||||
tr = td.BaseType;
|
||||
if (fd.IsNotSerialized)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd.FieldType is GenericParameter gp && gp.Owner == td)
|
||||
{
|
||||
fd.FieldType = ((GenericInstanceType)tr).GenericArguments[gp.Position];
|
||||
}
|
||||
|
||||
yield return fd;
|
||||
}
|
||||
|
||||
tr = td.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,58 +4,57 @@ using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MirrorWeaver
|
||||
namespace MirrorWeaver;
|
||||
|
||||
public class ConsoleLogger : Logger
|
||||
{
|
||||
public class ConsoleLogger : Logger
|
||||
public void Warning(string message) => Warning(message, null);
|
||||
|
||||
public void Warning(string message, MemberReference mr)
|
||||
{
|
||||
public void Warning(string message) => Warning(message, null);
|
||||
|
||||
public void Warning(string message, MemberReference mr)
|
||||
if (mr != null)
|
||||
{
|
||||
if (mr != null)
|
||||
{
|
||||
message = $"{message} (at {mr})";
|
||||
}
|
||||
|
||||
Console.WriteLine(message);
|
||||
message = $"{message} (at {mr})";
|
||||
}
|
||||
|
||||
public void Error(string message) => Error(message, null);
|
||||
|
||||
public void Error(string message, MemberReference mr)
|
||||
{
|
||||
if (mr != null)
|
||||
{
|
||||
message = $"{message} (at {mr})";
|
||||
}
|
||||
|
||||
Console.Error.WriteLine(message);
|
||||
}
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
public static class Program
|
||||
public void Error(string message) => Error(message, null);
|
||||
|
||||
public void Error(string message, MemberReference mr)
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
if (mr != null)
|
||||
{
|
||||
var qsbDll = args[0];
|
||||
|
||||
var resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(Path.GetDirectoryName(qsbDll));
|
||||
var assembly = AssemblyDefinition.ReadAssembly(qsbDll, new ReaderParameters
|
||||
{
|
||||
ReadWrite = true,
|
||||
AssemblyResolver = resolver,
|
||||
SymbolReaderProvider = new DefaultSymbolReaderProvider(false)
|
||||
});
|
||||
|
||||
var log = new ConsoleLogger();
|
||||
var weaver = new Weaver(log);
|
||||
if (!weaver.Weave(assembly, resolver, out _))
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
assembly.Write(new WriterParameters { WriteSymbols = assembly.MainModule.HasSymbols });
|
||||
message = $"{message} (at {mr})";
|
||||
}
|
||||
|
||||
Console.Error.WriteLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var qsbDll = args[0];
|
||||
|
||||
var resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(Path.GetDirectoryName(qsbDll));
|
||||
var assembly = AssemblyDefinition.ReadAssembly(qsbDll, new ReaderParameters
|
||||
{
|
||||
ReadWrite = true,
|
||||
AssemblyResolver = resolver,
|
||||
SymbolReaderProvider = new DefaultSymbolReaderProvider(false)
|
||||
});
|
||||
|
||||
var log = new ConsoleLogger();
|
||||
var weaver = new Weaver(log);
|
||||
if (!weaver.Weave(assembly, resolver, out _))
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
assembly.Write(new WriterParameters { WriteSymbols = assembly.MainModule.HasSymbols });
|
||||
}
|
||||
}
|
@ -4,83 +4,82 @@ using Mono.Cecil;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace MirrorWeaver
|
||||
namespace MirrorWeaver;
|
||||
|
||||
public static class QSBReaderWriterProcessor
|
||||
{
|
||||
public static class QSBReaderWriterProcessor
|
||||
/// <summary>
|
||||
/// finds usages of the generic read/write methods and generates read/write functions for them.
|
||||
/// <para/>
|
||||
/// traverses from non generic classes up thru base types
|
||||
/// in order to replace generic parameters with their corresponding generic arguments.
|
||||
/// </summary>
|
||||
public static void Process(ModuleDefinition module, Writers writers, Readers readers, ref bool weavingFailed)
|
||||
{
|
||||
/// <summary>
|
||||
/// finds usages of the generic read/write methods and generates read/write functions for them.
|
||||
/// <para/>
|
||||
/// traverses from non generic classes up thru base types
|
||||
/// in order to replace generic parameters with their corresponding generic arguments.
|
||||
/// </summary>
|
||||
public static void Process(ModuleDefinition module, Writers writers, Readers readers, ref bool weavingFailed)
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var NetworkWriter_Write = typeof(NetworkWriter).GetMethod(nameof(NetworkWriter.Write));
|
||||
var NetworkReader_Read = typeof(NetworkReader).GetMethod(nameof(NetworkReader.Read));
|
||||
|
||||
var count = 0;
|
||||
foreach (var type in module.GetTypes())
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
var NetworkWriter_Write = typeof(NetworkWriter).GetMethod(nameof(NetworkWriter.Write));
|
||||
var NetworkReader_Read = typeof(NetworkReader).GetMethod(nameof(NetworkReader.Read));
|
||||
|
||||
var count = 0;
|
||||
foreach (var type in module.GetTypes())
|
||||
if (type.HasGenericParameters)
|
||||
{
|
||||
if (type.HasGenericParameters)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
TypeReference currentType = type;
|
||||
while (currentType != null)
|
||||
TypeReference currentType = type;
|
||||
while (currentType != null)
|
||||
{
|
||||
var currentTypeDef = currentType.Resolve();
|
||||
foreach (var method in currentTypeDef.Methods)
|
||||
{
|
||||
var currentTypeDef = currentType.Resolve();
|
||||
foreach (var method in currentTypeDef.Methods)
|
||||
if (!method.HasBody)
|
||||
{
|
||||
if (!method.HasBody)
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var instruction in method.Body.Instructions)
|
||||
{
|
||||
if (instruction.Operand is not GenericInstanceMethod calledMethod)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var instruction in method.Body.Instructions)
|
||||
if (calledMethod.DeclaringType.FullName == NetworkWriter_Write.DeclaringType.FullName &&
|
||||
calledMethod.Name == NetworkWriter_Write.Name)
|
||||
{
|
||||
if (instruction.Operand is not GenericInstanceMethod calledMethod)
|
||||
var argType = calledMethod.GenericArguments[0];
|
||||
|
||||
if (argType is GenericParameter genericParameter && genericParameter.Owner == currentTypeDef)
|
||||
{
|
||||
continue;
|
||||
argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position];
|
||||
}
|
||||
|
||||
if (calledMethod.DeclaringType.FullName == NetworkWriter_Write.DeclaringType.FullName &&
|
||||
calledMethod.Name == NetworkWriter_Write.Name)
|
||||
writers.GetWriteFunc(argType, ref weavingFailed);
|
||||
count++;
|
||||
}
|
||||
else if (calledMethod.DeclaringType.FullName == NetworkReader_Read.DeclaringType.FullName &&
|
||||
calledMethod.Name == NetworkReader_Read.Name)
|
||||
{
|
||||
var argType = calledMethod.GenericArguments[0];
|
||||
|
||||
if (argType is GenericParameter genericParameter && genericParameter.Owner == currentTypeDef)
|
||||
{
|
||||
var argType = calledMethod.GenericArguments[0];
|
||||
|
||||
if (argType is GenericParameter genericParameter && genericParameter.Owner == currentTypeDef)
|
||||
{
|
||||
argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position];
|
||||
}
|
||||
|
||||
writers.GetWriteFunc(argType, ref weavingFailed);
|
||||
count++;
|
||||
argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position];
|
||||
}
|
||||
else if (calledMethod.DeclaringType.FullName == NetworkReader_Read.DeclaringType.FullName &&
|
||||
calledMethod.Name == NetworkReader_Read.Name)
|
||||
{
|
||||
var argType = calledMethod.GenericArguments[0];
|
||||
|
||||
if (argType is GenericParameter genericParameter && genericParameter.Owner == currentTypeDef)
|
||||
{
|
||||
argType = ((GenericInstanceType)currentType).GenericArguments[genericParameter.Position];
|
||||
}
|
||||
|
||||
readers.GetReadFunc(argType, ref weavingFailed);
|
||||
count++;
|
||||
}
|
||||
readers.GetReadFunc(argType, ref weavingFailed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
currentType = currentTypeDef.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"got/generated {count} read/write funcs in {sw.ElapsedMilliseconds} ms");
|
||||
currentType = currentTypeDef.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"got/generated {count} read/write funcs in {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
}
|
@ -5,19 +5,18 @@ using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.Anglerfish
|
||||
namespace QSB.Anglerfish;
|
||||
|
||||
public class AnglerManager : WorldObjectManager
|
||||
{
|
||||
public class AnglerManager : WorldObjectManager
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public static readonly List<AnglerfishController> Anglers = new();
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public static readonly List<AnglerfishController> Anglers = new();
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
Anglers.Clear();
|
||||
Anglers.AddRange(QSBWorldSync.GetUnityObjects<AnglerfishController>().SortDeterministic());
|
||||
QSBWorldSync.Init<QSBAngler, AnglerfishController>(Anglers);
|
||||
}
|
||||
Anglers.Clear();
|
||||
Anglers.AddRange(QSBWorldSync.GetUnityObjects<AnglerfishController>().SortDeterministic());
|
||||
QSBWorldSync.Init<QSBAngler, AnglerfishController>(Anglers);
|
||||
}
|
||||
}
|
@ -4,72 +4,71 @@ using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Anglerfish.Messages
|
||||
namespace QSB.Anglerfish.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// angler state, target transform, and local disturbance pos
|
||||
/// </summary>
|
||||
public class AnglerDataMessage : QSBWorldObjectMessage<QSBAngler, AnglerfishController.AnglerState>
|
||||
{
|
||||
/// <summary>
|
||||
/// angler state, target transform, and local disturbance pos
|
||||
/// </summary>
|
||||
public class AnglerDataMessage : QSBWorldObjectMessage<QSBAngler, AnglerfishController.AnglerState>
|
||||
private uint TargetId;
|
||||
private Vector3 LocalDisturbancePos;
|
||||
|
||||
public AnglerDataMessage(QSBAngler qsbAngler)
|
||||
{
|
||||
private uint TargetId;
|
||||
private Vector3 LocalDisturbancePos;
|
||||
Data = qsbAngler.AttachedObject._currentState;
|
||||
TargetId = TargetToId(qsbAngler.TargetTransform);
|
||||
LocalDisturbancePos = qsbAngler.AttachedObject._localDisturbancePos;
|
||||
}
|
||||
|
||||
public AnglerDataMessage(QSBAngler qsbAngler)
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(TargetId);
|
||||
writer.Write(LocalDisturbancePos);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
TargetId = reader.Read<uint>();
|
||||
LocalDisturbancePos = reader.ReadVector3();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.TargetTransform = IdToTarget(TargetId);
|
||||
WorldObject.AttachedObject._localDisturbancePos = LocalDisturbancePos;
|
||||
WorldObject.AttachedObject.ChangeState(Data);
|
||||
}
|
||||
|
||||
private static uint TargetToId(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
Data = qsbAngler.AttachedObject._currentState;
|
||||
TargetId = TargetToId(qsbAngler.TargetTransform);
|
||||
LocalDisturbancePos = qsbAngler.AttachedObject._localDisturbancePos;
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
if (transform == Locator.GetShipTransform())
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(TargetId);
|
||||
writer.Write(LocalDisturbancePos);
|
||||
return uint.MaxValue - 1;
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
return QSBPlayerManager.LocalPlayerId;
|
||||
}
|
||||
|
||||
private static Transform IdToTarget(uint id)
|
||||
{
|
||||
if (id == uint.MaxValue)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
TargetId = reader.Read<uint>();
|
||||
LocalDisturbancePos = reader.ReadVector3();
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
if (id == uint.MaxValue - 1)
|
||||
{
|
||||
WorldObject.TargetTransform = IdToTarget(TargetId);
|
||||
WorldObject.AttachedObject._localDisturbancePos = LocalDisturbancePos;
|
||||
WorldObject.AttachedObject.ChangeState(Data);
|
||||
return Locator.GetShipTransform();
|
||||
}
|
||||
|
||||
private static uint TargetToId(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
if (transform == Locator.GetShipTransform())
|
||||
{
|
||||
return uint.MaxValue - 1;
|
||||
}
|
||||
|
||||
return QSBPlayerManager.LocalPlayerId;
|
||||
}
|
||||
|
||||
private static Transform IdToTarget(uint id)
|
||||
{
|
||||
if (id == uint.MaxValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id == uint.MaxValue - 1)
|
||||
{
|
||||
return Locator.GetShipTransform();
|
||||
}
|
||||
|
||||
return QSBPlayerManager.GetPlayer(id).Body.transform;
|
||||
}
|
||||
return QSBPlayerManager.GetPlayer(id).Body.transform;
|
||||
}
|
||||
}
|
@ -6,69 +6,88 @@ using QSB.Patches;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Anglerfish.Patches
|
||||
namespace QSB.Anglerfish.Patches;
|
||||
|
||||
public class AnglerPatches : QSBPatch
|
||||
{
|
||||
public class AnglerPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.GetTargetPosition))]
|
||||
public static bool GetTargetPosition(AnglerfishController __instance, out Vector3 __result)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.GetTargetPosition))]
|
||||
public static bool GetTargetPosition(AnglerfishController __instance, out Vector3 __result)
|
||||
if (qsbAngler.TargetTransform != null)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (qsbAngler.TargetTransform != null)
|
||||
{
|
||||
__result = qsbAngler.TargetTransform.position;
|
||||
return false;
|
||||
}
|
||||
|
||||
__result = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos);
|
||||
|
||||
__result = qsbAngler.TargetTransform.position;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnSectorOccupantRemoved))]
|
||||
public static bool OnSectorOccupantRemoved(AnglerfishController __instance,
|
||||
SectorDetector sectorDetector)
|
||||
__result = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnSectorOccupantRemoved))]
|
||||
public static bool OnSectorOccupantRemoved(AnglerfishController __instance,
|
||||
SectorDetector sectorDetector)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (qsbAngler.TargetTransform != null && sectorDetector.GetAttachedOWRigidbody().transform == qsbAngler.TargetTransform)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (qsbAngler.TargetTransform != null && sectorDetector.GetAttachedOWRigidbody().transform == qsbAngler.TargetTransform)
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.UpdateState))]
|
||||
public static bool UpdateState(AnglerfishController __instance)
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.UpdateState))]
|
||||
public static bool UpdateState(AnglerfishController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
switch (__instance._currentState)
|
||||
{
|
||||
case AnglerfishController.AnglerState.Investigating:
|
||||
if ((__instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos) - __instance._anglerBody.GetPosition()).sqrMagnitude < __instance._arrivalDistance * __instance._arrivalDistance)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
switch (__instance._currentState)
|
||||
{
|
||||
case AnglerfishController.AnglerState.Investigating:
|
||||
if ((__instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos) - __instance._anglerBody.GetPosition()).sqrMagnitude < __instance._arrivalDistance * __instance._arrivalDistance)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Chasing:
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Chasing:
|
||||
if (qsbAngler.TargetTransform == null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((qsbAngler.TargetTransform.position - __instance._anglerBody.GetPosition()).sqrMagnitude > __instance._escapeDistance * __instance._escapeDistance)
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Consuming:
|
||||
if (!__instance._consumeComplete)
|
||||
{
|
||||
if (qsbAngler.TargetTransform == null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
@ -76,257 +95,237 @@ namespace QSB.Anglerfish.Patches
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((qsbAngler.TargetTransform.position - __instance._anglerBody.GetPosition()).sqrMagnitude > __instance._escapeDistance * __instance._escapeDistance)
|
||||
var num = Time.time - __instance._consumeStartTime;
|
||||
if (qsbAngler.TargetTransform.CompareTag("Player") && num > __instance._consumeDeathDelay)
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
__instance._consumeComplete = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qsbAngler.TargetTransform.CompareTag("Ship"))
|
||||
{
|
||||
if (num > __instance._consumeShipCrushDelay)
|
||||
{
|
||||
qsbAngler.TargetTransform.GetComponentInChildren<ShipDamageController>().TriggerSystemFailure();
|
||||
}
|
||||
|
||||
if (num > __instance._consumeDeathDelay)
|
||||
{
|
||||
if (PlayerState.IsInsideShip())
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
}
|
||||
|
||||
__instance._consumeComplete = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Stunned:
|
||||
__instance._stunTimer -= Time.deltaTime;
|
||||
if (__instance._stunTimer <= 0f)
|
||||
{
|
||||
if (qsbAngler.TargetTransform != null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Consuming:
|
||||
if (!__instance._consumeComplete)
|
||||
{
|
||||
if (qsbAngler.TargetTransform == null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
var num = Time.time - __instance._consumeStartTime;
|
||||
if (qsbAngler.TargetTransform.CompareTag("Player") && num > __instance._consumeDeathDelay)
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
__instance._consumeComplete = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qsbAngler.TargetTransform.CompareTag("Ship"))
|
||||
{
|
||||
if (num > __instance._consumeShipCrushDelay)
|
||||
{
|
||||
qsbAngler.TargetTransform.GetComponentInChildren<ShipDamageController>().TriggerSystemFailure();
|
||||
}
|
||||
|
||||
if (num > __instance._consumeDeathDelay)
|
||||
{
|
||||
if (PlayerState.IsInsideShip())
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
}
|
||||
|
||||
__instance._consumeComplete = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qsbAngler.TargetTransform = null;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
break;
|
||||
case AnglerfishController.AnglerState.Stunned:
|
||||
__instance._stunTimer -= Time.deltaTime;
|
||||
if (__instance._stunTimer <= 0f)
|
||||
{
|
||||
if (qsbAngler.TargetTransform != null)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.UpdateMovement))]
|
||||
public static bool UpdateMovement(AnglerfishController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
qsbAngler.UpdateTargetVelocity();
|
||||
|
||||
if (__instance._anglerBody.GetVelocity().sqrMagnitude > Mathf.Pow(__instance._chaseSpeed * 1.5f, 2f))
|
||||
{
|
||||
__instance.ApplyDrag(10f);
|
||||
}
|
||||
|
||||
switch (__instance._currentState)
|
||||
{
|
||||
case AnglerfishController.AnglerState.Lurking:
|
||||
__instance.ApplyDrag(1f);
|
||||
return false;
|
||||
case AnglerfishController.AnglerState.Investigating:
|
||||
{
|
||||
var targetPos = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos);
|
||||
__instance.RotateTowardsTarget(targetPos, __instance._turnSpeed, __instance._turnSpeed);
|
||||
if (!__instance._turningInPlace)
|
||||
{
|
||||
__instance.MoveTowardsTarget(targetPos, __instance._investigateSpeed, __instance._acceleration);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case AnglerfishController.AnglerState.Chasing:
|
||||
{
|
||||
var velocity = qsbAngler.TargetVelocity;
|
||||
var normalized = velocity.normalized;
|
||||
var from = __instance._anglerBody.GetPosition() + __instance.transform.TransformDirection(__instance._mouthOffset) - qsbAngler.TargetTransform.position;
|
||||
var magnitude = velocity.magnitude;
|
||||
var num = Vector3.Angle(from, normalized);
|
||||
var num2 = magnitude * 2f;
|
||||
var d = num2;
|
||||
if (num < 90f)
|
||||
{
|
||||
var magnitude2 = from.magnitude;
|
||||
var num3 = magnitude2 * Mathf.Sin(num * 0.017453292f);
|
||||
var num4 = magnitude2 * Mathf.Cos(num * 0.017453292f);
|
||||
var magnitude3 = __instance._anglerBody.GetVelocity().magnitude;
|
||||
var num5 = num4 / Mathf.Max(magnitude, 0.0001f);
|
||||
var num6 = num3 / Mathf.Max(magnitude3, 0.0001f);
|
||||
var num7 = num5 / num6;
|
||||
if (num7 <= 1f)
|
||||
{
|
||||
var t = Mathf.Clamp01(num7);
|
||||
d = Mathf.Lerp(num2, num4, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
var num8 = Mathf.InverseLerp(1f, 4f, num7);
|
||||
d = Mathf.Lerp(num4, 0f, num8 * num8);
|
||||
}
|
||||
}
|
||||
|
||||
__instance._targetPos = qsbAngler.TargetTransform.position + normalized * d;
|
||||
__instance.RotateTowardsTarget(__instance._targetPos, __instance._turnSpeed, __instance._quickTurnSpeed);
|
||||
if (!__instance._turningInPlace)
|
||||
{
|
||||
__instance.MoveTowardsTarget(__instance._targetPos, __instance._chaseSpeed, __instance._acceleration);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case AnglerfishController.AnglerState.Consuming:
|
||||
__instance.ApplyDrag(1f);
|
||||
return false;
|
||||
case AnglerfishController.AnglerState.Stunned:
|
||||
__instance.ApplyDrag(0.5f);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnImpact))]
|
||||
public static bool OnImpact(AnglerfishController __instance,
|
||||
ImpactData impact)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
var attachedOWRigidbody = impact.otherCollider.GetAttachedOWRigidbody();
|
||||
if ((attachedOWRigidbody.CompareTag("Player") || attachedOWRigidbody.CompareTag("Ship"))
|
||||
&& __instance._currentState != AnglerfishController.AnglerState.Consuming
|
||||
&& __instance._currentState != AnglerfishController.AnglerState.Stunned)
|
||||
{
|
||||
qsbAngler.TargetTransform = attachedOWRigidbody.transform;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnClosestAudibleNoise))]
|
||||
public static bool OnClosestAudibleNoise(AnglerfishController __instance,
|
||||
NoiseMaker noiseMaker)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (__instance._currentState is AnglerfishController.AnglerState.Consuming or AnglerfishController.AnglerState.Stunned)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((noiseMaker.GetNoiseOrigin() - __instance.transform.position).sqrMagnitude < __instance._pursueDistance * __instance._pursueDistance)
|
||||
{
|
||||
if (qsbAngler.TargetTransform != noiseMaker.GetAttachedBody().transform)
|
||||
{
|
||||
qsbAngler.TargetTransform = noiseMaker.GetAttachedBody().transform;
|
||||
if (__instance._currentState != AnglerfishController.AnglerState.Chasing)
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
}
|
||||
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Lurking);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (__instance._currentState is AnglerfishController.AnglerState.Lurking or AnglerfishController.AnglerState.Investigating)
|
||||
{
|
||||
__instance._localDisturbancePos = __instance._brambleBody.transform.InverseTransformPoint(noiseMaker.GetNoiseOrigin());
|
||||
if (__instance._currentState != AnglerfishController.AnglerState.Investigating)
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.UpdateMovement))]
|
||||
public static bool UpdateMovement(AnglerfishController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
qsbAngler.UpdateTargetVelocity();
|
||||
|
||||
if (__instance._anglerBody.GetVelocity().sqrMagnitude > Mathf.Pow(__instance._chaseSpeed * 1.5f, 2f))
|
||||
{
|
||||
__instance.ApplyDrag(10f);
|
||||
}
|
||||
|
||||
switch (__instance._currentState)
|
||||
{
|
||||
case AnglerfishController.AnglerState.Lurking:
|
||||
__instance.ApplyDrag(1f);
|
||||
return false;
|
||||
case AnglerfishController.AnglerState.Investigating:
|
||||
{
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Investigating);
|
||||
var targetPos = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos);
|
||||
__instance.RotateTowardsTarget(targetPos, __instance._turnSpeed, __instance._turnSpeed);
|
||||
if (!__instance._turningInPlace)
|
||||
{
|
||||
__instance.MoveTowardsTarget(targetPos, __instance._investigateSpeed, __instance._acceleration);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case AnglerfishController.AnglerState.Chasing:
|
||||
{
|
||||
var velocity = qsbAngler.TargetVelocity;
|
||||
var normalized = velocity.normalized;
|
||||
var from = __instance._anglerBody.GetPosition() + __instance.transform.TransformDirection(__instance._mouthOffset) - qsbAngler.TargetTransform.position;
|
||||
var magnitude = velocity.magnitude;
|
||||
var num = Vector3.Angle(from, normalized);
|
||||
var num2 = magnitude * 2f;
|
||||
var d = num2;
|
||||
if (num < 90f)
|
||||
{
|
||||
var magnitude2 = from.magnitude;
|
||||
var num3 = magnitude2 * Mathf.Sin(num * 0.017453292f);
|
||||
var num4 = magnitude2 * Mathf.Cos(num * 0.017453292f);
|
||||
var magnitude3 = __instance._anglerBody.GetVelocity().magnitude;
|
||||
var num5 = num4 / Mathf.Max(magnitude, 0.0001f);
|
||||
var num6 = num3 / Mathf.Max(magnitude3, 0.0001f);
|
||||
var num7 = num5 / num6;
|
||||
if (num7 <= 1f)
|
||||
{
|
||||
var t = Mathf.Clamp01(num7);
|
||||
d = Mathf.Lerp(num2, num4, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
var num8 = Mathf.InverseLerp(1f, 4f, num7);
|
||||
d = Mathf.Lerp(num4, 0f, num8 * num8);
|
||||
}
|
||||
}
|
||||
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
__instance._targetPos = qsbAngler.TargetTransform.position + normalized * d;
|
||||
__instance.RotateTowardsTarget(__instance._targetPos, __instance._turnSpeed, __instance._quickTurnSpeed);
|
||||
if (!__instance._turningInPlace)
|
||||
{
|
||||
__instance.MoveTowardsTarget(__instance._targetPos, __instance._chaseSpeed, __instance._acceleration);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case AnglerfishController.AnglerState.Consuming:
|
||||
__instance.ApplyDrag(1f);
|
||||
return false;
|
||||
case AnglerfishController.AnglerState.Stunned:
|
||||
__instance.ApplyDrag(0.5f);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnImpact))]
|
||||
public static bool OnImpact(AnglerfishController __instance,
|
||||
ImpactData impact)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
var attachedOWRigidbody = impact.otherCollider.GetAttachedOWRigidbody();
|
||||
if ((attachedOWRigidbody.CompareTag("Player") || attachedOWRigidbody.CompareTag("Ship"))
|
||||
&& __instance._currentState != AnglerfishController.AnglerState.Consuming
|
||||
&& __instance._currentState != AnglerfishController.AnglerState.Stunned)
|
||||
{
|
||||
qsbAngler.TargetTransform = attachedOWRigidbody.transform;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnCaughtObject))]
|
||||
public static bool OnCaughtObject(AnglerfishController __instance,
|
||||
OWRigidbody caughtBody)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance._currentState == AnglerfishController.AnglerState.Consuming)
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnClosestAudibleNoise))]
|
||||
public static bool OnClosestAudibleNoise(AnglerfishController __instance,
|
||||
NoiseMaker noiseMaker)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (__instance._currentState is AnglerfishController.AnglerState.Consuming or AnglerfishController.AnglerState.Stunned)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((noiseMaker.GetNoiseOrigin() - __instance.transform.position).sqrMagnitude < __instance._pursueDistance * __instance._pursueDistance)
|
||||
{
|
||||
if (qsbAngler.TargetTransform != noiseMaker.GetAttachedBody().transform)
|
||||
{
|
||||
if (!qsbAngler.TargetTransform.CompareTag("Player") && caughtBody.CompareTag("Player"))
|
||||
qsbAngler.TargetTransform = noiseMaker.GetAttachedBody().transform;
|
||||
if (__instance._currentState != AnglerfishController.AnglerState.Chasing)
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Chasing);
|
||||
}
|
||||
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (caughtBody.CompareTag("Player") || caughtBody.CompareTag("Ship"))
|
||||
}
|
||||
else if (__instance._currentState is AnglerfishController.AnglerState.Lurking or AnglerfishController.AnglerState.Investigating)
|
||||
{
|
||||
__instance._localDisturbancePos = __instance._brambleBody.transform.InverseTransformPoint(noiseMaker.GetNoiseOrigin());
|
||||
if (__instance._currentState != AnglerfishController.AnglerState.Investigating)
|
||||
{
|
||||
qsbAngler.TargetTransform = caughtBody.transform;
|
||||
__instance._consumeStartTime = Time.time;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Consuming);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Investigating);
|
||||
}
|
||||
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AnglerfishController), nameof(AnglerfishController.OnCaughtObject))]
|
||||
public static bool OnCaughtObject(AnglerfishController __instance,
|
||||
OWRigidbody caughtBody)
|
||||
{
|
||||
var qsbAngler = __instance.GetWorldObject<QSBAngler>();
|
||||
|
||||
if (__instance._currentState == AnglerfishController.AnglerState.Consuming)
|
||||
{
|
||||
if (!qsbAngler.TargetTransform.CompareTag("Player") && caughtBody.CompareTag("Player"))
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Digestion);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (caughtBody.CompareTag("Player") || caughtBody.CompareTag("Ship"))
|
||||
{
|
||||
qsbAngler.TargetTransform = caughtBody.transform;
|
||||
__instance._consumeStartTime = Time.time;
|
||||
__instance.ChangeState(AnglerfishController.AnglerState.Consuming);
|
||||
qsbAngler.SendMessage(new AnglerDataMessage(qsbAngler));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -5,95 +5,94 @@ using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Anglerfish.TransformSync
|
||||
namespace QSB.Anglerfish.TransformSync;
|
||||
|
||||
public class AnglerTransformSync : UnsectoredRigidbodySync
|
||||
{
|
||||
public class AnglerTransformSync : UnsectoredRigidbodySync
|
||||
protected override bool UseInterpolation => false;
|
||||
protected override bool AllowInactiveAttachedObject => true; // since they deactivate when suspended
|
||||
|
||||
private QSBAngler _qsbAngler;
|
||||
private static readonly List<AnglerTransformSync> _instances = new();
|
||||
|
||||
protected override OWRigidbody InitAttachedRigidbody()
|
||||
=> _qsbAngler.AttachedObject._anglerBody;
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
protected override bool UseInterpolation => false;
|
||||
protected override bool AllowInactiveAttachedObject => true; // since they deactivate when suspended
|
||||
|
||||
private QSBAngler _qsbAngler;
|
||||
private static readonly List<AnglerTransformSync> _instances = new();
|
||||
|
||||
protected override OWRigidbody InitAttachedRigidbody()
|
||||
=> _qsbAngler.AttachedObject._anglerBody;
|
||||
|
||||
public override void OnStartClient()
|
||||
_instances.Add(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
_instances.Add(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.RegisterAuthQueue();
|
||||
}
|
||||
|
||||
base.OnStartClient();
|
||||
netIdentity.RegisterAuthQueue();
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
_instances.Remove(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
base.OnStartClient();
|
||||
}
|
||||
|
||||
base.OnStopClient();
|
||||
public override void OnStopClient()
|
||||
{
|
||||
_instances.Remove(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
protected override float SendInterval => 1;
|
||||
protected override bool UseReliableRpc => true;
|
||||
base.OnStopClient();
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
protected override float SendInterval => 1;
|
||||
protected override bool UseReliableRpc => true;
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
_qsbAngler = AnglerManager.Anglers[_instances.IndexOf(this)].GetWorldObject<QSBAngler>();
|
||||
_qsbAngler.TransformSync = this;
|
||||
|
||||
base.Init();
|
||||
SetReferenceTransform(_qsbAngler.AttachedObject._brambleBody.transform);
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody += OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody += OnSuspend;
|
||||
netIdentity.UpdateAuthQueue(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
|
||||
|
||||
protected override void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
_qsbAngler = AnglerManager.Anglers[_instances.IndexOf(this)].GetWorldObject<QSBAngler>();
|
||||
_qsbAngler.TransformSync = this;
|
||||
|
||||
base.Init();
|
||||
SetReferenceTransform(_qsbAngler.AttachedObject._brambleBody.transform);
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody += OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody += OnSuspend;
|
||||
netIdentity.UpdateAuthQueue(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
base.OnRenderObject();
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._arrivalDistance, Color.blue);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._pursueDistance, Color.red);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._escapeDistance, Color.yellow);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition()
|
||||
+ AttachedRigidbody.transform.TransformDirection(_qsbAngler.AttachedObject._mouthOffset), 3, Color.grey);
|
||||
|
||||
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);
|
||||
Popcron.Gizmos.Line(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._targetPos, Color.red);
|
||||
Popcron.Gizmos.Sphere(_qsbAngler.AttachedObject._targetPos, 5, Color.red);
|
||||
}
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
|
||||
|
||||
protected override void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines
|
||||
|| !IsValid
|
||||
|| !ReferenceTransform
|
||||
|| !AttachedTransform.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnRenderObject();
|
||||
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._arrivalDistance, Color.blue);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._pursueDistance, Color.red);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._escapeDistance, Color.yellow);
|
||||
Popcron.Gizmos.Sphere(AttachedRigidbody.GetPosition()
|
||||
+ AttachedRigidbody.transform.TransformDirection(_qsbAngler.AttachedObject._mouthOffset), 3, Color.grey);
|
||||
|
||||
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);
|
||||
Popcron.Gizmos.Line(AttachedRigidbody.GetPosition(), _qsbAngler.AttachedObject._targetPos, Color.red);
|
||||
Popcron.Gizmos.Sphere(_qsbAngler.AttachedObject._targetPos, 5, Color.red);
|
||||
}
|
||||
|
||||
// Popcron.Gizmos.Line(AttachedObject.GetPosition(), _qsbAngler.AttachedObject.GetTargetPosition(), Color.white);
|
||||
}
|
||||
// Popcron.Gizmos.Line(AttachedObject.GetPosition(), _qsbAngler.AttachedObject.GetTargetPosition(), Color.white);
|
||||
}
|
||||
}
|
@ -7,48 +7,47 @@ using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Anglerfish.WorldObjects
|
||||
namespace QSB.Anglerfish.WorldObjects;
|
||||
|
||||
public class QSBAngler : WorldObject<AnglerfishController>
|
||||
{
|
||||
public class QSBAngler : WorldObject<AnglerfishController>
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public AnglerTransformSync TransformSync;
|
||||
public Transform TargetTransform;
|
||||
public Vector3 TargetVelocity { get; private set; }
|
||||
|
||||
private Vector3 _lastTargetPosition;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public AnglerTransformSync TransformSync;
|
||||
public Transform TargetTransform;
|
||||
public Vector3 TargetVelocity { get; private set; }
|
||||
|
||||
private Vector3 _lastTargetPosition;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
NetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.singleton.AnglerPrefab));
|
||||
}
|
||||
|
||||
await UniTask.WaitUntil(() => TransformSync, cancellationToken: ct);
|
||||
NetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.singleton.AnglerPrefab));
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
await UniTask.WaitUntil(() => TransformSync, cancellationToken: ct);
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
NetworkServer.Destroy(TransformSync.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new AnglerDataMessage(this) { To = to });
|
||||
|
||||
public void UpdateTargetVelocity()
|
||||
{
|
||||
if (TargetTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TargetVelocity = TargetTransform.position - _lastTargetPosition;
|
||||
_lastTargetPosition = TargetTransform.position;
|
||||
NetworkServer.Destroy(TransformSync.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new AnglerDataMessage(this) { To = to });
|
||||
|
||||
public void UpdateTargetVelocity()
|
||||
{
|
||||
if (TargetTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TargetVelocity = TargetTransform.position - _lastTargetPosition;
|
||||
_lastTargetPosition = TargetTransform.position;
|
||||
}
|
||||
}
|
@ -3,21 +3,20 @@ using QSB.Animation.NPC.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.Animation.NPC
|
||||
{
|
||||
internal class CharacterAnimManager : WorldObjectManager
|
||||
{
|
||||
// im assuming this is used in the eye as well
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
namespace QSB.Animation.NPC;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
QSBWorldSync.Init<QSBCharacterAnimController, CharacterAnimController>();
|
||||
QSBWorldSync.Init<QSBTravelerController, TravelerController>();
|
||||
QSBWorldSync.Init<QSBSolanumController, NomaiConversationManager>();
|
||||
QSBWorldSync.Init<QSBSolanumAnimController, SolanumAnimController>();
|
||||
QSBWorldSync.Init<QSBHearthianRecorderEffects, HearthianRecorderEffects>();
|
||||
QSBWorldSync.Init<QSBTravelerEyeController, TravelerEyeController>();
|
||||
}
|
||||
internal class CharacterAnimManager : WorldObjectManager
|
||||
{
|
||||
// im assuming this is used in the eye as well
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
QSBWorldSync.Init<QSBCharacterAnimController, CharacterAnimController>();
|
||||
QSBWorldSync.Init<QSBTravelerController, TravelerController>();
|
||||
QSBWorldSync.Init<QSBSolanumController, NomaiConversationManager>();
|
||||
QSBWorldSync.Init<QSBSolanumAnimController, SolanumAnimController>();
|
||||
QSBWorldSync.Init<QSBHearthianRecorderEffects, HearthianRecorderEffects>();
|
||||
QSBWorldSync.Init<QSBTravelerEyeController, TravelerEyeController>();
|
||||
}
|
||||
}
|
@ -1,22 +1,21 @@
|
||||
using QSB.Animation.NPC.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.Animation.NPC.Messages
|
||||
{
|
||||
internal class NpcAnimationMessage : QSBWorldObjectMessage<INpcAnimController, bool>
|
||||
{
|
||||
public NpcAnimationMessage(bool start) => Data = start;
|
||||
namespace QSB.Animation.NPC.Messages;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
internal class NpcAnimationMessage : QSBWorldObjectMessage<INpcAnimController, bool>
|
||||
{
|
||||
public NpcAnimationMessage(bool start) => Data = start;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (Data)
|
||||
{
|
||||
if (Data)
|
||||
{
|
||||
WorldObject.StartConversation();
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldObject.EndConversation();
|
||||
}
|
||||
WorldObject.StartConversation();
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldObject.EndConversation();
|
||||
}
|
||||
}
|
||||
}
|
@ -12,158 +12,157 @@ using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.NPC.Patches
|
||||
namespace QSB.Animation.NPC.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class CharacterAnimationPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class CharacterAnimationPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnAnimatorIK))]
|
||||
public static bool AnimatorIKReplacement(
|
||||
CharacterAnimController __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnAnimatorIK))]
|
||||
public static bool AnimatorIKReplacement(
|
||||
CharacterAnimController __instance)
|
||||
if (!QSBWorldSync.AllObjectsReady || ConversationManager.Instance == null)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady || ConversationManager.Instance == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(__instance._dialogueTree);
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(__instance._dialogueTree);
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
|
||||
if (__instance.playerTrackingZone == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (__instance.playerTrackingZone == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbObj = __instance.playerTrackingZone.GetWorldObject<QSBCharacterTrigger>(); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is
|
||||
var qsbObj = __instance.playerTrackingZone.GetWorldObject<QSBCharacterTrigger>(); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is
|
||||
|
||||
PlayerInfo playerToUse = null;
|
||||
if (__instance._inConversation)
|
||||
PlayerInfo playerToUse = null;
|
||||
if (__instance._inConversation)
|
||||
{
|
||||
if (playerId == uint.MaxValue)
|
||||
{
|
||||
if (playerId == uint.MaxValue)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {__instance.name} is in conversation with a null player! Defaulting to active camera.", MessageType.Error);
|
||||
playerToUse = QSBPlayerManager.LocalPlayer;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerToUse = player.CameraBody == null
|
||||
? QSBPlayerManager.LocalPlayer
|
||||
: player;
|
||||
}
|
||||
}
|
||||
else if (!__instance.lookOnlyWhenTalking && qsbObj.Occupants.Count != 0) // IDEA : maybe this would be more fun if characters looked between players at random times? :P
|
||||
{
|
||||
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.Occupants, __instance.transform.position);
|
||||
}
|
||||
else if (QSBPlayerManager.PlayerList.Count != 0)
|
||||
{
|
||||
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(__instance.transform.position, true);
|
||||
}
|
||||
|
||||
var localPosition = playerToUse != null
|
||||
? __instance._animator.transform.InverseTransformPoint(playerToUse.CameraBody.transform.position)
|
||||
: Vector3.zero;
|
||||
|
||||
var targetWeight = __instance.headTrackingWeight;
|
||||
if (__instance.lookOnlyWhenTalking)
|
||||
{
|
||||
if (!__instance._inConversation
|
||||
|| qsbObj.Occupants.Count == 0
|
||||
|| !qsbObj.Occupants.Contains(playerToUse))
|
||||
{
|
||||
targetWeight *= 0;
|
||||
}
|
||||
DebugLog.ToConsole($"Error - {__instance.name} is in conversation with a null player! Defaulting to active camera.", MessageType.Error);
|
||||
playerToUse = QSBPlayerManager.LocalPlayer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (qsbObj.Occupants.Count == 0
|
||||
|| !qsbObj.Occupants.Contains(playerToUse))
|
||||
{
|
||||
targetWeight *= 0;
|
||||
}
|
||||
playerToUse = player.CameraBody == null
|
||||
? QSBPlayerManager.LocalPlayer
|
||||
: player;
|
||||
}
|
||||
|
||||
__instance._currentLookWeight = Mathf.Lerp(__instance._currentLookWeight, targetWeight, Time.deltaTime * 2f);
|
||||
__instance._currentLookTarget = __instance.lookSpring.Update(__instance._currentLookTarget, localPosition, Time.deltaTime);
|
||||
__instance._animator.SetLookAtPosition(__instance._animator.transform.TransformPoint(__instance._currentLookTarget));
|
||||
__instance._animator.SetLookAtWeight(__instance._currentLookWeight);
|
||||
return false;
|
||||
|
||||
}
|
||||
else if (!__instance.lookOnlyWhenTalking && qsbObj.Occupants.Count != 0) // IDEA : maybe this would be more fun if characters looked between players at random times? :P
|
||||
{
|
||||
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.Occupants, __instance.transform.position);
|
||||
}
|
||||
else if (QSBPlayerManager.PlayerList.Count != 0)
|
||||
{
|
||||
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(__instance.transform.position, true);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(FacePlayerWhenTalking), nameof(FacePlayerWhenTalking.OnStartConversation))]
|
||||
public static bool OnStartConversation(FacePlayerWhenTalking __instance)
|
||||
var localPosition = playerToUse != null
|
||||
? __instance._animator.transform.InverseTransformPoint(playerToUse.CameraBody.transform.position)
|
||||
: Vector3.zero;
|
||||
|
||||
var targetWeight = __instance.headTrackingWeight;
|
||||
if (__instance.lookOnlyWhenTalking)
|
||||
{
|
||||
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(__instance._dialogueTree);
|
||||
if (playerId == uint.MaxValue)
|
||||
if (!__instance._inConversation
|
||||
|| qsbObj.Occupants.Count == 0
|
||||
|| !qsbObj.Occupants.Contains(playerToUse))
|
||||
{
|
||||
DebugLog.ToConsole($"Error - No player talking to {__instance._dialogueTree.name}!", MessageType.Error);
|
||||
return false;
|
||||
targetWeight *= 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (qsbObj.Occupants.Count == 0
|
||||
|| !qsbObj.Occupants.Contains(playerToUse))
|
||||
{
|
||||
targetWeight *= 0;
|
||||
}
|
||||
}
|
||||
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
__instance._currentLookWeight = Mathf.Lerp(__instance._currentLookWeight, targetWeight, Time.deltaTime * 2f);
|
||||
__instance._currentLookTarget = __instance.lookSpring.Update(__instance._currentLookTarget, localPosition, Time.deltaTime);
|
||||
__instance._animator.SetLookAtPosition(__instance._animator.transform.TransformPoint(__instance._currentLookTarget));
|
||||
__instance._animator.SetLookAtWeight(__instance._currentLookWeight);
|
||||
return false;
|
||||
|
||||
var distance = player.Body.transform.position - __instance.transform.position;
|
||||
var vector2 = distance - Vector3.Project(distance, __instance.transform.up);
|
||||
var angle = Vector3.Angle(__instance.transform.forward, vector2) * Mathf.Sign(Vector3.Dot(vector2, __instance.transform.right));
|
||||
var axis = __instance.transform.parent.InverseTransformDirection(__instance.transform.up);
|
||||
var lhs = Quaternion.AngleAxis(angle, axis);
|
||||
__instance.FaceLocalRotation(lhs * __instance.transform.localRotation);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(FacePlayerWhenTalking), nameof(FacePlayerWhenTalking.OnStartConversation))]
|
||||
public static bool OnStartConversation(FacePlayerWhenTalking __instance)
|
||||
{
|
||||
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(__instance._dialogueTree);
|
||||
if (playerId == uint.MaxValue)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - No player talking to {__instance._dialogueTree.name}!", MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))]
|
||||
public static bool StartConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
|
||||
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
|
||||
if (ownerOfThis == default)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
|
||||
ownerOfThis.SendMessage(new NpcAnimationMessage(true));
|
||||
var distance = player.Body.transform.position - __instance.transform.position;
|
||||
var vector2 = distance - Vector3.Project(distance, __instance.transform.up);
|
||||
var angle = Vector3.Angle(__instance.transform.forward, vector2) * Mathf.Sign(Vector3.Dot(vector2, __instance.transform.right));
|
||||
var axis = __instance.transform.parent.InverseTransformDirection(__instance.transform.up);
|
||||
var lhs = Quaternion.AngleAxis(angle, axis);
|
||||
__instance.FaceLocalRotation(lhs * __instance.transform.localRotation);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))]
|
||||
public static bool StartConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
|
||||
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
|
||||
if (ownerOfThis == default)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))]
|
||||
public static bool EndConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
|
||||
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
|
||||
if (ownerOfThis == default)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ownerOfThis.SendMessage(new NpcAnimationMessage(true));
|
||||
return true;
|
||||
}
|
||||
|
||||
ownerOfThis.SendMessage(new NpcAnimationMessage(false));
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))]
|
||||
public static bool EndConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
|
||||
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
|
||||
if (ownerOfThis == default)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(KidRockController), nameof(KidRockController.Update))]
|
||||
public static bool UpdateReplacement(KidRockController __instance)
|
||||
ownerOfThis.SendMessage(new NpcAnimationMessage(false));
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(KidRockController), nameof(KidRockController.Update))]
|
||||
public static bool UpdateReplacement(KidRockController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbObj = QSBWorldSync.GetWorldObjects<QSBCharacterAnimController>().First(x => x.GetDialogueTree() == __instance._dialogueTree);
|
||||
|
||||
if (!__instance._throwingRock && !qsbObj.InConversation() && Time.time > __instance._nextThrowTime)
|
||||
{
|
||||
__instance.StartRockThrow();
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbObj = QSBWorldSync.GetWorldObjects<QSBCharacterAnimController>().First(x => x.GetDialogueTree() == __instance._dialogueTree);
|
||||
|
||||
if (!__instance._throwingRock && !qsbObj.InConversation() && Time.time > __instance._nextThrowTime)
|
||||
{
|
||||
__instance.StartRockThrow();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -6,205 +6,204 @@ using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.NPC.Patches
|
||||
namespace QSB.Animation.NPC.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class SolanumPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class SolanumPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SolanumAnimController), nameof(SolanumAnimController.LateUpdate))]
|
||||
public static bool SolanumLateUpdateReplacement(SolanumAnimController __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SolanumAnimController), nameof(SolanumAnimController.LateUpdate))]
|
||||
public static bool SolanumLateUpdateReplacement(SolanumAnimController __instance)
|
||||
if (__instance._animatorStateEvents == null)
|
||||
{
|
||||
if (__instance._animatorStateEvents == null)
|
||||
{
|
||||
__instance._animatorStateEvents = __instance._animator.GetBehaviour<AnimatorStateEvents>();
|
||||
__instance._animatorStateEvents.OnEnterState += __instance.OnEnterAnimatorState;
|
||||
}
|
||||
|
||||
var qsbObj = __instance.GetWorldObject<QSBSolanumAnimController>();
|
||||
var playersInHeadZone = qsbObj.Trigger.Occupants;
|
||||
|
||||
var targetCamera = playersInHeadZone == null || playersInHeadZone.Count == 0
|
||||
? __instance._playerCameraTransform
|
||||
: QSBPlayerManager.GetClosestPlayerToWorldPoint(playersInHeadZone, __instance.transform.position).CameraBody.transform;
|
||||
|
||||
var targetValue = Quaternion.LookRotation(targetCamera.position - __instance._headBoneTransform.position, __instance.transform.up);
|
||||
__instance._currentLookRotation = __instance._lookSpring.Update(__instance._currentLookRotation, targetValue, Time.deltaTime);
|
||||
|
||||
var position = __instance._headBoneTransform.position + (__instance._currentLookRotation * Vector3.forward);
|
||||
__instance._localLookPosition = __instance.transform.InverseTransformPoint(position);
|
||||
|
||||
return false;
|
||||
__instance._animatorStateEvents = __instance._animator.GetBehaviour<AnimatorStateEvents>();
|
||||
__instance._animatorStateEvents.OnEnterState += __instance.OnEnterAnimatorState;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
|
||||
public static bool ReplacementUpdate(NomaiConversationManager __instance)
|
||||
var qsbObj = __instance.GetWorldObject<QSBSolanumAnimController>();
|
||||
var playersInHeadZone = qsbObj.Trigger.Occupants;
|
||||
|
||||
var targetCamera = playersInHeadZone == null || playersInHeadZone.Count == 0
|
||||
? __instance._playerCameraTransform
|
||||
: QSBPlayerManager.GetClosestPlayerToWorldPoint(playersInHeadZone, __instance.transform.position).CameraBody.transform;
|
||||
|
||||
var targetValue = Quaternion.LookRotation(targetCamera.position - __instance._headBoneTransform.position, __instance.transform.up);
|
||||
__instance._currentLookRotation = __instance._lookSpring.Update(__instance._currentLookRotation, targetValue, Time.deltaTime);
|
||||
|
||||
var position = __instance._headBoneTransform.position + (__instance._currentLookRotation * Vector3.forward);
|
||||
__instance._localLookPosition = __instance.transform.InverseTransformPoint(position);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
|
||||
public static bool ReplacementUpdate(NomaiConversationManager __instance)
|
||||
{
|
||||
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
|
||||
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
|
||||
|
||||
if (!__instance._initialized)
|
||||
{
|
||||
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
|
||||
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
|
||||
__instance.InitializeNomaiText();
|
||||
}
|
||||
|
||||
if (!__instance._initialized)
|
||||
{
|
||||
__instance.InitializeNomaiText();
|
||||
}
|
||||
//var heldItem = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem();
|
||||
//var holdingConversationStone = heldItem != null && heldItem is NomaiConversationStone;
|
||||
var holdingConversationStone = QSBPlayerManager.GetPlayerCarryItems().Any(x => x.Item2 != null && x.Item2.GetItemType() == ItemType.ConversationStone);
|
||||
|
||||
//var heldItem = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem();
|
||||
//var holdingConversationStone = heldItem != null && heldItem is NomaiConversationStone;
|
||||
var holdingConversationStone = QSBPlayerManager.GetPlayerCarryItems().Any(x => x.Item2 != null && x.Item2.GetItemType() == ItemType.ConversationStone);
|
||||
switch (__instance._state)
|
||||
{
|
||||
case NomaiConversationManager.State.WatchingSky:
|
||||
if (__instance._playerInWatchVolume)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._solanumAnimController.StartWatchingPlayer();
|
||||
}
|
||||
|
||||
switch (__instance._state)
|
||||
{
|
||||
case NomaiConversationManager.State.WatchingSky:
|
||||
if (__instance._playerInWatchVolume)
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.WatchingPlayer:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
{
|
||||
// player left watch zone
|
||||
if (!__instance._playerInWatchVolume)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._solanumAnimController.StartWatchingPlayer();
|
||||
__instance._state = NomaiConversationManager.State.WatchingSky;
|
||||
__instance._solanumAnimController.StopWatchingPlayer();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.WatchingPlayer:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
else if (__instance._dialogueComplete)
|
||||
{
|
||||
// player left watch zone
|
||||
if (!__instance._playerInWatchVolume)
|
||||
// create conversation stones
|
||||
if (__instance._dialogueComplete && !__instance._conversationStonesCreated)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingSky;
|
||||
__instance._solanumAnimController.StopWatchingPlayer();
|
||||
__instance._stoneCreationTimer -= Time.deltaTime;
|
||||
if (__instance._stoneCreationTimer <= 0f)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.CreatingStones;
|
||||
__instance._solanumAnimController.PlayCreateWordStones();
|
||||
}
|
||||
}
|
||||
else if (__instance._dialogueComplete)
|
||||
else if (__instance._conversationStonesCreated && !__instance._cairnRaised)
|
||||
{
|
||||
// create conversation stones
|
||||
if (__instance._dialogueComplete && !__instance._conversationStonesCreated)
|
||||
if (!holdingConversationStone)
|
||||
{
|
||||
__instance._stoneCreationTimer -= Time.deltaTime;
|
||||
if (__instance._stoneCreationTimer <= 0f)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.CreatingStones;
|
||||
__instance._solanumAnimController.PlayCreateWordStones();
|
||||
}
|
||||
}
|
||||
else if (__instance._conversationStonesCreated && !__instance._cairnRaised)
|
||||
{
|
||||
if (!holdingConversationStone)
|
||||
{
|
||||
__instance._stoneGestureTimer -= Time.deltaTime;
|
||||
if (__instance._stoneGestureTimer <= 0f)
|
||||
{
|
||||
__instance._solanumAnimController.PlayGestureToWordStones();
|
||||
__instance._stoneGestureTimer = UnityEngine.Random.Range(8f, 16f);
|
||||
}
|
||||
}
|
||||
// raise cairns
|
||||
else if (__instance._solanumAnimController.IsPlayerLooking())
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.RaisingCairns;
|
||||
__instance._solanumAnimController.PlayRaiseCairns();
|
||||
__instance._cairnAnimator.SetTrigger("Raise");
|
||||
__instance._cairnCollision.SetActivation(true);
|
||||
}
|
||||
}
|
||||
else if (__instance._activeResponseText == null && __instance._hasValidSocketedStonePair)
|
||||
{
|
||||
__instance._activeResponseText = __instance._pendingResponseText;
|
||||
__instance._pendingResponseText = null;
|
||||
__instance._state = NomaiConversationManager.State.WritingResponse;
|
||||
__instance._solanumAnimController.StartWritingMessage();
|
||||
}
|
||||
else if (__instance._activeResponseText != null && (!__instance._hasValidSocketedStonePair || __instance._pendingResponseText != null))
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.ErasingResponse;
|
||||
__instance._solanumAnimController.StartWritingMessage();
|
||||
}
|
||||
else if (!holdingConversationStone)
|
||||
{
|
||||
if (__instance._playerWasHoldingStone)
|
||||
{
|
||||
__instance.ResetStoneGestureTimer();
|
||||
}
|
||||
|
||||
__instance._stoneGestureTimer -= Time.deltaTime;
|
||||
if (__instance._stoneGestureTimer < 0f)
|
||||
if (__instance._stoneGestureTimer <= 0f)
|
||||
{
|
||||
__instance._solanumAnimController.PlayGestureToWordStones();
|
||||
__instance.ResetStoneGestureTimer();
|
||||
__instance._stoneGestureTimer = UnityEngine.Random.Range(8f, 16f);
|
||||
}
|
||||
}
|
||||
else
|
||||
// raise cairns
|
||||
else if (__instance._solanumAnimController.IsPlayerLooking())
|
||||
{
|
||||
if (!__instance._playerWasHoldingStone)
|
||||
{
|
||||
__instance.ResetCairnGestureTimer();
|
||||
}
|
||||
|
||||
__instance._cairnGestureTimer -= Time.deltaTime;
|
||||
if (__instance._cairnGestureTimer < 0f)
|
||||
{
|
||||
__instance._solanumAnimController.PlayGestureToCairns();
|
||||
__instance.ResetCairnGestureTimer();
|
||||
}
|
||||
__instance._state = NomaiConversationManager.State.RaisingCairns;
|
||||
__instance._solanumAnimController.PlayRaiseCairns();
|
||||
__instance._cairnAnimator.SetTrigger("Raise");
|
||||
__instance._cairnCollision.SetActivation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.CreatingStones:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._conversationStonesCreated = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.RaisingCairns:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._cairnRaised = true;
|
||||
__instance._stoneSocketATrigger.SetActivation(true);
|
||||
__instance._stoneSocketBTrigger.SetActivation(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.ErasingResponse:
|
||||
if (!__instance._solanumAnimController.isStartingWrite && !__instance._activeResponseText.IsAnimationPlaying())
|
||||
{
|
||||
__instance._activeResponseText = null;
|
||||
if (__instance._pendingResponseText == null)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._solanumAnimController.StopWritingMessage(false);
|
||||
}
|
||||
else
|
||||
else if (__instance._activeResponseText == null && __instance._hasValidSocketedStonePair)
|
||||
{
|
||||
__instance._activeResponseText = __instance._pendingResponseText;
|
||||
__instance._pendingResponseText = null;
|
||||
__instance._state = NomaiConversationManager.State.WritingResponse;
|
||||
__instance._activeResponseText.Show();
|
||||
__instance._solanumAnimController.StartWritingMessage();
|
||||
}
|
||||
else if (__instance._activeResponseText != null && (!__instance._hasValidSocketedStonePair || __instance._pendingResponseText != null))
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.ErasingResponse;
|
||||
__instance._solanumAnimController.StartWritingMessage();
|
||||
}
|
||||
else if (!holdingConversationStone)
|
||||
{
|
||||
if (__instance._playerWasHoldingStone)
|
||||
{
|
||||
__instance.ResetStoneGestureTimer();
|
||||
}
|
||||
|
||||
__instance._stoneGestureTimer -= Time.deltaTime;
|
||||
if (__instance._stoneGestureTimer < 0f)
|
||||
{
|
||||
__instance._solanumAnimController.PlayGestureToWordStones();
|
||||
__instance.ResetStoneGestureTimer();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!__instance._playerWasHoldingStone)
|
||||
{
|
||||
__instance.ResetCairnGestureTimer();
|
||||
}
|
||||
|
||||
__instance._cairnGestureTimer -= Time.deltaTime;
|
||||
if (__instance._cairnGestureTimer < 0f)
|
||||
{
|
||||
__instance._solanumAnimController.PlayGestureToCairns();
|
||||
__instance.ResetCairnGestureTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.WritingResponse:
|
||||
if (!__instance._solanumAnimController.isStartingWrite && !__instance._activeResponseText.IsAnimationPlaying())
|
||||
case NomaiConversationManager.State.CreatingStones:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._conversationStonesCreated = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.RaisingCairns:
|
||||
if (!__instance._solanumAnimController.isPerformingAction)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._cairnRaised = true;
|
||||
__instance._stoneSocketATrigger.SetActivation(true);
|
||||
__instance._stoneSocketBTrigger.SetActivation(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NomaiConversationManager.State.ErasingResponse:
|
||||
if (!__instance._solanumAnimController.isStartingWrite && !__instance._activeResponseText.IsAnimationPlaying())
|
||||
{
|
||||
__instance._activeResponseText = null;
|
||||
if (__instance._pendingResponseText == null)
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._solanumAnimController.StopWritingMessage(true);
|
||||
__instance._solanumAnimController.StopWritingMessage(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._activeResponseText = __instance._pendingResponseText;
|
||||
__instance._pendingResponseText = null;
|
||||
__instance._state = NomaiConversationManager.State.WritingResponse;
|
||||
__instance._activeResponseText.Show();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
__instance._playerWasHoldingStone = holdingConversationStone;
|
||||
case NomaiConversationManager.State.WritingResponse:
|
||||
if (!__instance._solanumAnimController.isStartingWrite && !__instance._activeResponseText.IsAnimationPlaying())
|
||||
{
|
||||
__instance._state = NomaiConversationManager.State.WatchingPlayer;
|
||||
__instance._solanumAnimController.StopWritingMessage(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
__instance._playerWasHoldingStone = holdingConversationStone;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -4,188 +4,187 @@ using QSB.Utility;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.NPC.Patches
|
||||
namespace QSB.Animation.NPC.Patches;
|
||||
|
||||
public class TravelerControllerPatches : QSBPatch
|
||||
{
|
||||
public class TravelerControllerPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.OnStartConversation))]
|
||||
public static bool OnStartConversation(TravelerController __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
__instance._talking = true;
|
||||
// call directly instead of firing event
|
||||
__instance.StartConversation();
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.OnStartConversation))]
|
||||
public static bool OnStartConversation(TravelerController __instance)
|
||||
{
|
||||
__instance._talking = true;
|
||||
// call directly instead of firing event
|
||||
__instance.StartConversation();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.OnEndConversation))]
|
||||
public static bool OnEndConversation(TravelerController __instance)
|
||||
{
|
||||
// call directly instead of firing event
|
||||
__instance.EndConversation(__instance._delayToRestartAudio);
|
||||
__instance._talking = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.StartConversation))]
|
||||
public static bool StartConversation(TravelerController __instance)
|
||||
{
|
||||
if (__instance._animator != null && __instance._animator.enabled)
|
||||
{
|
||||
__instance._playingAnimID = __instance._animator.IsInTransition(0)
|
||||
? __instance._animator.GetNextAnimatorStateInfo(0).fullPathHash
|
||||
: __instance._animator.GetCurrentAnimatorStateInfo(0).fullPathHash;
|
||||
__instance._animator.SetTrigger("Talking");
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().StopTravelerAudio(__instance);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GabbroTravelerController), nameof(GabbroTravelerController.StartConversation))]
|
||||
public static bool StartConversation(GabbroTravelerController __instance)
|
||||
{
|
||||
if (__instance._animator.enabled)
|
||||
{
|
||||
__instance._animator.CrossFadeInFixedTime("Gabbro_Talking", 1.8f);
|
||||
__instance._hammockAnimator.CrossFadeInFixedTime("GabbroHammock_Talking", 1.8f);
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().StopTravelerAudio(__instance);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.EndConversation))]
|
||||
public static bool EndConversation(TravelerController __instance, float audioDelay)
|
||||
{
|
||||
if (__instance._animator != null && __instance._animator.enabled)
|
||||
{
|
||||
if (audioDelay > 0f)
|
||||
{
|
||||
__instance._animator.CrossFadeInFixedTime(__instance._playingAnimID, audioDelay, -1, -audioDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._animator.SetTrigger("Playing");
|
||||
}
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().PlayTravelerAudio(__instance, audioDelay);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GabbroTravelerController), nameof(GabbroTravelerController.EndConversation))]
|
||||
public static bool EndConversation(GabbroTravelerController __instance, float audioDelay)
|
||||
{
|
||||
if (__instance._animator.enabled)
|
||||
{
|
||||
__instance._animator.CrossFadeInFixedTime("Gabbro_Playing", audioDelay, -1, -audioDelay);
|
||||
__instance._hammockAnimator.CrossFadeInFixedTime("GabbroHammock_Playing", audioDelay, -1, -audioDelay);
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().PlayTravelerAudio(__instance, audioDelay);
|
||||
if (DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_REMINDER") || DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_ATTENTION"))
|
||||
{
|
||||
var conditionState = DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_ATTENTION");
|
||||
DialogueConditionManager.SharedInstance.SetConditionState("MAP_PROMPT_REMINDER");
|
||||
DialogueConditionManager.SharedInstance.SetConditionState("MAP_PROMPT_ATTENTION");
|
||||
GlobalMessenger<bool>.FireEvent("TriggerMapPromptReminder", conditionState);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static class TravelerAudioManagerExtensions
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.OnEndConversation))]
|
||||
public static bool OnEndConversation(TravelerController __instance)
|
||||
{
|
||||
/// bad, but works great
|
||||
private static SignalName? TravelerToSignalName(TravelerController traveler)
|
||||
// call directly instead of firing event
|
||||
__instance.EndConversation(__instance._delayToRestartAudio);
|
||||
__instance._talking = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.StartConversation))]
|
||||
public static bool StartConversation(TravelerController __instance)
|
||||
{
|
||||
if (__instance._animator != null && __instance._animator.enabled)
|
||||
{
|
||||
var name = traveler.name;
|
||||
|
||||
if (name.Contains("Esker"))
|
||||
{
|
||||
return SignalName.Traveler_Esker;
|
||||
}
|
||||
|
||||
if (name.Contains("Chert"))
|
||||
{
|
||||
return SignalName.Traveler_Chert;
|
||||
}
|
||||
|
||||
if (name.Contains("Riebeck"))
|
||||
{
|
||||
return SignalName.Traveler_Riebeck;
|
||||
}
|
||||
|
||||
if (name.Contains("Gabbro"))
|
||||
{
|
||||
return SignalName.Traveler_Gabbro;
|
||||
}
|
||||
|
||||
if (name.Contains("Feldspar"))
|
||||
{
|
||||
return SignalName.Traveler_Feldspar;
|
||||
}
|
||||
|
||||
if (name.Contains("Nomai"))
|
||||
{
|
||||
return SignalName.Traveler_Nomai;
|
||||
}
|
||||
|
||||
if (name.Contains("Prisoner"))
|
||||
{
|
||||
return SignalName.Traveler_Prisoner;
|
||||
}
|
||||
|
||||
return null;
|
||||
__instance._playingAnimID = __instance._animator.IsInTransition(0)
|
||||
? __instance._animator.GetNextAnimatorStateInfo(0).fullPathHash
|
||||
: __instance._animator.GetCurrentAnimatorStateInfo(0).fullPathHash;
|
||||
__instance._animator.SetTrigger("Talking");
|
||||
}
|
||||
|
||||
internal static void StopTravelerAudio(this TravelerAudioManager manager, TravelerController traveler)
|
||||
Locator.GetTravelerAudioManager().StopTravelerAudio(__instance);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GabbroTravelerController), nameof(GabbroTravelerController.StartConversation))]
|
||||
public static bool StartConversation(GabbroTravelerController __instance)
|
||||
{
|
||||
if (__instance._animator.enabled)
|
||||
{
|
||||
var signalName = TravelerToSignalName(traveler);
|
||||
if (signalName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var signal = manager._signals.First(x => x.GetName() == signalName);
|
||||
|
||||
signal.GetOWAudioSource().FadeOut(0.5f);
|
||||
__instance._animator.CrossFadeInFixedTime("Gabbro_Talking", 1.8f);
|
||||
__instance._hammockAnimator.CrossFadeInFixedTime("GabbroHammock_Talking", 1.8f);
|
||||
}
|
||||
|
||||
internal static void PlayTravelerAudio(this TravelerAudioManager manager, TravelerController traveler, float audioDelay)
|
||||
Locator.GetTravelerAudioManager().StopTravelerAudio(__instance);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TravelerController), nameof(TravelerController.EndConversation))]
|
||||
public static bool EndConversation(TravelerController __instance, float audioDelay)
|
||||
{
|
||||
if (__instance._animator != null && __instance._animator.enabled)
|
||||
{
|
||||
var signalName = TravelerToSignalName(traveler);
|
||||
if (signalName == null)
|
||||
if (audioDelay > 0f)
|
||||
{
|
||||
return;
|
||||
__instance._animator.CrossFadeInFixedTime(__instance._playingAnimID, audioDelay, -1, -audioDelay);
|
||||
}
|
||||
|
||||
var signal = manager._signals.First(x => x.GetName() == signalName);
|
||||
|
||||
manager._playAfterDelay = false;
|
||||
manager._playAudioTime = Time.time + audioDelay;
|
||||
Delay.RunWhen(() => Time.time >= manager._playAudioTime, () =>
|
||||
else
|
||||
{
|
||||
if (!signal.IsOnlyAudibleToScope() || signal.GetOWAudioSource().isPlaying)
|
||||
{
|
||||
signal.GetOWAudioSource().FadeIn(0.5f);
|
||||
signal.GetOWAudioSource().timeSamples = 0;
|
||||
}
|
||||
});
|
||||
__instance._animator.SetTrigger("Playing");
|
||||
}
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().PlayTravelerAudio(__instance, audioDelay);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GabbroTravelerController), nameof(GabbroTravelerController.EndConversation))]
|
||||
public static bool EndConversation(GabbroTravelerController __instance, float audioDelay)
|
||||
{
|
||||
if (__instance._animator.enabled)
|
||||
{
|
||||
__instance._animator.CrossFadeInFixedTime("Gabbro_Playing", audioDelay, -1, -audioDelay);
|
||||
__instance._hammockAnimator.CrossFadeInFixedTime("GabbroHammock_Playing", audioDelay, -1, -audioDelay);
|
||||
}
|
||||
|
||||
Locator.GetTravelerAudioManager().PlayTravelerAudio(__instance, audioDelay);
|
||||
if (DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_REMINDER") || DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_ATTENTION"))
|
||||
{
|
||||
var conditionState = DialogueConditionManager.SharedInstance.GetConditionState("MAP_PROMPT_ATTENTION");
|
||||
DialogueConditionManager.SharedInstance.SetConditionState("MAP_PROMPT_REMINDER");
|
||||
DialogueConditionManager.SharedInstance.SetConditionState("MAP_PROMPT_ATTENTION");
|
||||
GlobalMessenger<bool>.FireEvent("TriggerMapPromptReminder", conditionState);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class TravelerAudioManagerExtensions
|
||||
{
|
||||
/// bad, but works great
|
||||
private static SignalName? TravelerToSignalName(TravelerController traveler)
|
||||
{
|
||||
var name = traveler.name;
|
||||
|
||||
if (name.Contains("Esker"))
|
||||
{
|
||||
return SignalName.Traveler_Esker;
|
||||
}
|
||||
|
||||
if (name.Contains("Chert"))
|
||||
{
|
||||
return SignalName.Traveler_Chert;
|
||||
}
|
||||
|
||||
if (name.Contains("Riebeck"))
|
||||
{
|
||||
return SignalName.Traveler_Riebeck;
|
||||
}
|
||||
|
||||
if (name.Contains("Gabbro"))
|
||||
{
|
||||
return SignalName.Traveler_Gabbro;
|
||||
}
|
||||
|
||||
if (name.Contains("Feldspar"))
|
||||
{
|
||||
return SignalName.Traveler_Feldspar;
|
||||
}
|
||||
|
||||
if (name.Contains("Nomai"))
|
||||
{
|
||||
return SignalName.Traveler_Nomai;
|
||||
}
|
||||
|
||||
if (name.Contains("Prisoner"))
|
||||
{
|
||||
return SignalName.Traveler_Prisoner;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static void StopTravelerAudio(this TravelerAudioManager manager, TravelerController traveler)
|
||||
{
|
||||
var signalName = TravelerToSignalName(traveler);
|
||||
if (signalName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var signal = manager._signals.First(x => x.GetName() == signalName);
|
||||
|
||||
signal.GetOWAudioSource().FadeOut(0.5f);
|
||||
}
|
||||
|
||||
internal static void PlayTravelerAudio(this TravelerAudioManager manager, TravelerController traveler, float audioDelay)
|
||||
{
|
||||
var signalName = TravelerToSignalName(traveler);
|
||||
if (signalName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var signal = manager._signals.First(x => x.GetName() == signalName);
|
||||
|
||||
manager._playAfterDelay = false;
|
||||
manager._playAudioTime = Time.time + audioDelay;
|
||||
Delay.RunWhen(() => Time.time >= manager._playAudioTime, () =>
|
||||
{
|
||||
if (!signal.IsOnlyAudibleToScope() || signal.GetOWAudioSource().isPlaying)
|
||||
{
|
||||
signal.GetOWAudioSource().FadeIn(0.5f);
|
||||
signal.GetOWAudioSource().timeSamples = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public interface INpcAnimController : IWorldObject
|
||||
{
|
||||
public interface INpcAnimController : IWorldObject
|
||||
{
|
||||
CharacterDialogueTree GetDialogueTree();
|
||||
void StartConversation();
|
||||
void EndConversation();
|
||||
}
|
||||
CharacterDialogueTree GetDialogueTree();
|
||||
void StartConversation();
|
||||
void EndConversation();
|
||||
}
|
@ -2,17 +2,16 @@
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
internal abstract class NpcAnimController<T> : WorldObject<T>, INpcAnimController
|
||||
where T : MonoBehaviour
|
||||
{
|
||||
internal abstract class NpcAnimController<T> : WorldObject<T>, INpcAnimController
|
||||
where T : MonoBehaviour
|
||||
{
|
||||
public abstract CharacterDialogueTree GetDialogueTree();
|
||||
public abstract CharacterDialogueTree GetDialogueTree();
|
||||
|
||||
public void StartConversation()
|
||||
=> GetDialogueTree().RaiseEvent(nameof(CharacterDialogueTree.OnStartConversation));
|
||||
public void StartConversation()
|
||||
=> GetDialogueTree().RaiseEvent(nameof(CharacterDialogueTree.OnStartConversation));
|
||||
|
||||
public void EndConversation()
|
||||
=> GetDialogueTree().RaiseEvent(nameof(CharacterDialogueTree.OnEndConversation));
|
||||
}
|
||||
public void EndConversation()
|
||||
=> GetDialogueTree().RaiseEvent(nameof(CharacterDialogueTree.OnEndConversation));
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
internal class QSBCharacterAnimController : NpcAnimController<CharacterAnimController>
|
||||
{
|
||||
internal class QSBCharacterAnimController : NpcAnimController<CharacterAnimController>
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueTree;
|
||||
|
||||
public bool InConversation()
|
||||
=> AttachedObject._inConversation;
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueTree;
|
||||
|
||||
public bool InConversation()
|
||||
=> AttachedObject._inConversation;
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
{
|
||||
internal class QSBHearthianRecorderEffects : NpcAnimController<HearthianRecorderEffects>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._characterDialogueTree;
|
||||
internal class QSBHearthianRecorderEffects : NpcAnimController<HearthianRecorderEffects>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._characterDialogueTree;
|
||||
}
|
@ -2,16 +2,15 @@
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// only used to get QSBSolanumTrigger from SolanumAnimController
|
||||
/// </summary>
|
||||
internal class QSBSolanumAnimController : WorldObject<SolanumAnimController>
|
||||
{
|
||||
private QSBSolanumTrigger _trigger;
|
||||
public QSBSolanumTrigger Trigger => _trigger ??= QSBWorldSync.GetWorldObjects<QSBSolanumTrigger>().First();
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public override void SendInitialState(uint to) { }
|
||||
}
|
||||
/// <summary>
|
||||
/// only used to get QSBSolanumTrigger from SolanumAnimController
|
||||
/// </summary>
|
||||
internal class QSBSolanumAnimController : WorldObject<SolanumAnimController>
|
||||
{
|
||||
private QSBSolanumTrigger _trigger;
|
||||
public QSBSolanumTrigger Trigger => _trigger ??= QSBWorldSync.GetWorldObjects<QSBSolanumTrigger>().First();
|
||||
|
||||
public override void SendInitialState(uint to) { }
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
{
|
||||
internal class QSBSolanumController : NpcAnimController<NomaiConversationManager>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._characterDialogueTree;
|
||||
internal class QSBSolanumController : NpcAnimController<NomaiConversationManager>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._characterDialogueTree;
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
{
|
||||
internal class QSBTravelerController : NpcAnimController<TravelerController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueSystem;
|
||||
internal class QSBTravelerController : NpcAnimController<TravelerController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueSystem;
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
namespace QSB.Animation.NPC.WorldObjects
|
||||
{
|
||||
internal class QSBTravelerEyeController : NpcAnimController<TravelerEyeController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
namespace QSB.Animation.NPC.WorldObjects;
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueTree;
|
||||
internal class QSBTravelerEyeController : NpcAnimController<TravelerEyeController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override CharacterDialogueTree GetDialogueTree()
|
||||
=> AttachedObject._dialogueTree;
|
||||
}
|
@ -1,18 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player
|
||||
namespace QSB.Animation.Player;
|
||||
|
||||
public class AnimFloatParam
|
||||
{
|
||||
public class AnimFloatParam
|
||||
public float Current { get; private set; }
|
||||
public float Target { get; set; }
|
||||
|
||||
private float _velocity;
|
||||
|
||||
public float Smooth(float smoothTime)
|
||||
{
|
||||
public float Current { get; private set; }
|
||||
public float Target { get; set; }
|
||||
|
||||
private float _velocity;
|
||||
|
||||
public float Smooth(float smoothTime)
|
||||
{
|
||||
Current = Mathf.SmoothDamp(Current, Target, ref _velocity, smoothTime);
|
||||
return Current;
|
||||
}
|
||||
Current = Mathf.SmoothDamp(Current, Target, ref _velocity, smoothTime);
|
||||
return Current;
|
||||
}
|
||||
}
|
@ -9,192 +9,191 @@ using QSB.Utility;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player
|
||||
namespace QSB.Animation.Player;
|
||||
|
||||
public class AnimationSync : PlayerSyncObject
|
||||
{
|
||||
public class AnimationSync : PlayerSyncObject
|
||||
private RuntimeAnimatorController _suitedAnimController;
|
||||
private AnimatorOverrideController _unsuitedAnimController;
|
||||
private GameObject _suitedGraphics;
|
||||
private GameObject _unsuitedGraphics;
|
||||
private PlayerCharacterController _playerController;
|
||||
private CrouchSync _crouchSync;
|
||||
|
||||
public AnimatorMirror Mirror { get; private set; }
|
||||
public bool InSuitedUpState { get; set; }
|
||||
public Animator VisibleAnimator { get; private set; }
|
||||
public Animator InvisibleAnimator { get; private set; }
|
||||
public NetworkAnimator NetworkAnimator { get; private set; }
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
private RuntimeAnimatorController _suitedAnimController;
|
||||
private AnimatorOverrideController _unsuitedAnimController;
|
||||
private GameObject _suitedGraphics;
|
||||
private GameObject _unsuitedGraphics;
|
||||
private PlayerCharacterController _playerController;
|
||||
private CrouchSync _crouchSync;
|
||||
InvisibleAnimator = gameObject.GetRequiredComponent<Animator>();
|
||||
NetworkAnimator = gameObject.GetRequiredComponent<NetworkAnimator>();
|
||||
NetworkAnimator.enabled = false;
|
||||
}
|
||||
|
||||
public AnimatorMirror Mirror { get; private set; }
|
||||
public bool InSuitedUpState { get; set; }
|
||||
public Animator VisibleAnimator { get; private set; }
|
||||
public Animator InvisibleAnimator { get; private set; }
|
||||
public NetworkAnimator NetworkAnimator { get; private set; }
|
||||
|
||||
protected void Awake()
|
||||
private void InitCommon(Transform modelRoot)
|
||||
{
|
||||
try
|
||||
{
|
||||
InvisibleAnimator = gameObject.GetRequiredComponent<Animator>();
|
||||
NetworkAnimator = gameObject.GetRequiredComponent<NetworkAnimator>();
|
||||
NetworkAnimator.enabled = false;
|
||||
}
|
||||
|
||||
private void InitCommon(Transform modelRoot)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (modelRoot == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to InitCommon with null body!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
VisibleAnimator = modelRoot.GetComponent<Animator>();
|
||||
Mirror = modelRoot.gameObject.AddComponent<AnimatorMirror>();
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
Mirror.Init(VisibleAnimator, InvisibleAnimator);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mirror.Init(InvisibleAnimator, VisibleAnimator);
|
||||
}
|
||||
|
||||
NetworkAnimator.enabled = true;
|
||||
NetworkAnimator.Invoke("Awake");
|
||||
|
||||
_suitedAnimController = Instantiate(QSBCore.NetworkAssetBundle.LoadAsset<RuntimeAnimatorController>("Assets/GameAssets/AnimatorController/Player.controller"));
|
||||
_unsuitedAnimController = Instantiate(QSBCore.NetworkAssetBundle.LoadAsset<AnimatorOverrideController>("Assets/GameAssets/AnimatorOverrideController/PlayerUnsuitedOverride.overrideController"));
|
||||
_suitedGraphics = modelRoot.GetChild(1).gameObject;
|
||||
_unsuitedGraphics = modelRoot.GetChild(0).gameObject;
|
||||
|
||||
VisibleAnimator.SetLayerWeight(2, 1f);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugLog.ToConsole($"Exception thrown when running InitCommon on {(modelRoot != null ? modelRoot.name : "NULL BODY")}. {ex.Message} Stacktrace: {ex.StackTrace}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitLocal(Transform body)
|
||||
{
|
||||
InitCommon(body);
|
||||
|
||||
_playerController = body.parent.GetComponent<PlayerCharacterController>();
|
||||
|
||||
InitCrouchSync();
|
||||
InitAccelerationSync();
|
||||
}
|
||||
|
||||
public void InitRemote(Transform body)
|
||||
{
|
||||
InitCommon(body);
|
||||
SetSuitState(QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse);
|
||||
InitCrouchSync();
|
||||
InitAccelerationSync();
|
||||
ThrusterManager.CreateRemotePlayerVFX(Player);
|
||||
|
||||
Delay.RunWhen(() => Player.CameraBody != null,
|
||||
() => body.GetComponent<PlayerHeadRotationSync>().Init(Player.CameraBody.transform));
|
||||
}
|
||||
|
||||
private void InitAccelerationSync()
|
||||
{
|
||||
Player.JetpackAcceleration = GetComponent<JetpackAccelerationSync>();
|
||||
var thrusterModel = hasAuthority ? Locator.GetPlayerBody().GetComponent<ThrusterModel>() : null;
|
||||
Player.JetpackAcceleration.Init(thrusterModel);
|
||||
}
|
||||
|
||||
private void InitCrouchSync()
|
||||
{
|
||||
_crouchSync = GetComponent<CrouchSync>();
|
||||
_crouchSync.Init(_playerController, VisibleAnimator);
|
||||
}
|
||||
|
||||
public void SetSuitState(bool suitedUp)
|
||||
{
|
||||
if (!Player.IsReady)
|
||||
if (modelRoot == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to InitCommon with null body!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player == QSBPlayerManager.LocalPlayer)
|
||||
VisibleAnimator = modelRoot.GetComponent<Animator>();
|
||||
Mirror = modelRoot.gameObject.AddComponent<AnimatorMirror>();
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
new PlayerSuitMessage(suitedUp).Send();
|
||||
}
|
||||
|
||||
if (InSuitedUpState == suitedUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InSuitedUpState = suitedUp;
|
||||
if (_unsuitedAnimController == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Unsuited controller is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
if (_suitedAnimController == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Suited controller is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
if (_unsuitedGraphics == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - _unsuitedGraphics is null! ({PlayerId})", MessageType.Warning);
|
||||
}
|
||||
|
||||
if (_suitedGraphics == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - _suitedGraphics is null! ({PlayerId})", MessageType.Warning);
|
||||
}
|
||||
|
||||
var controller = suitedUp ? _suitedAnimController : _unsuitedAnimController;
|
||||
if (_unsuitedGraphics != null)
|
||||
{
|
||||
_unsuitedGraphics?.SetActive(!suitedUp);
|
||||
}
|
||||
|
||||
if (_suitedGraphics != null)
|
||||
{
|
||||
_suitedGraphics?.SetActive(suitedUp);
|
||||
}
|
||||
|
||||
if (InvisibleAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - InvisibleAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
Mirror.Init(VisibleAnimator, InvisibleAnimator);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvisibleAnimator.runtimeAnimatorController = controller;
|
||||
Mirror.Init(InvisibleAnimator, VisibleAnimator);
|
||||
}
|
||||
|
||||
if (VisibleAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - VisibleAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisibleAnimator.runtimeAnimatorController = controller;
|
||||
}
|
||||
|
||||
// Avoids "jumping" when putting on suit
|
||||
if (VisibleAnimator != null)
|
||||
{
|
||||
VisibleAnimator.SetTrigger("Grounded");
|
||||
}
|
||||
|
||||
if (InvisibleAnimator != null)
|
||||
{
|
||||
InvisibleAnimator.SetTrigger("Grounded");
|
||||
}
|
||||
|
||||
if (NetworkAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - NetworkAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
else if (Mirror == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Mirror is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
Mirror.RebuildFloatParams();
|
||||
NetworkAnimator.enabled = true;
|
||||
NetworkAnimator.Invoke("Awake");
|
||||
|
||||
_suitedAnimController = Instantiate(QSBCore.NetworkAssetBundle.LoadAsset<RuntimeAnimatorController>("Assets/GameAssets/AnimatorController/Player.controller"));
|
||||
_unsuitedAnimController = Instantiate(QSBCore.NetworkAssetBundle.LoadAsset<AnimatorOverrideController>("Assets/GameAssets/AnimatorOverrideController/PlayerUnsuitedOverride.overrideController"));
|
||||
_suitedGraphics = modelRoot.GetChild(1).gameObject;
|
||||
_unsuitedGraphics = modelRoot.GetChild(0).gameObject;
|
||||
|
||||
VisibleAnimator.SetLayerWeight(2, 1f);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugLog.ToConsole($"Exception thrown when running InitCommon on {(modelRoot != null ? modelRoot.name : "NULL BODY")}. {ex.Message} Stacktrace: {ex.StackTrace}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitLocal(Transform body)
|
||||
{
|
||||
InitCommon(body);
|
||||
|
||||
_playerController = body.parent.GetComponent<PlayerCharacterController>();
|
||||
|
||||
InitCrouchSync();
|
||||
InitAccelerationSync();
|
||||
}
|
||||
|
||||
public void InitRemote(Transform body)
|
||||
{
|
||||
InitCommon(body);
|
||||
SetSuitState(QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse);
|
||||
InitCrouchSync();
|
||||
InitAccelerationSync();
|
||||
ThrusterManager.CreateRemotePlayerVFX(Player);
|
||||
|
||||
Delay.RunWhen(() => Player.CameraBody != null,
|
||||
() => body.GetComponent<PlayerHeadRotationSync>().Init(Player.CameraBody.transform));
|
||||
}
|
||||
|
||||
private void InitAccelerationSync()
|
||||
{
|
||||
Player.JetpackAcceleration = GetComponent<JetpackAccelerationSync>();
|
||||
var thrusterModel = hasAuthority ? Locator.GetPlayerBody().GetComponent<ThrusterModel>() : null;
|
||||
Player.JetpackAcceleration.Init(thrusterModel);
|
||||
}
|
||||
|
||||
private void InitCrouchSync()
|
||||
{
|
||||
_crouchSync = GetComponent<CrouchSync>();
|
||||
_crouchSync.Init(_playerController, VisibleAnimator);
|
||||
}
|
||||
|
||||
public void SetSuitState(bool suitedUp)
|
||||
{
|
||||
if (!Player.IsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player == QSBPlayerManager.LocalPlayer)
|
||||
{
|
||||
new PlayerSuitMessage(suitedUp).Send();
|
||||
}
|
||||
|
||||
if (InSuitedUpState == suitedUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InSuitedUpState = suitedUp;
|
||||
if (_unsuitedAnimController == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Unsuited controller is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
if (_suitedAnimController == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Suited controller is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
if (_unsuitedGraphics == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - _unsuitedGraphics is null! ({PlayerId})", MessageType.Warning);
|
||||
}
|
||||
|
||||
if (_suitedGraphics == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - _suitedGraphics is null! ({PlayerId})", MessageType.Warning);
|
||||
}
|
||||
|
||||
var controller = suitedUp ? _suitedAnimController : _unsuitedAnimController;
|
||||
if (_unsuitedGraphics != null)
|
||||
{
|
||||
_unsuitedGraphics?.SetActive(!suitedUp);
|
||||
}
|
||||
|
||||
if (_suitedGraphics != null)
|
||||
{
|
||||
_suitedGraphics?.SetActive(suitedUp);
|
||||
}
|
||||
|
||||
if (InvisibleAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - InvisibleAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvisibleAnimator.runtimeAnimatorController = controller;
|
||||
}
|
||||
|
||||
if (VisibleAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - VisibleAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisibleAnimator.runtimeAnimatorController = controller;
|
||||
}
|
||||
|
||||
// Avoids "jumping" when putting on suit
|
||||
if (VisibleAnimator != null)
|
||||
{
|
||||
VisibleAnimator.SetTrigger("Grounded");
|
||||
}
|
||||
|
||||
if (InvisibleAnimator != null)
|
||||
{
|
||||
InvisibleAnimator.SetTrigger("Grounded");
|
||||
}
|
||||
|
||||
if (NetworkAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - NetworkAnimator is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
else if (Mirror == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Mirror is null. ({PlayerId})", MessageType.Error);
|
||||
}
|
||||
|
||||
Mirror.RebuildFloatParams();
|
||||
NetworkAnimator.Invoke("Awake");
|
||||
}
|
||||
}
|
@ -4,99 +4,98 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player
|
||||
namespace QSB.Animation.Player;
|
||||
|
||||
public class AnimatorMirror : MonoBehaviour
|
||||
{
|
||||
public class AnimatorMirror : MonoBehaviour
|
||||
private const float SmoothTime = 0.05f;
|
||||
|
||||
private Animator _from;
|
||||
private Animator _to;
|
||||
|
||||
private readonly Dictionary<string, AnimFloatParam> _floatParams = new();
|
||||
|
||||
public void Init(Animator from, Animator to)
|
||||
{
|
||||
private const float SmoothTime = 0.05f;
|
||||
|
||||
private Animator _from;
|
||||
private Animator _to;
|
||||
|
||||
private readonly Dictionary<string, AnimFloatParam> _floatParams = new();
|
||||
|
||||
public void Init(Animator from, Animator to)
|
||||
if (from == null)
|
||||
{
|
||||
if (from == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to init AnimatorMirror with null \"from\".", MessageType.Error);
|
||||
}
|
||||
DebugLog.ToConsole($"Error - Trying to init AnimatorMirror with null \"from\".", MessageType.Error);
|
||||
}
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to init AnimatorMirror with null \"to\".", MessageType.Error);
|
||||
}
|
||||
if (to == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Trying to init AnimatorMirror with null \"to\".", MessageType.Error);
|
||||
}
|
||||
|
||||
if (to == null || from == null)
|
||||
{
|
||||
// Doing the return this way so you can see if one or both are null
|
||||
return;
|
||||
}
|
||||
if (to == null || from == null)
|
||||
{
|
||||
// Doing the return this way so you can see if one or both are null
|
||||
return;
|
||||
}
|
||||
|
||||
_from = from;
|
||||
_to = to;
|
||||
if (_from.runtimeAnimatorController == null)
|
||||
{
|
||||
_from.runtimeAnimatorController = _to.runtimeAnimatorController;
|
||||
}
|
||||
else if (_to.runtimeAnimatorController == null)
|
||||
{
|
||||
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
|
||||
}
|
||||
_from = from;
|
||||
_to = to;
|
||||
if (_from.runtimeAnimatorController == null)
|
||||
{
|
||||
_from.runtimeAnimatorController = _to.runtimeAnimatorController;
|
||||
}
|
||||
else if (_to.runtimeAnimatorController == null)
|
||||
{
|
||||
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
|
||||
}
|
||||
|
||||
RebuildFloatParams();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_to == null || _from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_to.runtimeAnimatorController != _from.runtimeAnimatorController)
|
||||
{
|
||||
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
|
||||
RebuildFloatParams();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
SyncParams();
|
||||
SmoothFloats();
|
||||
}
|
||||
|
||||
private void SyncParams()
|
||||
{
|
||||
foreach (var fromParam in _from.parameters)
|
||||
{
|
||||
if (_to == null || _from == null)
|
||||
switch (fromParam.type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
case AnimatorControllerParameterType.Float:
|
||||
_floatParams[fromParam.name].Target = _from.GetFloat(fromParam.name);
|
||||
break;
|
||||
|
||||
if (_to.runtimeAnimatorController != _from.runtimeAnimatorController)
|
||||
{
|
||||
_to.runtimeAnimatorController = _from.runtimeAnimatorController;
|
||||
RebuildFloatParams();
|
||||
}
|
||||
|
||||
SyncParams();
|
||||
SmoothFloats();
|
||||
}
|
||||
|
||||
private void SyncParams()
|
||||
{
|
||||
foreach (var fromParam in _from.parameters)
|
||||
{
|
||||
switch (fromParam.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Float:
|
||||
_floatParams[fromParam.name].Target = _from.GetFloat(fromParam.name);
|
||||
break;
|
||||
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
_to.SetBool(fromParam.name, _from.GetBool(fromParam.name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SmoothFloats()
|
||||
{
|
||||
foreach (var floatParam in _floatParams)
|
||||
{
|
||||
var current = floatParam.Value.Smooth(SmoothTime);
|
||||
_to.SetFloat(floatParam.Key, current);
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildFloatParams()
|
||||
{
|
||||
_floatParams.Clear();
|
||||
foreach (var param in _from.parameters.Where(p => p.type == AnimatorControllerParameterType.Float))
|
||||
{
|
||||
_floatParams.Add(param.name, new AnimFloatParam());
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
_to.SetBool(fromParam.name, _from.GetBool(fromParam.name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SmoothFloats()
|
||||
{
|
||||
foreach (var floatParam in _floatParams)
|
||||
{
|
||||
var current = floatParam.Value.Smooth(SmoothTime);
|
||||
_to.SetFloat(floatParam.Key, current);
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildFloatParams()
|
||||
{
|
||||
_floatParams.Clear();
|
||||
foreach (var param in _from.parameters.Where(p => p.type == AnimatorControllerParameterType.Float))
|
||||
{
|
||||
_floatParams.Add(param.name, new AnimFloatParam());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,59 +2,58 @@
|
||||
using QSB.Utility.VariableSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player
|
||||
namespace QSB.Animation.Player;
|
||||
|
||||
public class CrouchSync : NetworkBehaviour
|
||||
{
|
||||
public class CrouchSync : NetworkBehaviour
|
||||
public AnimFloatParam CrouchParam { get; } = new AnimFloatParam();
|
||||
|
||||
private const float CrouchSmoothTime = 0.05f;
|
||||
public const int CrouchLayerIndex = 1;
|
||||
|
||||
private PlayerCharacterController _playerController;
|
||||
private Animator _bodyAnim;
|
||||
|
||||
public FloatVariableSyncer CrouchVariableSyncer;
|
||||
|
||||
public void Init(PlayerCharacterController playerController, Animator bodyAnim)
|
||||
{
|
||||
public AnimFloatParam CrouchParam { get; } = new AnimFloatParam();
|
||||
_playerController = playerController;
|
||||
_bodyAnim = bodyAnim;
|
||||
}
|
||||
|
||||
private const float CrouchSmoothTime = 0.05f;
|
||||
public const int CrouchLayerIndex = 1;
|
||||
|
||||
private PlayerCharacterController _playerController;
|
||||
private Animator _bodyAnim;
|
||||
|
||||
public FloatVariableSyncer CrouchVariableSyncer;
|
||||
|
||||
public void Init(PlayerCharacterController playerController, Animator bodyAnim)
|
||||
public void Update()
|
||||
{
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
_playerController = playerController;
|
||||
_bodyAnim = bodyAnim;
|
||||
SyncLocalCrouch();
|
||||
return;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
SyncLocalCrouch();
|
||||
return;
|
||||
}
|
||||
SyncRemoteCrouch();
|
||||
}
|
||||
|
||||
SyncRemoteCrouch();
|
||||
private void SyncLocalCrouch()
|
||||
{
|
||||
if (_playerController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void SyncLocalCrouch()
|
||||
{
|
||||
if (_playerController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var jumpChargeFraction = _playerController.GetJumpCrouchFraction();
|
||||
CrouchVariableSyncer.Value = jumpChargeFraction;
|
||||
}
|
||||
|
||||
var jumpChargeFraction = _playerController.GetJumpCrouchFraction();
|
||||
CrouchVariableSyncer.Value = jumpChargeFraction;
|
||||
private void SyncRemoteCrouch()
|
||||
{
|
||||
if (_bodyAnim == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void SyncRemoteCrouch()
|
||||
{
|
||||
if (_bodyAnim == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CrouchParam.Target = CrouchVariableSyncer.Value;
|
||||
CrouchParam.Smooth(CrouchSmoothTime);
|
||||
var jumpChargeFraction = CrouchParam.Current;
|
||||
_bodyAnim.SetLayerWeight(CrouchLayerIndex, jumpChargeFraction);
|
||||
}
|
||||
CrouchParam.Target = CrouchVariableSyncer.Value;
|
||||
CrouchParam.Smooth(CrouchSmoothTime);
|
||||
var jumpChargeFraction = CrouchParam.Current;
|
||||
_bodyAnim.SetLayerWeight(CrouchLayerIndex, jumpChargeFraction);
|
||||
}
|
||||
}
|
@ -2,28 +2,27 @@
|
||||
using QSB.Player;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.Animation.Player.Messages
|
||||
namespace QSB.Animation.Player.Messages;
|
||||
|
||||
internal class AnimationTriggerMessage : QSBMessage<string>
|
||||
{
|
||||
internal class AnimationTriggerMessage : QSBMessage<string>
|
||||
public AnimationTriggerMessage(string name) => Data = name;
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
public AnimationTriggerMessage(string name) => Data = name;
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
var animationSync = QSBPlayerManager.GetPlayer(From).AnimationSync;
|
||||
if (animationSync == null)
|
||||
{
|
||||
var animationSync = QSBPlayerManager.GetPlayer(From).AnimationSync;
|
||||
if (animationSync == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (animationSync.VisibleAnimator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
animationSync.VisibleAnimator.SetTrigger(Data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (animationSync.VisibleAnimator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
animationSync.VisibleAnimator.SetTrigger(Data);
|
||||
}
|
||||
}
|
@ -3,47 +3,46 @@ using QSB.Player;
|
||||
using QSB.Player.TransformSync;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.Animation.Player.Messages
|
||||
namespace QSB.Animation.Player.Messages;
|
||||
|
||||
public class PlayerSuitMessage : QSBMessage<bool>
|
||||
{
|
||||
public class PlayerSuitMessage : QSBMessage<bool>
|
||||
static PlayerSuitMessage()
|
||||
{
|
||||
static PlayerSuitMessage()
|
||||
GlobalMessenger.AddListener(OWEvents.SuitUp, () => Handle(true));
|
||||
GlobalMessenger.AddListener(OWEvents.RemoveSuit, () => Handle(false));
|
||||
}
|
||||
|
||||
private static void Handle(bool on)
|
||||
{
|
||||
if (PlayerTransformSync.LocalInstance)
|
||||
{
|
||||
GlobalMessenger.AddListener(OWEvents.SuitUp, () => Handle(true));
|
||||
GlobalMessenger.AddListener(OWEvents.RemoveSuit, () => Handle(false));
|
||||
}
|
||||
|
||||
private static void Handle(bool on)
|
||||
{
|
||||
if (PlayerTransformSync.LocalInstance)
|
||||
{
|
||||
new PlayerSuitMessage(on).Send();
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerSuitMessage(bool on) => Data = on;
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
player.SuitedUp = Data;
|
||||
|
||||
if (!player.IsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var animator = player.AnimationSync;
|
||||
animator.SetSuitState(Data);
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
QSBPlayerManager.LocalPlayer.SuitedUp = Data;
|
||||
var animator = QSBPlayerManager.LocalPlayer.AnimationSync;
|
||||
animator.InSuitedUpState = Data;
|
||||
new PlayerSuitMessage(on).Send();
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerSuitMessage(bool on) => Data = on;
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
player.SuitedUp = Data;
|
||||
|
||||
if (!player.IsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var animator = player.AnimationSync;
|
||||
animator.SetSuitState(Data);
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
QSBPlayerManager.LocalPlayer.SuitedUp = Data;
|
||||
var animator = QSBPlayerManager.LocalPlayer.AnimationSync;
|
||||
animator.InSuitedUpState = Data;
|
||||
}
|
||||
}
|
@ -5,129 +5,128 @@ using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player.Patches
|
||||
namespace QSB.Animation.Player.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class PlayerAnimationPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
internal class PlayerAnimationPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerAnimController), nameof(PlayerAnimController.LateUpdate))]
|
||||
public static bool LateUpdateReplacement(
|
||||
PlayerAnimController __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerAnimController), nameof(PlayerAnimController.LateUpdate))]
|
||||
public static bool LateUpdateReplacement(
|
||||
PlayerAnimController __instance)
|
||||
var isGrounded = __instance._playerController.IsGrounded();
|
||||
var isAttached = PlayerState.IsAttached();
|
||||
var isInZeroG = PlayerState.InZeroG();
|
||||
var isFlying = __instance._playerJetpack.GetLocalAcceleration().y > 0f;
|
||||
var movementVector = Vector3.zero;
|
||||
if (!isAttached)
|
||||
{
|
||||
var isGrounded = __instance._playerController.IsGrounded();
|
||||
var isAttached = PlayerState.IsAttached();
|
||||
var isInZeroG = PlayerState.InZeroG();
|
||||
var isFlying = __instance._playerJetpack.GetLocalAcceleration().y > 0f;
|
||||
var movementVector = Vector3.zero;
|
||||
if (!isAttached)
|
||||
{
|
||||
movementVector = __instance._playerController.GetRelativeGroundVelocity();
|
||||
}
|
||||
|
||||
if (Mathf.Abs(movementVector.x) < 0.05f)
|
||||
{
|
||||
movementVector.x = 0f;
|
||||
}
|
||||
|
||||
if (Mathf.Abs(movementVector.z) < 0.05f)
|
||||
{
|
||||
movementVector.z = 0f;
|
||||
}
|
||||
|
||||
if (isFlying)
|
||||
{
|
||||
__instance._ungroundedTime = Time.time;
|
||||
}
|
||||
|
||||
var freefallMagnitude = 0f;
|
||||
var timeInFreefall = 0f;
|
||||
var lastGroundBody = __instance._playerController.GetLastGroundBody();
|
||||
if (!isGrounded && !isAttached && !isInZeroG && lastGroundBody != null)
|
||||
{
|
||||
freefallMagnitude = (__instance._playerController.GetAttachedOWRigidbody().GetVelocity() - lastGroundBody.GetPointVelocity(__instance._playerController.transform.position)).magnitude;
|
||||
timeInFreefall = Time.time - __instance._ungroundedTime;
|
||||
}
|
||||
|
||||
__instance._animator.SetFloat("RunSpeedX", movementVector.x / 3f);
|
||||
__instance._animator.SetFloat("RunSpeedY", movementVector.z / 3f);
|
||||
__instance._animator.SetFloat("TurnSpeed", __instance._playerController.GetTurning());
|
||||
__instance._animator.SetBool("Grounded", isGrounded || isAttached || PlayerState.IsRecentlyDetached());
|
||||
__instance._animator.SetLayerWeight(CrouchSync.CrouchLayerIndex, __instance._playerController.GetJumpCrouchFraction());
|
||||
__instance._animator.SetFloat("FreefallSpeed", freefallMagnitude / 15f * (timeInFreefall / 3f));
|
||||
__instance._animator.SetBool("InZeroG", isInZeroG || isFlying);
|
||||
__instance._animator.SetBool("UsingJetpack", isInZeroG && PlayerState.IsWearingSuit());
|
||||
if (__instance._justBecameGrounded)
|
||||
{
|
||||
if (__instance._justTookFallDamage)
|
||||
{
|
||||
__instance._animator.SetTrigger("LandHard");
|
||||
new AnimationTriggerMessage("LandHard").Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._animator.SetTrigger("Land");
|
||||
new AnimationTriggerMessage("Land").Send();
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrounded)
|
||||
{
|
||||
var leftFootLift = __instance._animator.GetFloat("LeftFootLift");
|
||||
if (!__instance._leftFootGrounded && leftFootLift < 0.333f)
|
||||
{
|
||||
__instance._leftFootGrounded = true;
|
||||
__instance.RaiseEvent(nameof(__instance.OnLeftFootGrounded));
|
||||
}
|
||||
else if (__instance._leftFootGrounded && leftFootLift > 0.666f)
|
||||
{
|
||||
__instance._leftFootGrounded = false;
|
||||
__instance.RaiseEvent(nameof(__instance.OnLeftFootLift));
|
||||
}
|
||||
|
||||
var rightFootLift = __instance._animator.GetFloat("RightFootLift");
|
||||
if (!__instance._rightFootGrounded && rightFootLift < 0.333f)
|
||||
{
|
||||
__instance._rightFootGrounded = true;
|
||||
__instance.RaiseEvent(nameof(__instance.OnRightFootGrounded));
|
||||
}
|
||||
else if (__instance._rightFootGrounded && rightFootLift > 0.666f)
|
||||
{
|
||||
__instance._rightFootGrounded = false;
|
||||
__instance.RaiseEvent(nameof(__instance.OnRightFootLift));
|
||||
}
|
||||
}
|
||||
|
||||
__instance._justBecameGrounded = false;
|
||||
__instance._justTookFallDamage = false;
|
||||
var usingTool = Locator.GetToolModeSwapper().GetToolMode() != ToolMode.None;
|
||||
if ((usingTool && !__instance._rightArmHidden) || (!usingTool && __instance._rightArmHidden))
|
||||
{
|
||||
__instance._rightArmHidden = usingTool;
|
||||
for (var i = 0; i < __instance._rightArmObjects.Length; i++)
|
||||
{
|
||||
__instance._rightArmObjects[i].layer = (!__instance._rightArmHidden) ? __instance._defaultLayer : __instance._probeOnlyLayer;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
movementVector = __instance._playerController.GetRelativeGroundVelocity();
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerAnimController), nameof(PlayerAnimController.OnPlayerJump))]
|
||||
public static bool OnPlayerJumpReplacement(PlayerAnimController __instance)
|
||||
if (Mathf.Abs(movementVector.x) < 0.05f)
|
||||
{
|
||||
movementVector.x = 0f;
|
||||
}
|
||||
|
||||
if (Mathf.Abs(movementVector.z) < 0.05f)
|
||||
{
|
||||
movementVector.z = 0f;
|
||||
}
|
||||
|
||||
if (isFlying)
|
||||
{
|
||||
__instance._ungroundedTime = Time.time;
|
||||
if (!__instance.isActiveAndEnabled)
|
||||
}
|
||||
|
||||
var freefallMagnitude = 0f;
|
||||
var timeInFreefall = 0f;
|
||||
var lastGroundBody = __instance._playerController.GetLastGroundBody();
|
||||
if (!isGrounded && !isAttached && !isInZeroG && lastGroundBody != null)
|
||||
{
|
||||
freefallMagnitude = (__instance._playerController.GetAttachedOWRigidbody().GetVelocity() - lastGroundBody.GetPointVelocity(__instance._playerController.transform.position)).magnitude;
|
||||
timeInFreefall = Time.time - __instance._ungroundedTime;
|
||||
}
|
||||
|
||||
__instance._animator.SetFloat("RunSpeedX", movementVector.x / 3f);
|
||||
__instance._animator.SetFloat("RunSpeedY", movementVector.z / 3f);
|
||||
__instance._animator.SetFloat("TurnSpeed", __instance._playerController.GetTurning());
|
||||
__instance._animator.SetBool("Grounded", isGrounded || isAttached || PlayerState.IsRecentlyDetached());
|
||||
__instance._animator.SetLayerWeight(CrouchSync.CrouchLayerIndex, __instance._playerController.GetJumpCrouchFraction());
|
||||
__instance._animator.SetFloat("FreefallSpeed", freefallMagnitude / 15f * (timeInFreefall / 3f));
|
||||
__instance._animator.SetBool("InZeroG", isInZeroG || isFlying);
|
||||
__instance._animator.SetBool("UsingJetpack", isInZeroG && PlayerState.IsWearingSuit());
|
||||
if (__instance._justBecameGrounded)
|
||||
{
|
||||
if (__instance._justTookFallDamage)
|
||||
{
|
||||
return false;
|
||||
__instance._animator.SetTrigger("LandHard");
|
||||
new AnimationTriggerMessage("LandHard").Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._animator.SetTrigger("Land");
|
||||
new AnimationTriggerMessage("Land").Send();
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrounded)
|
||||
{
|
||||
var leftFootLift = __instance._animator.GetFloat("LeftFootLift");
|
||||
if (!__instance._leftFootGrounded && leftFootLift < 0.333f)
|
||||
{
|
||||
__instance._leftFootGrounded = true;
|
||||
__instance.RaiseEvent(nameof(__instance.OnLeftFootGrounded));
|
||||
}
|
||||
else if (__instance._leftFootGrounded && leftFootLift > 0.666f)
|
||||
{
|
||||
__instance._leftFootGrounded = false;
|
||||
__instance.RaiseEvent(nameof(__instance.OnLeftFootLift));
|
||||
}
|
||||
|
||||
__instance._animator.SetTrigger("Jump");
|
||||
new AnimationTriggerMessage("Jump").Send();
|
||||
var rightFootLift = __instance._animator.GetFloat("RightFootLift");
|
||||
if (!__instance._rightFootGrounded && rightFootLift < 0.333f)
|
||||
{
|
||||
__instance._rightFootGrounded = true;
|
||||
__instance.RaiseEvent(nameof(__instance.OnRightFootGrounded));
|
||||
}
|
||||
else if (__instance._rightFootGrounded && rightFootLift > 0.666f)
|
||||
{
|
||||
__instance._rightFootGrounded = false;
|
||||
__instance.RaiseEvent(nameof(__instance.OnRightFootLift));
|
||||
}
|
||||
}
|
||||
|
||||
__instance._justBecameGrounded = false;
|
||||
__instance._justTookFallDamage = false;
|
||||
var usingTool = Locator.GetToolModeSwapper().GetToolMode() != ToolMode.None;
|
||||
if ((usingTool && !__instance._rightArmHidden) || (!usingTool && __instance._rightArmHidden))
|
||||
{
|
||||
__instance._rightArmHidden = usingTool;
|
||||
for (var i = 0; i < __instance._rightArmObjects.Length; i++)
|
||||
{
|
||||
__instance._rightArmObjects[i].layer = (!__instance._rightArmHidden) ? __instance._defaultLayer : __instance._probeOnlyLayer;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerAnimController), nameof(PlayerAnimController.OnPlayerJump))]
|
||||
public static bool OnPlayerJumpReplacement(PlayerAnimController __instance)
|
||||
{
|
||||
__instance._ungroundedTime = Time.time;
|
||||
if (!__instance.isActiveAndEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance._animator.SetTrigger("Jump");
|
||||
new AnimationTriggerMessage("Jump").Send();
|
||||
return false;
|
||||
}
|
||||
}
|
@ -2,44 +2,43 @@
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player
|
||||
namespace QSB.Animation.Player;
|
||||
|
||||
public class PlayerHeadRotationSync : MonoBehaviour
|
||||
{
|
||||
public class PlayerHeadRotationSync : MonoBehaviour
|
||||
private Animator _attachedAnimator;
|
||||
private Transform _lookBase;
|
||||
private bool _isSetUp;
|
||||
|
||||
public void Init(Transform lookBase)
|
||||
{
|
||||
private Animator _attachedAnimator;
|
||||
private Transform _lookBase;
|
||||
private bool _isSetUp;
|
||||
_attachedAnimator = GetComponent<Animator>();
|
||||
_lookBase = lookBase;
|
||||
_isSetUp = true;
|
||||
}
|
||||
|
||||
public void Init(Transform lookBase)
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!_isSetUp)
|
||||
{
|
||||
_attachedAnimator = GetComponent<Animator>();
|
||||
_lookBase = lookBase;
|
||||
_isSetUp = true;
|
||||
return;
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
if (_attachedAnimator == null)
|
||||
{
|
||||
if (!_isSetUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_attachedAnimator == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - _attachedAnimator is null!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lookBase == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - _lookBase is null!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var bone = _attachedAnimator.GetBoneTransform(HumanBodyBones.Head);
|
||||
// Get the camera's local rotation with respect to the player body
|
||||
var lookLocalRotation = Quaternion.Inverse(_attachedAnimator.transform.rotation) * _lookBase.rotation;
|
||||
bone.localRotation = Quaternion.Euler(0f, 0f, lookLocalRotation.eulerAngles.x);
|
||||
DebugLog.ToConsole($"Error - _attachedAnimator is null!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lookBase == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - _lookBase is null!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var bone = _attachedAnimator.GetBoneTransform(HumanBodyBones.Head);
|
||||
// Get the camera's local rotation with respect to the player body
|
||||
var lookLocalRotation = Quaternion.Inverse(_attachedAnimator.transform.rotation) * _lookBase.rotation;
|
||||
bone.localRotation = Quaternion.Euler(0f, 0f, lookLocalRotation.eulerAngles.x);
|
||||
}
|
||||
}
|
@ -1,32 +1,31 @@
|
||||
using Mirror;
|
||||
using QSB.Utility.VariableSync;
|
||||
|
||||
namespace QSB.Animation.Player.Thrusters
|
||||
namespace QSB.Animation.Player.Thrusters;
|
||||
|
||||
public class JetpackAccelerationSync : NetworkBehaviour
|
||||
{
|
||||
public class JetpackAccelerationSync : NetworkBehaviour
|
||||
public Vector3VariableSyncer AccelerationVariableSyncer;
|
||||
public BoolVariableSyncer ThrustingVariableSyncer;
|
||||
|
||||
private ThrusterModel _thrusterModel;
|
||||
|
||||
public void Init(ThrusterModel model) => _thrusterModel = model;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
public Vector3VariableSyncer AccelerationVariableSyncer;
|
||||
public BoolVariableSyncer ThrustingVariableSyncer;
|
||||
|
||||
private ThrusterModel _thrusterModel;
|
||||
|
||||
public void Init(ThrusterModel model) => _thrusterModel = model;
|
||||
|
||||
public void Update()
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
SyncLocalAccel();
|
||||
}
|
||||
SyncLocalAccel();
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncLocalAccel()
|
||||
private void SyncLocalAccel()
|
||||
{
|
||||
if (_thrusterModel != null)
|
||||
{
|
||||
if (_thrusterModel != null)
|
||||
{
|
||||
AccelerationVariableSyncer.Value = _thrusterModel.GetLocalAcceleration();
|
||||
ThrustingVariableSyncer.Value = _thrusterModel.IsTranslationalThrusterFiring();
|
||||
}
|
||||
AccelerationVariableSyncer.Value = _thrusterModel.GetLocalAcceleration();
|
||||
ThrustingVariableSyncer.Value = _thrusterModel.IsTranslationalThrusterFiring();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,92 +2,91 @@
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player.Thrusters
|
||||
namespace QSB.Animation.Player.Thrusters;
|
||||
|
||||
internal class RemoteThrusterFlameController : MonoBehaviour
|
||||
{
|
||||
internal class RemoteThrusterFlameController : MonoBehaviour
|
||||
[SerializeField]
|
||||
private Thruster _thruster;
|
||||
|
||||
[SerializeField]
|
||||
private Light _light;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _scaleByThrust = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private DampedSpring _scaleSpring = new();
|
||||
|
||||
[SerializeField]
|
||||
private float _belowMaxThrustScalar = 1f;
|
||||
|
||||
private MeshRenderer _thrusterRenderer;
|
||||
private Vector3 _thrusterFilter;
|
||||
private float _baseLightRadius;
|
||||
private float _currentScale;
|
||||
|
||||
private bool _initialized;
|
||||
private PlayerInfo _attachedPlayer;
|
||||
|
||||
// TODO : Make flames not appear underwater (Check original code!)
|
||||
|
||||
public void Init(PlayerInfo player)
|
||||
{
|
||||
[SerializeField]
|
||||
private Thruster _thruster;
|
||||
_attachedPlayer = player;
|
||||
|
||||
[SerializeField]
|
||||
private Light _light;
|
||||
_thrusterRenderer = GetComponent<MeshRenderer>();
|
||||
_thrusterFilter = OWUtilities.GetShipThrusterFilter(_thruster);
|
||||
_baseLightRadius = _light.range;
|
||||
_currentScale = 0f;
|
||||
_thrusterRenderer.enabled = false;
|
||||
_light.enabled = false;
|
||||
_light.shadows = LightShadows.Soft;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _scaleByThrust = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private DampedSpring _scaleSpring = new();
|
||||
|
||||
[SerializeField]
|
||||
private float _belowMaxThrustScalar = 1f;
|
||||
|
||||
private MeshRenderer _thrusterRenderer;
|
||||
private Vector3 _thrusterFilter;
|
||||
private float _baseLightRadius;
|
||||
private float _currentScale;
|
||||
|
||||
private bool _initialized;
|
||||
private PlayerInfo _attachedPlayer;
|
||||
|
||||
// TODO : Make flames not appear underwater (Check original code!)
|
||||
|
||||
public void Init(PlayerInfo player)
|
||||
private void Update()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
_attachedPlayer = player;
|
||||
return;
|
||||
}
|
||||
|
||||
_thrusterRenderer = GetComponent<MeshRenderer>();
|
||||
_thrusterFilter = OWUtilities.GetShipThrusterFilter(_thruster);
|
||||
_baseLightRadius = _light.range;
|
||||
var num = _scaleByThrust.Evaluate(GetThrustFraction());
|
||||
if (_belowMaxThrustScalar < 1f)
|
||||
{
|
||||
num *= _belowMaxThrustScalar;
|
||||
}
|
||||
|
||||
_currentScale = _scaleSpring.Update(_currentScale, num, Time.deltaTime);
|
||||
if (_currentScale < 0f)
|
||||
{
|
||||
_currentScale = 0f;
|
||||
_thrusterRenderer.enabled = false;
|
||||
_light.enabled = false;
|
||||
_light.shadows = LightShadows.Soft;
|
||||
_initialized = true;
|
||||
_scaleSpring.ResetVelocity();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
if (_currentScale <= 0.001f)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = _scaleByThrust.Evaluate(GetThrustFraction());
|
||||
if (_belowMaxThrustScalar < 1f)
|
||||
{
|
||||
num *= _belowMaxThrustScalar;
|
||||
}
|
||||
|
||||
_currentScale = _scaleSpring.Update(_currentScale, num, Time.deltaTime);
|
||||
if (_currentScale < 0f)
|
||||
{
|
||||
_currentScale = 0f;
|
||||
_scaleSpring.ResetVelocity();
|
||||
}
|
||||
|
||||
if (_currentScale <= 0.001f)
|
||||
{
|
||||
_currentScale = 0f;
|
||||
_scaleSpring.ResetVelocity();
|
||||
}
|
||||
|
||||
transform.localScale = Vector3.one * _currentScale;
|
||||
_light.range = _baseLightRadius * _currentScale;
|
||||
_thrusterRenderer.enabled = _currentScale > 0f;
|
||||
_light.enabled = _currentScale > 0f;
|
||||
_currentScale = 0f;
|
||||
_scaleSpring.ResetVelocity();
|
||||
}
|
||||
|
||||
private float GetThrustFraction() => Vector3.Dot(_attachedPlayer.JetpackAcceleration.AccelerationVariableSyncer.Value, _thrusterFilter);
|
||||
transform.localScale = Vector3.one * _currentScale;
|
||||
_light.range = _baseLightRadius * _currentScale;
|
||||
_thrusterRenderer.enabled = _currentScale > 0f;
|
||||
_light.enabled = _currentScale > 0f;
|
||||
}
|
||||
|
||||
private void OnRenderObject()
|
||||
private float GetThrustFraction() => Vector3.Dot(_attachedPlayer.JetpackAcceleration.AccelerationVariableSyncer.Value, _thrusterFilter);
|
||||
|
||||
private void OnRenderObject()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines || !QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines || !QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Popcron.Gizmos.Sphere(_light.transform.position, 0.05f, Color.yellow, 4);
|
||||
Popcron.Gizmos.Line(_light.transform.position, _light.transform.parent.position, Color.yellow);
|
||||
return;
|
||||
}
|
||||
|
||||
Popcron.Gizmos.Sphere(_light.transform.position, 0.05f, Color.yellow, 4);
|
||||
Popcron.Gizmos.Line(_light.transform.position, _light.transform.parent.position, Color.yellow);
|
||||
}
|
||||
}
|
@ -2,87 +2,86 @@
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player.Thrusters
|
||||
namespace QSB.Animation.Player.Thrusters;
|
||||
|
||||
internal class RemoteThrusterWashController : MonoBehaviour
|
||||
{
|
||||
internal class RemoteThrusterWashController : MonoBehaviour
|
||||
[SerializeField]
|
||||
private float _raycastDistance = 10f;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _emissionDistanceScale;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _emissionThrusterScale;
|
||||
|
||||
[SerializeField]
|
||||
private ParticleSystem _defaultParticleSystem;
|
||||
|
||||
private ParticleSystem.MainModule _defaultMainModule;
|
||||
private ParticleSystem.EmissionModule _defaultEmissionModule;
|
||||
private float _baseDefaultEmissionRate;
|
||||
|
||||
private PlayerInfo _attachedPlayer;
|
||||
|
||||
private bool _initialised;
|
||||
|
||||
public void Init(PlayerInfo player)
|
||||
{
|
||||
[SerializeField]
|
||||
private float _raycastDistance = 10f;
|
||||
_attachedPlayer = player;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _emissionDistanceScale;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _emissionThrusterScale;
|
||||
|
||||
[SerializeField]
|
||||
private ParticleSystem _defaultParticleSystem;
|
||||
|
||||
private ParticleSystem.MainModule _defaultMainModule;
|
||||
private ParticleSystem.EmissionModule _defaultEmissionModule;
|
||||
private float _baseDefaultEmissionRate;
|
||||
|
||||
private PlayerInfo _attachedPlayer;
|
||||
|
||||
private bool _initialised;
|
||||
|
||||
public void Init(PlayerInfo player)
|
||||
if (_defaultParticleSystem == null)
|
||||
{
|
||||
_attachedPlayer = player;
|
||||
|
||||
if (_defaultParticleSystem == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - DefaultParticleSystem is null!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_defaultMainModule = _defaultParticleSystem.main;
|
||||
_defaultEmissionModule = _defaultParticleSystem.emission;
|
||||
_baseDefaultEmissionRate = _defaultEmissionModule.rateOverTime.constant;
|
||||
|
||||
_initialised = true;
|
||||
DebugLog.ToConsole($"Error - DefaultParticleSystem is null!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
_defaultMainModule = _defaultParticleSystem.main;
|
||||
_defaultEmissionModule = _defaultParticleSystem.emission;
|
||||
_baseDefaultEmissionRate = _defaultEmissionModule.rateOverTime.constant;
|
||||
|
||||
_initialised = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_initialised)
|
||||
{
|
||||
if (!_initialised)
|
||||
return;
|
||||
}
|
||||
|
||||
RaycastHit hitInfo = default;
|
||||
var aboveSurface = false;
|
||||
var emissionThrusterScale = _emissionThrusterScale.Evaluate(_attachedPlayer.JetpackAcceleration.AccelerationVariableSyncer.Value.y);
|
||||
if (emissionThrusterScale > 0f)
|
||||
{
|
||||
aboveSurface = Physics.Raycast(transform.position, transform.forward, out hitInfo, _raycastDistance, OWLayerMask.physicalMask);
|
||||
}
|
||||
|
||||
emissionThrusterScale = (!aboveSurface) ? 0f : (emissionThrusterScale * _emissionDistanceScale.Evaluate(hitInfo.distance));
|
||||
|
||||
if (emissionThrusterScale > 0f)
|
||||
{
|
||||
var position = hitInfo.point + (hitInfo.normal * 0.25f);
|
||||
var rotation = Quaternion.LookRotation(hitInfo.normal);
|
||||
if (!_defaultParticleSystem.isPlaying)
|
||||
{
|
||||
return;
|
||||
_defaultParticleSystem.Play();
|
||||
}
|
||||
|
||||
RaycastHit hitInfo = default;
|
||||
var aboveSurface = false;
|
||||
var emissionThrusterScale = _emissionThrusterScale.Evaluate(_attachedPlayer.JetpackAcceleration.AccelerationVariableSyncer.Value.y);
|
||||
if (emissionThrusterScale > 0f)
|
||||
_defaultEmissionModule.rateOverTimeMultiplier = _baseDefaultEmissionRate * emissionThrusterScale;
|
||||
_defaultParticleSystem.transform.SetPositionAndRotation(position, rotation);
|
||||
if (_defaultMainModule.customSimulationSpace != hitInfo.transform)
|
||||
{
|
||||
aboveSurface = Physics.Raycast(transform.position, transform.forward, out hitInfo, _raycastDistance, OWLayerMask.physicalMask);
|
||||
_defaultMainModule.customSimulationSpace = hitInfo.transform;
|
||||
_defaultParticleSystem.Clear();
|
||||
}
|
||||
|
||||
emissionThrusterScale = (!aboveSurface) ? 0f : (emissionThrusterScale * _emissionDistanceScale.Evaluate(hitInfo.distance));
|
||||
|
||||
if (emissionThrusterScale > 0f)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_defaultParticleSystem.isPlaying)
|
||||
{
|
||||
var position = hitInfo.point + (hitInfo.normal * 0.25f);
|
||||
var rotation = Quaternion.LookRotation(hitInfo.normal);
|
||||
if (!_defaultParticleSystem.isPlaying)
|
||||
{
|
||||
_defaultParticleSystem.Play();
|
||||
}
|
||||
|
||||
_defaultEmissionModule.rateOverTimeMultiplier = _baseDefaultEmissionRate * emissionThrusterScale;
|
||||
_defaultParticleSystem.transform.SetPositionAndRotation(position, rotation);
|
||||
if (_defaultMainModule.customSimulationSpace != hitInfo.transform)
|
||||
{
|
||||
_defaultMainModule.customSimulationSpace = hitInfo.transform;
|
||||
_defaultParticleSystem.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_defaultParticleSystem.isPlaying)
|
||||
{
|
||||
_defaultParticleSystem.Stop(false, ParticleSystemStopBehavior.StopEmitting);
|
||||
}
|
||||
_defaultParticleSystem.Stop(false, ParticleSystemStopBehavior.StopEmitting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,30 @@
|
||||
using QSB.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Animation.Player.Thrusters
|
||||
namespace QSB.Animation.Player.Thrusters;
|
||||
|
||||
internal static class ThrusterManager
|
||||
{
|
||||
internal static class ThrusterManager
|
||||
public static void CreateRemotePlayerVFX(PlayerInfo player)
|
||||
{
|
||||
public static void CreateRemotePlayerVFX(PlayerInfo player)
|
||||
{
|
||||
var newVfx = player.Body.transform.Find("REMOTE_PlayerVFX").gameObject;
|
||||
var newVfx = player.Body.transform.Find("REMOTE_PlayerVFX").gameObject;
|
||||
|
||||
CreateThrusterWashController(newVfx.transform.Find("ThrusterWash").gameObject, player);
|
||||
CreateThrusterFlameController(newVfx, player);
|
||||
}
|
||||
CreateThrusterWashController(newVfx.transform.Find("ThrusterWash").gameObject, player);
|
||||
CreateThrusterFlameController(newVfx, player);
|
||||
}
|
||||
|
||||
private static void CreateThrusterFlameController(GameObject root, PlayerInfo player)
|
||||
private static void CreateThrusterFlameController(GameObject root, PlayerInfo player)
|
||||
{
|
||||
var existingControllers = root.GetComponentsInChildren<RemoteThrusterFlameController>(true);
|
||||
foreach (var controller in existingControllers)
|
||||
{
|
||||
var existingControllers = root.GetComponentsInChildren<RemoteThrusterFlameController>(true);
|
||||
foreach (var controller in existingControllers)
|
||||
{
|
||||
controller.Init(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateThrusterWashController(GameObject root, PlayerInfo player)
|
||||
{
|
||||
var newObj = root.GetComponent<RemoteThrusterWashController>();
|
||||
newObj.Init(player);
|
||||
controller.Init(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateThrusterWashController(GameObject root, PlayerInfo player)
|
||||
{
|
||||
var newObj = root.GetComponent<RemoteThrusterWashController>();
|
||||
newObj.Init(player);
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
namespace QSB.Audio
|
||||
namespace QSB.Audio;
|
||||
|
||||
internal class QSBJetpackThrusterAudio : QSBThrusterAudio
|
||||
{
|
||||
internal class QSBJetpackThrusterAudio : QSBThrusterAudio
|
||||
{
|
||||
public OWAudioSource _underwaterSource;
|
||||
public OWAudioSource _oxygenSource;
|
||||
public OWAudioSource _boostSource;
|
||||
}
|
||||
public OWAudioSource _underwaterSource;
|
||||
public OWAudioSource _oxygenSource;
|
||||
public OWAudioSource _boostSource;
|
||||
}
|
@ -1,22 +1,21 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Audio
|
||||
namespace QSB.Audio;
|
||||
|
||||
public class QSBPlayerAudioController : MonoBehaviour
|
||||
{
|
||||
public class QSBPlayerAudioController : MonoBehaviour
|
||||
{
|
||||
public OWAudioSource _oneShotExternalSource;
|
||||
public OWAudioSource _repairToolSource;
|
||||
public OWAudioSource _oneShotExternalSource;
|
||||
public OWAudioSource _repairToolSource;
|
||||
|
||||
public void PlayEquipTool()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorEquip);
|
||||
public void PlayEquipTool()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorEquip);
|
||||
|
||||
public void PlayUnequipTool()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorUnequip);
|
||||
public void PlayUnequipTool()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorUnequip);
|
||||
|
||||
public void PlayTurnOnFlashlight()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOn);
|
||||
public void PlayTurnOnFlashlight()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOn);
|
||||
|
||||
public void PlayTurnOffFlashlight()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOff);
|
||||
}
|
||||
public void PlayTurnOffFlashlight()
|
||||
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOff);
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Audio
|
||||
namespace QSB.Audio;
|
||||
|
||||
internal class QSBThrusterAudio : MonoBehaviour
|
||||
{
|
||||
internal class QSBThrusterAudio : MonoBehaviour
|
||||
{
|
||||
public OWAudioSource _translationalSource;
|
||||
public OWAudioSource _rotationalSource;
|
||||
}
|
||||
public OWAudioSource _translationalSource;
|
||||
public OWAudioSource _rotationalSource;
|
||||
}
|
@ -2,38 +2,37 @@
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.AuthoritySync
|
||||
namespace QSB.AuthoritySync;
|
||||
|
||||
/// <summary>
|
||||
/// always sent to host
|
||||
/// </summary>
|
||||
public class AuthQueueMessage : QSBMessage<(uint NetId, AuthQueueAction Action)>
|
||||
{
|
||||
public AuthQueueMessage(uint netId, AuthQueueAction action)
|
||||
{
|
||||
To = 0;
|
||||
Data.NetId = netId;
|
||||
Data.Action = action;
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
public override void OnReceiveRemote() => NetworkServer.spawned[Data.NetId].ServerUpdateAuthQueue(From, Data.Action);
|
||||
}
|
||||
|
||||
public enum AuthQueueAction
|
||||
{
|
||||
/// <summary>
|
||||
/// always sent to host
|
||||
/// add player to the queue
|
||||
/// </summary>
|
||||
public class AuthQueueMessage : QSBMessage<(uint NetId, AuthQueueAction Action)>
|
||||
{
|
||||
public AuthQueueMessage(uint netId, AuthQueueAction action)
|
||||
{
|
||||
To = 0;
|
||||
Data.NetId = netId;
|
||||
Data.Action = action;
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
public override void OnReceiveRemote() => NetworkServer.spawned[Data.NetId].ServerUpdateAuthQueue(From, Data.Action);
|
||||
}
|
||||
|
||||
public enum AuthQueueAction
|
||||
{
|
||||
/// <summary>
|
||||
/// add player to the queue
|
||||
/// </summary>
|
||||
Add,
|
||||
/// <summary>
|
||||
/// remove player from the queue
|
||||
/// </summary>
|
||||
Remove,
|
||||
/// <summary>
|
||||
/// add player to the queue and force them to the front
|
||||
/// </summary>
|
||||
Force
|
||||
}
|
||||
}
|
||||
Add,
|
||||
/// <summary>
|
||||
/// remove player from the queue
|
||||
/// </summary>
|
||||
Remove,
|
||||
/// <summary>
|
||||
/// add player to the queue and force them to the front
|
||||
/// </summary>
|
||||
Force
|
||||
}
|
@ -3,95 +3,94 @@ using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QSB.AuthoritySync
|
||||
namespace QSB.AuthoritySync;
|
||||
|
||||
public static class AuthorityManager
|
||||
{
|
||||
public static class AuthorityManager
|
||||
#region host only
|
||||
|
||||
/// <summary>
|
||||
/// whoever is first gets authority
|
||||
/// </summary>
|
||||
private static readonly Dictionary<NetworkIdentity, List<uint>> _authQueue = new();
|
||||
|
||||
public static void RegisterAuthQueue(this NetworkIdentity identity) => _authQueue.Add(identity, new List<uint>());
|
||||
public static void UnregisterAuthQueue(this NetworkIdentity identity) => _authQueue.Remove(identity);
|
||||
|
||||
public static void ServerUpdateAuthQueue(this NetworkIdentity identity, uint id, AuthQueueAction action)
|
||||
{
|
||||
#region host only
|
||||
var authQueue = _authQueue[identity];
|
||||
var oldOwner = authQueue.Count != 0 ? authQueue[0] : uint.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// whoever is first gets authority
|
||||
/// </summary>
|
||||
private static readonly Dictionary<NetworkIdentity, List<uint>> _authQueue = new();
|
||||
|
||||
public static void RegisterAuthQueue(this NetworkIdentity identity) => _authQueue.Add(identity, new List<uint>());
|
||||
public static void UnregisterAuthQueue(this NetworkIdentity identity) => _authQueue.Remove(identity);
|
||||
|
||||
public static void ServerUpdateAuthQueue(this NetworkIdentity identity, uint id, AuthQueueAction action)
|
||||
switch (action)
|
||||
{
|
||||
var authQueue = _authQueue[identity];
|
||||
var oldOwner = authQueue.Count != 0 ? authQueue[0] : uint.MaxValue;
|
||||
case AuthQueueAction.Add:
|
||||
authQueue.SafeAdd(id);
|
||||
break;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case AuthQueueAction.Add:
|
||||
authQueue.SafeAdd(id);
|
||||
break;
|
||||
case AuthQueueAction.Remove:
|
||||
authQueue.Remove(id);
|
||||
break;
|
||||
|
||||
case AuthQueueAction.Remove:
|
||||
authQueue.Remove(id);
|
||||
break;
|
||||
|
||||
case AuthQueueAction.Force:
|
||||
authQueue.Remove(id);
|
||||
authQueue.Insert(0, id);
|
||||
break;
|
||||
}
|
||||
|
||||
var newOwner = authQueue.Count != 0 ? authQueue[0] : uint.MaxValue;
|
||||
if (oldOwner != newOwner)
|
||||
{
|
||||
SetAuthority(identity, newOwner);
|
||||
}
|
||||
case AuthQueueAction.Force:
|
||||
authQueue.Remove(id);
|
||||
authQueue.Insert(0, id);
|
||||
break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// transfer authority to a different client
|
||||
/// </summary>
|
||||
public static void OnDisconnect(NetworkConnectionToClient conn)
|
||||
var newOwner = authQueue.Count != 0 ? authQueue[0] : uint.MaxValue;
|
||||
if (oldOwner != newOwner)
|
||||
{
|
||||
var id = conn.GetPlayerId();
|
||||
foreach (var identity in _authQueue.Keys)
|
||||
{
|
||||
identity.ServerUpdateAuthQueue(id, AuthQueueAction.Remove);
|
||||
}
|
||||
SetAuthority(identity, newOwner);
|
||||
}
|
||||
|
||||
public static void SetAuthority(this NetworkIdentity identity, uint id)
|
||||
{
|
||||
var oldConn = identity.connectionToClient;
|
||||
var newConn = id != uint.MaxValue ? id.GetNetworkConnection() : null;
|
||||
|
||||
if (oldConn == newConn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
identity.RemoveClientAuthority();
|
||||
|
||||
if (newConn != null)
|
||||
{
|
||||
identity.AssignClientAuthority(newConn);
|
||||
}
|
||||
|
||||
// DebugLog.DebugWrite($"{identity.NetId}:{identity.gameObject.name} - "
|
||||
// + $"set authority to {id}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region any client
|
||||
|
||||
public static void UpdateAuthQueue(this NetworkIdentity identity, AuthQueueAction action)
|
||||
{
|
||||
if (action == AuthQueueAction.Force && identity.hasAuthority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new AuthQueueMessage(identity.netId, action).Send();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// transfer authority to a different client
|
||||
/// </summary>
|
||||
public static void OnDisconnect(NetworkConnectionToClient conn)
|
||||
{
|
||||
var id = conn.GetPlayerId();
|
||||
foreach (var identity in _authQueue.Keys)
|
||||
{
|
||||
identity.ServerUpdateAuthQueue(id, AuthQueueAction.Remove);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetAuthority(this NetworkIdentity identity, uint id)
|
||||
{
|
||||
var oldConn = identity.connectionToClient;
|
||||
var newConn = id != uint.MaxValue ? id.GetNetworkConnection() : null;
|
||||
|
||||
if (oldConn == newConn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
identity.RemoveClientAuthority();
|
||||
|
||||
if (newConn != null)
|
||||
{
|
||||
identity.AssignClientAuthority(newConn);
|
||||
}
|
||||
|
||||
// DebugLog.DebugWrite($"{identity.NetId}:{identity.gameObject.name} - "
|
||||
// + $"set authority to {id}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region any client
|
||||
|
||||
public static void UpdateAuthQueue(this NetworkIdentity identity, AuthQueueAction action)
|
||||
{
|
||||
if (action == AuthQueueAction.Force && identity.hasAuthority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new AuthQueueMessage(identity.netId, action).Send();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -3,13 +3,12 @@ using QSB.CampfireSync.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.CampfireSync
|
||||
{
|
||||
internal class CampfireManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
namespace QSB.CampfireSync;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBCampfire, Campfire>();
|
||||
}
|
||||
internal class CampfireManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBCampfire, Campfire>();
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using QSB.CampfireSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.CampfireSync.Messages
|
||||
{
|
||||
internal class CampfireStateMessage : QSBWorldObjectMessage<QSBCampfire, Campfire.State>
|
||||
{
|
||||
public CampfireStateMessage(Campfire.State state) => Data = state;
|
||||
namespace QSB.CampfireSync.Messages;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.SetState(Data);
|
||||
}
|
||||
internal class CampfireStateMessage : QSBWorldObjectMessage<QSBCampfire, Campfire.State>
|
||||
{
|
||||
public CampfireStateMessage(Campfire.State state) => Data = state;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.SetState(Data);
|
||||
}
|
@ -6,80 +6,79 @@ using QSB.Patches;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.CampfireSync.Patches
|
||||
namespace QSB.CampfireSync.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class CampfirePatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
internal class CampfirePatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Campfire), nameof(Campfire.OnPressInteract))]
|
||||
public static bool LightCampfireEvent(Campfire __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Campfire), nameof(Campfire.OnPressInteract))]
|
||||
public static bool LightCampfireEvent(Campfire __instance)
|
||||
var qsbCampfire = __instance.GetWorldObject<QSBCampfire>();
|
||||
if (__instance._state == Campfire.State.LIT)
|
||||
{
|
||||
var qsbCampfire = __instance.GetWorldObject<QSBCampfire>();
|
||||
if (__instance._state == Campfire.State.LIT)
|
||||
{
|
||||
qsbCampfire.StartRoasting();
|
||||
}
|
||||
else
|
||||
{
|
||||
qsbCampfire.SetState(Campfire.State.LIT);
|
||||
qsbCampfire.SendMessage(new CampfireStateMessage(Campfire.State.LIT));
|
||||
Locator.GetFlashlight().TurnOff(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
qsbCampfire.StartRoasting();
|
||||
}
|
||||
else
|
||||
{
|
||||
qsbCampfire.SetState(Campfire.State.LIT);
|
||||
qsbCampfire.SendMessage(new CampfireStateMessage(Campfire.State.LIT));
|
||||
Locator.GetFlashlight().TurnOff(false);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Campfire), nameof(Campfire.Update))]
|
||||
public static bool UpdateReplacement(Campfire __instance)
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Campfire), nameof(Campfire.Update))]
|
||||
public static bool UpdateReplacement(Campfire __instance)
|
||||
{
|
||||
var targetLitFraction = 0f;
|
||||
switch (__instance._state)
|
||||
{
|
||||
var targetLitFraction = 0f;
|
||||
switch (__instance._state)
|
||||
{
|
||||
case Campfire.State.UNLIT:
|
||||
targetLitFraction = 0f;
|
||||
break;
|
||||
case Campfire.State.LIT:
|
||||
targetLitFraction = 1f;
|
||||
break;
|
||||
case Campfire.State.SMOLDERING:
|
||||
targetLitFraction = 0.4f;
|
||||
break;
|
||||
}
|
||||
|
||||
if (__instance._litFraction != targetLitFraction)
|
||||
{
|
||||
__instance.SetLitFraction(Mathf.MoveTowards(__instance._litFraction, targetLitFraction, Time.deltaTime));
|
||||
}
|
||||
|
||||
if (__instance._canSleepHere)
|
||||
{
|
||||
__instance._sleepPrompt.SetVisibility(false);
|
||||
if (__instance._interactVolumeFocus && !__instance._isPlayerSleeping && !__instance._isPlayerRoasting && OWInput.IsInputMode(InputMode.Character))
|
||||
{
|
||||
__instance._sleepPrompt.SetVisibility(true);
|
||||
__instance._sleepPrompt.SetDisplayState(__instance.CanSleepHereNow() ? ScreenPrompt.DisplayState.Normal : ScreenPrompt.DisplayState.GrayedOut);
|
||||
if (OWInput.IsNewlyPressed(InputLibrary.interactSecondary, InputMode.All) && __instance.CanSleepHereNow())
|
||||
{
|
||||
__instance.StartSleeping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__instance._isPlayerSleeping)
|
||||
{
|
||||
__instance._wakePrompt.SetVisibility(OWInput.IsInputMode(InputMode.None) && Time.timeSinceLevelLoad - __instance._fastForwardStartTime > __instance.GetWakePromptDelay());
|
||||
if (__instance.ShouldWakeUp())
|
||||
{
|
||||
__instance.StopSleeping(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
case Campfire.State.UNLIT:
|
||||
targetLitFraction = 0f;
|
||||
break;
|
||||
case Campfire.State.LIT:
|
||||
targetLitFraction = 1f;
|
||||
break;
|
||||
case Campfire.State.SMOLDERING:
|
||||
targetLitFraction = 0.4f;
|
||||
break;
|
||||
}
|
||||
|
||||
if (__instance._litFraction != targetLitFraction)
|
||||
{
|
||||
__instance.SetLitFraction(Mathf.MoveTowards(__instance._litFraction, targetLitFraction, Time.deltaTime));
|
||||
}
|
||||
|
||||
if (__instance._canSleepHere)
|
||||
{
|
||||
__instance._sleepPrompt.SetVisibility(false);
|
||||
if (__instance._interactVolumeFocus && !__instance._isPlayerSleeping && !__instance._isPlayerRoasting && OWInput.IsInputMode(InputMode.Character))
|
||||
{
|
||||
__instance._sleepPrompt.SetVisibility(true);
|
||||
__instance._sleepPrompt.SetDisplayState(__instance.CanSleepHereNow() ? ScreenPrompt.DisplayState.Normal : ScreenPrompt.DisplayState.GrayedOut);
|
||||
if (OWInput.IsNewlyPressed(InputLibrary.interactSecondary, InputMode.All) && __instance.CanSleepHereNow())
|
||||
{
|
||||
__instance.StartSleeping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__instance._isPlayerSleeping)
|
||||
{
|
||||
__instance._wakePrompt.SetVisibility(OWInput.IsInputMode(InputMode.None) && Time.timeSinceLevelLoad - __instance._fastForwardStartTime > __instance.GetWakePromptDelay());
|
||||
if (__instance.ShouldWakeUp())
|
||||
{
|
||||
__instance.StopSleeping(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -2,20 +2,19 @@
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.CampfireSync.WorldObjects
|
||||
namespace QSB.CampfireSync.WorldObjects;
|
||||
|
||||
public class QSBCampfire : WorldObject<Campfire>
|
||||
{
|
||||
public class QSBCampfire : WorldObject<Campfire>
|
||||
{
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new CampfireStateMessage(GetState()) { To = to });
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new CampfireStateMessage(GetState()) { To = to });
|
||||
|
||||
public void StartRoasting()
|
||||
=> AttachedObject.StartRoasting();
|
||||
public void StartRoasting()
|
||||
=> AttachedObject.StartRoasting();
|
||||
|
||||
public Campfire.State GetState()
|
||||
=> AttachedObject.GetState();
|
||||
public Campfire.State GetState()
|
||||
=> AttachedObject.GetState();
|
||||
|
||||
public void SetState(Campfire.State newState)
|
||||
=> AttachedObject.SetState(newState);
|
||||
}
|
||||
public void SetState(Campfire.State newState)
|
||||
=> AttachedObject.SetState(newState);
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
namespace QSB.ClientServerStateSync
|
||||
namespace QSB.ClientServerStateSync;
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
public enum ClientState
|
||||
{
|
||||
NotLoaded,
|
||||
InTitleScreen,
|
||||
AliveInSolarSystem,
|
||||
DeadInSolarSystem,
|
||||
AliveInEye,
|
||||
WaitingForOthersToBeReady,
|
||||
WatchingLongCredits,
|
||||
WatchingShortCredits
|
||||
}
|
||||
NotLoaded,
|
||||
InTitleScreen,
|
||||
AliveInSolarSystem,
|
||||
DeadInSolarSystem,
|
||||
AliveInEye,
|
||||
WaitingForOthersToBeReady,
|
||||
WatchingLongCredits,
|
||||
WatchingShortCredits
|
||||
}
|
@ -5,182 +5,181 @@ using QSB.Player.TransformSync;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ClientServerStateSync
|
||||
namespace QSB.ClientServerStateSync;
|
||||
|
||||
internal class ClientStateManager : MonoBehaviour
|
||||
{
|
||||
internal class ClientStateManager : MonoBehaviour
|
||||
public static ClientStateManager Instance { get; private set; }
|
||||
|
||||
public event ChangeStateEvent OnChangeState;
|
||||
public delegate void ChangeStateEvent(ClientState newState);
|
||||
|
||||
private void Awake()
|
||||
=> Instance = this;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
public static ClientStateManager Instance { get; private set; }
|
||||
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
|
||||
Delay.RunWhen(() => PlayerTransformSync.LocalInstance != null,
|
||||
() => new ClientStateMessage(ForceGetCurrentState()).Send());
|
||||
}
|
||||
|
||||
public event ChangeStateEvent OnChangeState;
|
||||
public delegate void ChangeStateEvent(ClientState newState);
|
||||
private void OnDestroy() =>
|
||||
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
|
||||
|
||||
private void Awake()
|
||||
=> Instance = this;
|
||||
|
||||
private void Start()
|
||||
public void ChangeClientState(ClientState newState)
|
||||
{
|
||||
if (PlayerTransformSync.LocalInstance == null || QSBPlayerManager.LocalPlayer.State == newState)
|
||||
{
|
||||
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
|
||||
Delay.RunWhen(() => PlayerTransformSync.LocalInstance != null,
|
||||
() => new ClientStateMessage(ForceGetCurrentState()).Send());
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnDestroy() =>
|
||||
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
|
||||
QSBPlayerManager.LocalPlayer.State = newState;
|
||||
OnChangeState?.Invoke(newState);
|
||||
}
|
||||
|
||||
public void ChangeClientState(ClientState newState)
|
||||
private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse)
|
||||
{
|
||||
var serverState = ServerStateManager.Instance.GetServerState();
|
||||
|
||||
ClientState newState;
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
if (PlayerTransformSync.LocalInstance == null || QSBPlayerManager.LocalPlayer.State == newState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.State = newState;
|
||||
OnChangeState?.Invoke(newState);
|
||||
switch (newScene)
|
||||
{
|
||||
case OWScene.TitleScreen:
|
||||
newState = ClientState.InTitleScreen;
|
||||
break;
|
||||
case OWScene.Credits_Fast:
|
||||
newState = ClientState.WatchingShortCredits;
|
||||
break;
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
newState = ClientState.WatchingLongCredits;
|
||||
break;
|
||||
case OWScene.SolarSystem:
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
// reloading scene
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loading in from title screen
|
||||
newState = ClientState.AliveInSolarSystem;
|
||||
}
|
||||
|
||||
break;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
newState = ClientState.AliveInEye;
|
||||
break;
|
||||
default:
|
||||
newState = ClientState.NotLoaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse)
|
||||
else
|
||||
{
|
||||
var serverState = ServerStateManager.Instance.GetServerState();
|
||||
|
||||
ClientState newState;
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
switch (newScene)
|
||||
{
|
||||
case OWScene.TitleScreen:
|
||||
newState = ClientState.InTitleScreen;
|
||||
break;
|
||||
case OWScene.Credits_Fast:
|
||||
newState = ClientState.WatchingShortCredits;
|
||||
break;
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
newState = ClientState.WatchingLongCredits;
|
||||
break;
|
||||
case OWScene.SolarSystem:
|
||||
if (serverState == ServerState.WaitingForAllPlayersToDie)
|
||||
{
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (newScene)
|
||||
{
|
||||
case OWScene.TitleScreen:
|
||||
newState = ClientState.InTitleScreen;
|
||||
break;
|
||||
case OWScene.Credits_Fast:
|
||||
newState = ClientState.WatchingShortCredits;
|
||||
break;
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
newState = ClientState.WatchingLongCredits;
|
||||
break;
|
||||
case OWScene.SolarSystem:
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
// reloading scene
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loading in from title screen
|
||||
newState = ClientState.AliveInSolarSystem;
|
||||
}
|
||||
|
||||
break;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
newState = ClientState.AliveInEye;
|
||||
break;
|
||||
default:
|
||||
newState = ClientState.NotLoaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (newScene)
|
||||
{
|
||||
case OWScene.TitleScreen:
|
||||
newState = ClientState.InTitleScreen;
|
||||
break;
|
||||
case OWScene.Credits_Fast:
|
||||
newState = ClientState.WatchingShortCredits;
|
||||
break;
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
newState = ClientState.WatchingLongCredits;
|
||||
break;
|
||||
case OWScene.SolarSystem:
|
||||
if (serverState == ServerState.WaitingForAllPlayersToDie)
|
||||
{
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
// reloading scene
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loading in from title screen
|
||||
if (serverState == ServerState.WaitingForAllPlayersToReady)
|
||||
{
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
newState = ClientState.AliveInSolarSystem;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
// reloading scene
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
// loading in from title screen
|
||||
if (serverState == ServerState.WaitingForAllPlayersToReady)
|
||||
{
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
newState = ClientState.AliveInEye;
|
||||
newState = ClientState.AliveInSolarSystem;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
newState = ClientState.NotLoaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
if (serverState == ServerState.WaitingForAllPlayersToReady)
|
||||
{
|
||||
newState = ClientState.WaitingForOthersToBeReady;
|
||||
}
|
||||
else
|
||||
{
|
||||
newState = ClientState.AliveInEye;
|
||||
}
|
||||
|
||||
new ClientStateMessage(newState).Send();
|
||||
}
|
||||
|
||||
public void OnDeath()
|
||||
{
|
||||
var currentScene = QSBSceneManager.CurrentScene;
|
||||
if (currentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ClientStateMessage(ClientState.DeadInSolarSystem).Send();
|
||||
}
|
||||
else if (currentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - You died in the Eye? HOW DID YOU DO THAT?!", OWML.Common.MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// whaaaaaaaaa
|
||||
DebugLog.ToConsole($"Error - You died... in a menu? In the credits? In any case, you should never see this. :P", OWML.Common.MessageType.Error);
|
||||
break;
|
||||
default:
|
||||
newState = ClientState.NotLoaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRespawn()
|
||||
{
|
||||
var currentScene = QSBSceneManager.CurrentScene;
|
||||
if (currentScene == OWScene.SolarSystem)
|
||||
{
|
||||
DebugLog.DebugWrite($"RESPAWN!");
|
||||
new ClientStateMessage(ClientState.AliveInSolarSystem).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Player tried to respawn in scene {currentScene}", OWML.Common.MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private static ClientState ForceGetCurrentState()
|
||||
=> QSBSceneManager.CurrentScene switch
|
||||
{
|
||||
OWScene.TitleScreen => ClientState.InTitleScreen,
|
||||
OWScene.Credits_Fast => ClientState.WatchingShortCredits,
|
||||
OWScene.Credits_Final or OWScene.PostCreditsScene => ClientState.WatchingLongCredits,
|
||||
OWScene.SolarSystem => ClientState.AliveInSolarSystem,
|
||||
OWScene.EyeOfTheUniverse => ClientState.AliveInEye,
|
||||
_ => ClientState.NotLoaded
|
||||
};
|
||||
new ClientStateMessage(newState).Send();
|
||||
}
|
||||
|
||||
public void OnDeath()
|
||||
{
|
||||
var currentScene = QSBSceneManager.CurrentScene;
|
||||
if (currentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ClientStateMessage(ClientState.DeadInSolarSystem).Send();
|
||||
}
|
||||
else if (currentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - You died in the Eye? HOW DID YOU DO THAT?!", OWML.Common.MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// whaaaaaaaaa
|
||||
DebugLog.ToConsole($"Error - You died... in a menu? In the credits? In any case, you should never see this. :P", OWML.Common.MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRespawn()
|
||||
{
|
||||
var currentScene = QSBSceneManager.CurrentScene;
|
||||
if (currentScene == OWScene.SolarSystem)
|
||||
{
|
||||
DebugLog.DebugWrite($"RESPAWN!");
|
||||
new ClientStateMessage(ClientState.AliveInSolarSystem).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Player tried to respawn in scene {currentScene}", OWML.Common.MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private static ClientState ForceGetCurrentState()
|
||||
=> QSBSceneManager.CurrentScene switch
|
||||
{
|
||||
OWScene.TitleScreen => ClientState.InTitleScreen,
|
||||
OWScene.Credits_Fast => ClientState.WatchingShortCredits,
|
||||
OWScene.Credits_Final or OWScene.PostCreditsScene => ClientState.WatchingLongCredits,
|
||||
OWScene.SolarSystem => ClientState.AliveInSolarSystem,
|
||||
OWScene.EyeOfTheUniverse => ClientState.AliveInEye,
|
||||
_ => ClientState.NotLoaded
|
||||
};
|
||||
}
|
@ -3,28 +3,27 @@ using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.ClientServerStateSync.Messages
|
||||
namespace QSB.ClientServerStateSync.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// sets the state both locally and remotely
|
||||
/// </summary>
|
||||
internal class ClientStateMessage : QSBMessage<ClientState>
|
||||
{
|
||||
/// <summary>
|
||||
/// sets the state both locally and remotely
|
||||
/// </summary>
|
||||
internal class ClientStateMessage : QSBMessage<ClientState>
|
||||
public ClientStateMessage(ClientState state) => Data = state;
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
=> ClientStateManager.Instance.ChangeClientState(Data);
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
public ClientStateMessage(ClientState state) => Data = state;
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
=> ClientStateManager.Instance.ChangeClientState(Data);
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
if (From == uint.MaxValue)
|
||||
{
|
||||
if (From == uint.MaxValue)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - ID is uint.MaxValue!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
player.State = Data;
|
||||
DebugLog.ToConsole($"Error - ID is uint.MaxValue!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
player.State = Data;
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.ClientServerStateSync.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// sets the state both locally and remotely
|
||||
/// </summary>
|
||||
internal class ServerStateMessage : QSBMessage<ServerState>
|
||||
{
|
||||
public ServerStateMessage(ServerState state) => Data = state;
|
||||
namespace QSB.ClientServerStateSync.Messages;
|
||||
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
public override void OnReceiveRemote()
|
||||
=> ServerStateManager.Instance.ChangeServerState(Data);
|
||||
}
|
||||
/// <summary>
|
||||
/// sets the state both locally and remotely
|
||||
/// </summary>
|
||||
internal class ServerStateMessage : QSBMessage<ServerState>
|
||||
{
|
||||
public ServerStateMessage(ServerState state) => Data = state;
|
||||
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
public override void OnReceiveRemote()
|
||||
=> ServerStateManager.Instance.ChangeServerState(Data);
|
||||
}
|
@ -1,26 +1,25 @@
|
||||
namespace QSB.ClientServerStateSync
|
||||
namespace QSB.ClientServerStateSync;
|
||||
|
||||
public enum ServerState
|
||||
{
|
||||
public enum ServerState
|
||||
{
|
||||
// When in menus
|
||||
NotLoaded,
|
||||
// When in menus
|
||||
NotLoaded,
|
||||
|
||||
// When in any credits
|
||||
Credits,
|
||||
// When in any credits
|
||||
Credits,
|
||||
|
||||
// For normal play in SolarSystem
|
||||
InSolarSystem,
|
||||
// For normal play in SolarSystem
|
||||
InSolarSystem,
|
||||
|
||||
// For normal play in EyeOfTheUniverse
|
||||
InEye,
|
||||
// For normal play in EyeOfTheUniverse
|
||||
InEye,
|
||||
|
||||
// At end of loop, waiting for everyone to be ready to reload the scene
|
||||
WaitingForAllPlayersToDie,
|
||||
// At end of loop, waiting for everyone to be ready to reload the scene
|
||||
WaitingForAllPlayersToDie,
|
||||
|
||||
// At start of loop, waiting for everybody to be ready to start playing
|
||||
WaitingForAllPlayersToReady,
|
||||
// At start of loop, waiting for everybody to be ready to start playing
|
||||
WaitingForAllPlayersToReady,
|
||||
|
||||
// When the statue has been activated
|
||||
InStatueCutscene
|
||||
}
|
||||
// When the statue has been activated
|
||||
InStatueCutscene
|
||||
}
|
@ -7,155 +7,154 @@ using QSB.Utility;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ClientServerStateSync
|
||||
namespace QSB.ClientServerStateSync;
|
||||
|
||||
internal class ServerStateManager : MonoBehaviour
|
||||
{
|
||||
internal class ServerStateManager : MonoBehaviour
|
||||
public static ServerStateManager Instance { get; private set; }
|
||||
|
||||
public event ChangeStateEvent OnChangeState;
|
||||
public delegate void ChangeStateEvent(ServerState newState);
|
||||
|
||||
private ServerState _currentState;
|
||||
private bool _blockNextCheck;
|
||||
|
||||
private void Awake()
|
||||
=> Instance = this;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
public static ServerStateManager Instance { get; private set; }
|
||||
|
||||
public event ChangeStateEvent OnChangeState;
|
||||
public delegate void ChangeStateEvent(ServerState newState);
|
||||
|
||||
private ServerState _currentState;
|
||||
private bool _blockNextCheck;
|
||||
|
||||
private void Awake()
|
||||
=> Instance = this;
|
||||
|
||||
private void Start()
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
|
||||
GlobalMessenger.AddListener("TriggerSupernova", OnTriggerSupernova);
|
||||
|
||||
Delay.RunWhen(() => PlayerTransformSync.LocalInstance != null,
|
||||
() => new ServerStateMessage(ForceGetCurrentState()).Send());
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
|
||||
GlobalMessenger.AddListener("TriggerSupernova", OnTriggerSupernova);
|
||||
|
||||
Delay.RunWhen(() => PlayerTransformSync.LocalInstance != null,
|
||||
() => new ServerStateMessage(ForceGetCurrentState()).Send());
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
|
||||
GlobalMessenger.RemoveListener("TriggerSupernova", OnTriggerSupernova);
|
||||
}
|
||||
|
||||
public void ChangeServerState(ServerState newState)
|
||||
{
|
||||
if (_currentState == newState)
|
||||
{
|
||||
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
|
||||
GlobalMessenger.RemoveListener("TriggerSupernova", OnTriggerSupernova);
|
||||
return;
|
||||
}
|
||||
|
||||
public void ChangeServerState(ServerState newState)
|
||||
_currentState = newState;
|
||||
OnChangeState?.Invoke(newState);
|
||||
}
|
||||
|
||||
public ServerState GetServerState()
|
||||
=> _currentState;
|
||||
|
||||
private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse)
|
||||
{
|
||||
switch (newScene)
|
||||
{
|
||||
if (_currentState == newState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
case OWScene.Credits_Fast:
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
new ServerStateMessage(ServerState.Credits).Send();
|
||||
break;
|
||||
|
||||
_currentState = newState;
|
||||
OnChangeState?.Invoke(newState);
|
||||
}
|
||||
case OWScene.TitleScreen:
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
break;
|
||||
|
||||
public ServerState GetServerState()
|
||||
=> _currentState;
|
||||
|
||||
private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse)
|
||||
{
|
||||
switch (newScene)
|
||||
{
|
||||
case OWScene.Credits_Fast:
|
||||
case OWScene.Credits_Final:
|
||||
case OWScene.PostCreditsScene:
|
||||
new ServerStateMessage(ServerState.Credits).Send();
|
||||
break;
|
||||
|
||||
case OWScene.TitleScreen:
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
break;
|
||||
|
||||
case OWScene.SolarSystem:
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToReady).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
new ServerStateMessage(ServerState.InSolarSystem).Send();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToReady).Send();
|
||||
break;
|
||||
|
||||
case OWScene.None:
|
||||
case OWScene.Undefined:
|
||||
default:
|
||||
DebugLog.ToConsole($"Warning - newScene is {newScene}!", OWML.Common.MessageType.Warning);
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerSupernova()
|
||||
{
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToDie).Send();
|
||||
}
|
||||
}
|
||||
|
||||
private ServerState ForceGetCurrentState()
|
||||
{
|
||||
var currentScene = LoadManager.GetCurrentScene();
|
||||
|
||||
switch (currentScene)
|
||||
{
|
||||
case OWScene.SolarSystem:
|
||||
return ServerState.InSolarSystem;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
return ServerState.InEye;
|
||||
default:
|
||||
return ServerState.NotLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_blockNextCheck)
|
||||
{
|
||||
_blockNextCheck = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentState == ServerState.WaitingForAllPlayersToReady)
|
||||
{
|
||||
if (QSBPlayerManager.PlayerList.All(x
|
||||
=> x.State is ClientState.WaitingForOthersToBeReady
|
||||
or ClientState.AliveInSolarSystem
|
||||
or ClientState.AliveInEye))
|
||||
case OWScene.SolarSystem:
|
||||
if (oldScene == OWScene.SolarSystem)
|
||||
{
|
||||
DebugLog.DebugWrite($"All ready!!");
|
||||
new StartLoopMessage().Send();
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ServerStateMessage(ServerState.InSolarSystem).Send();
|
||||
}
|
||||
else if (QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
new ServerStateMessage(ServerState.InEye).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - All players were ready in non-universe scene!?", OWML.Common.MessageType.Error);
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
}
|
||||
|
||||
_blockNextCheck = true;
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToReady).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
new ServerStateMessage(ServerState.InSolarSystem).Send();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToReady).Send();
|
||||
break;
|
||||
|
||||
case OWScene.None:
|
||||
case OWScene.Undefined:
|
||||
default:
|
||||
DebugLog.ToConsole($"Warning - newScene is {newScene}!", OWML.Common.MessageType.Warning);
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerSupernova()
|
||||
{
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToDie).Send();
|
||||
}
|
||||
}
|
||||
|
||||
private ServerState ForceGetCurrentState()
|
||||
{
|
||||
var currentScene = LoadManager.GetCurrentScene();
|
||||
|
||||
switch (currentScene)
|
||||
{
|
||||
case OWScene.SolarSystem:
|
||||
return ServerState.InSolarSystem;
|
||||
case OWScene.EyeOfTheUniverse:
|
||||
return ServerState.InEye;
|
||||
default:
|
||||
return ServerState.NotLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_blockNextCheck)
|
||||
{
|
||||
_blockNextCheck = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentState == ServerState.WaitingForAllPlayersToReady)
|
||||
{
|
||||
if (QSBPlayerManager.PlayerList.All(x
|
||||
=> x.State is ClientState.WaitingForOthersToBeReady
|
||||
or ClientState.AliveInSolarSystem
|
||||
or ClientState.AliveInEye))
|
||||
{
|
||||
DebugLog.DebugWrite($"All ready!!");
|
||||
new StartLoopMessage().Send();
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ServerStateMessage(ServerState.InSolarSystem).Send();
|
||||
}
|
||||
else if (QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
new ServerStateMessage(ServerState.InEye).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - All players were ready in non-universe scene!?", OWML.Common.MessageType.Error);
|
||||
new ServerStateMessage(ServerState.NotLoaded).Send();
|
||||
}
|
||||
|
||||
_blockNextCheck = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ConversationSync
|
||||
namespace QSB.ConversationSync;
|
||||
|
||||
public class CameraFacingBillboard : MonoBehaviour
|
||||
{
|
||||
public class CameraFacingBillboard : MonoBehaviour
|
||||
private OWCamera _activeCam;
|
||||
|
||||
private void Awake()
|
||||
=> GlobalMessenger<OWCamera>.AddListener("SwitchActiveCamera", OnSwitchActiveCamera);
|
||||
|
||||
private void Start()
|
||||
{
|
||||
private OWCamera _activeCam;
|
||||
|
||||
private void Awake()
|
||||
=> GlobalMessenger<OWCamera>.AddListener("SwitchActiveCamera", OnSwitchActiveCamera);
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_activeCam = Locator.GetActiveCamera();
|
||||
UpdateRotation();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
=> GlobalMessenger<OWCamera>.RemoveListener("SwitchActiveCamera", OnSwitchActiveCamera);
|
||||
|
||||
private void OnSwitchActiveCamera(OWCamera activeCamera)
|
||||
{
|
||||
_activeCam = activeCamera;
|
||||
UpdateRotation();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
=> UpdateRotation();
|
||||
|
||||
private void UpdateRotation()
|
||||
=> transform.LookAt(transform.position + (_activeCam.transform.rotation * Vector3.forward), _activeCam.transform.rotation * Vector3.up);
|
||||
_activeCam = Locator.GetActiveCamera();
|
||||
UpdateRotation();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
=> GlobalMessenger<OWCamera>.RemoveListener("SwitchActiveCamera", OnSwitchActiveCamera);
|
||||
|
||||
private void OnSwitchActiveCamera(OWCamera activeCamera)
|
||||
{
|
||||
_activeCam = activeCamera;
|
||||
UpdateRotation();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
=> UpdateRotation();
|
||||
|
||||
private void UpdateRotation()
|
||||
=> transform.LookAt(transform.position + (_activeCam.transform.rotation * Vector3.forward), _activeCam.transform.rotation * Vector3.up);
|
||||
}
|
@ -12,124 +12,123 @@ using System.Threading;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace QSB.ConversationSync
|
||||
namespace QSB.ConversationSync;
|
||||
|
||||
public class ConversationManager : WorldObjectManager
|
||||
{
|
||||
public class ConversationManager : WorldObjectManager
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
|
||||
public static ConversationManager Instance { get; private set; }
|
||||
public Dictionary<CharacterDialogueTree, GameObject> BoxMappings { get; } = new Dictionary<CharacterDialogueTree, GameObject>();
|
||||
|
||||
private GameObject _boxPrefab;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
Instance = this;
|
||||
|
||||
public static ConversationManager Instance { get; private set; }
|
||||
public Dictionary<CharacterDialogueTree, GameObject> BoxMappings { get; } = new Dictionary<CharacterDialogueTree, GameObject>();
|
||||
_boxPrefab = QSBCore.ConversationAssetBundle.LoadAsset<GameObject>("assets/Prefabs/dialoguebubble.prefab");
|
||||
|
||||
private GameObject _boxPrefab;
|
||||
|
||||
public void Start()
|
||||
var font = (Font)Resources.Load(@"fonts\english - latin\HVD Fonts - BrandonGrotesque-Bold_Dynamic");
|
||||
if (font == null)
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
_boxPrefab = QSBCore.ConversationAssetBundle.LoadAsset<GameObject>("assets/Prefabs/dialoguebubble.prefab");
|
||||
|
||||
var font = (Font)Resources.Load(@"fonts\english - latin\HVD Fonts - BrandonGrotesque-Bold_Dynamic");
|
||||
if (font == null)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Font is null!", MessageType.Error);
|
||||
}
|
||||
|
||||
_boxPrefab.GetComponent<Text>().font = font;
|
||||
_boxPrefab.GetComponent<Text>().color = Color.white;
|
||||
DebugLog.ToConsole("Error - Font is null!", MessageType.Error);
|
||||
}
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBRemoteDialogueTrigger, RemoteDialogueTrigger>();
|
||||
_boxPrefab.GetComponent<Text>().font = font;
|
||||
_boxPrefab.GetComponent<Text>().color = Color.white;
|
||||
}
|
||||
|
||||
public uint GetPlayerTalkingToTree(CharacterDialogueTree tree)
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBRemoteDialogueTrigger, RemoteDialogueTrigger>();
|
||||
|
||||
public uint GetPlayerTalkingToTree(CharacterDialogueTree tree)
|
||||
{
|
||||
var treeIndex = QSBWorldSync.OldDialogueTrees.IndexOf(tree);
|
||||
return QSBPlayerManager.PlayerList.All(x => x.CurrentCharacterDialogueTreeId != treeIndex)
|
||||
? uint.MaxValue
|
||||
: QSBPlayerManager.PlayerList.First(x => x.CurrentCharacterDialogueTreeId == treeIndex).PlayerId;
|
||||
}
|
||||
|
||||
public void SendPlayerOption(string text) =>
|
||||
new ConversationMessage(ConversationType.Player, (int)QSBPlayerManager.LocalPlayerId, text).Send();
|
||||
|
||||
public void SendCharacterDialogue(int id, string text)
|
||||
{
|
||||
if (id == -1)
|
||||
{
|
||||
var treeIndex = QSBWorldSync.OldDialogueTrees.IndexOf(tree);
|
||||
return QSBPlayerManager.PlayerList.All(x => x.CurrentCharacterDialogueTreeId != treeIndex)
|
||||
? uint.MaxValue
|
||||
: QSBPlayerManager.PlayerList.First(x => x.CurrentCharacterDialogueTreeId == treeIndex).PlayerId;
|
||||
DebugLog.ToConsole("Warning - Tried to send conv. event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
public void SendPlayerOption(string text) =>
|
||||
new ConversationMessage(ConversationType.Player, (int)QSBPlayerManager.LocalPlayerId, text).Send();
|
||||
new ConversationMessage(ConversationType.Character, id, text).Send();
|
||||
}
|
||||
|
||||
public void SendCharacterDialogue(int id, string text)
|
||||
public void CloseBoxPlayer() =>
|
||||
new ConversationMessage(ConversationType.ClosePlayer, (int)QSBPlayerManager.LocalPlayerId).Send();
|
||||
|
||||
public void CloseBoxCharacter(int id) =>
|
||||
new ConversationMessage(ConversationType.CloseCharacter, id).Send();
|
||||
|
||||
public void SendConvState(int charId, bool state)
|
||||
{
|
||||
if (charId == -1)
|
||||
{
|
||||
if (id == -1)
|
||||
{
|
||||
DebugLog.ToConsole("Warning - Tried to send conv. event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
new ConversationMessage(ConversationType.Character, id, text).Send();
|
||||
DebugLog.ToConsole("Warning - Tried to send conv. start/end event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
public void CloseBoxPlayer() =>
|
||||
new ConversationMessage(ConversationType.ClosePlayer, (int)QSBPlayerManager.LocalPlayerId).Send();
|
||||
new ConversationStartEndMessage(charId, state).Send();
|
||||
}
|
||||
|
||||
public void CloseBoxCharacter(int id) =>
|
||||
new ConversationMessage(ConversationType.CloseCharacter, id).Send();
|
||||
|
||||
public void SendConvState(int charId, bool state)
|
||||
public void DisplayPlayerConversationBox(uint playerId, string text)
|
||||
{
|
||||
if (playerId == QSBPlayerManager.LocalPlayerId)
|
||||
{
|
||||
if (charId == -1)
|
||||
{
|
||||
DebugLog.ToConsole("Warning - Tried to send conv. start/end event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
new ConversationStartEndMessage(charId, state).Send();
|
||||
DebugLog.ToConsole("Error - Cannot display conversation box for local player!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
public void DisplayPlayerConversationBox(uint playerId, string text)
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
|
||||
// Destroy old box if it exists
|
||||
var playerBox = player.CurrentDialogueBox;
|
||||
if (playerBox != null)
|
||||
{
|
||||
if (playerId == QSBPlayerManager.LocalPlayerId)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Cannot display conversation box for local player!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = QSBPlayerManager.GetPlayer(playerId);
|
||||
|
||||
// Destroy old box if it exists
|
||||
var playerBox = player.CurrentDialogueBox;
|
||||
if (playerBox != null)
|
||||
{
|
||||
Destroy(playerBox);
|
||||
}
|
||||
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentDialogueBox = CreateBox(player.Body.transform, 2, text);
|
||||
Destroy(playerBox);
|
||||
}
|
||||
|
||||
public void DisplayCharacterConversationBox(int index, string text)
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentDialogueBox = CreateBox(player.Body.transform, 2, text);
|
||||
}
|
||||
|
||||
public void DisplayCharacterConversationBox(int index, string text)
|
||||
{
|
||||
if (QSBWorldSync.OldDialogueTrees.ElementAtOrDefault(index) == null)
|
||||
{
|
||||
if (QSBWorldSync.OldDialogueTrees.ElementAtOrDefault(index) == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Tried to display character conversation box for id {index}! (Doesn't exist!)", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove old box if it exists
|
||||
var oldDialogueTree = QSBWorldSync.OldDialogueTrees[index];
|
||||
if (BoxMappings.ContainsKey(oldDialogueTree))
|
||||
{
|
||||
Destroy(BoxMappings[oldDialogueTree]);
|
||||
BoxMappings.Remove(oldDialogueTree);
|
||||
}
|
||||
|
||||
BoxMappings.Add(oldDialogueTree, CreateBox(oldDialogueTree.gameObject.transform, 2, text));
|
||||
DebugLog.ToConsole($"Error - Tried to display character conversation box for id {index}! (Doesn't exist!)", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
private GameObject CreateBox(Transform parent, float vertOffset, string text)
|
||||
// Remove old box if it exists
|
||||
var oldDialogueTree = QSBWorldSync.OldDialogueTrees[index];
|
||||
if (BoxMappings.ContainsKey(oldDialogueTree))
|
||||
{
|
||||
var newBox = Instantiate(_boxPrefab);
|
||||
newBox.SetActive(false);
|
||||
newBox.transform.SetParent(parent);
|
||||
newBox.transform.localPosition = new Vector3(0, vertOffset, 0);
|
||||
newBox.transform.rotation = parent.rotation;
|
||||
newBox.GetComponent<Text>().text = text;
|
||||
newBox.SetActive(true);
|
||||
return newBox;
|
||||
Destroy(BoxMappings[oldDialogueTree]);
|
||||
BoxMappings.Remove(oldDialogueTree);
|
||||
}
|
||||
|
||||
BoxMappings.Add(oldDialogueTree, CreateBox(oldDialogueTree.gameObject.transform, 2, text));
|
||||
}
|
||||
|
||||
private GameObject CreateBox(Transform parent, float vertOffset, string text)
|
||||
{
|
||||
var newBox = Instantiate(_boxPrefab);
|
||||
newBox.SetActive(false);
|
||||
newBox.transform.SetParent(parent);
|
||||
newBox.transform.localPosition = new Vector3(0, vertOffset, 0);
|
||||
newBox.transform.rotation = parent.rotation;
|
||||
newBox.GetComponent<Text>().text = text;
|
||||
newBox.SetActive(true);
|
||||
return newBox;
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace QSB.ConversationSync
|
||||
namespace QSB.ConversationSync;
|
||||
|
||||
public enum ConversationType
|
||||
{
|
||||
public enum ConversationType
|
||||
{
|
||||
Character,
|
||||
Player,
|
||||
CloseCharacter,
|
||||
ClosePlayer
|
||||
}
|
||||
Character,
|
||||
Player,
|
||||
CloseCharacter,
|
||||
ClosePlayer
|
||||
}
|
@ -4,47 +4,46 @@ using QSB.WorldSync;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
public class ConversationMessage : QSBMessage<(ConversationType Type, int Id, string Message)>
|
||||
{
|
||||
public class ConversationMessage : QSBMessage<(ConversationType Type, int Id, string Message)>
|
||||
public ConversationMessage(ConversationType type, int id, string message = "")
|
||||
{
|
||||
public ConversationMessage(ConversationType type, int id, string message = "")
|
||||
Data.Type = type;
|
||||
Data.Id = id;
|
||||
Data.Message = message;
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
switch (Data.Type)
|
||||
{
|
||||
Data.Type = type;
|
||||
Data.Id = id;
|
||||
Data.Message = message;
|
||||
}
|
||||
case ConversationType.Character:
|
||||
var translated = TextTranslation.Translate(Data.Message).Trim();
|
||||
translated = Regex.Replace(translated, @"<[Pp]ause=?\d*\.?\d*\s?\/?>", "");
|
||||
ConversationManager.Instance.DisplayCharacterConversationBox(Data.Id, translated);
|
||||
break;
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
case ConversationType.Player:
|
||||
ConversationManager.Instance.DisplayPlayerConversationBox((uint)Data.Id, Data.Message);
|
||||
break;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
switch (Data.Type)
|
||||
{
|
||||
case ConversationType.Character:
|
||||
var translated = TextTranslation.Translate(Data.Message).Trim();
|
||||
translated = Regex.Replace(translated, @"<[Pp]ause=?\d*\.?\d*\s?\/?>", "");
|
||||
ConversationManager.Instance.DisplayCharacterConversationBox(Data.Id, translated);
|
||||
case ConversationType.CloseCharacter:
|
||||
if (Data.Id == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case ConversationType.Player:
|
||||
ConversationManager.Instance.DisplayPlayerConversationBox((uint)Data.Id, Data.Message);
|
||||
break;
|
||||
var tree = QSBWorldSync.OldDialogueTrees[Data.Id];
|
||||
Object.Destroy(ConversationManager.Instance.BoxMappings[tree]);
|
||||
break;
|
||||
|
||||
case ConversationType.CloseCharacter:
|
||||
if (Data.Id == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var tree = QSBWorldSync.OldDialogueTrees[Data.Id];
|
||||
Object.Destroy(ConversationManager.Instance.BoxMappings[tree]);
|
||||
break;
|
||||
|
||||
case ConversationType.ClosePlayer:
|
||||
Object.Destroy(QSBPlayerManager.GetPlayer((uint)Data.Id).CurrentDialogueBox);
|
||||
break;
|
||||
}
|
||||
case ConversationType.ClosePlayer:
|
||||
Object.Destroy(QSBPlayerManager.GetPlayer((uint)Data.Id).CurrentDialogueBox);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,53 +4,52 @@ using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
public class ConversationStartEndMessage : QSBMessage<(int TreeId, bool Start)>
|
||||
{
|
||||
public class ConversationStartEndMessage : QSBMessage<(int TreeId, bool Start)>
|
||||
public ConversationStartEndMessage(int treeId, bool start)
|
||||
{
|
||||
public ConversationStartEndMessage(int treeId, bool start)
|
||||
Data.TreeId = treeId;
|
||||
Data.Start = start;
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (Data.TreeId == -1)
|
||||
{
|
||||
Data.TreeId = treeId;
|
||||
Data.Start = start;
|
||||
DebugLog.ToConsole("Warning - Received conv. start/end event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
|
||||
var dialogueTree = QSBWorldSync.OldDialogueTrees[Data.TreeId];
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
if (Data.Start)
|
||||
{
|
||||
if (Data.TreeId == -1)
|
||||
{
|
||||
DebugLog.ToConsole("Warning - Received conv. start/end event with char id -1.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var dialogueTree = QSBWorldSync.OldDialogueTrees[Data.TreeId];
|
||||
|
||||
if (Data.Start)
|
||||
{
|
||||
StartConversation(From, Data.TreeId, dialogueTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
EndConversation(From, dialogueTree);
|
||||
}
|
||||
StartConversation(From, Data.TreeId, dialogueTree);
|
||||
}
|
||||
|
||||
private static void StartConversation(
|
||||
uint playerId,
|
||||
int treeId,
|
||||
CharacterDialogueTree tree)
|
||||
else
|
||||
{
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = treeId;
|
||||
tree.GetInteractVolume().DisableInteraction();
|
||||
}
|
||||
|
||||
private static void EndConversation(
|
||||
uint playerId,
|
||||
CharacterDialogueTree tree)
|
||||
{
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = -1;
|
||||
tree.GetInteractVolume().EnableInteraction();
|
||||
EndConversation(From, dialogueTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartConversation(
|
||||
uint playerId,
|
||||
int treeId,
|
||||
CharacterDialogueTree tree)
|
||||
{
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = treeId;
|
||||
tree.GetInteractVolume().DisableInteraction();
|
||||
}
|
||||
|
||||
private static void EndConversation(
|
||||
uint playerId,
|
||||
CharacterDialogueTree tree)
|
||||
{
|
||||
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = -1;
|
||||
tree.GetInteractVolume().EnableInteraction();
|
||||
}
|
||||
}
|
@ -1,57 +1,56 @@
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
public class DialogueConditionMessage : QSBMessage<(string Name, bool State)>
|
||||
{
|
||||
public class DialogueConditionMessage : QSBMessage<(string Name, bool State)>
|
||||
public DialogueConditionMessage(string name, bool state)
|
||||
{
|
||||
public DialogueConditionMessage(string name, bool state)
|
||||
Data.Name = name;
|
||||
Data.State = state;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
Data.Name = name;
|
||||
Data.State = state;
|
||||
QSBWorldSync.SetDialogueCondition(Data.Name, Data.State);
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
var sharedInstance = DialogueConditionManager.SharedInstance;
|
||||
|
||||
var flag = true;
|
||||
if (sharedInstance.ConditionExists(Data.Name))
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
if (sharedInstance._dictConditions[Data.Name] == Data.State)
|
||||
{
|
||||
QSBWorldSync.SetDialogueCondition(Data.Name, Data.State);
|
||||
flag = false;
|
||||
}
|
||||
|
||||
var sharedInstance = DialogueConditionManager.SharedInstance;
|
||||
|
||||
var flag = true;
|
||||
if (sharedInstance.ConditionExists(Data.Name))
|
||||
{
|
||||
if (sharedInstance._dictConditions[Data.Name] == Data.State)
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
|
||||
sharedInstance._dictConditions[Data.Name] = Data.State;
|
||||
}
|
||||
else
|
||||
{
|
||||
sharedInstance.AddCondition(Data.Name, Data.State);
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
GlobalMessenger<string, bool>.FireEvent("DialogueConditionChanged", Data.Name, Data.State);
|
||||
}
|
||||
|
||||
if (Data.Name == "LAUNCH_CODES_GIVEN")
|
||||
{
|
||||
PlayerData.LearnLaunchCodes();
|
||||
}
|
||||
sharedInstance._dictConditions[Data.Name] = Data.State;
|
||||
}
|
||||
else
|
||||
{
|
||||
sharedInstance.AddCondition(Data.Name, Data.State);
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
if (flag)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
QSBWorldSync.SetDialogueCondition(Data.Name, Data.State);
|
||||
}
|
||||
GlobalMessenger<string, bool>.FireEvent("DialogueConditionChanged", Data.Name, Data.State);
|
||||
}
|
||||
|
||||
if (Data.Name == "LAUNCH_CODES_GIVEN")
|
||||
{
|
||||
PlayerData.LearnLaunchCodes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
QSBWorldSync.SetDialogueCondition(Data.Name, Data.State);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
using QSB.ConversationSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
{
|
||||
internal class EnterRemoteDialogueMessage : QSBWorldObjectMessage<QSBRemoteDialogueTrigger, int>
|
||||
{
|
||||
public EnterRemoteDialogueMessage(int dialogueIndex)
|
||||
=> Data = dialogueIndex;
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> WorldObject.RemoteEnterDialogue(Data);
|
||||
}
|
||||
internal class EnterRemoteDialogueMessage : QSBWorldObjectMessage<QSBRemoteDialogueTrigger, int>
|
||||
{
|
||||
public EnterRemoteDialogueMessage(int dialogueIndex)
|
||||
=> Data = dialogueIndex;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> WorldObject.RemoteEnterDialogue(Data);
|
||||
}
|
@ -1,49 +1,48 @@
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
internal class PersistentConditionMessage : QSBMessage<(string Condition, bool State)>
|
||||
{
|
||||
internal class PersistentConditionMessage : QSBMessage<(string Condition, bool State)>
|
||||
public PersistentConditionMessage(string condition, bool state)
|
||||
{
|
||||
public PersistentConditionMessage(string condition, bool state)
|
||||
Data.Condition = condition;
|
||||
Data.State = state;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
Data.Condition = condition;
|
||||
Data.State = state;
|
||||
QSBWorldSync.SetPersistentCondition(Data.Condition, Data.State);
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
var gameSave = PlayerData._currentGameSave;
|
||||
if (gameSave.dictConditions.ContainsKey(Data.Condition))
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
QSBWorldSync.SetPersistentCondition(Data.Condition, Data.State);
|
||||
}
|
||||
|
||||
var gameSave = PlayerData._currentGameSave;
|
||||
if (gameSave.dictConditions.ContainsKey(Data.Condition))
|
||||
{
|
||||
gameSave.dictConditions[Data.Condition] = Data.State;
|
||||
}
|
||||
else
|
||||
{
|
||||
gameSave.dictConditions.Add(Data.Condition, Data.State);
|
||||
}
|
||||
|
||||
if (Data.Condition
|
||||
is not "LAUNCH_CODES_GIVEN"
|
||||
and not "PLAYER_ENTERED_TIMELOOPCORE"
|
||||
and not "PROBE_ENTERED_TIMELOOPCORE"
|
||||
and not "PLAYER_ENTERED_TIMELOOPCORE_MULTIPLE")
|
||||
{
|
||||
PlayerData.SaveCurrentGame();
|
||||
}
|
||||
gameSave.dictConditions[Data.Condition] = Data.State;
|
||||
}
|
||||
else
|
||||
{
|
||||
gameSave.dictConditions.Add(Data.Condition, Data.State);
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
if (Data.Condition
|
||||
is not "LAUNCH_CODES_GIVEN"
|
||||
and not "PLAYER_ENTERED_TIMELOOPCORE"
|
||||
and not "PROBE_ENTERED_TIMELOOPCORE"
|
||||
and not "PLAYER_ENTERED_TIMELOOPCORE_MULTIPLE")
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
QSBWorldSync.SetPersistentCondition(Data.Condition, Data.State);
|
||||
}
|
||||
PlayerData.SaveCurrentGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
QSBWorldSync.SetPersistentCondition(Data.Condition, Data.State);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,49 +3,48 @@ using QSB.ConversationSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.ConversationSync.Messages
|
||||
namespace QSB.ConversationSync.Messages;
|
||||
|
||||
internal class RemoteDialogueInitialStateMessage : QSBWorldObjectMessage<QSBRemoteDialogueTrigger>
|
||||
{
|
||||
internal class RemoteDialogueInitialStateMessage : QSBWorldObjectMessage<QSBRemoteDialogueTrigger>
|
||||
private bool _inRemoteDialogue;
|
||||
private bool[] _activatedDialogues;
|
||||
private int _dialogueIndex;
|
||||
private bool _colliderEnabled;
|
||||
|
||||
public RemoteDialogueInitialStateMessage(RemoteDialogueTrigger trigger)
|
||||
{
|
||||
private bool _inRemoteDialogue;
|
||||
private bool[] _activatedDialogues;
|
||||
private int _dialogueIndex;
|
||||
private bool _colliderEnabled;
|
||||
_inRemoteDialogue = trigger._inRemoteDialogue;
|
||||
_activatedDialogues = trigger._activatedDialogues;
|
||||
_dialogueIndex = trigger._listDialogues
|
||||
.IndexOf(x => x.dialogue == trigger._activeRemoteDialogue);
|
||||
_colliderEnabled = trigger._collider.enabled;
|
||||
}
|
||||
|
||||
public RemoteDialogueInitialStateMessage(RemoteDialogueTrigger trigger)
|
||||
{
|
||||
_inRemoteDialogue = trigger._inRemoteDialogue;
|
||||
_activatedDialogues = trigger._activatedDialogues;
|
||||
_dialogueIndex = trigger._listDialogues
|
||||
.IndexOf(x => x.dialogue == trigger._activeRemoteDialogue);
|
||||
_colliderEnabled = trigger._collider.enabled;
|
||||
}
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(_inRemoteDialogue);
|
||||
writer.Write(_activatedDialogues);
|
||||
writer.Write(_dialogueIndex);
|
||||
writer.Write(_colliderEnabled);
|
||||
}
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(_inRemoteDialogue);
|
||||
writer.Write(_activatedDialogues);
|
||||
writer.Write(_dialogueIndex);
|
||||
writer.Write(_colliderEnabled);
|
||||
}
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
_inRemoteDialogue = reader.Read<bool>();
|
||||
_activatedDialogues = reader.Read<bool[]>();
|
||||
_dialogueIndex = reader.Read<int>();
|
||||
_colliderEnabled = reader.Read<bool>();
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
_inRemoteDialogue = reader.Read<bool>();
|
||||
_activatedDialogues = reader.Read<bool[]>();
|
||||
_dialogueIndex = reader.Read<int>();
|
||||
_colliderEnabled = reader.Read<bool>();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var trigger = WorldObject.AttachedObject;
|
||||
trigger._activatedDialogues = _activatedDialogues;
|
||||
trigger._inRemoteDialogue = _inRemoteDialogue;
|
||||
trigger._activeRemoteDialogue = trigger._listDialogues.ElementAtOrDefault(_dialogueIndex).dialogue;
|
||||
trigger._collider.enabled = _colliderEnabled;
|
||||
}
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var trigger = WorldObject.AttachedObject;
|
||||
trigger._activatedDialogues = _activatedDialogues;
|
||||
trigger._inRemoteDialogue = _inRemoteDialogue;
|
||||
trigger._activeRemoteDialogue = trigger._listDialogues.ElementAtOrDefault(_dialogueIndex).dialogue;
|
||||
trigger._collider.enabled = _colliderEnabled;
|
||||
}
|
||||
}
|
@ -9,170 +9,169 @@ using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.ConversationSync.Patches
|
||||
namespace QSB.ConversationSync.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class ConversationPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class ConversationPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
public static readonly string[] PersistentConditionsToSync =
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
"MET_SOLANUM",
|
||||
"MET_PRISONER",
|
||||
"TALKED_TO_GABBRO",
|
||||
"GABBRO_MERGE_TRIGGERED",
|
||||
"KNOWS_MEDITATION"
|
||||
};
|
||||
|
||||
public static readonly string[] PersistentConditionsToSync =
|
||||
{
|
||||
"MET_SOLANUM",
|
||||
"MET_PRISONER",
|
||||
"TALKED_TO_GABBRO",
|
||||
"GABBRO_MERGE_TRIGGERED",
|
||||
"KNOWS_MEDITATION"
|
||||
};
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DialogueConditionManager), nameof(DialogueConditionManager.SetConditionState))]
|
||||
public static bool SetConditionState(string conditionName, bool conditionState)
|
||||
{
|
||||
new DialogueConditionMessage(conditionName, conditionState).Send();
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DialogueConditionManager), nameof(DialogueConditionManager.SetConditionState))]
|
||||
public static bool SetConditionState(string conditionName, bool conditionState)
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))]
|
||||
public static void CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
var index = QSBWorldSync.OldDialogueTrees.FindIndex(x => x == __instance);
|
||||
if (index == -1)
|
||||
{
|
||||
new DialogueConditionMessage(conditionName, conditionState).Send();
|
||||
DebugLog.ToConsole($"Warning - Index for tree {__instance.name} was -1.", MessageType.Warning);
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = index;
|
||||
ConversationManager.Instance.SendConvState(index, true);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))]
|
||||
public static bool CharacterDialogueTree_EndConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
if (!__instance.enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId == -1)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Ending conversation with CurrentDialogueId of -1! Called from {__instance.name}", MessageType.Warning);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))]
|
||||
public static void CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance)
|
||||
ConversationManager.Instance.SendConvState(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, false);
|
||||
ConversationManager.Instance.CloseBoxCharacter(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId);
|
||||
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = -1;
|
||||
ConversationManager.Instance.CloseBoxPlayer();
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.InputDialogueOption))]
|
||||
public static bool CharacterDialogueTree_InputDialogueOption(CharacterDialogueTree __instance, int optionIndex)
|
||||
{
|
||||
if (optionIndex < 0)
|
||||
{
|
||||
var index = QSBWorldSync.OldDialogueTrees.FindIndex(x => x == __instance);
|
||||
if (index == -1)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Index for tree {__instance.name} was -1.", MessageType.Warning);
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = index;
|
||||
ConversationManager.Instance.SendConvState(index, true);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))]
|
||||
public static bool CharacterDialogueTree_EndConversation(CharacterDialogueTree __instance)
|
||||
{
|
||||
if (!__instance.enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId == -1)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Ending conversation with CurrentDialogueId of -1! Called from {__instance.name}", MessageType.Warning);
|
||||
return true;
|
||||
}
|
||||
|
||||
ConversationManager.Instance.SendConvState(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, false);
|
||||
ConversationManager.Instance.CloseBoxCharacter(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId);
|
||||
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = -1;
|
||||
// in a page where there is no selectable options
|
||||
ConversationManager.Instance.CloseBoxPlayer();
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.InputDialogueOption))]
|
||||
public static bool CharacterDialogueTree_InputDialogueOption(CharacterDialogueTree __instance, int optionIndex)
|
||||
var selectedOption = __instance._currentDialogueBox.OptionFromUIIndex(optionIndex);
|
||||
ConversationManager.Instance.SendPlayerOption(selectedOption.Text);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DialogueNode), nameof(DialogueNode.GetNextPage))]
|
||||
public static void DialogueNode_GetNextPage(DialogueNode __instance)
|
||||
{
|
||||
var key = __instance._name + __instance._listPagesToDisplay[__instance._currentPage];
|
||||
// Sending key so translation can be done on client side - should make different language-d clients compatible
|
||||
Delay.RunWhen(() => QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId != -1,
|
||||
() => ConversationManager.Instance.SendCharacterDialogue(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, key));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RemoteDialogueTrigger), nameof(RemoteDialogueTrigger.ConversationTriggered))]
|
||||
public static bool ConversationTriggeredReplacement(RemoteDialogueTrigger __instance, out bool __result, out RemoteDialogueTrigger.RemoteDialogueCondition dialogue)
|
||||
{
|
||||
dialogue = default;
|
||||
var dialogueIndex = -1;
|
||||
for (var i = 0; i < __instance._listDialogues.Length; i++)
|
||||
{
|
||||
if (optionIndex < 0)
|
||||
if (!__instance._activatedDialogues[i])
|
||||
{
|
||||
// in a page where there is no selectable options
|
||||
ConversationManager.Instance.CloseBoxPlayer();
|
||||
return true;
|
||||
}
|
||||
var allConditionsMet = true;
|
||||
var anyConditionsMet = __instance._listDialogues[i].prereqConditions.Length == 0;
|
||||
|
||||
var selectedOption = __instance._currentDialogueBox.OptionFromUIIndex(optionIndex);
|
||||
ConversationManager.Instance.SendPlayerOption(selectedOption.Text);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DialogueNode), nameof(DialogueNode.GetNextPage))]
|
||||
public static void DialogueNode_GetNextPage(DialogueNode __instance)
|
||||
{
|
||||
var key = __instance._name + __instance._listPagesToDisplay[__instance._currentPage];
|
||||
// Sending key so translation can be done on client side - should make different language-d clients compatible
|
||||
Delay.RunWhen(() => QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId != -1,
|
||||
() => ConversationManager.Instance.SendCharacterDialogue(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, key));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RemoteDialogueTrigger), nameof(RemoteDialogueTrigger.ConversationTriggered))]
|
||||
public static bool ConversationTriggeredReplacement(RemoteDialogueTrigger __instance, out bool __result, out RemoteDialogueTrigger.RemoteDialogueCondition dialogue)
|
||||
{
|
||||
dialogue = default;
|
||||
var dialogueIndex = -1;
|
||||
for (var i = 0; i < __instance._listDialogues.Length; i++)
|
||||
{
|
||||
if (!__instance._activatedDialogues[i])
|
||||
foreach (var prereqCondition in __instance._listDialogues[i].prereqConditions)
|
||||
{
|
||||
var allConditionsMet = true;
|
||||
var anyConditionsMet = __instance._listDialogues[i].prereqConditions.Length == 0;
|
||||
|
||||
foreach (var prereqCondition in __instance._listDialogues[i].prereqConditions)
|
||||
if (DialogueConditionManager.SharedInstance.GetConditionState(prereqCondition))
|
||||
{
|
||||
if (DialogueConditionManager.SharedInstance.GetConditionState(prereqCondition))
|
||||
{
|
||||
anyConditionsMet = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
allConditionsMet = false;
|
||||
}
|
||||
anyConditionsMet = true;
|
||||
}
|
||||
|
||||
var conditionsMet = false;
|
||||
var prereqConditionType = __instance._listDialogues[i].prereqConditionType;
|
||||
if (prereqConditionType != RemoteDialogueTrigger.MultiConditionType.OR)
|
||||
else
|
||||
{
|
||||
if (prereqConditionType == RemoteDialogueTrigger.MultiConditionType.AND && allConditionsMet)
|
||||
{
|
||||
conditionsMet = true;
|
||||
}
|
||||
allConditionsMet = false;
|
||||
}
|
||||
else if (anyConditionsMet)
|
||||
}
|
||||
|
||||
var conditionsMet = false;
|
||||
var prereqConditionType = __instance._listDialogues[i].prereqConditionType;
|
||||
if (prereqConditionType != RemoteDialogueTrigger.MultiConditionType.OR)
|
||||
{
|
||||
if (prereqConditionType == RemoteDialogueTrigger.MultiConditionType.AND && allConditionsMet)
|
||||
{
|
||||
conditionsMet = true;
|
||||
}
|
||||
}
|
||||
else if (anyConditionsMet)
|
||||
{
|
||||
conditionsMet = true;
|
||||
}
|
||||
|
||||
if (conditionsMet && __instance._listDialogues[i].priority < int.MaxValue)
|
||||
{
|
||||
dialogue = __instance._listDialogues[i];
|
||||
dialogueIndex = i;
|
||||
}
|
||||
if (conditionsMet && __instance._listDialogues[i].priority < int.MaxValue)
|
||||
{
|
||||
dialogue = __instance._listDialogues[i];
|
||||
dialogueIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dialogueIndex == -1)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance._activatedDialogues[dialogueIndex] = true;
|
||||
__result = true;
|
||||
|
||||
__instance.GetWorldObject<QSBRemoteDialogueTrigger>()
|
||||
.SendMessage(new EnterRemoteDialogueMessage(dialogueIndex));
|
||||
|
||||
if (dialogueIndex == -1)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SetPersistentCondition))]
|
||||
public static void SetPersistentCondition(string condition, bool state)
|
||||
{
|
||||
if (PersistentConditionsToSync.Contains(condition))
|
||||
{
|
||||
new PersistentConditionMessage(condition, state).Send();
|
||||
}
|
||||
}
|
||||
__instance._activatedDialogues[dialogueIndex] = true;
|
||||
__result = true;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DialogueConditionManager), nameof(DialogueConditionManager.AddCondition))]
|
||||
public static bool AddCondition(string conditionName, bool conditionState)
|
||||
__instance.GetWorldObject<QSBRemoteDialogueTrigger>()
|
||||
.SendMessage(new EnterRemoteDialogueMessage(dialogueIndex));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SetPersistentCondition))]
|
||||
public static void SetPersistentCondition(string condition, bool state)
|
||||
{
|
||||
if (PersistentConditionsToSync.Contains(condition))
|
||||
{
|
||||
new DialogueConditionMessage(conditionName, conditionState).Send();
|
||||
return true;
|
||||
new PersistentConditionMessage(condition, state).Send();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DialogueConditionManager), nameof(DialogueConditionManager.AddCondition))]
|
||||
public static bool AddCondition(string conditionName, bool conditionState)
|
||||
{
|
||||
new DialogueConditionMessage(conditionName, conditionState).Send();
|
||||
return true;
|
||||
}
|
||||
}
|
@ -2,20 +2,19 @@
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.ConversationSync.WorldObjects
|
||||
namespace QSB.ConversationSync.WorldObjects;
|
||||
|
||||
internal class QSBRemoteDialogueTrigger : WorldObject<RemoteDialogueTrigger>
|
||||
{
|
||||
internal class QSBRemoteDialogueTrigger : WorldObject<RemoteDialogueTrigger>
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new RemoteDialogueInitialStateMessage(AttachedObject) { To = to });
|
||||
|
||||
public void RemoteEnterDialogue(int dialogueIndex)
|
||||
{
|
||||
public override void SendInitialState(uint to) =>
|
||||
this.SendMessage(new RemoteDialogueInitialStateMessage(AttachedObject) { To = to });
|
||||
var dialogueCondition = AttachedObject._listDialogues[dialogueIndex];
|
||||
AttachedObject._activeRemoteDialogue = dialogueCondition.dialogue;
|
||||
AttachedObject._inRemoteDialogue = true;
|
||||
|
||||
public void RemoteEnterDialogue(int dialogueIndex)
|
||||
{
|
||||
var dialogueCondition = AttachedObject._listDialogues[dialogueIndex];
|
||||
AttachedObject._activeRemoteDialogue = dialogueCondition.dialogue;
|
||||
AttachedObject._inRemoteDialogue = true;
|
||||
|
||||
AttachedObject._activatedDialogues[dialogueIndex] = true;
|
||||
}
|
||||
AttachedObject._activatedDialogues[dialogueIndex] = true;
|
||||
}
|
||||
}
|
@ -4,28 +4,27 @@ using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.DeathSync.Messages
|
||||
namespace QSB.DeathSync.Messages;
|
||||
|
||||
// when all players die
|
||||
internal class EndLoopMessage : QSBMessage
|
||||
{
|
||||
// when all players die
|
||||
internal class EndLoopMessage : QSBMessage
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
DebugLog.DebugWrite($" ~~~~ END LOOP - all players are dead ~~~~ ");
|
||||
if (ServerStateManager.Instance.GetServerState() == ServerState.WaitingForAllPlayersToDie)
|
||||
{
|
||||
DebugLog.DebugWrite($" ~~~~ END LOOP - all players are dead ~~~~ ");
|
||||
if (ServerStateManager.Instance.GetServerState() == ServerState.WaitingForAllPlayersToDie)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QSBPatchManager.DoUnpatchType(QSBPatchTypes.RespawnTime);
|
||||
QSBPatchManager.DoUnpatchType(QSBPatchTypes.RespawnTime);
|
||||
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.TimeLoop);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToDie).Send();
|
||||
}
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.TimeLoop);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
new ServerStateMessage(ServerState.WaitingForAllPlayersToDie).Send();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,48 +5,47 @@ using QSB.Player;
|
||||
using QSB.RespawnSync;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.DeathSync.Messages
|
||||
namespace QSB.DeathSync.Messages;
|
||||
|
||||
public class PlayerDeathMessage : QSBMessage<DeathType>
|
||||
{
|
||||
public class PlayerDeathMessage : QSBMessage<DeathType>
|
||||
private int NecronomiconIndex;
|
||||
|
||||
public PlayerDeathMessage(DeathType type)
|
||||
{
|
||||
private int NecronomiconIndex;
|
||||
Data = type;
|
||||
NecronomiconIndex = Necronomicon.GetRandomIndex(type);
|
||||
}
|
||||
|
||||
public PlayerDeathMessage(DeathType type)
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(NecronomiconIndex);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
NecronomiconIndex = reader.Read<int>();
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
RespawnManager.Instance.OnPlayerDeath(player);
|
||||
ClientStateManager.Instance.OnDeath();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
var playerName = player.Name;
|
||||
var deathMessage = Necronomicon.GetPhrase(Data, NecronomiconIndex);
|
||||
if (deathMessage != null)
|
||||
{
|
||||
Data = type;
|
||||
NecronomiconIndex = Necronomicon.GetRandomIndex(type);
|
||||
DebugLog.ToAll(string.Format(deathMessage, playerName));
|
||||
}
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(NecronomiconIndex);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
NecronomiconIndex = reader.Read<int>();
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
RespawnManager.Instance.OnPlayerDeath(player);
|
||||
ClientStateManager.Instance.OnDeath();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
var playerName = player.Name;
|
||||
var deathMessage = Necronomicon.GetPhrase(Data, NecronomiconIndex);
|
||||
if (deathMessage != null)
|
||||
{
|
||||
DebugLog.ToAll(string.Format(deathMessage, playerName));
|
||||
}
|
||||
|
||||
RespawnManager.Instance.OnPlayerDeath(player);
|
||||
}
|
||||
RespawnManager.Instance.OnPlayerDeath(player);
|
||||
}
|
||||
}
|
@ -4,28 +4,27 @@ using QSB.ClientServerStateSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.DeathSync.Messages
|
||||
{
|
||||
internal class StartLoopMessage : QSBMessage
|
||||
{
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
namespace QSB.DeathSync.Messages;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
internal class StartLoopMessage : QSBMessage
|
||||
{
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
DebugLog.DebugWrite($" ~~~ LOOP START ~~~");
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
DebugLog.DebugWrite($" ~~~ LOOP START ~~~");
|
||||
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)
|
||||
{
|
||||
new ClientStateMessage(ClientState.AliveInSolarSystem).Send();
|
||||
}
|
||||
else if (QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
new ClientStateMessage(ClientState.AliveInEye).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Got StartLoop event when not in universe!", MessageType.Error);
|
||||
new ClientStateMessage(ClientState.NotLoaded).Send();
|
||||
}
|
||||
new ClientStateMessage(ClientState.AliveInSolarSystem).Send();
|
||||
}
|
||||
else if (QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse)
|
||||
{
|
||||
new ClientStateMessage(ClientState.AliveInEye).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Got StartLoop event when not in universe!", MessageType.Error);
|
||||
new ClientStateMessage(ClientState.NotLoaded).Send();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,156 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QSB.DeathSync
|
||||
namespace QSB.DeathSync;
|
||||
|
||||
public static class Necronomicon
|
||||
{
|
||||
public static class Necronomicon
|
||||
private static readonly Dictionary<DeathType, string[]> Darkhold = new()
|
||||
{
|
||||
private static readonly Dictionary<DeathType, string[]> Darkhold = new()
|
||||
{
|
||||
DeathType.Default,
|
||||
new[] // Running out of health
|
||||
{
|
||||
DeathType.Default,
|
||||
new[] // Running out of health
|
||||
{
|
||||
"{0} died",
|
||||
"{0} was killed"
|
||||
}
|
||||
},
|
||||
"{0} died",
|
||||
"{0} was killed"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Impact,
|
||||
new[] // Hitting the ground/wall/object
|
||||
{
|
||||
DeathType.Impact,
|
||||
new[] // Hitting the ground/wall/object
|
||||
{
|
||||
"{0} forgot to use retro-rockets",
|
||||
"{0} bonked into the ground too hard",
|
||||
"{0} hit the ground too hard",
|
||||
"{0} went splat",
|
||||
"{0} died",
|
||||
"{0} was killed",
|
||||
"{0} died due to impact",
|
||||
"{0} impacted the ground too hard"
|
||||
}
|
||||
},
|
||||
"{0} forgot to use retro-rockets",
|
||||
"{0} bonked into the ground too hard",
|
||||
"{0} hit the ground too hard",
|
||||
"{0} went splat",
|
||||
"{0} died",
|
||||
"{0} was killed",
|
||||
"{0} died due to impact",
|
||||
"{0} impacted the ground too hard"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Asphyxiation,
|
||||
new[] // Running out of oxygen
|
||||
{
|
||||
DeathType.Asphyxiation,
|
||||
new[] // Running out of oxygen
|
||||
{
|
||||
"{0} forgot to breathe",
|
||||
"{0} asphyxiated",
|
||||
"{0} died due to asphyxiation",
|
||||
"{0} forgot how to breathe",
|
||||
"{0} forgot to check their oxygen",
|
||||
"{0} ran out of air",
|
||||
"{0} ran out of oxygen",
|
||||
"{0} didn't need air anyway"
|
||||
}
|
||||
},
|
||||
"{0} forgot to breathe",
|
||||
"{0} asphyxiated",
|
||||
"{0} died due to asphyxiation",
|
||||
"{0} forgot how to breathe",
|
||||
"{0} forgot to check their oxygen",
|
||||
"{0} ran out of air",
|
||||
"{0} ran out of oxygen",
|
||||
"{0} didn't need air anyway"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Energy,
|
||||
new[] // Electricity, sun, etc.
|
||||
{
|
||||
DeathType.Energy,
|
||||
new[] // Electricity, sun, etc.
|
||||
{
|
||||
"{0} was cooked",
|
||||
"{0} died",
|
||||
"{0} was killed"
|
||||
}
|
||||
},
|
||||
"{0} was cooked",
|
||||
"{0} died",
|
||||
"{0} was killed"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Supernova,
|
||||
new[] // Supernova
|
||||
{
|
||||
DeathType.Supernova,
|
||||
new[] // Supernova
|
||||
{
|
||||
"{0} ran out of time",
|
||||
"{0} burnt up",
|
||||
"{0} got vaporized",
|
||||
"{0} lost track of time",
|
||||
"{0} got front row seats to the supernova",
|
||||
"{0} heard the music",
|
||||
"{0} watched the sun go kaboom",
|
||||
"{0} became cosmic marshmallow",
|
||||
"{0} photosynthesized too much",
|
||||
"{0} died due to the supernova"
|
||||
}
|
||||
},
|
||||
"{0} ran out of time",
|
||||
"{0} burnt up",
|
||||
"{0} got vaporized",
|
||||
"{0} lost track of time",
|
||||
"{0} got front row seats to the supernova",
|
||||
"{0} heard the music",
|
||||
"{0} watched the sun go kaboom",
|
||||
"{0} became cosmic marshmallow",
|
||||
"{0} photosynthesized too much",
|
||||
"{0} died due to the supernova"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Digestion,
|
||||
new[] // Anglerfish
|
||||
{
|
||||
DeathType.Digestion,
|
||||
new[] // Anglerfish
|
||||
{
|
||||
"{0} was eaten",
|
||||
"{0} found a fish",
|
||||
"{0} encountered an evil creature",
|
||||
"{0} messed with the wrong fish",
|
||||
"{0} was digested",
|
||||
"{0} died due to digestion"
|
||||
}
|
||||
},
|
||||
"{0} was eaten",
|
||||
"{0} found a fish",
|
||||
"{0} encountered an evil creature",
|
||||
"{0} messed with the wrong fish",
|
||||
"{0} was digested",
|
||||
"{0} died due to digestion"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Crushed,
|
||||
new[] // Crushed in sand
|
||||
{
|
||||
DeathType.Crushed,
|
||||
new[] // Crushed in sand
|
||||
{
|
||||
"{0} went through the tunnel too slow",
|
||||
"{0} didn't make it out in time",
|
||||
"{0} was squished",
|
||||
"{0} was crushed",
|
||||
"{0} was buried",
|
||||
"{0} went swimming in the sand",
|
||||
"{0} underestimated the danger of sand",
|
||||
"{0} died due to being crushed"
|
||||
}
|
||||
},
|
||||
"{0} went through the tunnel too slow",
|
||||
"{0} didn't make it out in time",
|
||||
"{0} was squished",
|
||||
"{0} was crushed",
|
||||
"{0} was buried",
|
||||
"{0} went swimming in the sand",
|
||||
"{0} underestimated the danger of sand",
|
||||
"{0} died due to being crushed"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.Lava,
|
||||
new[] // Lava
|
||||
{
|
||||
DeathType.Lava,
|
||||
new[] // Lava
|
||||
{
|
||||
"{0} died in lava",
|
||||
"{0} was melted",
|
||||
"{0} tried to swim in lava",
|
||||
"{0} didn't know what the glowy orange liquid was",
|
||||
"{0} fell into lava",
|
||||
"{0} became one with the glowing gooey rock",
|
||||
"{0} died due to lava",
|
||||
"{0} got burnt in the lava"
|
||||
}
|
||||
},
|
||||
"{0} died in lava",
|
||||
"{0} was melted",
|
||||
"{0} tried to swim in lava",
|
||||
"{0} didn't know what the glowy orange liquid was",
|
||||
"{0} fell into lava",
|
||||
"{0} became one with the glowing gooey rock",
|
||||
"{0} died due to lava",
|
||||
"{0} got burnt in the lava"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.BlackHole,
|
||||
new[] // ATP core black hole
|
||||
{
|
||||
DeathType.BlackHole,
|
||||
new[] // ATP core black hole
|
||||
{
|
||||
"{0} should visit the Ash Twin Project again",
|
||||
"{0} waited inside the Ash Twin Project",
|
||||
"{0} chased their memories"
|
||||
}
|
||||
},
|
||||
"{0} should visit the Ash Twin Project again",
|
||||
"{0} waited inside the Ash Twin Project",
|
||||
"{0} chased their memories"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.DreamExplosion,
|
||||
new[] // using the prototype
|
||||
{
|
||||
DeathType.DreamExplosion,
|
||||
new[] // using the prototype
|
||||
{
|
||||
"{0} exploded",
|
||||
"{0} was an early adopter",
|
||||
"{0} went kaboom",
|
||||
"{0} was fried",
|
||||
"{0} died due to explosion",
|
||||
"{0} used the wrong artifact"
|
||||
}
|
||||
},
|
||||
"{0} exploded",
|
||||
"{0} was an early adopter",
|
||||
"{0} went kaboom",
|
||||
"{0} was fried",
|
||||
"{0} died due to explosion",
|
||||
"{0} used the wrong artifact"
|
||||
}
|
||||
},
|
||||
{
|
||||
DeathType.CrushedByElevator,
|
||||
new[] // elevator-induced pancakeness
|
||||
{
|
||||
DeathType.CrushedByElevator,
|
||||
new[] // elevator-induced pancakeness
|
||||
{
|
||||
"{0} was crushed",
|
||||
"{0} was squished",
|
||||
"{0} was crushed by an elevator",
|
||||
"{0} stood under an elevator",
|
||||
"{0} became a flat-hearther",
|
||||
"{0} was squished by an elevator"
|
||||
}
|
||||
},
|
||||
};
|
||||
"{0} was crushed",
|
||||
"{0} was squished",
|
||||
"{0} was crushed by an elevator",
|
||||
"{0} stood under an elevator",
|
||||
"{0} became a flat-hearther",
|
||||
"{0} was squished by an elevator"
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
public static string GetPhrase(DeathType deathType, int index)
|
||||
=> Darkhold.ContainsKey(deathType)
|
||||
? Darkhold[deathType][index]
|
||||
: null;
|
||||
public static string GetPhrase(DeathType deathType, int index)
|
||||
=> Darkhold.ContainsKey(deathType)
|
||||
? Darkhold[deathType][index]
|
||||
: null;
|
||||
|
||||
public static int GetRandomIndex(DeathType deathType)
|
||||
=> Darkhold.ContainsKey(deathType)
|
||||
? new Random().Next(0, Darkhold[deathType].Length)
|
||||
: -1;
|
||||
}
|
||||
public static int GetRandomIndex(DeathType deathType)
|
||||
=> Darkhold.ContainsKey(deathType)
|
||||
? new Random().Next(0, Darkhold[deathType].Length)
|
||||
: -1;
|
||||
}
|
@ -8,220 +8,219 @@ using QSB.Utility;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.DeathSync.Patches
|
||||
namespace QSB.DeathSync.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class DeathPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class DeathPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipEjectionSystem), nameof(ShipEjectionSystem.OnPressInteract))]
|
||||
public static bool DisableEjection()
|
||||
=> false;
|
||||
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipDetachableLeg), nameof(ShipDetachableLeg.Detach))]
|
||||
public static bool ShipDetachableLeg_Detach(out OWRigidbody __result)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
__result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipEjectionSystem), nameof(ShipEjectionSystem.OnPressInteract))]
|
||||
public static bool DisableEjection()
|
||||
=> false;
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipDetachableModule), nameof(ShipDetachableModule.Detach))]
|
||||
public static bool ShipDetachableModule_Detach(out OWRigidbody __result)
|
||||
{
|
||||
__result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipDetachableLeg), nameof(ShipDetachableLeg.Detach))]
|
||||
public static bool ShipDetachableLeg_Detach(out OWRigidbody __result)
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerResources), nameof(PlayerResources.OnImpact))]
|
||||
public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact)
|
||||
{
|
||||
if (PlayerState.IsInsideShip())
|
||||
{
|
||||
__result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : Remove with future functionality.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShipDetachableModule), nameof(ShipDetachableModule.Detach))]
|
||||
public static bool ShipDetachableModule_Detach(out OWRigidbody __result)
|
||||
var speed = Mathf.Clamp01((impact.speed - __instance.GetMinImpactSpeed()) / (__instance.GetMaxImpactSpeed() - __instance.GetMinImpactSpeed()));
|
||||
var tookDamage = __instance.ApplyInstantDamage(100f * speed, InstantDamageType.Impact);
|
||||
if (tookDamage && __instance._currentHealth <= 0f && !PlayerState.IsDead())
|
||||
{
|
||||
Locator.GetDeathManager().SetImpactDeathSpeed(impact.speed);
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Impact);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.FixedUpdate))]
|
||||
public static bool HighSpeedImpactSensor_FixedUpdate(
|
||||
HighSpeedImpactSensor __instance
|
||||
)
|
||||
{
|
||||
if (__instance._isPlayer && (PlayerState.IsAttached() || PlayerState.IsInsideShuttle() || PlayerState.UsingNomaiRemoteCamera()))
|
||||
{
|
||||
__result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerResources), nameof(PlayerResources.OnImpact))]
|
||||
public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact)
|
||||
if (__instance._dieNextUpdate && !__instance._dead)
|
||||
{
|
||||
if (PlayerState.IsInsideShip())
|
||||
__instance._dead = true;
|
||||
__instance._dieNextUpdate = false;
|
||||
if (__instance.gameObject.CompareTag("Player"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var speed = Mathf.Clamp01((impact.speed - __instance.GetMinImpactSpeed()) / (__instance.GetMaxImpactSpeed() - __instance.GetMinImpactSpeed()));
|
||||
var tookDamage = __instance.ApplyInstantDamage(100f * speed, InstantDamageType.Impact);
|
||||
if (tookDamage && __instance._currentHealth <= 0f && !PlayerState.IsDead())
|
||||
{
|
||||
Locator.GetDeathManager().SetImpactDeathSpeed(impact.speed);
|
||||
Locator.GetDeathManager().SetImpactDeathSpeed(__instance._impactSpeed);
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Impact);
|
||||
}
|
||||
else if (__instance.gameObject.CompareTag("Ship"))
|
||||
{
|
||||
__instance.GetComponent<ShipDamageController>().Explode();
|
||||
}
|
||||
}
|
||||
|
||||
if (__instance._isPlayer && PlayerState.IsInsideShip())
|
||||
{
|
||||
var shipCenter = Locator.GetShipTransform().position + (Locator.GetShipTransform().up * 2f);
|
||||
var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter);
|
||||
if (distanceFromShip > 8f)
|
||||
{
|
||||
__instance._body.SetPosition(shipCenter);
|
||||
}
|
||||
|
||||
if (!__instance._dead)
|
||||
{
|
||||
var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
__instance._impactSpeed = a.magnitude;
|
||||
__instance._body.AddVelocityChange(-a);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.FixedUpdate))]
|
||||
public static bool HighSpeedImpactSensor_FixedUpdate(
|
||||
HighSpeedImpactSensor __instance
|
||||
)
|
||||
var passiveReferenceFrame = __instance._sectorDetector.GetPassiveReferenceFrame();
|
||||
if (!__instance._dead && passiveReferenceFrame != null)
|
||||
{
|
||||
if (__instance._isPlayer && (PlayerState.IsAttached() || PlayerState.IsInsideShuttle() || PlayerState.UsingNomaiRemoteCamera()))
|
||||
var relativeVelocity = __instance._body.GetVelocity() - passiveReferenceFrame.GetOWRigidBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (relativeVelocity.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance._dieNextUpdate && !__instance._dead)
|
||||
{
|
||||
__instance._dead = true;
|
||||
__instance._dieNextUpdate = false;
|
||||
if (__instance.gameObject.CompareTag("Player"))
|
||||
var hitCount = Physics.RaycastNonAlloc(__instance.transform.TransformPoint(__instance._localOffset), relativeVelocity, __instance._raycastHits, (relativeVelocity.magnitude * Time.deltaTime) + __instance._radius, OWLayerMask.physicalMask, QueryTriggerInteraction.Ignore);
|
||||
for (var i = 0; i < hitCount; i++)
|
||||
{
|
||||
Locator.GetDeathManager().SetImpactDeathSpeed(__instance._impactSpeed);
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Impact);
|
||||
}
|
||||
else if (__instance.gameObject.CompareTag("Ship"))
|
||||
{
|
||||
__instance.GetComponent<ShipDamageController>().Explode();
|
||||
}
|
||||
}
|
||||
|
||||
if (__instance._isPlayer && PlayerState.IsInsideShip())
|
||||
{
|
||||
var shipCenter = Locator.GetShipTransform().position + (Locator.GetShipTransform().up * 2f);
|
||||
var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter);
|
||||
if (distanceFromShip > 8f)
|
||||
{
|
||||
__instance._body.SetPosition(shipCenter);
|
||||
}
|
||||
|
||||
if (!__instance._dead)
|
||||
{
|
||||
var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
if (__instance._raycastHits[i].rigidbody.mass > 10f && !__instance._raycastHits[i].rigidbody.Equals(__instance._body.GetRigidbody()))
|
||||
{
|
||||
__instance._impactSpeed = a.magnitude;
|
||||
__instance._body.AddVelocityChange(-a);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var passiveReferenceFrame = __instance._sectorDetector.GetPassiveReferenceFrame();
|
||||
if (!__instance._dead && passiveReferenceFrame != null)
|
||||
{
|
||||
var relativeVelocity = __instance._body.GetVelocity() - passiveReferenceFrame.GetOWRigidBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (relativeVelocity.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
var hitCount = Physics.RaycastNonAlloc(__instance.transform.TransformPoint(__instance._localOffset), relativeVelocity, __instance._raycastHits, (relativeVelocity.magnitude * Time.deltaTime) + __instance._radius, OWLayerMask.physicalMask, QueryTriggerInteraction.Ignore);
|
||||
for (var i = 0; i < hitCount; i++)
|
||||
{
|
||||
if (__instance._raycastHits[i].rigidbody.mass > 10f && !__instance._raycastHits[i].rigidbody.Equals(__instance._body.GetRigidbody()))
|
||||
var owRigidbody = __instance._raycastHits[i].rigidbody.GetComponent<OWRigidbody>();
|
||||
if (owRigidbody == null)
|
||||
{
|
||||
var owRigidbody = __instance._raycastHits[i].rigidbody.GetComponent<OWRigidbody>();
|
||||
if (owRigidbody == null)
|
||||
DebugLog.ToConsole("Rigidbody does not have attached OWRigidbody!!!", OWML.Common.MessageType.Error);
|
||||
Debug.Break();
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeVelocity = __instance._body.GetVelocity() - owRigidbody.GetPointVelocity(__instance._body.GetPosition());
|
||||
var a2 = Vector3.Project(relativeVelocity, __instance._raycastHits[i].normal);
|
||||
if (a2.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
DebugLog.ToConsole("Rigidbody does not have attached OWRigidbody!!!", OWML.Common.MessageType.Error);
|
||||
Debug.Break();
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeVelocity = __instance._body.GetVelocity() - owRigidbody.GetPointVelocity(__instance._body.GetPosition());
|
||||
var a2 = Vector3.Project(relativeVelocity, __instance._raycastHits[i].normal);
|
||||
if (a2.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
__instance._body.AddVelocityChange(-a2);
|
||||
__instance._impactSpeed = a2.magnitude;
|
||||
if (!PlayerState.IsInsideTheEye())
|
||||
{
|
||||
__instance._body.AddVelocityChange(-a2);
|
||||
__instance._impactSpeed = a2.magnitude;
|
||||
if (!PlayerState.IsInsideTheEye())
|
||||
{
|
||||
__instance._dieNextUpdate = true;
|
||||
}
|
||||
|
||||
break;
|
||||
__instance._dieNextUpdate = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))]
|
||||
public static bool DeathManager_KillPlayer_Prefix(DeathType deathType)
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))]
|
||||
public static bool DeathManager_KillPlayer_Prefix(DeathType deathType)
|
||||
{
|
||||
if (RespawnOnDeath.Instance == null)
|
||||
{
|
||||
if (RespawnOnDeath.Instance == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (RespawnOnDeath.Instance.AllowedDeathTypes.Contains(deathType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QSBPlayerManager.LocalPlayer.IsDead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var deadPlayersCount = QSBPlayerManager.PlayerList.Count(x => x.IsDead);
|
||||
|
||||
if (deadPlayersCount == QSBPlayerManager.PlayerList.Count - 1)
|
||||
{
|
||||
new EndLoopMessage().Send();
|
||||
return true;
|
||||
}
|
||||
|
||||
RespawnOnDeath.Instance.ResetPlayer();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))]
|
||||
public static void DeathManager_KillPlayer_Postfix(DeathType deathType)
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer.IsDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.IsDead = true;
|
||||
new PlayerDeathMessage(deathType).Send();
|
||||
|
||||
if (PlayerAttachWatcher.Current)
|
||||
{
|
||||
PlayerAttachWatcher.Current.DetachPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(ShipDamageController), nameof(ShipDamageController.Awake))]
|
||||
public static void ShipDamageController_Awake(ShipDamageController __instance)
|
||||
=> __instance._exploded = true;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DestructionVolume), nameof(DestructionVolume.VanishShip))]
|
||||
public static bool DestructionVolume_VanishShip(DestructionVolume __instance)
|
||||
{
|
||||
if (RespawnOnDeath.Instance == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ShipTransformSync.LocalInstance.hasAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerState.IsInsideShip() || PlayerState.UsingShipComputer() || PlayerState.AtFlightConsole())
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(__instance._deathType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (RespawnOnDeath.Instance.AllowedDeathTypes.Contains(deathType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QSBPlayerManager.LocalPlayer.IsDead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var deadPlayersCount = QSBPlayerManager.PlayerList.Count(x => x.IsDead);
|
||||
|
||||
if (deadPlayersCount == QSBPlayerManager.PlayerList.Count - 1)
|
||||
{
|
||||
new EndLoopMessage().Send();
|
||||
return true;
|
||||
}
|
||||
|
||||
RespawnOnDeath.Instance.ResetPlayer();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))]
|
||||
public static void DeathManager_KillPlayer_Postfix(DeathType deathType)
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer.IsDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.IsDead = true;
|
||||
new PlayerDeathMessage(deathType).Send();
|
||||
|
||||
if (PlayerAttachWatcher.Current)
|
||||
{
|
||||
PlayerAttachWatcher.Current.DetachPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(ShipDamageController), nameof(ShipDamageController.Awake))]
|
||||
public static void ShipDamageController_Awake(ShipDamageController __instance)
|
||||
=> __instance._exploded = true;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DestructionVolume), nameof(DestructionVolume.VanishShip))]
|
||||
public static bool DestructionVolume_VanishShip(DestructionVolume __instance)
|
||||
{
|
||||
if (RespawnOnDeath.Instance == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ShipTransformSync.LocalInstance.hasAuthority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerState.IsInsideShip() || PlayerState.UsingShipComputer() || PlayerState.AtFlightConsole())
|
||||
{
|
||||
Locator.GetDeathManager().KillPlayer(__instance._deathType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -2,182 +2,181 @@
|
||||
using QSB.Patches;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.DeathSync.Patches
|
||||
namespace QSB.DeathSync.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class MapPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
internal class MapPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.RespawnTime;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MapController), nameof(MapController.EnterMapView))]
|
||||
public static bool MapController_EnterMapView(
|
||||
MapController __instance
|
||||
)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.RespawnTime;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MapController), nameof(MapController.EnterMapView))]
|
||||
public static bool MapController_EnterMapView(
|
||||
MapController __instance
|
||||
)
|
||||
if (__instance._isMapMode)
|
||||
{
|
||||
if (__instance._isMapMode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance._mapMarkerManager.SetVisible(true);
|
||||
GlobalMessenger.FireEvent("EnterMapView");
|
||||
GlobalMessenger<OWCamera>.FireEvent("SwitchActiveCamera", __instance._mapCamera);
|
||||
if (__instance._audioSource.isPlaying)
|
||||
{
|
||||
__instance._audioSource.Stop();
|
||||
__instance._audioSource.SetLocalVolume(1f);
|
||||
__instance._audioSource.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._audioSource.SetLocalVolume(1f);
|
||||
__instance._audioSource.Play();
|
||||
}
|
||||
__instance._mapMarkerManager.SetVisible(true);
|
||||
GlobalMessenger.FireEvent("EnterMapView");
|
||||
GlobalMessenger<OWCamera>.FireEvent("SwitchActiveCamera", __instance._mapCamera);
|
||||
if (__instance._audioSource.isPlaying)
|
||||
{
|
||||
__instance._audioSource.Stop();
|
||||
__instance._audioSource.SetLocalVolume(1f);
|
||||
__instance._audioSource.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._audioSource.SetLocalVolume(1f);
|
||||
__instance._audioSource.Play();
|
||||
}
|
||||
|
||||
Locator.GetAudioMixer().MixMap();
|
||||
__instance._activeCam.enabled = false;
|
||||
__instance._mapCamera.enabled = true;
|
||||
__instance._gridRenderer.enabled = false;
|
||||
__instance._targetTransform = null;
|
||||
__instance._lockedToTargetTransform = false;
|
||||
__instance._position = RespawnOnDeath.Instance.DeathPositionWorld - Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();
|
||||
Locator.GetAudioMixer().MixMap();
|
||||
__instance._activeCam.enabled = false;
|
||||
__instance._mapCamera.enabled = true;
|
||||
__instance._gridRenderer.enabled = false;
|
||||
__instance._targetTransform = null;
|
||||
__instance._lockedToTargetTransform = false;
|
||||
__instance._position = RespawnOnDeath.Instance.DeathPositionWorld - Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();
|
||||
__instance._position.y = 0f;
|
||||
__instance._yaw = __instance._defaultYawAngle;
|
||||
__instance._pitch = __instance._initialPitchAngle;
|
||||
__instance._zoom = __instance._initialZoomDist;
|
||||
__instance._targetZoom = __instance._defaultZoomDist;
|
||||
__instance.transform.rotation = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, RespawnOnDeath.Instance.DeathPlayerForwardVector);
|
||||
__instance.transform.position = RespawnOnDeath.Instance.DeathPositionWorld;
|
||||
__instance._interpPosition = true;
|
||||
__instance._interpPitch = true;
|
||||
__instance._interpZoom = true;
|
||||
__instance._framingPlayer = __instance._lockedToTargetTransform;
|
||||
__instance._lockTimer = __instance._lockOnMoveLength;
|
||||
__instance._gridOverrideSize = (__instance._currentRFrame == null) ? 0f : __instance._currentRFrame.GetAutopilotArrivalDistance();
|
||||
__instance._gridOverride = __instance._gridOverrideSize > 0f;
|
||||
__instance._gridTimer = (!__instance._gridOverride) ? 0f : __instance._gridLockOnLength;
|
||||
__instance._revealLength = 20f;
|
||||
__instance._revealTimer = 0f;
|
||||
__instance._isMapMode = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MapController), nameof(MapController.LateUpdate))]
|
||||
public static bool MapController_LateUpdate(
|
||||
MapController __instance
|
||||
)
|
||||
{
|
||||
__instance._lockTimer = Mathf.Min(__instance._lockTimer + Time.deltaTime, __instance._lockOnMoveLength);
|
||||
__instance._revealTimer = Mathf.Min(__instance._revealTimer + Time.deltaTime, __instance._revealLength);
|
||||
|
||||
var revealFraction = Mathf.Clamp01(__instance._revealTimer / __instance._revealLength);
|
||||
var smoothedRevealFraction = Mathf.SmoothStep(0f, 1f, revealFraction);
|
||||
|
||||
var canInteractWith = __instance._revealTimer > 18f;
|
||||
|
||||
if (__instance._screenPromptsVisible && __instance._isPaused)
|
||||
{
|
||||
__instance._closePrompt.SetVisibility(false);
|
||||
__instance._panPrompt.SetVisibility(false);
|
||||
__instance._rotatePrompt.SetVisibility(false);
|
||||
__instance._zoomPrompt.SetVisibility(false);
|
||||
__instance._screenPromptsVisible = false;
|
||||
}
|
||||
else if (!__instance._screenPromptsVisible && canInteractWith && !__instance._isPaused)
|
||||
{
|
||||
__instance._closePrompt.SetVisibility(false);
|
||||
__instance._panPrompt.SetVisibility(true);
|
||||
__instance._rotatePrompt.SetVisibility(true);
|
||||
__instance._zoomPrompt.SetVisibility(true);
|
||||
__instance._screenPromptsVisible = true;
|
||||
}
|
||||
|
||||
var XZinput = Vector2.zero;
|
||||
var lookInput = Vector2.zero;
|
||||
var zoomInput = 0f;
|
||||
if (canInteractWith)
|
||||
{
|
||||
XZinput = OWInput.GetAxisValue(InputLibrary.moveXZ);
|
||||
lookInput = InputLibrary.look.GetAxisValue(false);
|
||||
zoomInput = OWInput.GetValue(InputLibrary.mapZoomIn) - OWInput.GetValue(InputLibrary.mapZoomOut);
|
||||
lookInput.y *= -1f;
|
||||
zoomInput *= -1f;
|
||||
}
|
||||
|
||||
__instance._lockedToTargetTransform &= XZinput.sqrMagnitude < 0.01f;
|
||||
__instance._interpPosition &= XZinput.sqrMagnitude < 0.01f;
|
||||
__instance._interpPitch &= Mathf.Abs(lookInput.y) < 0.1f;
|
||||
__instance._interpZoom &= Mathf.Abs(zoomInput) < 0.1f;
|
||||
|
||||
if (__instance._interpPosition)
|
||||
{
|
||||
var a = __instance._activeCam.transform.position - Locator.GetCenterOfTheUniverse().GetOffsetPosition();
|
||||
var b = Vector3.zero;
|
||||
__instance._position = Vector3.Lerp(a, b, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
var normalized = Vector3.Scale(__instance.transform.forward + __instance.transform.up, new Vector3(1f, 0f, 1f)).normalized;
|
||||
var a2 = (__instance.transform.right * XZinput.x) + (normalized * XZinput.y);
|
||||
__instance._position += a2 * __instance._panSpeed * __instance._zoom * Time.deltaTime;
|
||||
__instance._position.y = 0f;
|
||||
__instance._yaw = __instance._defaultYawAngle;
|
||||
__instance._pitch = __instance._initialPitchAngle;
|
||||
__instance._zoom = __instance._initialZoomDist;
|
||||
__instance._targetZoom = __instance._defaultZoomDist;
|
||||
__instance.transform.rotation = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, RespawnOnDeath.Instance.DeathPlayerForwardVector);
|
||||
__instance.transform.position = RespawnOnDeath.Instance.DeathPositionWorld;
|
||||
__instance._interpPosition = true;
|
||||
__instance._interpPitch = true;
|
||||
__instance._interpZoom = true;
|
||||
__instance._framingPlayer = __instance._lockedToTargetTransform;
|
||||
__instance._lockTimer = __instance._lockOnMoveLength;
|
||||
__instance._gridOverrideSize = (__instance._currentRFrame == null) ? 0f : __instance._currentRFrame.GetAutopilotArrivalDistance();
|
||||
__instance._gridOverride = __instance._gridOverrideSize > 0f;
|
||||
__instance._gridTimer = (!__instance._gridOverride) ? 0f : __instance._gridLockOnLength;
|
||||
__instance._revealLength = 20f;
|
||||
__instance._revealTimer = 0f;
|
||||
__instance._isMapMode = true;
|
||||
return false;
|
||||
if (__instance._position.sqrMagnitude > __instance._maxPanDistance * __instance._maxPanDistance)
|
||||
{
|
||||
__instance._position = __instance._position.normalized * __instance._maxPanDistance;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MapController), nameof(MapController.LateUpdate))]
|
||||
public static bool MapController_LateUpdate(
|
||||
MapController __instance
|
||||
)
|
||||
__instance._yaw += lookInput.x * __instance._yawSpeed * Time.deltaTime;
|
||||
__instance._yaw = OWMath.WrapAngle(__instance._yaw);
|
||||
if (__instance._interpPitch)
|
||||
{
|
||||
__instance._lockTimer = Mathf.Min(__instance._lockTimer + Time.deltaTime, __instance._lockOnMoveLength);
|
||||
__instance._revealTimer = Mathf.Min(__instance._revealTimer + Time.deltaTime, __instance._revealLength);
|
||||
|
||||
var revealFraction = Mathf.Clamp01(__instance._revealTimer / __instance._revealLength);
|
||||
var smoothedRevealFraction = Mathf.SmoothStep(0f, 1f, revealFraction);
|
||||
|
||||
var canInteractWith = __instance._revealTimer > 18f;
|
||||
|
||||
if (__instance._screenPromptsVisible && __instance._isPaused)
|
||||
{
|
||||
__instance._closePrompt.SetVisibility(false);
|
||||
__instance._panPrompt.SetVisibility(false);
|
||||
__instance._rotatePrompt.SetVisibility(false);
|
||||
__instance._zoomPrompt.SetVisibility(false);
|
||||
__instance._screenPromptsVisible = false;
|
||||
}
|
||||
else if (!__instance._screenPromptsVisible && canInteractWith && !__instance._isPaused)
|
||||
{
|
||||
__instance._closePrompt.SetVisibility(false);
|
||||
__instance._panPrompt.SetVisibility(true);
|
||||
__instance._rotatePrompt.SetVisibility(true);
|
||||
__instance._zoomPrompt.SetVisibility(true);
|
||||
__instance._screenPromptsVisible = true;
|
||||
}
|
||||
|
||||
var XZinput = Vector2.zero;
|
||||
var lookInput = Vector2.zero;
|
||||
var zoomInput = 0f;
|
||||
if (canInteractWith)
|
||||
{
|
||||
XZinput = OWInput.GetAxisValue(InputLibrary.moveXZ);
|
||||
lookInput = InputLibrary.look.GetAxisValue(false);
|
||||
zoomInput = OWInput.GetValue(InputLibrary.mapZoomIn) - OWInput.GetValue(InputLibrary.mapZoomOut);
|
||||
lookInput.y *= -1f;
|
||||
zoomInput *= -1f;
|
||||
}
|
||||
|
||||
__instance._lockedToTargetTransform &= XZinput.sqrMagnitude < 0.01f;
|
||||
__instance._interpPosition &= XZinput.sqrMagnitude < 0.01f;
|
||||
__instance._interpPitch &= Mathf.Abs(lookInput.y) < 0.1f;
|
||||
__instance._interpZoom &= Mathf.Abs(zoomInput) < 0.1f;
|
||||
|
||||
if (__instance._interpPosition)
|
||||
{
|
||||
var a = __instance._activeCam.transform.position - Locator.GetCenterOfTheUniverse().GetOffsetPosition();
|
||||
var b = Vector3.zero;
|
||||
__instance._position = Vector3.Lerp(a, b, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
var normalized = Vector3.Scale(__instance.transform.forward + __instance.transform.up, new Vector3(1f, 0f, 1f)).normalized;
|
||||
var a2 = (__instance.transform.right * XZinput.x) + (normalized * XZinput.y);
|
||||
__instance._position += a2 * __instance._panSpeed * __instance._zoom * Time.deltaTime;
|
||||
__instance._position.y = 0f;
|
||||
if (__instance._position.sqrMagnitude > __instance._maxPanDistance * __instance._maxPanDistance)
|
||||
{
|
||||
__instance._position = __instance._position.normalized * __instance._maxPanDistance;
|
||||
}
|
||||
}
|
||||
|
||||
__instance._yaw += lookInput.x * __instance._yawSpeed * Time.deltaTime;
|
||||
__instance._yaw = OWMath.WrapAngle(__instance._yaw);
|
||||
if (__instance._interpPitch)
|
||||
{
|
||||
__instance._pitch = Mathf.Lerp(__instance._initialPitchAngle, __instance._defaultPitchAngle, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._pitch += lookInput.y * __instance._pitchSpeed * Time.deltaTime;
|
||||
__instance._pitch = Mathf.Clamp(__instance._pitch, __instance._minPitchAngle, __instance._maxPitchAngle);
|
||||
}
|
||||
|
||||
if (__instance._interpZoom)
|
||||
{
|
||||
__instance._zoom = Mathf.Lerp(__instance._initialZoomDist, __instance._targetZoom, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._zoom += zoomInput * __instance._zoomSpeed * Time.deltaTime;
|
||||
__instance._zoom = Mathf.Clamp(__instance._zoom, __instance._minZoomDistance, __instance._maxZoomDistance);
|
||||
}
|
||||
|
||||
__instance._mapCamera.nearClipPlane = Mathf.Lerp(0.1f, 1f, smoothedRevealFraction);
|
||||
|
||||
var finalRotation = Quaternion.Euler(__instance._pitch, __instance._yaw, 0f);
|
||||
|
||||
var num4 = revealFraction * (2f - revealFraction);
|
||||
|
||||
var num5 = Mathf.SmoothStep(0f, 1f, num4);
|
||||
|
||||
// Create rotation that's looking down at the player from above
|
||||
var lookingDownAtPlayer = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, Vector3.up);
|
||||
|
||||
// Get starting position - distance above player
|
||||
var startingPosition = RespawnOnDeath.Instance.DeathPositionWorld;
|
||||
startingPosition += RespawnOnDeath.Instance.DeathPlayerUpVector * num5 * __instance._observatoryRevealDist;
|
||||
|
||||
// Lerp to final rotation
|
||||
__instance.transform.rotation = Quaternion.Lerp(lookingDownAtPlayer, finalRotation, num5);
|
||||
|
||||
// Lerp reveal twist
|
||||
__instance.transform.rotation *= Quaternion.AngleAxis(Mathf.Lerp(__instance._observatoryRevealTwist, 0f, num4), Vector3.forward);
|
||||
|
||||
var endPosition = __instance._position + (-__instance.transform.forward * __instance._zoom) + Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();
|
||||
|
||||
// Lerp to final position
|
||||
__instance.transform.position = Vector3.Lerp(startingPosition, endPosition, num5);
|
||||
|
||||
return false;
|
||||
__instance._pitch = Mathf.Lerp(__instance._initialPitchAngle, __instance._defaultPitchAngle, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._pitch += lookInput.y * __instance._pitchSpeed * Time.deltaTime;
|
||||
__instance._pitch = Mathf.Clamp(__instance._pitch, __instance._minPitchAngle, __instance._maxPitchAngle);
|
||||
}
|
||||
|
||||
if (__instance._interpZoom)
|
||||
{
|
||||
__instance._zoom = Mathf.Lerp(__instance._initialZoomDist, __instance._targetZoom, smoothedRevealFraction);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance._zoom += zoomInput * __instance._zoomSpeed * Time.deltaTime;
|
||||
__instance._zoom = Mathf.Clamp(__instance._zoom, __instance._minZoomDistance, __instance._maxZoomDistance);
|
||||
}
|
||||
|
||||
__instance._mapCamera.nearClipPlane = Mathf.Lerp(0.1f, 1f, smoothedRevealFraction);
|
||||
|
||||
var finalRotation = Quaternion.Euler(__instance._pitch, __instance._yaw, 0f);
|
||||
|
||||
var num4 = revealFraction * (2f - revealFraction);
|
||||
|
||||
var num5 = Mathf.SmoothStep(0f, 1f, num4);
|
||||
|
||||
// Create rotation that's looking down at the player from above
|
||||
var lookingDownAtPlayer = Quaternion.LookRotation(-RespawnOnDeath.Instance.DeathPlayerUpVector, Vector3.up);
|
||||
|
||||
// Get starting position - distance above player
|
||||
var startingPosition = RespawnOnDeath.Instance.DeathPositionWorld;
|
||||
startingPosition += RespawnOnDeath.Instance.DeathPlayerUpVector * num5 * __instance._observatoryRevealDist;
|
||||
|
||||
// Lerp to final rotation
|
||||
__instance.transform.rotation = Quaternion.Lerp(lookingDownAtPlayer, finalRotation, num5);
|
||||
|
||||
// Lerp reveal twist
|
||||
__instance.transform.rotation *= Quaternion.AngleAxis(Mathf.Lerp(__instance._observatoryRevealTwist, 0f, num4), Vector3.forward);
|
||||
|
||||
var endPosition = __instance._position + (-__instance.transform.forward * __instance._zoom) + Locator.GetCenterOfTheUniverse().GetStaticReferenceFrame().GetPosition();
|
||||
|
||||
// Lerp to final position
|
||||
__instance.transform.position = Vector3.Lerp(startingPosition, endPosition, num5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -7,118 +7,117 @@ using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.DeathSync
|
||||
namespace QSB.DeathSync;
|
||||
|
||||
public class RespawnOnDeath : MonoBehaviour
|
||||
{
|
||||
public class RespawnOnDeath : MonoBehaviour
|
||||
public static RespawnOnDeath Instance;
|
||||
|
||||
public readonly DeathType[] AllowedDeathTypes = {
|
||||
DeathType.BigBang,
|
||||
DeathType.Supernova,
|
||||
DeathType.TimeLoop
|
||||
};
|
||||
|
||||
private SpawnPoint _playerSpawnPoint;
|
||||
private PlayerSpawner _playerSpawner;
|
||||
private FluidDetector _fluidDetector;
|
||||
private PlayerResources _playerResources;
|
||||
private PlayerSpacesuit _spaceSuit;
|
||||
private SuitPickupVolume[] _suitPickupVolumes;
|
||||
private Vector3 _deathPositionRelative;
|
||||
|
||||
public Transform DeathClosestAstroObject { get; private set; }
|
||||
public Vector3 DeathPositionWorld
|
||||
=> DeathClosestAstroObject == null
|
||||
? Vector3.zero
|
||||
: DeathClosestAstroObject.TransformPoint(_deathPositionRelative);
|
||||
public Vector3 DeathPlayerUpVector { get; private set; }
|
||||
public Vector3 DeathPlayerForwardVector { get; private set; }
|
||||
|
||||
public void Awake() => Instance = this;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
public static RespawnOnDeath Instance;
|
||||
DebugLog.DebugWrite($"INIT");
|
||||
var playerTransform = Locator.GetPlayerTransform();
|
||||
_playerResources = playerTransform.GetComponent<PlayerResources>();
|
||||
_spaceSuit = Locator.GetPlayerSuit();
|
||||
_playerSpawner = FindObjectOfType<PlayerSpawner>();
|
||||
_suitPickupVolumes = FindObjectsOfType<SuitPickupVolume>();
|
||||
_fluidDetector = Locator.GetPlayerCamera().GetComponentInChildren<FluidDetector>();
|
||||
_playerSpawnPoint = GetSpawnPoint();
|
||||
}
|
||||
|
||||
public readonly DeathType[] AllowedDeathTypes = {
|
||||
DeathType.BigBang,
|
||||
DeathType.Supernova,
|
||||
DeathType.TimeLoop
|
||||
};
|
||||
|
||||
private SpawnPoint _playerSpawnPoint;
|
||||
private PlayerSpawner _playerSpawner;
|
||||
private FluidDetector _fluidDetector;
|
||||
private PlayerResources _playerResources;
|
||||
private PlayerSpacesuit _spaceSuit;
|
||||
private SuitPickupVolume[] _suitPickupVolumes;
|
||||
private Vector3 _deathPositionRelative;
|
||||
|
||||
public Transform DeathClosestAstroObject { get; private set; }
|
||||
public Vector3 DeathPositionWorld
|
||||
=> DeathClosestAstroObject == null
|
||||
? Vector3.zero
|
||||
: DeathClosestAstroObject.TransformPoint(_deathPositionRelative);
|
||||
public Vector3 DeathPlayerUpVector { get; private set; }
|
||||
public Vector3 DeathPlayerForwardVector { get; private set; }
|
||||
|
||||
public void Awake() => Instance = this;
|
||||
|
||||
public void Init()
|
||||
public void ResetPlayer()
|
||||
{
|
||||
DebugLog.DebugWrite($"RESET PLAYER");
|
||||
if (_playerSpawnPoint == null)
|
||||
{
|
||||
DebugLog.DebugWrite($"INIT");
|
||||
var playerTransform = Locator.GetPlayerTransform();
|
||||
_playerResources = playerTransform.GetComponent<PlayerResources>();
|
||||
_spaceSuit = Locator.GetPlayerSuit();
|
||||
_playerSpawner = FindObjectOfType<PlayerSpawner>();
|
||||
_suitPickupVolumes = FindObjectsOfType<SuitPickupVolume>();
|
||||
_fluidDetector = Locator.GetPlayerCamera().GetComponentInChildren<FluidDetector>();
|
||||
_playerSpawnPoint = GetSpawnPoint();
|
||||
DebugLog.ToConsole("Warning - _playerSpawnPoint is null!", MessageType.Warning);
|
||||
Init();
|
||||
}
|
||||
|
||||
public void ResetPlayer()
|
||||
RespawnManager.Instance.TriggerRespawnMap();
|
||||
|
||||
var inSpace = PlayerTransformSync.LocalInstance.SectorDetector.SectorList.Count == 0;
|
||||
|
||||
if (inSpace)
|
||||
{
|
||||
DebugLog.DebugWrite($"RESET PLAYER");
|
||||
if (_playerSpawnPoint == null)
|
||||
DeathClosestAstroObject = Locator.GetAstroObject(AstroObject.Name.Sun).transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allAstroobjects = QSBWorldSync.GetUnityObjects<AstroObject>().Where(x => x.GetAstroObjectName() != AstroObject.Name.None && x.GetAstroObjectType() != AstroObject.Type.Satellite);
|
||||
var closest = allAstroobjects.MinBy(x => Vector3.SqrMagnitude(x.transform.position));
|
||||
DeathClosestAstroObject = closest.transform;
|
||||
}
|
||||
|
||||
var deathPosition = Locator.GetPlayerTransform().position;
|
||||
_deathPositionRelative = DeathClosestAstroObject.InverseTransformPoint(deathPosition);
|
||||
DeathPlayerUpVector = Locator.GetPlayerTransform().up;
|
||||
DeathPlayerForwardVector = Locator.GetPlayerTransform().forward;
|
||||
|
||||
var playerBody = Locator.GetPlayerBody();
|
||||
playerBody.WarpToPositionRotation(_playerSpawnPoint.transform.position, _playerSpawnPoint.transform.rotation);
|
||||
playerBody.SetVelocity(_playerSpawnPoint.GetPointVelocity());
|
||||
_playerSpawnPoint.AddObjectToTriggerVolumes(Locator.GetPlayerDetector().gameObject);
|
||||
_playerSpawnPoint.AddObjectToTriggerVolumes(_fluidDetector.gameObject);
|
||||
_playerSpawnPoint.OnSpawnPlayer();
|
||||
|
||||
_playerResources._isSuffocating = false;
|
||||
_playerResources.DebugRefillResources();
|
||||
_spaceSuit.RemoveSuit(true);
|
||||
|
||||
foreach (var pickupVolume in _suitPickupVolumes)
|
||||
{
|
||||
if (!pickupVolume._containsSuit && pickupVolume._allowSuitReturn)
|
||||
{
|
||||
DebugLog.ToConsole("Warning - _playerSpawnPoint is null!", MessageType.Warning);
|
||||
Init();
|
||||
}
|
||||
|
||||
RespawnManager.Instance.TriggerRespawnMap();
|
||||
|
||||
var inSpace = PlayerTransformSync.LocalInstance.SectorDetector.SectorList.Count == 0;
|
||||
|
||||
if (inSpace)
|
||||
{
|
||||
DeathClosestAstroObject = Locator.GetAstroObject(AstroObject.Name.Sun).transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allAstroobjects = QSBWorldSync.GetUnityObjects<AstroObject>().Where(x => x.GetAstroObjectName() != AstroObject.Name.None && x.GetAstroObjectType() != AstroObject.Type.Satellite);
|
||||
var closest = allAstroobjects.MinBy(x => Vector3.SqrMagnitude(x.transform.position));
|
||||
DeathClosestAstroObject = closest.transform;
|
||||
}
|
||||
|
||||
var deathPosition = Locator.GetPlayerTransform().position;
|
||||
_deathPositionRelative = DeathClosestAstroObject.InverseTransformPoint(deathPosition);
|
||||
DeathPlayerUpVector = Locator.GetPlayerTransform().up;
|
||||
DeathPlayerForwardVector = Locator.GetPlayerTransform().forward;
|
||||
|
||||
var playerBody = Locator.GetPlayerBody();
|
||||
playerBody.WarpToPositionRotation(_playerSpawnPoint.transform.position, _playerSpawnPoint.transform.rotation);
|
||||
playerBody.SetVelocity(_playerSpawnPoint.GetPointVelocity());
|
||||
_playerSpawnPoint.AddObjectToTriggerVolumes(Locator.GetPlayerDetector().gameObject);
|
||||
_playerSpawnPoint.AddObjectToTriggerVolumes(_fluidDetector.gameObject);
|
||||
_playerSpawnPoint.OnSpawnPlayer();
|
||||
|
||||
_playerResources._isSuffocating = false;
|
||||
_playerResources.DebugRefillResources();
|
||||
_spaceSuit.RemoveSuit(true);
|
||||
|
||||
foreach (var pickupVolume in _suitPickupVolumes)
|
||||
{
|
||||
if (!pickupVolume._containsSuit && pickupVolume._allowSuitReturn)
|
||||
pickupVolume._containsSuit = true;
|
||||
pickupVolume._interactVolume.ChangePrompt(UITextType.SuitUpPrompt, pickupVolume._pickupSuitCommandIndex);
|
||||
pickupVolume._suitGeometry.SetActive(true);
|
||||
pickupVolume._suitOWCollider.SetActivation(true);
|
||||
foreach (var geo in pickupVolume._toolGeometry)
|
||||
{
|
||||
pickupVolume._containsSuit = true;
|
||||
pickupVolume._interactVolume.ChangePrompt(UITextType.SuitUpPrompt, pickupVolume._pickupSuitCommandIndex);
|
||||
pickupVolume._suitGeometry.SetActive(true);
|
||||
pickupVolume._suitOWCollider.SetActivation(true);
|
||||
foreach (var geo in pickupVolume._toolGeometry)
|
||||
{
|
||||
geo.SetActive(true);
|
||||
}
|
||||
geo.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
QSBPlayerManager.LocalPlayer.LocalFlashlight.TurnOff(false);
|
||||
}
|
||||
|
||||
private SpawnPoint GetSpawnPoint()
|
||||
QSBPlayerManager.LocalPlayer.LocalFlashlight.TurnOff(false);
|
||||
}
|
||||
|
||||
private SpawnPoint GetSpawnPoint()
|
||||
{
|
||||
var spawnList = _playerSpawner._spawnList;
|
||||
if (spawnList == null)
|
||||
{
|
||||
var spawnList = _playerSpawner._spawnList;
|
||||
if (spawnList == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - _spawnList was null for player spawner!", MessageType.Warning);
|
||||
return null;
|
||||
}
|
||||
|
||||
return spawnList.FirstOrDefault(spawnPoint =>
|
||||
spawnPoint.GetSpawnLocation() == SpawnLocation.TimberHearth
|
||||
&& spawnPoint.IsShipSpawn() == false);
|
||||
DebugLog.ToConsole($"Warning - _spawnList was null for player spawner!", MessageType.Warning);
|
||||
return null;
|
||||
}
|
||||
|
||||
return spawnList.FirstOrDefault(spawnPoint =>
|
||||
spawnPoint.GetSpawnLocation() == SpawnLocation.TimberHearth
|
||||
&& spawnPoint.IsShipSpawn() == false);
|
||||
}
|
||||
}
|
@ -3,13 +3,12 @@ using QSB.EchoesOfTheEye.AirlockSync.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AirlockSync
|
||||
{
|
||||
internal class AirlockManager : WorldObjectManager
|
||||
{
|
||||
// is this used in the prisoner sequence in the eye?
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
namespace QSB.EchoesOfTheEye.AirlockSync;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBGhostAirlock, GhostAirlock>();
|
||||
}
|
||||
internal class AirlockManager : WorldObjectManager
|
||||
{
|
||||
// is this used in the prisoner sequence in the eye?
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBGhostAirlock, GhostAirlock>();
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AirlockSync.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.AirlockSync.WorldObjects;
|
||||
|
||||
// will be implemented when eote
|
||||
internal class QSBGhostAirlock : WorldObject<GhostAirlock>
|
||||
{
|
||||
// will be implemented when eote
|
||||
internal class QSBGhostAirlock : WorldObject<GhostAirlock>
|
||||
{
|
||||
public override void SendInitialState(uint to) { }
|
||||
}
|
||||
public override void SendInitialState(uint to) { }
|
||||
}
|
@ -3,13 +3,12 @@ using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync
|
||||
{
|
||||
internal class LightSensorManager : WorldObjectManager
|
||||
{
|
||||
// see AirlockManager question
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBLightSensor, SingleLightSensor>();
|
||||
}
|
||||
internal class LightSensorManager : WorldObjectManager
|
||||
{
|
||||
// see AirlockManager question
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBLightSensor, SingleLightSensor>();
|
||||
}
|
@ -7,158 +7,157 @@ using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync.Patches
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync.Patches;
|
||||
|
||||
[HarmonyPatch]
|
||||
internal class LightSensorPatches : QSBPatch
|
||||
{
|
||||
[HarmonyPatch]
|
||||
internal class LightSensorPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SingleLightSensor), nameof(SingleLightSensor.UpdateIllumination))]
|
||||
public static bool UpdateIlluminationReplacement(SingleLightSensor __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SingleLightSensor), nameof(SingleLightSensor.UpdateIllumination))]
|
||||
public static bool UpdateIlluminationReplacement(SingleLightSensor __instance)
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbLightSensor = __instance.GetWorldObject<QSBLightSensor>();
|
||||
qsbLightSensor._illuminatedByLocal = false;
|
||||
__instance._illuminated = false;
|
||||
if (__instance._illuminatingDreamLanternList != null)
|
||||
{
|
||||
__instance._illuminatingDreamLanternList.Clear();
|
||||
}
|
||||
var qsbLightSensor = __instance.GetWorldObject<QSBLightSensor>();
|
||||
qsbLightSensor._illuminatedByLocal = false;
|
||||
__instance._illuminated = false;
|
||||
if (__instance._illuminatingDreamLanternList != null)
|
||||
{
|
||||
__instance._illuminatingDreamLanternList.Clear();
|
||||
}
|
||||
|
||||
var vector = __instance.transform.TransformPoint(__instance._localSensorOffset);
|
||||
var sensorWorldDir = Vector3.zero;
|
||||
if (__instance._directionalSensor)
|
||||
{
|
||||
sensorWorldDir = __instance.transform.TransformDirection(__instance._localDirection).normalized;
|
||||
}
|
||||
var vector = __instance.transform.TransformPoint(__instance._localSensorOffset);
|
||||
var sensorWorldDir = Vector3.zero;
|
||||
if (__instance._directionalSensor)
|
||||
{
|
||||
sensorWorldDir = __instance.transform.TransformDirection(__instance._localDirection).normalized;
|
||||
}
|
||||
|
||||
if (__instance._lightSources == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (__instance._lightSources == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < __instance._lightSources.Count; i++)
|
||||
{
|
||||
var source = __instance._lightSources[i];
|
||||
for (var i = 0; i < __instance._lightSources.Count; i++)
|
||||
{
|
||||
var source = __instance._lightSources[i];
|
||||
|
||||
if ((__instance._lightSourceMask & source.GetLightSourceType()) == source.GetLightSourceType()
|
||||
&& source.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance))
|
||||
if ((__instance._lightSourceMask & source.GetLightSourceType()) == source.GetLightSourceType()
|
||||
&& source.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance))
|
||||
{
|
||||
var lightSourceType = source.GetLightSourceType();
|
||||
switch (lightSourceType)
|
||||
{
|
||||
var lightSourceType = source.GetLightSourceType();
|
||||
switch (lightSourceType)
|
||||
{
|
||||
case LightSourceType.UNDEFINED:
|
||||
{
|
||||
var light = source as OWLight2;
|
||||
var occludableLight = light.GetLight().shadows != LightShadows.None
|
||||
&& light.GetLight().shadowStrength > 0.5f;
|
||||
case LightSourceType.UNDEFINED:
|
||||
{
|
||||
var light = source as OWLight2;
|
||||
var occludableLight = light.GetLight().shadows != LightShadows.None
|
||||
&& light.GetLight().shadowStrength > 0.5f;
|
||||
|
||||
if (light.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(light.transform.position, vector, sensorWorldDir, occludableLight))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.FLASHLIGHT:
|
||||
{
|
||||
if (source is Flashlight light && light == Locator.GetFlashlight())
|
||||
{
|
||||
var position = Locator.GetPlayerCamera().transform.position;
|
||||
var to = __instance.transform.position - position;
|
||||
if (Vector3.Angle(Locator.GetPlayerCamera().transform.forward, to) <= __instance._maxSpotHalfAngle
|
||||
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var player = QSBPlayerManager.PlayerList.First(x => x.FlashLight == source as QSBFlashlight);
|
||||
|
||||
var position = player.Camera.transform.position;
|
||||
var to = __instance.transform.position - position;
|
||||
if (Vector3.Angle(player.Camera.transform.forward, to) <= __instance._maxSpotHalfAngle
|
||||
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.PROBE:
|
||||
{
|
||||
var probe = Locator.GetProbe();
|
||||
if (probe != null
|
||||
&& probe.IsLaunched()
|
||||
&& !probe.IsRetrieving()
|
||||
&& probe.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(probe.GetLightSourcePosition(), vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.PROBE:
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.DREAM_LANTERN:
|
||||
case LightSourceType.PROBE | LightSourceType.DREAM_LANTERN:
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.PROBE | LightSourceType.DREAM_LANTERN:
|
||||
break;
|
||||
case LightSourceType.DREAM_LANTERN:
|
||||
{
|
||||
var dreamLanternController = __instance._lightSources[i] as DreamLanternController;
|
||||
if (dreamLanternController.IsLit()
|
||||
&& dreamLanternController.IsFocused(__instance._lanternFocusThreshold)
|
||||
&& dreamLanternController.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(dreamLanternController.GetLightPosition(), vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminatingDreamLanternList.Add(dreamLanternController);
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.SIMPLE_LANTERN:
|
||||
foreach (var light in __instance._lightSources[i].GetLights())
|
||||
{
|
||||
var occludableLight = light.GetLight().shadows != LightShadows.None
|
||||
&& light.GetLight().shadowStrength > 0.5f;
|
||||
var maxDistance = Mathf.Min(__instance._maxSimpleLanternDistance, __instance._maxDistance);
|
||||
if (light.CheckIlluminationAtPoint(vector, __instance._sensorRadius, maxDistance) && !__instance.CheckOcclusion(light.transform.position, vector, sensorWorldDir, occludableLight))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (lightSourceType == LightSourceType.VOLUME_ONLY)
|
||||
if (light.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(light.transform.position, vector, sensorWorldDir, occludableLight))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
case LightSourceType.FLASHLIGHT:
|
||||
{
|
||||
if (source is Flashlight light && light == Locator.GetFlashlight())
|
||||
{
|
||||
var position = Locator.GetPlayerCamera().transform.position;
|
||||
var to = __instance.transform.position - position;
|
||||
if (Vector3.Angle(Locator.GetPlayerCamera().transform.forward, to) <= __instance._maxSpotHalfAngle
|
||||
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var player = QSBPlayerManager.PlayerList.First(x => x.FlashLight == source as QSBFlashlight);
|
||||
|
||||
var position = player.Camera.transform.position;
|
||||
var to = __instance.transform.position - position;
|
||||
if (Vector3.Angle(player.Camera.transform.forward, to) <= __instance._maxSpotHalfAngle
|
||||
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.PROBE:
|
||||
{
|
||||
var probe = Locator.GetProbe();
|
||||
if (probe != null
|
||||
&& probe.IsLaunched()
|
||||
&& !probe.IsRetrieving()
|
||||
&& probe.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(probe.GetLightSourcePosition(), vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.PROBE:
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.DREAM_LANTERN:
|
||||
case LightSourceType.PROBE | LightSourceType.DREAM_LANTERN:
|
||||
case LightSourceType.FLASHLIGHT | LightSourceType.PROBE | LightSourceType.DREAM_LANTERN:
|
||||
break;
|
||||
case LightSourceType.DREAM_LANTERN:
|
||||
{
|
||||
var dreamLanternController = __instance._lightSources[i] as DreamLanternController;
|
||||
if (dreamLanternController.IsLit()
|
||||
&& dreamLanternController.IsFocused(__instance._lanternFocusThreshold)
|
||||
&& dreamLanternController.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
|
||||
&& !__instance.CheckOcclusion(dreamLanternController.GetLightPosition(), vector, sensorWorldDir))
|
||||
{
|
||||
__instance._illuminatingDreamLanternList.Add(dreamLanternController);
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.SIMPLE_LANTERN:
|
||||
foreach (var light in __instance._lightSources[i].GetLights())
|
||||
{
|
||||
var occludableLight = light.GetLight().shadows != LightShadows.None
|
||||
&& light.GetLight().shadowStrength > 0.5f;
|
||||
var maxDistance = Mathf.Min(__instance._maxSimpleLanternDistance, __instance._maxDistance);
|
||||
if (light.CheckIlluminationAtPoint(vector, __instance._sensorRadius, maxDistance) && !__instance.CheckOcclusion(light.transform.position, vector, sensorWorldDir, occludableLight))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (lightSourceType == LightSourceType.VOLUME_ONLY)
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
qsbLightSensor._illuminatedByLocal = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -3,44 +3,43 @@ using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
|
||||
|
||||
// will be implemented when eote
|
||||
internal class QSBLightSensor : WorldObject<SingleLightSensor>
|
||||
{
|
||||
// will be implemented when eote
|
||||
internal class QSBLightSensor : WorldObject<SingleLightSensor>
|
||||
internal bool _illuminatedByLocal;
|
||||
|
||||
public event Action OnDetectLocalLight;
|
||||
public event Action OnDetectLocalDarkness;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
internal bool _illuminatedByLocal;
|
||||
|
||||
public event Action OnDetectLocalLight;
|
||||
public event Action OnDetectLocalDarkness;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
AttachedObject.OnDetectLight += OnDetectLight;
|
||||
AttachedObject.OnDetectDarkness += OnDetectDarkness;
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
AttachedObject.OnDetectLight -= OnDetectLight;
|
||||
AttachedObject.OnDetectDarkness -= OnDetectDarkness;
|
||||
}
|
||||
|
||||
private void OnDetectLight()
|
||||
{
|
||||
if (_illuminatedByLocal)
|
||||
{
|
||||
OnDetectLocalLight?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetectDarkness()
|
||||
{
|
||||
if (_illuminatedByLocal)
|
||||
{
|
||||
OnDetectLocalDarkness?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to) { }
|
||||
AttachedObject.OnDetectLight += OnDetectLight;
|
||||
AttachedObject.OnDetectDarkness += OnDetectDarkness;
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
AttachedObject.OnDetectLight -= OnDetectLight;
|
||||
AttachedObject.OnDetectDarkness -= OnDetectDarkness;
|
||||
}
|
||||
|
||||
private void OnDetectLight()
|
||||
{
|
||||
if (_illuminatedByLocal)
|
||||
{
|
||||
OnDetectLocalLight?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetectDarkness()
|
||||
{
|
||||
if (_illuminatedByLocal)
|
||||
{
|
||||
OnDetectLocalDarkness?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to) { }
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using QSB.EchoesOfTheEye.RaftSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.Messages
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.Messages;
|
||||
|
||||
public class RaftDockOnPressInteractMessage : QSBWorldObjectMessage<QSBRaftDock>
|
||||
{
|
||||
public class RaftDockOnPressInteractMessage : QSBWorldObjectMessage<QSBRaftDock>
|
||||
{
|
||||
public override void OnReceiveRemote() => WorldObject.OnPressInteract();
|
||||
}
|
||||
}
|
||||
public override void OnReceiveRemote() => WorldObject.OnPressInteract();
|
||||
}
|
@ -8,57 +8,56 @@ using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.Patches
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.Patches;
|
||||
|
||||
public class RaftPatches : QSBPatch
|
||||
{
|
||||
public class RaftPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RaftController), nameof(RaftController.OnPressInteract))]
|
||||
private static bool OnPressInteract(RaftController __instance)
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
__instance._interactReceiver.SetInteractionEnabled(false);
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RaftController), nameof(RaftController.OnPressInteract))]
|
||||
private static bool OnPressInteract(RaftController __instance)
|
||||
var qsbRaft = __instance.GetWorldObject<QSBRaft>();
|
||||
qsbRaft.TransformSync.netIdentity.UpdateAuthQueue(AuthQueueAction.Force);
|
||||
Delay.RunWhen(() => qsbRaft.TransformSync.hasAuthority, () =>
|
||||
{
|
||||
__instance._interactReceiver.SetInteractionEnabled(false);
|
||||
var normalized = Vector3.ProjectOnPlane(Locator.GetPlayerCamera().transform.forward, __instance.transform.up).normalized;
|
||||
__instance._raftBody.AddVelocityChange(normalized * 5f);
|
||||
__instance._effectsController.PlayRaftPush();
|
||||
__instance._pushTime = Time.time;
|
||||
});
|
||||
|
||||
var qsbRaft = __instance.GetWorldObject<QSBRaft>();
|
||||
qsbRaft.TransformSync.netIdentity.UpdateAuthQueue(AuthQueueAction.Force);
|
||||
Delay.RunWhen(() => qsbRaft.TransformSync.hasAuthority, () =>
|
||||
{
|
||||
var normalized = Vector3.ProjectOnPlane(Locator.GetPlayerCamera().transform.forward, __instance.transform.up).normalized;
|
||||
__instance._raftBody.AddVelocityChange(normalized * 5f);
|
||||
__instance._effectsController.PlayRaftPush();
|
||||
__instance._pushTime = Time.time;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RaftDock), nameof(RaftDock.OnPressInteract))]
|
||||
private static bool OnPressInteract(RaftDock __instance)
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(RaftDock), nameof(RaftDock.OnPressInteract))]
|
||||
private static bool OnPressInteract(RaftDock __instance)
|
||||
{
|
||||
if (__instance._raft != null && __instance._state == RaftCarrier.DockState.Docked)
|
||||
{
|
||||
if (__instance._raft != null && __instance._state == RaftCarrier.DockState.Docked)
|
||||
{
|
||||
__instance._raftUndockCountDown = __instance._raft.dropDelay;
|
||||
__instance._state = RaftCarrier.DockState.WaitForExit;
|
||||
__instance._raft.SetRailingRaised(true);
|
||||
if (__instance._gearInterface != null)
|
||||
{
|
||||
__instance._gearInterface.AddRotation(90f);
|
||||
}
|
||||
|
||||
__instance.enabled = true;
|
||||
|
||||
__instance.GetWorldObject<QSBRaftDock>().SendMessage(new RaftDockOnPressInteractMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance._raftUndockCountDown = __instance._raft.dropDelay;
|
||||
__instance._state = RaftCarrier.DockState.WaitForExit;
|
||||
__instance._raft.SetRailingRaised(true);
|
||||
if (__instance._gearInterface != null)
|
||||
{
|
||||
__instance._gearInterface.PlayFailure();
|
||||
__instance._gearInterface.AddRotation(90f);
|
||||
}
|
||||
|
||||
__instance.enabled = true;
|
||||
|
||||
__instance.GetWorldObject<QSBRaftDock>().SendMessage(new RaftDockOnPressInteractMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance._gearInterface != null)
|
||||
{
|
||||
__instance._gearInterface.PlayFailure();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,22 +5,21 @@ using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync
|
||||
namespace QSB.EchoesOfTheEye.RaftSync;
|
||||
|
||||
public class RaftManager : WorldObjectManager
|
||||
{
|
||||
public class RaftManager : WorldObjectManager
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public static readonly List<RaftController> Rafts = new();
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
Rafts.Clear();
|
||||
Rafts.AddRange(QSBWorldSync.GetUnityObjects<RaftController>().SortDeterministic());
|
||||
QSBWorldSync.Init<QSBRaft, RaftController>(Rafts);
|
||||
|
||||
public static readonly List<RaftController> Rafts = new();
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
Rafts.Clear();
|
||||
Rafts.AddRange(QSBWorldSync.GetUnityObjects<RaftController>().SortDeterministic());
|
||||
QSBWorldSync.Init<QSBRaft, RaftController>(Rafts);
|
||||
|
||||
QSBWorldSync.Init<QSBRaftDock, RaftDock>();
|
||||
QSBWorldSync.Init<QSBDamRaftLift, DamRaftLift>();
|
||||
}
|
||||
QSBWorldSync.Init<QSBRaftDock, RaftDock>();
|
||||
QSBWorldSync.Init<QSBDamRaftLift, DamRaftLift>();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,83 +5,82 @@ using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.TransformSync
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.TransformSync;
|
||||
|
||||
public class RaftTransformSync : UnsectoredRigidbodySync
|
||||
{
|
||||
public class RaftTransformSync : UnsectoredRigidbodySync
|
||||
protected override bool UseInterpolation => false;
|
||||
|
||||
private QSBRaft _qsbRaft;
|
||||
private static readonly List<RaftTransformSync> _instances = new();
|
||||
|
||||
protected override OWRigidbody InitAttachedRigidbody() => _qsbRaft.AttachedObject._raftBody;
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
protected override bool UseInterpolation => false;
|
||||
|
||||
private QSBRaft _qsbRaft;
|
||||
private static readonly List<RaftTransformSync> _instances = new();
|
||||
|
||||
protected override OWRigidbody InitAttachedRigidbody() => _qsbRaft.AttachedObject._raftBody;
|
||||
|
||||
public override void OnStartClient()
|
||||
_instances.Add(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
_instances.Add(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.RegisterAuthQueue();
|
||||
}
|
||||
|
||||
base.OnStartClient();
|
||||
netIdentity.RegisterAuthQueue();
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
_instances.Remove(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
base.OnStartClient();
|
||||
}
|
||||
|
||||
base.OnStopClient();
|
||||
public override void OnStopClient()
|
||||
{
|
||||
_instances.Remove(this);
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
netIdentity.UnregisterAuthQueue();
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
_qsbRaft = RaftManager.Rafts[_instances.IndexOf(this)].GetWorldObject<QSBRaft>();
|
||||
_qsbRaft.TransformSync = this;
|
||||
base.OnStopClient();
|
||||
}
|
||||
|
||||
base.Init();
|
||||
SetReferenceTransform(AttachedRigidbody.GetOrigParent());
|
||||
protected override void Init()
|
||||
{
|
||||
_qsbRaft = RaftManager.Rafts[_instances.IndexOf(this)].GetWorldObject<QSBRaft>();
|
||||
_qsbRaft.TransformSync = this;
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody += OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody += OnSuspend;
|
||||
netIdentity.UpdateAuthQueue(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
base.Init();
|
||||
SetReferenceTransform(AttachedRigidbody.GetOrigParent());
|
||||
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody += OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody += OnSuspend;
|
||||
netIdentity.UpdateAuthQueue(AttachedRigidbody.IsSuspended() ? AuthQueueAction.Remove : AuthQueueAction.Add);
|
||||
}
|
||||
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
protected override void Uninit()
|
||||
{
|
||||
base.Uninit();
|
||||
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
|
||||
AttachedRigidbody.OnUnsuspendOWRigidbody -= OnUnsuspend;
|
||||
AttachedRigidbody.OnSuspendOWRigidbody -= OnSuspend;
|
||||
}
|
||||
|
||||
public override void OnStartAuthority() => DebugLog.DebugWrite($"{this} - authority = true");
|
||||
public override void OnStopAuthority() => DebugLog.DebugWrite($"{this} - authority = false");
|
||||
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
|
||||
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
|
||||
|
||||
/// <summary>
|
||||
/// replacement for base method
|
||||
/// using SetPos/Rot instead of Move
|
||||
/// </summary>
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
var targetPos = ReferenceTransform.FromRelPos(transform.position);
|
||||
var targetRot = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
public override void OnStartAuthority() => DebugLog.DebugWrite($"{this} - authority = true");
|
||||
public override void OnStopAuthority() => DebugLog.DebugWrite($"{this} - authority = false");
|
||||
|
||||
AttachedRigidbody.SetPosition(targetPos);
|
||||
AttachedRigidbody.SetRotation(targetRot);
|
||||
/// <summary>
|
||||
/// replacement for base method
|
||||
/// using SetPos/Rot instead of Move
|
||||
/// </summary>
|
||||
protected override void ApplyToAttached()
|
||||
{
|
||||
var targetPos = ReferenceTransform.FromRelPos(transform.position);
|
||||
var targetRot = ReferenceTransform.FromRelRot(transform.rotation);
|
||||
|
||||
var targetVelocity = ReferenceRigidbody.FromRelVel(Velocity, targetPos);
|
||||
var targetAngularVelocity = ReferenceRigidbody.FromRelAngVel(AngularVelocity);
|
||||
AttachedRigidbody.SetPosition(targetPos);
|
||||
AttachedRigidbody.SetRotation(targetRot);
|
||||
|
||||
AttachedRigidbody.SetVelocity(targetVelocity);
|
||||
AttachedRigidbody.SetAngularVelocity(targetAngularVelocity);
|
||||
}
|
||||
var targetVelocity = ReferenceRigidbody.FromRelVel(Velocity, targetPos);
|
||||
var targetAngularVelocity = ReferenceRigidbody.FromRelAngVel(AngularVelocity);
|
||||
|
||||
AttachedRigidbody.SetVelocity(targetVelocity);
|
||||
AttachedRigidbody.SetAngularVelocity(targetAngularVelocity);
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects;
|
||||
|
||||
public class QSBDamRaftLift : WorldObject<DamRaftLift>
|
||||
{
|
||||
public class QSBDamRaftLift : WorldObject<DamRaftLift>
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
// todo SendInitialState
|
||||
}
|
||||
}
|
||||
}
|
@ -8,58 +8,57 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects;
|
||||
|
||||
public class QSBRaft : WorldObject<RaftController>
|
||||
{
|
||||
public class QSBRaft : WorldObject<RaftController>
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public RaftTransformSync TransformSync;
|
||||
|
||||
private QSBLightSensor[] _lightSensors;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public RaftTransformSync TransformSync;
|
||||
|
||||
private QSBLightSensor[] _lightSensors;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
NetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.singleton.RaftPrefab));
|
||||
}
|
||||
|
||||
await UniTask.WaitUntil(() => TransformSync, cancellationToken: ct);
|
||||
|
||||
await UniTask.WaitUntil(() => QSBWorldSync.AllObjectsAdded, cancellationToken: ct);
|
||||
_lightSensors = AttachedObject._lightSensors.Select(x => x.GetWorldObject<QSBLightSensor>()).ToArray();
|
||||
|
||||
foreach (var lightSensor in _lightSensors)
|
||||
{
|
||||
lightSensor.OnDetectLocalLight += OnDetectLocalLight;
|
||||
}
|
||||
NetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.singleton.RaftPrefab));
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
NetworkServer.Destroy(TransformSync.gameObject);
|
||||
}
|
||||
await UniTask.WaitUntil(() => TransformSync, cancellationToken: ct);
|
||||
|
||||
foreach (var lightSensor in _lightSensors)
|
||||
{
|
||||
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
||||
}
|
||||
}
|
||||
await UniTask.WaitUntil(() => QSBWorldSync.AllObjectsAdded, cancellationToken: ct);
|
||||
_lightSensors = AttachedObject._lightSensors.Select(x => x.GetWorldObject<QSBLightSensor>()).ToArray();
|
||||
|
||||
private void OnDetectLocalLight()
|
||||
foreach (var lightSensor in _lightSensors)
|
||||
{
|
||||
if (AttachedObject.IsPlayerRiding())
|
||||
{
|
||||
TransformSync.netIdentity.UpdateAuthQueue(AuthQueueAction.Force);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo?? SendInitialState
|
||||
lightSensor.OnDetectLocalLight += OnDetectLocalLight;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
NetworkServer.Destroy(TransformSync.gameObject);
|
||||
}
|
||||
|
||||
foreach (var lightSensor in _lightSensors)
|
||||
{
|
||||
lightSensor.OnDetectLocalLight -= OnDetectLocalLight;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetectLocalLight()
|
||||
{
|
||||
if (AttachedObject.IsPlayerRiding())
|
||||
{
|
||||
TransformSync.netIdentity.UpdateAuthQueue(AuthQueueAction.Force);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo?? SendInitialState
|
||||
}
|
||||
}
|
@ -1,34 +1,33 @@
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects;
|
||||
|
||||
public class QSBRaftDock : WorldObject<RaftDock>
|
||||
{
|
||||
public class QSBRaftDock : WorldObject<RaftDock>
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public void OnPressInteract()
|
||||
{
|
||||
if (AttachedObject._raft != null && AttachedObject._state == RaftCarrier.DockState.Docked)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public void OnPressInteract()
|
||||
{
|
||||
if (AttachedObject._raft != null && AttachedObject._state == RaftCarrier.DockState.Docked)
|
||||
{
|
||||
AttachedObject._raftUndockCountDown = AttachedObject._raft.dropDelay;
|
||||
AttachedObject._state = RaftCarrier.DockState.WaitForExit;
|
||||
AttachedObject._raft.SetRailingRaised(true);
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
AttachedObject._gearInterface.AddRotation(90f);
|
||||
}
|
||||
|
||||
AttachedObject.enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject._raftUndockCountDown = AttachedObject._raft.dropDelay;
|
||||
AttachedObject._state = RaftCarrier.DockState.WaitForExit;
|
||||
AttachedObject._raft.SetRailingRaised(true);
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
AttachedObject._gearInterface.PlayFailure();
|
||||
AttachedObject._gearInterface.AddRotation(90f);
|
||||
}
|
||||
|
||||
AttachedObject.enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
AttachedObject._gearInterface.PlayFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using QSB.EchoesOfTheEye.SlideProjectors.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages;
|
||||
|
||||
internal class NextSlideMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
{
|
||||
internal class NextSlideMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
{
|
||||
public override void OnReceiveRemote() => WorldObject.NextSlide();
|
||||
}
|
||||
public override void OnReceiveRemote() => WorldObject.NextSlide();
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using QSB.EchoesOfTheEye.SlideProjectors.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages;
|
||||
|
||||
internal class PreviousSlideMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
{
|
||||
internal class PreviousSlideMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
{
|
||||
public override void OnReceiveRemote() => WorldObject.PreviousSlide();
|
||||
}
|
||||
public override void OnReceiveRemote() => WorldObject.PreviousSlide();
|
||||
}
|
@ -2,54 +2,53 @@
|
||||
using QSB.EchoesOfTheEye.SlideProjectors.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Messages;
|
||||
|
||||
public class ProjectorAuthorityMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
{
|
||||
public class ProjectorAuthorityMessage : QSBWorldObjectMessage<QSBSlideProjector>
|
||||
private uint AuthorityOwner;
|
||||
|
||||
public ProjectorAuthorityMessage(uint authorityOwner) => AuthorityOwner = authorityOwner;
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
private uint AuthorityOwner;
|
||||
base.Serialize(writer);
|
||||
writer.Write(AuthorityOwner);
|
||||
}
|
||||
|
||||
public ProjectorAuthorityMessage(uint authorityOwner) => AuthorityOwner = authorityOwner;
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
AuthorityOwner = reader.Read<uint>();
|
||||
}
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
public override bool ShouldReceive
|
||||
{
|
||||
get
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(AuthorityOwner);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
AuthorityOwner = reader.Read<uint>();
|
||||
}
|
||||
|
||||
public override bool ShouldReceive
|
||||
{
|
||||
get
|
||||
if (!base.ShouldReceive)
|
||||
{
|
||||
if (!base.ShouldReceive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deciding if to change the object's owner
|
||||
// Message
|
||||
// | = 0 | > 0 |
|
||||
// = 0 | No | Yes |
|
||||
// > 0 | Yes | No |
|
||||
// if Obj==Message then No
|
||||
// Obj
|
||||
|
||||
return (WorldObject.ControllingPlayer == 0 || AuthorityOwner == 0)
|
||||
&& WorldObject.ControllingPlayer != AuthorityOwner;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
// Deciding if to change the object's owner
|
||||
// Message
|
||||
// | = 0 | > 0 |
|
||||
// = 0 | No | Yes |
|
||||
// > 0 | Yes | No |
|
||||
// if Obj==Message then No
|
||||
// Obj
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.ControllingPlayer = AuthorityOwner;
|
||||
WorldObject.OnChangeAuthority(AuthorityOwner);
|
||||
return (WorldObject.ControllingPlayer == 0 || AuthorityOwner == 0)
|
||||
&& WorldObject.ControllingPlayer != AuthorityOwner;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.ControllingPlayer = AuthorityOwner;
|
||||
WorldObject.OnChangeAuthority(AuthorityOwner);
|
||||
}
|
||||
}
|
@ -6,34 +6,33 @@ using QSB.Patches;
|
||||
using QSB.Player;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Patches
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.Patches;
|
||||
|
||||
internal class ProjectorPatches : QSBPatch
|
||||
{
|
||||
internal class ProjectorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.OnPressInteract))]
|
||||
public static void Interact(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new ProjectorAuthorityMessage(QSBPlayerManager.LocalPlayerId));
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.OnPressInteract))]
|
||||
public static void Interact(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new ProjectorAuthorityMessage(QSBPlayerManager.LocalPlayerId));
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.CancelInteraction))]
|
||||
public static void CancelInteract(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new ProjectorAuthorityMessage(0));
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.CancelInteraction))]
|
||||
public static void CancelInteract(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new ProjectorAuthorityMessage(0));
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.NextSlide))]
|
||||
public static void NextSlide(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new NextSlideMessage());
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.NextSlide))]
|
||||
public static void NextSlide(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new NextSlideMessage());
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.PreviousSlide))]
|
||||
public static void PreviousSlide(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new PreviousSlideMessage());
|
||||
}
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideProjector), nameof(SlideProjector.PreviousSlide))]
|
||||
public static void PreviousSlide(SlideProjector __instance) =>
|
||||
__instance.GetWorldObject<QSBSlideProjector>()
|
||||
.SendMessage(new PreviousSlideMessage());
|
||||
}
|
@ -3,12 +3,11 @@ using QSB.EchoesOfTheEye.SlideProjectors.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors
|
||||
{
|
||||
internal class SlideProjectorManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBSlideProjector, SlideProjector>();
|
||||
}
|
||||
internal class SlideProjectorManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => QSBWorldSync.Init<QSBSlideProjector, SlideProjector>();
|
||||
}
|
@ -3,81 +3,80 @@ using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.WorldObjects
|
||||
namespace QSB.EchoesOfTheEye.SlideProjectors.WorldObjects;
|
||||
|
||||
public class QSBSlideProjector : WorldObject<SlideProjector>
|
||||
{
|
||||
public class QSBSlideProjector : WorldObject<SlideProjector>
|
||||
public uint ControllingPlayer;
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
public uint ControllingPlayer;
|
||||
DebugLog.DebugWrite($"Init {this}");
|
||||
}
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
DebugLog.DebugWrite($"Init {this}");
|
||||
}
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
// todo SendInitialState
|
||||
}
|
||||
public void OnChangeAuthority(uint newOwner)
|
||||
{
|
||||
DebugLog.DebugWrite($"{this} change ControllingPlayer to {newOwner}");
|
||||
}
|
||||
|
||||
public void OnChangeAuthority(uint newOwner)
|
||||
public void NextSlide()
|
||||
{
|
||||
var hasChangedSlide = false;
|
||||
if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.NextSlideAvailable())
|
||||
{
|
||||
DebugLog.DebugWrite($"{this} change ControllingPlayer to {newOwner}");
|
||||
}
|
||||
|
||||
public void NextSlide()
|
||||
{
|
||||
var hasChangedSlide = false;
|
||||
if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.NextSlideAvailable())
|
||||
hasChangedSlide = AttachedObject._slideItem.slidesContainer.IncreaseSlideIndex();
|
||||
if (hasChangedSlide)
|
||||
{
|
||||
hasChangedSlide = AttachedObject._slideItem.slidesContainer.IncreaseSlideIndex();
|
||||
if (hasChangedSlide)
|
||||
if (AttachedObject._oneShotSource != null)
|
||||
{
|
||||
if (AttachedObject._oneShotSource != null)
|
||||
{
|
||||
AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Next);
|
||||
}
|
||||
AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Next);
|
||||
}
|
||||
|
||||
if (AttachedObject.IsProjectorFullyLit())
|
||||
{
|
||||
AttachedObject._slideItem.slidesContainer.SetCurrentRead();
|
||||
AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(true);
|
||||
}
|
||||
if (AttachedObject.IsProjectorFullyLit())
|
||||
{
|
||||
AttachedObject._slideItem.slidesContainer.SetCurrentRead();
|
||||
AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AttachedObject._gearInterface != null)
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
var audioVolume = hasChangedSlide ? 0f : 0.5f;
|
||||
AttachedObject._gearInterface.AddRotation(45f, audioVolume);
|
||||
}
|
||||
}
|
||||
|
||||
public void PreviousSlide()
|
||||
{
|
||||
var hasChangedSlide = false;
|
||||
if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.PrevSlideAvailable())
|
||||
{
|
||||
hasChangedSlide = AttachedObject._slideItem.slidesContainer.DecreaseSlideIndex();
|
||||
if (hasChangedSlide)
|
||||
{
|
||||
var audioVolume = hasChangedSlide ? 0f : 0.5f;
|
||||
AttachedObject._gearInterface.AddRotation(45f, audioVolume);
|
||||
if (AttachedObject._oneShotSource != null)
|
||||
{
|
||||
AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Prev);
|
||||
}
|
||||
|
||||
if (AttachedObject.IsProjectorFullyLit())
|
||||
{
|
||||
AttachedObject._slideItem.slidesContainer.SetCurrentRead();
|
||||
AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PreviousSlide()
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
var hasChangedSlide = false;
|
||||
if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.PrevSlideAvailable())
|
||||
{
|
||||
hasChangedSlide = AttachedObject._slideItem.slidesContainer.DecreaseSlideIndex();
|
||||
if (hasChangedSlide)
|
||||
{
|
||||
if (AttachedObject._oneShotSource != null)
|
||||
{
|
||||
AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Prev);
|
||||
}
|
||||
|
||||
if (AttachedObject.IsProjectorFullyLit())
|
||||
{
|
||||
AttachedObject._slideItem.slidesContainer.SetCurrentRead();
|
||||
AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AttachedObject._gearInterface != null)
|
||||
{
|
||||
var audioVolume = hasChangedSlide ? 0f : 0.5f;
|
||||
AttachedObject._gearInterface.AddRotation(-45f, audioVolume);
|
||||
}
|
||||
var audioVolume = hasChangedSlide ? 0f : 0.5f;
|
||||
AttachedObject._gearInterface.AddRotation(-45f, audioVolume);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,12 @@ using QSB.ElevatorSync.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.ElevatorSync
|
||||
{
|
||||
public class ElevatorManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
namespace QSB.ElevatorSync;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBElevator, Elevator>();
|
||||
}
|
||||
public class ElevatorManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
=> QSBWorldSync.Init<QSBElevator, Elevator>();
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using QSB.ElevatorSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.ElevatorSync.Messages
|
||||
{
|
||||
public class ElevatorMessage : QSBWorldObjectMessage<QSBElevator, bool>
|
||||
{
|
||||
public ElevatorMessage(bool isGoingUp) => Data = isGoingUp;
|
||||
namespace QSB.ElevatorSync.Messages;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.RemoteCall(Data);
|
||||
}
|
||||
public class ElevatorMessage : QSBWorldObjectMessage<QSBElevator, bool>
|
||||
{
|
||||
public ElevatorMessage(bool isGoingUp) => Data = isGoingUp;
|
||||
|
||||
public override void OnReceiveRemote() => WorldObject.RemoteCall(Data);
|
||||
}
|
@ -5,20 +5,19 @@ using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.ElevatorSync.Patches
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class ElevatorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
namespace QSB.ElevatorSync.Patches;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(Elevator), nameof(Elevator.StartLift))]
|
||||
public static void Elevator_StartLift(Elevator __instance)
|
||||
{
|
||||
var isGoingUp = __instance._goingToTheEnd;
|
||||
var qsbElevator = __instance.GetWorldObject<QSBElevator>();
|
||||
qsbElevator.SendMessage(new ElevatorMessage(isGoingUp));
|
||||
}
|
||||
[HarmonyPatch]
|
||||
public class ElevatorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(Elevator), nameof(Elevator.StartLift))]
|
||||
public static void Elevator_StartLift(Elevator __instance)
|
||||
{
|
||||
var isGoingUp = __instance._goingToTheEnd;
|
||||
var qsbElevator = __instance.GetWorldObject<QSBElevator>();
|
||||
qsbElevator.SendMessage(new ElevatorMessage(isGoingUp));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user