Merge pull request #619 from misternebula/update-mirror

Update Mirror
This commit is contained in:
_nebula 2023-04-26 23:49:28 +01:00 committed by GitHub
commit 9ab9158a50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1491 additions and 1608 deletions

View File

@ -50,12 +50,11 @@ using System.Collections.Generic;
/// MIT License /// MIT License
/// </summary> /// </summary>
namespace EpicTransport; namespace EpicTransport {
public class BidirectionalDictionary<T1, T2> : IEnumerable public class BidirectionalDictionary<T1, T2> : IEnumerable {
{ private Dictionary<T1, T2> t1ToT2Dict = new Dictionary<T1, T2>();
private Dictionary<T1, T2> t1ToT2Dict = new(); private Dictionary<T2, T1> t2ToT1Dict = new Dictionary<T2, T1>();
private Dictionary<T2, T1> t2ToT1Dict = new();
public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys; public IEnumerable<T1> FirstTypes => t1ToT2Dict.Keys;
public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys; public IEnumerable<T2> SecondTypes => t2ToT1Dict.Keys;
@ -64,14 +63,12 @@ public class BidirectionalDictionary<T1, T2> : IEnumerable
public int Count => t1ToT2Dict.Count; public int Count => t1ToT2Dict.Count;
public void Add(T1 key, T2 value) public void Add(T1 key, T2 value) {
{
t1ToT2Dict[key] = value; t1ToT2Dict[key] = value;
t2ToT1Dict[value] = key; t2ToT1Dict[value] = key;
} }
public void Add(T2 key, T1 value) public void Add(T2 key, T1 value) {
{
t2ToT1Dict[key] = value; t2ToT1Dict[key] = value;
t1ToT2Dict[value] = key; t1ToT2Dict[value] = key;
} }
@ -88,43 +85,36 @@ public class BidirectionalDictionary<T1, T2> : IEnumerable
public bool Contains(T2 key) => t2ToT1Dict.ContainsKey(key); public bool Contains(T2 key) => t2ToT1Dict.ContainsKey(key);
public void Remove(T1 key) public void Remove(T1 key) {
{ if (Contains(key)) {
if (Contains(key)) T2 val = t1ToT2Dict[key];
{
var val = t1ToT2Dict[key];
t1ToT2Dict.Remove(key); t1ToT2Dict.Remove(key);
t2ToT1Dict.Remove(val); t2ToT1Dict.Remove(val);
} }
} }
public void Remove(T2 key) {
public void Remove(T2 key) if (Contains(key)) {
{ T1 val = t2ToT1Dict[key];
if (Contains(key))
{
var val = t2ToT1Dict[key];
t1ToT2Dict.Remove(val); t1ToT2Dict.Remove(val);
t2ToT1Dict.Remove(key); t2ToT1Dict.Remove(key);
} }
} }
public T1 this[T2 key] public T1 this[T2 key] {
{
get => t2ToT1Dict[key]; get => t2ToT1Dict[key];
set set {
{
t2ToT1Dict[key] = value; t2ToT1Dict[key] = value;
t1ToT2Dict[value] = key; t1ToT2Dict[value] = key;
} }
} }
public T2 this[T1 key] public T2 this[T1 key] {
{
get => t1ToT2Dict[key]; get => t1ToT2Dict[key];
set set {
{
t1ToT2Dict[key] = value; t1ToT2Dict[key] = value;
t2ToT1Dict[value] = key; t2ToT1Dict[value] = key;
} }
} }
}
} }

View File

@ -1,14 +1,14 @@
using Epic.OnlineServices; using Epic.OnlineServices;
using Epic.OnlineServices.P2P; using Epic.OnlineServices.P2P;
using Mirror;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace EpicTransport; namespace EpicTransport {
public class Client : Common {
public class Client : Common
{
public SocketId socketId; public SocketId socketId;
public ProductUserId serverId; public ProductUserId serverId;
@ -18,6 +18,7 @@ public class Client : Common
private event Action<byte[], int> OnReceivedData; private event Action<byte[], int> OnReceivedData;
private event Action OnConnected; private event Action OnConnected;
public event Action OnDisconnected; public event Action OnDisconnected;
// CHANGED
private Action<string> SetTransportError; private Action<string> SetTransportError;
private TimeSpan ConnectionTimeout; private TimeSpan ConnectionTimeout;
@ -28,11 +29,12 @@ public class Client : Common
private TaskCompletionSource<Task> connectedComplete; private TaskCompletionSource<Task> connectedComplete;
private CancellationTokenSource cancelToken; private CancellationTokenSource cancelToken;
private Client(EosTransport transport) : base(transport) => ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.timeout)); private Client(EosTransport transport) : base(transport) {
ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.timeout));
}
public static Client CreateClient(EosTransport transport, string host) public static Client CreateClient(EosTransport transport, string host) {
{ Client c = new Client(transport);
var c = new Client(transport);
c.hostAddress = host; c.hostAddress = host;
c.socketId = new SocketId() { SocketName = RandomString.Generate(20) }; c.socketId = new SocketId() { SocketName = RandomString.Generate(20) };
@ -40,17 +42,16 @@ public class Client : Common
c.OnConnected += () => transport.OnClientConnected.Invoke(); c.OnConnected += () => transport.OnClientConnected.Invoke();
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke(); c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel); c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel);
// CHANGED
c.SetTransportError = transport.SetTransportError; c.SetTransportError = transport.SetTransportError;
return c; return c;
} }
public async void Connect(string host) public async void Connect(string host) {
{
cancelToken = new CancellationTokenSource(); cancelToken = new CancellationTokenSource();
try try {
{
hostProductId = ProductUserId.FromString(host); hostProductId = ProductUserId.FromString(host);
serverId = hostProductId; serverId = hostProductId;
connectedComplete = new TaskCompletionSource<Task>(); connectedComplete = new TaskCompletionSource<Task>();
@ -61,8 +62,8 @@ public class Client : Common
Task connectedCompleteTask = connectedComplete.Task; Task connectedCompleteTask = connectedComplete.Task;
if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout /*, cancelToken.Token*/)) != connectedCompleteTask) if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout/*, cancelToken.Token*/)) != connectedCompleteTask) {
{ // CHANGED
SetTransportError($"Connection to {host} timed out."); SetTransportError($"Connection to {host} timed out.");
Debug.LogError($"Connection to {host} timed out."); Debug.LogError($"Connection to {host} timed out.");
OnConnected -= SetConnectedComplete; OnConnected -= SetConnectedComplete;
@ -70,40 +71,32 @@ public class Client : Common
} }
OnConnected -= SetConnectedComplete; OnConnected -= SetConnectedComplete;
} } catch (FormatException) {
catch (FormatException) // CHANGED
{
SetTransportError("Connection string was not in the right format. Did you enter a ProductId?"); 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?"); Debug.LogError($"Connection string was not in the right format. Did you enter a ProductId?");
Error = true; Error = true;
OnConnectionFailed(hostProductId); OnConnectionFailed(hostProductId);
} } catch (Exception ex) {
catch (Exception ex) // CHANGED
{
SetTransportError(ex.Message); SetTransportError(ex.Message);
Debug.LogError(ex.Message); Debug.LogError(ex.Message);
Error = true; Error = true;
OnConnectionFailed(hostProductId); OnConnectionFailed(hostProductId);
} } finally {
finally if (Error) {
{
if (Error)
{
OnConnectionFailed(null); OnConnectionFailed(null);
} }
} }
} }
public void Disconnect() public void Disconnect() {
{ if (serverId != null) {
if (serverId != null)
{
CloseP2PSessionWithUser(serverId, socketId); CloseP2PSessionWithUser(serverId, socketId);
serverId = null; serverId = null;
} } else {
else
{
return; return;
} }
@ -117,15 +110,12 @@ public class Client : Common
private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task); private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task);
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel) protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
if (clientUserId != hostProductId) if (clientUserId != hostProductId) {
{
Debug.LogError("Received a message from an unknown"); Debug.LogError("Received a message from an unknown");
return; return;
} }
@ -133,50 +123,41 @@ public class Client : Common
OnReceivedData.Invoke(data, channel); OnReceivedData.Invoke(data, channel);
} }
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result) protected override void OnNewConnection(OnIncomingConnectionRequestInfo result) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
if (deadSockets.Contains(result.SocketId.SocketName)) if (deadSockets.Contains(result.SocketId.SocketName)) {
{
Debug.LogError("Received incoming connection request from dead socket"); Debug.LogError("Received incoming connection request from dead socket");
return; return;
} }
if (hostProductId == result.RemoteUserId) if (hostProductId == result.RemoteUserId) {
{
EOSSDKComponent.GetP2PInterface().AcceptConnection( EOSSDKComponent.GetP2PInterface().AcceptConnection(
new AcceptConnectionOptions() new AcceptConnectionOptions() {
{
LocalUserId = EOSSDKComponent.LocalUserProductId, LocalUserId = EOSSDKComponent.LocalUserProductId,
RemoteUserId = result.RemoteUserId, RemoteUserId = result.RemoteUserId,
SocketId = result.SocketId SocketId = result.SocketId
}); });
} } else {
else
{
Debug.LogError("P2P Acceptance Request from unknown host ID."); Debug.LogError("P2P Acceptance Request from unknown host ID.");
} }
} }
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId) protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
switch (type) switch (type) {
{
case InternalMessages.ACCEPT_CONNECT: case InternalMessages.ACCEPT_CONNECT:
Connected = true; Connected = true;
OnConnected.Invoke(); OnConnected.Invoke();
Debug.Log("Connection established."); Debug.Log("Connection established.");
break; break;
case InternalMessages.DISCONNECT: case InternalMessages.DISCONNECT:
// CHANGED
SetTransportError("host disconnected"); SetTransportError("host disconnected");
Connected = false; Connected = false;
Debug.Log("Disconnected."); Debug.Log("Disconnected.");
@ -189,8 +170,9 @@ public class Client : Common
} }
} }
public void Send(byte[] data, int channelId) => Send(hostProductId, socketId, data, (byte)channelId); public void Send(byte[] data, int channelId) => Send(hostProductId, socketId, data, (byte) channelId);
protected override void OnConnectionFailed(ProductUserId remoteId) => OnDisconnected.Invoke(); protected override void OnConnectionFailed(ProductUserId remoteId) => OnDisconnected.Invoke();
public void EosNotInitialized() => OnDisconnected.Invoke(); public void EosNotInitialized() => OnDisconnected.Invoke();
}
} }

View File

@ -1,34 +1,32 @@
using Epic.OnlineServices; 
using Epic.OnlineServices;
using Epic.OnlineServices.P2P; using Epic.OnlineServices.P2P;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace EpicTransport; namespace EpicTransport {
public abstract class Common {
public abstract class Common
{
private PacketReliability[] channels; private PacketReliability[] channels;
private int internal_ch => channels.Length; private int internal_ch => channels.Length;
protected enum InternalMessages : byte protected enum InternalMessages : byte {
{
CONNECT, CONNECT,
ACCEPT_CONNECT, ACCEPT_CONNECT,
DISCONNECT DISCONNECT
} }
protected struct PacketKey protected struct PacketKey {
{
public ProductUserId productUserId; public ProductUserId productUserId;
public byte channel; public byte channel;
} }
private OnIncomingConnectionRequestCallback OnIncomingConnectionRequest; private OnIncomingConnectionRequestCallback OnIncomingConnectionRequest;
private ulong incomingNotificationId = 0; ulong incomingNotificationId = 0;
private OnRemoteConnectionClosedCallback OnRemoteConnectionClosed; private OnRemoteConnectionClosedCallback OnRemoteConnectionClosed;
private ulong outgoingNotificationId = 0; ulong outgoingNotificationId = 0;
protected readonly EosTransport transport; protected readonly EosTransport transport;
@ -36,15 +34,14 @@ public abstract class Common
public bool ignoreAllMessages = false; public bool ignoreAllMessages = false;
// Mapping from PacketKey to a List of Packet Lists // Mapping from PacketKey to a List of Packet Lists
protected Dictionary<PacketKey, List<List<Packet>>> incomingPackets = new(); protected Dictionary<PacketKey, List<List<Packet>>> incomingPackets = new Dictionary<PacketKey, List<List<Packet>>>();
protected Common(EosTransport transport) protected Common(EosTransport transport) {
{
channels = transport.Channels; channels = transport.Channels;
deadSockets = new List<string>(); deadSockets = new List<string>();
var addNotifyPeerConnectionRequestOptions = new AddNotifyPeerConnectionRequestOptions(); AddNotifyPeerConnectionRequestOptions addNotifyPeerConnectionRequestOptions = new AddNotifyPeerConnectionRequestOptions();
addNotifyPeerConnectionRequestOptions.LocalUserId = EOSSDKComponent.LocalUserProductId; addNotifyPeerConnectionRequestOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
addNotifyPeerConnectionRequestOptions.SocketId = null; addNotifyPeerConnectionRequestOptions.SocketId = null;
@ -54,25 +51,24 @@ public abstract class Common
incomingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionRequest(addNotifyPeerConnectionRequestOptions, incomingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionRequest(addNotifyPeerConnectionRequestOptions,
null, OnIncomingConnectionRequest); null, OnIncomingConnectionRequest);
var addNotifyPeerConnectionClosedOptions = new AddNotifyPeerConnectionClosedOptions(); AddNotifyPeerConnectionClosedOptions addNotifyPeerConnectionClosedOptions = new AddNotifyPeerConnectionClosedOptions();
addNotifyPeerConnectionClosedOptions.LocalUserId = EOSSDKComponent.LocalUserProductId; addNotifyPeerConnectionClosedOptions.LocalUserId = EOSSDKComponent.LocalUserProductId;
addNotifyPeerConnectionClosedOptions.SocketId = null; addNotifyPeerConnectionClosedOptions.SocketId = null;
outgoingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionClosed(addNotifyPeerConnectionClosedOptions, outgoingNotificationId = EOSSDKComponent.GetP2PInterface().AddNotifyPeerConnectionClosed(addNotifyPeerConnectionClosedOptions,
null, OnRemoteConnectionClosed); null, OnRemoteConnectionClosed);
if (outgoingNotificationId == 0 || incomingNotificationId == 0) if (outgoingNotificationId == 0 || incomingNotificationId == 0) {
{
Debug.LogError("Couldn't bind notifications with P2P interface"); Debug.LogError("Couldn't bind notifications with P2P interface");
} }
incomingPackets = new Dictionary<PacketKey, List<List<Packet>>>(); incomingPackets = new Dictionary<PacketKey, List<List<Packet>>>();
this.transport = transport; this.transport = transport;
} }
protected void Dispose() protected void Dispose() {
{
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionRequest(incomingNotificationId); EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionRequest(incomingNotificationId);
EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionClosed(outgoingNotificationId); EOSSDKComponent.GetP2PInterface().RemoveNotifyPeerConnectionClosed(outgoingNotificationId);
@ -81,17 +77,14 @@ public abstract class Common
protected abstract void OnNewConnection(OnIncomingConnectionRequestInfo result); protected abstract void OnNewConnection(OnIncomingConnectionRequestInfo result);
private void OnConnectFail(OnRemoteConnectionClosedInfo result) private void OnConnectFail(OnRemoteConnectionClosedInfo result) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
OnConnectionFailed(result.RemoteUserId); OnConnectionFailed(result.RemoteUserId);
switch (result.Reason) switch (result.Reason) {
{
case ConnectionClosedReason.ClosedByLocalUser: case ConnectionClosedReason.ClosedByLocalUser:
throw new Exception("Connection cLosed: The Connection was gracecfully closed by the local user."); throw new Exception("Connection cLosed: The Connection was gracecfully closed by the local user.");
case ConnectionClosedReason.ClosedByPeer: case ConnectionClosedReason.ClosedByPeer:
@ -118,13 +111,11 @@ public abstract class Common
} }
} }
protected void SendInternal(ProductUserId target, SocketId socketId, InternalMessages type) protected void SendInternal(ProductUserId target, SocketId socketId, InternalMessages type) {
{ EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions() {
EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions()
{
AllowDelayedDelivery = true, AllowDelayedDelivery = true,
Channel = (byte)internal_ch, Channel = (byte) internal_ch,
Data = new byte[] { (byte)type }, Data = new byte[] { (byte) type },
LocalUserId = EOSSDKComponent.LocalUserProductId, LocalUserId = EOSSDKComponent.LocalUserProductId,
Reliability = PacketReliability.ReliableOrdered, Reliability = PacketReliability.ReliableOrdered,
RemoteUserId = target, RemoteUserId = target,
@ -132,10 +123,9 @@ public abstract class Common
}); });
} }
protected void Send(ProductUserId host, SocketId socketId, byte[] msgBuffer, byte channel)
{ protected void Send(ProductUserId host, SocketId socketId, byte[] msgBuffer, byte channel) {
var result = EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions() Result result = EOSSDKComponent.GetP2PInterface().SendPacket(new SendPacketOptions() {
{
AllowDelayedDelivery = true, AllowDelayedDelivery = true,
Channel = channel, Channel = channel,
Data = msgBuffer, Data = msgBuffer,
@ -145,23 +135,19 @@ public abstract class Common
SocketId = socketId SocketId = socketId
}); });
if (result != Result.Success) if(result != Result.Success) {
{
Debug.LogError("Send failed " + result); Debug.LogError("Send failed " + result);
} }
} }
private bool Receive(out ProductUserId clientProductUserId, out SocketId socketId, out byte[] receiveBuffer, byte channel) private bool Receive(out ProductUserId clientProductUserId, out SocketId socketId, out byte[] receiveBuffer, byte channel) {
{ Result result = EOSSDKComponent.GetP2PInterface().ReceivePacket(new ReceivePacketOptions() {
var result = EOSSDKComponent.GetP2PInterface().ReceivePacket(new ReceivePacketOptions()
{
LocalUserId = EOSSDKComponent.LocalUserProductId, LocalUserId = EOSSDKComponent.LocalUserProductId,
MaxDataSizeBytes = P2PInterface.MaxPacketSize, MaxDataSizeBytes = P2PInterface.MaxPacketSize,
RequestedChannel = channel RequestedChannel = channel
}, out clientProductUserId, out socketId, out channel, out receiveBuffer); }, out clientProductUserId, out socketId, out channel, out receiveBuffer);
if (result == Result.Success) if (result == Result.Success) {
{
return true; return true;
} }
@ -170,160 +156,127 @@ public abstract class Common
return false; return false;
} }
protected virtual void CloseP2PSessionWithUser(ProductUserId clientUserID, SocketId socketId) protected virtual void CloseP2PSessionWithUser(ProductUserId clientUserID, SocketId socketId) {
{ if (socketId == null) {
if (socketId == null)
{
Debug.LogWarning("Socket ID == null | " + ignoreAllMessages); Debug.LogWarning("Socket ID == null | " + ignoreAllMessages);
return; return;
} }
if (deadSockets == null) if (deadSockets == null) {
{
Debug.LogWarning("DeadSockets == null"); Debug.LogWarning("DeadSockets == null");
return; return;
} }
if (deadSockets.Contains(socketId.SocketName)) if (deadSockets.Contains(socketId.SocketName)) {
{
return; return;
} } else {
else
{
deadSockets.Add(socketId.SocketName); deadSockets.Add(socketId.SocketName);
} }
} }
protected void WaitForClose(ProductUserId clientUserID, SocketId socketId) => transport.StartCoroutine(DelayedClose(clientUserID, socketId));
private IEnumerator DelayedClose(ProductUserId clientUserID, SocketId socketId) protected void WaitForClose(ProductUserId clientUserID, SocketId socketId) => transport.StartCoroutine(DelayedClose(clientUserID, socketId));
{ private IEnumerator DelayedClose(ProductUserId clientUserID, SocketId socketId) {
yield return null; yield return null;
CloseP2PSessionWithUser(clientUserID, socketId); CloseP2PSessionWithUser(clientUserID, socketId);
} }
public void ReceiveData() public void ReceiveData() {
{ try {
try
{
// Internal Channel, no fragmentation here // Internal Channel, no fragmentation here
var socketId = new SocketId(); SocketId socketId = new SocketId();
while (transport.enabled && Receive(out var clientUserID, out socketId, out var internalMessage, (byte)internal_ch)) while (transport.enabled && Receive(out ProductUserId clientUserID, out socketId, out byte[] internalMessage, (byte) internal_ch)) {
{ if (internalMessage.Length == 1) {
if (internalMessage.Length == 1) OnReceiveInternalData((InternalMessages) internalMessage[0], clientUserID, socketId);
{
OnReceiveInternalData((InternalMessages)internalMessage[0], clientUserID, socketId);
return; // Wait one frame return; // Wait one frame
} } else {
else
{
Debug.Log("Incorrect package length on internal channel."); Debug.Log("Incorrect package length on internal channel.");
} }
} }
// Insert new packet at the correct location in the incoming queue // Insert new packet at the correct location in the incoming queue
for (var chNum = 0; chNum < channels.Length; chNum++) for (int chNum = 0; chNum < channels.Length; chNum++) {
{ while (transport.enabled && Receive(out ProductUserId clientUserID, out socketId, out byte[] receiveBuffer, (byte) chNum)) {
while (transport.enabled && Receive(out var clientUserID, out socketId, out var receiveBuffer, (byte)chNum)) PacketKey incomingPacketKey = new PacketKey();
{
var incomingPacketKey = new PacketKey();
incomingPacketKey.productUserId = clientUserID; incomingPacketKey.productUserId = clientUserID;
incomingPacketKey.channel = (byte)chNum; incomingPacketKey.channel = (byte)chNum;
var packet = new Packet(); Packet packet = new Packet();
packet.FromBytes(receiveBuffer); packet.FromBytes(receiveBuffer);
if (!incomingPackets.ContainsKey(incomingPacketKey)) if (!incomingPackets.ContainsKey(incomingPacketKey)) {
{
incomingPackets.Add(incomingPacketKey, new List<List<Packet>>()); incomingPackets.Add(incomingPacketKey, new List<List<Packet>>());
} }
var packetListIndex = incomingPackets[incomingPacketKey].Count; int packetListIndex = incomingPackets[incomingPacketKey].Count;
for (var i = 0; i < incomingPackets[incomingPacketKey].Count; i++) for(int i = 0; i < incomingPackets[incomingPacketKey].Count; i++) {
{ if(incomingPackets[incomingPacketKey][i][0].id == packet.id) {
if (incomingPackets[incomingPacketKey][i][0].id == packet.id)
{
packetListIndex = i; packetListIndex = i;
break; break;
} }
} }
if (packetListIndex == incomingPackets[incomingPacketKey].Count) if (packetListIndex == incomingPackets[incomingPacketKey].Count) {
{
incomingPackets[incomingPacketKey].Add(new List<Packet>()); incomingPackets[incomingPacketKey].Add(new List<Packet>());
} }
var insertionIndex = -1; int insertionIndex = -1;
for (var i = 0; i < incomingPackets[incomingPacketKey][packetListIndex].Count; i++) for (int i = 0; i < incomingPackets[incomingPacketKey][packetListIndex].Count; i++) {
{ if (incomingPackets[incomingPacketKey][packetListIndex][i].fragment > packet.fragment) {
if (incomingPackets[incomingPacketKey][packetListIndex][i].fragment > packet.fragment)
{
insertionIndex = i; insertionIndex = i;
break; break;
} }
} }
if (insertionIndex >= 0) if (insertionIndex >= 0) {
{
incomingPackets[incomingPacketKey][packetListIndex].Insert(insertionIndex, packet); incomingPackets[incomingPacketKey][packetListIndex].Insert(insertionIndex, packet);
} } else {
else
{
incomingPackets[incomingPacketKey][packetListIndex].Add(packet); incomingPackets[incomingPacketKey][packetListIndex].Add(packet);
} }
} }
} }
// Find fully received packets // Find fully received packets
var emptyPacketLists = new List<List<Packet>>(); List<List<Packet>> emptyPacketLists = new List<List<Packet>>();
foreach (var keyValuePair in incomingPackets) foreach(KeyValuePair<PacketKey, List<List<Packet>>> keyValuePair in incomingPackets) {
{ for(int packetList = 0; packetList < keyValuePair.Value.Count; packetList++) {
for (var packetList = 0; packetList < keyValuePair.Value.Count; packetList++) bool packetReady = true;
{ int packetLength = 0;
var packetReady = true; for (int packet = 0; packet < keyValuePair.Value[packetList].Count; packet++) {
var packetLength = 0; Packet tempPacket = keyValuePair.Value[packetList][packet];
for (var packet = 0; packet < keyValuePair.Value[packetList].Count; packet++) if (tempPacket.fragment != packet || (packet == keyValuePair.Value[packetList].Count - 1 && tempPacket.moreFragments)) {
{
var tempPacket = keyValuePair.Value[packetList][packet];
if (tempPacket.fragment != packet || packet == keyValuePair.Value[packetList].Count - 1 && tempPacket.moreFragments)
{
packetReady = false; packetReady = false;
} } else {
else
{
packetLength += tempPacket.data.Length; packetLength += tempPacket.data.Length;
} }
} }
if (packetReady) if (packetReady) {
{ byte[] data = new byte[packetLength];
var data = new byte[packetLength]; int dataIndex = 0;
var dataIndex = 0;
for (var packet = 0; packet < keyValuePair.Value[packetList].Count; packet++) for (int packet = 0; packet < keyValuePair.Value[packetList].Count; packet++) {
{
Array.Copy(keyValuePair.Value[packetList][packet].data, 0, data, dataIndex, keyValuePair.Value[packetList][packet].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; dataIndex += keyValuePair.Value[packetList][packet].data.Length;
} }
OnReceiveData(data, keyValuePair.Key.productUserId, keyValuePair.Key.channel); OnReceiveData(data, keyValuePair.Key.productUserId, keyValuePair.Key.channel);
//keyValuePair.Value[packetList].Clear(); if(transport.ServerActive() || transport.ClientActive())
emptyPacketLists.Add(keyValuePair.Value[packetList]); emptyPacketLists.Add(keyValuePair.Value[packetList]);
} }
} }
for (var i = 0; i < emptyPacketLists.Count; i++) for (int i = 0; i < emptyPacketLists.Count; i++) {
{
keyValuePair.Value.Remove(emptyPacketLists[i]); keyValuePair.Value.Remove(emptyPacketLists[i]);
} }
emptyPacketLists.Clear(); emptyPacketLists.Clear();
} }
}
catch (Exception e)
{
} catch (Exception e) {
Debug.LogException(e); Debug.LogException(e);
} }
} }
@ -331,4 +284,5 @@ public abstract class Common
protected abstract void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserID, SocketId socketId); protected abstract void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserID, SocketId socketId);
protected abstract void OnReceiveData(byte[] data, ProductUserId clientUserID, int channel); protected abstract void OnReceiveData(byte[] data, ProductUserId clientUserID, int channel);
protected abstract void OnConnectionFailed(ProductUserId remoteId); protected abstract void OnConnectionFailed(ProductUserId remoteId);
}
} }

View File

@ -1,6 +1,10 @@
using Epic.OnlineServices; using Epic.OnlineServices;
using Epic.OnlineServices.Logging; using Epic.OnlineServices.Logging;
using Epic.OnlineServices.Platform; using Epic.OnlineServices.Platform;
using System;
using System.Runtime.InteropServices;
using UnityEngine; using UnityEngine;
/// <summary> /// <summary>
@ -10,39 +14,42 @@ using UnityEngine;
/// after releasing the SDK the game has to be restarted in order to initialize the SDK again. /// 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. /// In the unity editor the OnDestroy function will not run so that we dont have to restart the editor after play.
/// </summary> /// </summary>
namespace EpicTransport; namespace EpicTransport {
[DefaultExecutionOrder(-32000)]
public class EOSSDKComponent : MonoBehaviour {
[DefaultExecutionOrder(-32000)]
public class EOSSDKComponent : MonoBehaviour
{
// Unity Inspector shown variables // Unity Inspector shown variables
[SerializeField] [SerializeField]
// CHANGED
public EosApiKey apiKeys; public EosApiKey apiKeys;
[Header("User Login")] [Header("User Login")]
public bool authInterfaceLogin = false; public bool authInterfaceLogin = false;
public Epic.OnlineServices.Auth.LoginCredentialType authInterfaceCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal; public Epic.OnlineServices.Auth.LoginCredentialType authInterfaceCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
public uint devAuthToolPort = 7878; public uint devAuthToolPort = 7878;
public string devAuthToolCredentialName = ""; public string devAuthToolCredentialName = "";
public ExternalCredentialType connectInterfaceCredentialType = ExternalCredentialType.DeviceidAccessToken; public Epic.OnlineServices.ExternalCredentialType connectInterfaceCredentialType = Epic.OnlineServices.ExternalCredentialType.DeviceidAccessToken;
public string deviceModel = "PC Windows 64bit"; public string deviceModel = "PC Windows 64bit";
[SerializeField] private string displayName = "User"; [SerializeField] private string displayName = "User";
public static string DisplayName {
public static string DisplayName get {
{ return Instance.displayName;
get => Instance.displayName; }
set => Instance.displayName = value; set {
Instance.displayName = value;
}
} }
[Header("Misc")] [Header("Misc")]
public LogLevel epicLoggerLevel = LogLevel.Error; public LogLevel epicLoggerLevel = LogLevel.Error;
[SerializeField] [SerializeField] private bool collectPlayerMetrics = true;
private bool collectPlayerMetrics = true; public static bool CollectPlayerMetrics {
get {
public static bool CollectPlayerMetrics => Instance.collectPlayerMetrics; return Instance.collectPlayerMetrics;
}
}
public bool checkForEpicLauncherAndRestart = false; public bool checkForEpicLauncherAndRestart = false;
public bool delayedInitialization = false; public bool delayedInitialization = false;
@ -54,6 +61,7 @@ public class EOSSDKComponent : MonoBehaviour
private ulong authExpirationHandle; private ulong authExpirationHandle;
private string authInterfaceLoginCredentialId = null; private string authInterfaceLoginCredentialId = null;
public static void SetAuthInterfaceLoginCredentialId(string credentialId) => Instance.authInterfaceLoginCredentialId = credentialId; public static void SetAuthInterfaceLoginCredentialId(string credentialId) => Instance.authInterfaceLoginCredentialId = credentialId;
private string authInterfaceCredentialToken = null; private string authInterfaceCredentialToken = null;
@ -81,43 +89,61 @@ public class EOSSDKComponent : MonoBehaviour
public static Epic.OnlineServices.UI.UIInterface GetUIInterface() => Instance.EOS.GetUIInterface(); public static Epic.OnlineServices.UI.UIInterface GetUIInterface() => Instance.EOS.GetUIInterface();
public static Epic.OnlineServices.UserInfo.UserInfoInterface GetUserInfoInterface() => Instance.EOS.GetUserInfoInterface(); public static Epic.OnlineServices.UserInfo.UserInfoInterface GetUserInfoInterface() => Instance.EOS.GetUserInfoInterface();
protected EpicAccountId localUserAccountId; protected EpicAccountId localUserAccountId;
public static EpicAccountId LocalUserAccountId => Instance.localUserAccountId; public static EpicAccountId LocalUserAccountId {
get {
return Instance.localUserAccountId;
}
}
protected string localUserAccountIdString; protected string localUserAccountIdString;
public static string LocalUserAccountIdString => Instance.localUserAccountIdString; public static string LocalUserAccountIdString {
get {
return Instance.localUserAccountIdString;
}
}
protected ProductUserId localUserProductId; protected ProductUserId localUserProductId;
public static ProductUserId LocalUserProductId => Instance.localUserProductId; public static ProductUserId LocalUserProductId {
get {
return Instance.localUserProductId;
}
}
protected string localUserProductIdString; protected string localUserProductIdString;
public static string LocalUserProductIdString => Instance.localUserProductIdString; public static string LocalUserProductIdString {
get {
return Instance.localUserProductIdString;
}
}
protected bool initialized; protected bool initialized;
public static bool Initialized => Instance.initialized; public static bool Initialized {
get {
return Instance.initialized;
}
}
protected bool isConnecting; protected bool isConnecting;
public static bool IsConnecting => Instance.isConnecting; public static bool IsConnecting {
get {
return Instance.isConnecting;
}
}
protected static EOSSDKComponent instance; protected static EOSSDKComponent instance;
protected static EOSSDKComponent Instance {
protected static EOSSDKComponent Instance get {
{ if (instance == null) {
get
{
if (instance == null)
{
return new GameObject("EOSSDKComponent").AddComponent<EOSSDKComponent>(); return new GameObject("EOSSDKComponent").AddComponent<EOSSDKComponent>();
} } else {
else
{
return instance; return instance;
} }
} }
} }
public static void Tick() public static void Tick() {
{
instance.platformTickTimer -= Time.deltaTime; instance.platformTickTimer -= Time.deltaTime;
instance.EOS.Tick(); instance.EOS.Tick();
} }
@ -163,26 +189,23 @@ public class EOSSDKComponent : MonoBehaviour
private IntPtr libraryPointer; private IntPtr libraryPointer;
#endif #endif
private void Awake() private void Awake() {
{
// Initialize Java version of the SDK with a reference to the VM with JNI // 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 // 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) if (Application.platform == RuntimePlatform.Android)
{ {
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
var context = activity.Call<AndroidJavaObject>("getApplicationContext"); AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
var EOS_SDK_JAVA = new AndroidJavaClass("com.epicgames.mobile.eossdk.EOSSDK"); AndroidJavaClass EOS_SDK_JAVA = new AndroidJavaClass("com.epicgames.mobile.eossdk.EOSSDK");
EOS_SDK_JAVA.CallStatic("init", context); EOS_SDK_JAVA.CallStatic("init", context);
} }
// Prevent multiple instances // Prevent multiple instances
if (instance != null) if (instance != null) {
{
Destroy(gameObject); Destroy(gameObject);
return; return;
} }
instance = this; instance = this;
#if UNITY_EDITOR #if UNITY_EDITOR
@ -196,18 +219,15 @@ public class EOSSDKComponent : MonoBehaviour
Bindings.Hook(libraryPointer, GetProcAddress); Bindings.Hook(libraryPointer, GetProcAddress);
#endif #endif
if (!delayedInitialization) if (!delayedInitialization) {
{
Initialize(); Initialize();
} }
} }
protected void InitializeImplementation() protected void InitializeImplementation() {
{
isConnecting = true; isConnecting = true;
var initializeOptions = new InitializeOptions() var initializeOptions = new InitializeOptions() {
{
ProductName = apiKeys.epicProductName, ProductName = apiKeys.epicProductName,
ProductVersion = apiKeys.epicProductVersion ProductVersion = apiKeys.epicProductVersion
}; };
@ -216,8 +236,7 @@ public class EOSSDKComponent : MonoBehaviour
// 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. // 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; var isAlreadyConfiguredInEditor = Application.isEditor && initializeResult == Result.AlreadyConfigured;
if (initializeResult != Result.Success && !isAlreadyConfiguredInEditor) if (initializeResult != Result.Success && !isAlreadyConfiguredInEditor) {
{
throw new System.Exception("Failed to initialize platform: " + initializeResult); throw new System.Exception("Failed to initialize platform: " + initializeResult);
} }
@ -226,13 +245,11 @@ public class EOSSDKComponent : MonoBehaviour
LoggingInterface.SetLogLevel(LogCategory.AllCategories, epicLoggerLevel); LoggingInterface.SetLogLevel(LogCategory.AllCategories, epicLoggerLevel);
LoggingInterface.SetCallback(message => Logger.EpicDebugLog(message)); LoggingInterface.SetCallback(message => Logger.EpicDebugLog(message));
var options = new Options() var options = new Options() {
{
ProductId = apiKeys.epicProductId, ProductId = apiKeys.epicProductId,
SandboxId = apiKeys.epicSandboxId, SandboxId = apiKeys.epicSandboxId,
DeploymentId = apiKeys.epicDeploymentId, DeploymentId = apiKeys.epicDeploymentId,
ClientCredentials = new ClientCredentials() ClientCredentials = new ClientCredentials() {
{
ClientId = apiKeys.epicClientId, ClientId = apiKeys.epicClientId,
ClientSecret = apiKeys.epicClientSecret ClientSecret = apiKeys.epicClientSecret
}, },
@ -240,21 +257,18 @@ public class EOSSDKComponent : MonoBehaviour
}; };
EOS = PlatformInterface.Create(options); EOS = PlatformInterface.Create(options);
if (EOS == null) if (EOS == null) {
{
throw new System.Exception("Failed to create platform"); throw new System.Exception("Failed to create platform");
} }
if (checkForEpicLauncherAndRestart) if (checkForEpicLauncherAndRestart) {
{ Result result = EOS.CheckForLauncherAndRestart();
var result = EOS.CheckForLauncherAndRestart();
// If not started through epic launcher the app will be restarted and we can quit // If not started through epic launcher the app will be restarted and we can quit
if (result != Result.NoChange) if (result != Result.NoChange) {
{
// Log error if launcher check failed, but still quit to prevent hacking // Log error if launcher check failed, but still quit to prevent hacking
if (result == Result.UnexpectedError) if (result == Result.UnexpectedError) {
{
Debug.LogError("Unexpected Error while checking if app was started through epic launcher"); Debug.LogError("Unexpected Error while checking if app was started through epic launcher");
} }
@ -264,19 +278,15 @@ public class EOSSDKComponent : MonoBehaviour
// If we use the Auth interface then only login into the Connect interface after finishing the auth interface login // 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 we don't use the Auth interface we can directly login to the Connect interface
if (authInterfaceLogin) if (authInterfaceLogin) {
{ if (authInterfaceCredentialType == Epic.OnlineServices.Auth.LoginCredentialType.Developer) {
if (authInterfaceCredentialType == Epic.OnlineServices.Auth.LoginCredentialType.Developer)
{
authInterfaceLoginCredentialId = "localhost:" + devAuthToolPort; authInterfaceLoginCredentialId = "localhost:" + devAuthToolPort;
authInterfaceCredentialToken = devAuthToolCredentialName; authInterfaceCredentialToken = devAuthToolCredentialName;
} }
// Login to Auth Interface // Login to Auth Interface
var loginOptions = new Epic.OnlineServices.Auth.LoginOptions() Epic.OnlineServices.Auth.LoginOptions loginOptions = new Epic.OnlineServices.Auth.LoginOptions() {
{ Credentials = new Epic.OnlineServices.Auth.Credentials() {
Credentials = new Epic.OnlineServices.Auth.Credentials()
{
Type = authInterfaceCredentialType, Type = authInterfaceCredentialType,
Id = authInterfaceLoginCredentialId, Id = authInterfaceLoginCredentialId,
Token = authInterfaceCredentialToken Token = authInterfaceCredentialToken
@ -285,43 +295,33 @@ public class EOSSDKComponent : MonoBehaviour
}; };
EOS.GetAuthInterface().Login(loginOptions, null, OnAuthInterfaceLogin); EOS.GetAuthInterface().Login(loginOptions, null, OnAuthInterfaceLogin);
} } else {
else
{
// Login to Connect Interface // Login to Connect Interface
if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken) if (connectInterfaceCredentialType == Epic.OnlineServices.ExternalCredentialType.DeviceidAccessToken) {
{ Epic.OnlineServices.Connect.CreateDeviceIdOptions createDeviceIdOptions = new Epic.OnlineServices.Connect.CreateDeviceIdOptions();
var createDeviceIdOptions = new Epic.OnlineServices.Connect.CreateDeviceIdOptions();
createDeviceIdOptions.DeviceModel = deviceModel; createDeviceIdOptions.DeviceModel = deviceModel;
EOS.GetConnectInterface().CreateDeviceId(createDeviceIdOptions, null, OnCreateDeviceId); EOS.GetConnectInterface().CreateDeviceId(createDeviceIdOptions, null, OnCreateDeviceId);
} } else {
else
{
ConnectInterfaceLogin(); ConnectInterfaceLogin();
} }
} }
}
public static void Initialize() }
{ public static void Initialize() {
if (Instance.initialized || Instance.isConnecting) if (Instance.initialized || Instance.isConnecting) {
{
return; return;
} }
Instance.InitializeImplementation(); Instance.InitializeImplementation();
} }
private void OnAuthInterfaceLogin(Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo) private void OnAuthInterfaceLogin(Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo) {
{ if (loginCallbackInfo.ResultCode == Result.Success) {
if (loginCallbackInfo.ResultCode == Result.Success)
{
Debug.Log("Auth Interface Login succeeded"); Debug.Log("Auth Interface Login succeeded");
string accountIdString; string accountIdString;
var result = loginCallbackInfo.LocalUserId.ToString(out accountIdString); Result result = loginCallbackInfo.LocalUserId.ToString(out accountIdString);
if (Result.Success == result) if (Result.Success == result) {
{
Debug.Log("EOS User ID:" + accountIdString); Debug.Log("EOS User ID:" + accountIdString);
localUserAccountIdString = accountIdString; localUserAccountIdString = accountIdString;
@ -329,45 +329,32 @@ public class EOSSDKComponent : MonoBehaviour
} }
ConnectInterfaceLogin(); ConnectInterfaceLogin();
} } else if(Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode)){
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
{
Debug.Log("Login returned " + loginCallbackInfo.ResultCode); Debug.Log("Login returned " + loginCallbackInfo.ResultCode);
} }
} }
private void OnCreateDeviceId(Epic.OnlineServices.Connect.CreateDeviceIdCallbackInfo createDeviceIdCallbackInfo) private void OnCreateDeviceId(Epic.OnlineServices.Connect.CreateDeviceIdCallbackInfo createDeviceIdCallbackInfo) {
{ if (createDeviceIdCallbackInfo.ResultCode == Result.Success || createDeviceIdCallbackInfo.ResultCode == Result.DuplicateNotAllowed) {
if (createDeviceIdCallbackInfo.ResultCode == Result.Success || createDeviceIdCallbackInfo.ResultCode == Result.DuplicateNotAllowed)
{
ConnectInterfaceLogin(); ConnectInterfaceLogin();
} } else if(Epic.OnlineServices.Common.IsOperationComplete(createDeviceIdCallbackInfo.ResultCode)) {
else if (Epic.OnlineServices.Common.IsOperationComplete(createDeviceIdCallbackInfo.ResultCode))
{
Debug.Log("Device ID creation returned " + createDeviceIdCallbackInfo.ResultCode); Debug.Log("Device ID creation returned " + createDeviceIdCallbackInfo.ResultCode);
} }
} }
private void ConnectInterfaceLogin() private void ConnectInterfaceLogin() {
{
var loginOptions = new Epic.OnlineServices.Connect.LoginOptions(); var loginOptions = new Epic.OnlineServices.Connect.LoginOptions();
if (connectInterfaceCredentialType == ExternalCredentialType.Epic) if (connectInterfaceCredentialType == Epic.OnlineServices.ExternalCredentialType.Epic) {
{
Epic.OnlineServices.Auth.Token token; Epic.OnlineServices.Auth.Token token;
var result = EOS.GetAuthInterface().CopyUserAuthToken(new Epic.OnlineServices.Auth.CopyUserAuthTokenOptions(), localUserAccountId, out token); Result result = EOS.GetAuthInterface().CopyUserAuthToken(new Epic.OnlineServices.Auth.CopyUserAuthTokenOptions(), localUserAccountId, out token);
if (result == Result.Success) if (result == Result.Success) {
{
connectInterfaceCredentialToken = token.AccessToken; connectInterfaceCredentialToken = token.AccessToken;
} } else {
else
{
Debug.LogError("Failed to retrieve User Auth Token"); Debug.LogError("Failed to retrieve User Auth Token");
} }
} } else if (connectInterfaceCredentialType == Epic.OnlineServices.ExternalCredentialType.DeviceidAccessToken) {
else if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
{
loginOptions.UserLoginInfo = new Epic.OnlineServices.Connect.UserLoginInfo(); loginOptions.UserLoginInfo = new Epic.OnlineServices.Connect.UserLoginInfo();
loginOptions.UserLoginInfo.DisplayName = displayName; loginOptions.UserLoginInfo.DisplayName = displayName;
} }
@ -379,16 +366,13 @@ public class EOSSDKComponent : MonoBehaviour
EOS.GetConnectInterface().Login(loginOptions, null, OnConnectInterfaceLogin); EOS.GetConnectInterface().Login(loginOptions, null, OnConnectInterfaceLogin);
} }
private void OnConnectInterfaceLogin(Epic.OnlineServices.Connect.LoginCallbackInfo loginCallbackInfo) private void OnConnectInterfaceLogin(Epic.OnlineServices.Connect.LoginCallbackInfo loginCallbackInfo) {
{ if (loginCallbackInfo.ResultCode == Result.Success) {
if (loginCallbackInfo.ResultCode == Result.Success)
{
Debug.Log("Connect Interface Login succeeded"); Debug.Log("Connect Interface Login succeeded");
string productIdString; string productIdString;
var result = loginCallbackInfo.LocalUserId.ToString(out productIdString); Result result = loginCallbackInfo.LocalUserId.ToString(out productIdString);
if (Result.Success == result) if (Result.Success == result) {
{
Debug.Log("EOS User Product ID:" + productIdString); Debug.Log("EOS User Product ID:" + productIdString);
localUserProductIdString = productIdString; localUserProductIdString = productIdString;
@ -400,50 +384,36 @@ public class EOSSDKComponent : MonoBehaviour
var authExpirationOptions = new Epic.OnlineServices.Connect.AddNotifyAuthExpirationOptions(); var authExpirationOptions = new Epic.OnlineServices.Connect.AddNotifyAuthExpirationOptions();
authExpirationHandle = EOS.GetConnectInterface().AddNotifyAuthExpiration(authExpirationOptions, null, OnAuthExpiration); authExpirationHandle = EOS.GetConnectInterface().AddNotifyAuthExpiration(authExpirationOptions, null, OnAuthExpiration);
} } else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode)) {
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
{
Debug.Log("Login returned " + loginCallbackInfo.ResultCode + "\nRetrying..."); Debug.Log("Login returned " + loginCallbackInfo.ResultCode + "\nRetrying...");
EOS.GetConnectInterface().CreateUser(new Epic.OnlineServices.Connect.CreateUserOptions() { ContinuanceToken = loginCallbackInfo.ContinuanceToken }, null, (Epic.OnlineServices.Connect.CreateUserCallbackInfo cb) => 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; }
if (cb.ResultCode != Result.Success)
{
Debug.Log(cb.ResultCode);
return;
}
localUserProductId = cb.LocalUserId; localUserProductId = cb.LocalUserId;
ConnectInterfaceLogin(); ConnectInterfaceLogin();
}); });
} }
} }
private void OnAuthExpiration(Epic.OnlineServices.Connect.AuthExpirationCallbackInfo authExpirationCallbackInfo) private void OnAuthExpiration(Epic.OnlineServices.Connect.AuthExpirationCallbackInfo authExpirationCallbackInfo) {
{
Debug.Log("AuthExpiration callback"); Debug.Log("AuthExpiration callback");
EOS.GetConnectInterface().RemoveNotifyAuthExpiration(authExpirationHandle); EOS.GetConnectInterface().RemoveNotifyAuthExpiration(authExpirationHandle);
ConnectInterfaceLogin(); ConnectInterfaceLogin();
} }
// Calling tick on a regular interval is required for callbacks to work. // Calling tick on a regular interval is required for callbacks to work.
private void LateUpdate() private void LateUpdate() {
{ if (EOS != null) {
if (EOS != null)
{
platformTickTimer += Time.deltaTime; platformTickTimer += Time.deltaTime;
if (platformTickTimer >= platformTickIntervalInSeconds) if (platformTickTimer >= platformTickIntervalInSeconds) {
{
platformTickTimer = 0; platformTickTimer = 0;
EOS.Tick(); EOS.Tick();
} }
} }
} }
private void OnApplicationQuit() private void OnApplicationQuit() {
{ if (EOS != null) {
if (EOS != null)
{
EOS.Release(); EOS.Release();
EOS = null; EOS = null;
PlatformInterface.Shutdown(); PlatformInterface.Shutdown();
@ -461,4 +431,5 @@ public class EOSSDKComponent : MonoBehaviour
} }
#endif #endif
} }
}
} }

View File

@ -1,3 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
/// <summary> /// <summary>
@ -8,9 +10,9 @@ using UnityEngine;
/// Create -> EOS -> API Key /// Create -> EOS -> API Key
/// in order to create an instance of this scriptable object /// in order to create an instance of this scriptable object
/// </summary> /// </summary>
[CreateAssetMenu(fileName = "EosApiKey", menuName = "EOS/API Key", order = 1)] [CreateAssetMenu(fileName = "EosApiKey", menuName = "EOS/API Key", order = 1)]
public class EosApiKey : ScriptableObject public class EosApiKey : ScriptableObject {
{
public string epicProductName = "MyApplication"; public string epicProductName = "MyApplication";
public string epicProductVersion = "1.0"; public string epicProductVersion = "1.0";
public string epicProductId = ""; public string epicProductId = "";

View File

@ -1,18 +1,19 @@
using Epic.OnlineServices;
using Epic.OnlineServices.Metrics;
using Epic.OnlineServices.P2P;
using Mirror;
using System; using System;
using System.Collections; using System.Collections.Generic;
using System.IO;
using UnityEngine; using UnityEngine;
using Epic.OnlineServices.P2P;
using Epic.OnlineServices;
using Mirror;
using Epic.OnlineServices.Metrics;
using System.Collections;
namespace EpicTransport; namespace EpicTransport {
/// <summary> /// <summary>
/// EOS Transport following the Mirror transport standard /// EOS Transport following the Mirror transport standard
/// </summary> /// </summary>
public class EosTransport : Transport public class EosTransport : Transport {
{
private const string EPIC_SCHEME = "epic"; private const string EPIC_SCHEME = "epic";
private Client client; private Client client;
@ -40,20 +41,17 @@ public class EosTransport : Transport
private int packetId = 0; private int packetId = 0;
// CHANGED
public Action<string> SetTransportError; public Action<string> SetTransportError;
private void Awake() private void Awake() {
{
Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for EOS Transport."); 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"); Debug.Assert(Channels.Length < byte.MaxValue, "Too many channels configured for EOS Transport");
if (Channels[0] != PacketReliability.ReliableOrdered) 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."); 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) {
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."); 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.");
} }
@ -61,79 +59,59 @@ public class EosTransport : Transport
StartCoroutine("ChangeRelayStatus"); StartCoroutine("ChangeRelayStatus");
} }
public override void ClientEarlyUpdate() public override void ClientEarlyUpdate() {
{
EOSSDKComponent.Tick(); EOSSDKComponent.Tick();
if (activeNode != null) if (activeNode != null) {
{
ignoreCachedMessagesTimer += Time.deltaTime; ignoreCachedMessagesTimer += Time.deltaTime;
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds) if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds) {
{
activeNode.ignoreAllMessages = true; activeNode.ignoreAllMessages = true;
} } else {
else
{
activeNode.ignoreAllMessages = false; activeNode.ignoreAllMessages = false;
if (client != null && !client.isConnecting) if (client != null && !client.isConnecting) {
{ if (EOSSDKComponent.Initialized) {
if (EOSSDKComponent.Initialized)
{
client.Connect(client.hostAddress); client.Connect(client.hostAddress);
} } else {
else
{
Debug.LogError("EOS not initialized"); Debug.LogError("EOS not initialized");
client.EosNotInitialized(); client.EosNotInitialized();
} }
client.isConnecting = true; client.isConnecting = true;
} }
} }
} }
if (enabled) if (enabled) {
{
activeNode?.ReceiveData(); activeNode?.ReceiveData();
} }
} }
public override void ClientLateUpdate() { } public override void ClientLateUpdate() {}
public override void ServerEarlyUpdate() public override void ServerEarlyUpdate() {
{
EOSSDKComponent.Tick(); EOSSDKComponent.Tick();
if (activeNode != null) if (activeNode != null) {
{
ignoreCachedMessagesTimer += Time.deltaTime; ignoreCachedMessagesTimer += Time.deltaTime;
if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds) if (ignoreCachedMessagesTimer <= ignoreCachedMessagesAtStartUpInSeconds) {
{
activeNode.ignoreAllMessages = true; activeNode.ignoreAllMessages = true;
} } else {
else
{
activeNode.ignoreAllMessages = false; activeNode.ignoreAllMessages = false;
} }
} }
if (enabled) if (enabled) {
{
activeNode?.ReceiveData(); activeNode?.ReceiveData();
} }
} }
public override void ServerLateUpdate() { } public override void ServerLateUpdate() {}
public override bool ClientConnected() => ClientActive() && client.Connected; public override bool ClientConnected() => ClientActive() && client.Connected;
public override void ClientConnect(string address) {
public override void ClientConnect(string address) if (!EOSSDKComponent.Initialized) {
{
if (!EOSSDKComponent.Initialized)
{
Debug.LogError("EOS not initialized. Client could not be started."); Debug.LogError("EOS not initialized. Client could not be started.");
OnClientDisconnected.Invoke(); OnClientDisconnected.Invoke();
return; return;
@ -141,119 +119,96 @@ public class EosTransport : Transport
StartCoroutine("FetchEpicAccountId"); StartCoroutine("FetchEpicAccountId");
if (ServerActive()) if (ServerActive()) {
{
Debug.LogError("Transport already running as server!"); Debug.LogError("Transport already running as server!");
return; return;
} }
if (!ClientActive() || client.Error) if (!ClientActive() || client.Error) {
{
Debug.Log($"Starting client, target address {address}."); Debug.Log($"Starting client, target address {address}.");
client = Client.CreateClient(this, address); client = Client.CreateClient(this, address);
activeNode = client; activeNode = client;
if (EOSSDKComponent.CollectPlayerMetrics) if (EOSSDKComponent.CollectPlayerMetrics) {
{
// Start Metrics colletion session // Start Metrics colletion session
var sessionOptions = new BeginPlayerSessionOptions(); BeginPlayerSessionOptions sessionOptions = new BeginPlayerSessionOptions();
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId; sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
sessionOptions.ControllerType = UserControllerType.Unknown; sessionOptions.ControllerType = UserControllerType.Unknown;
sessionOptions.DisplayName = EOSSDKComponent.DisplayName; sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
sessionOptions.GameSessionId = null; sessionOptions.GameSessionId = null;
sessionOptions.ServerIp = null; sessionOptions.ServerIp = null;
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions); Result result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
if (result == Result.Success) if(result == Result.Success) {
{
Debug.Log("Started Metric Session"); Debug.Log("Started Metric Session");
} }
} }
} } else {
else
{
Debug.LogError("Client already running!"); Debug.LogError("Client already running!");
} }
} }
public override void ClientConnect(Uri uri) public override void ClientConnect(Uri uri) {
{
if (uri.Scheme != EPIC_SCHEME) if (uri.Scheme != EPIC_SCHEME)
{
throw new ArgumentException($"Invalid url {uri}, use {EPIC_SCHEME}://EpicAccountId instead", nameof(uri)); throw new ArgumentException($"Invalid url {uri}, use {EPIC_SCHEME}://EpicAccountId instead", nameof(uri));
}
ClientConnect(uri.Host); ClientConnect(uri.Host);
} }
public override void ClientSend(ArraySegment<byte> segment, int channelId) public override void ClientSend(ArraySegment<byte> segment, int channelId) {
{
Send(channelId, segment); Send(channelId, segment);
} }
public override void ClientDisconnect() public override void ClientDisconnect() {
{ if (ClientActive()) {
if (ClientActive())
{
Shutdown(); Shutdown();
} }
} }
public bool ClientActive() => client != null; public bool ClientActive() => client != null;
public override bool ServerActive() => server != null;
public override void ServerStart() public override bool ServerActive() => server != null;
{ public override void ServerStart() {
if (!EOSSDKComponent.Initialized) if (!EOSSDKComponent.Initialized) {
{
Debug.LogError("EOS not initialized. Server could not be started."); Debug.LogError("EOS not initialized. Server could not be started.");
return; return;
} }
StartCoroutine("FetchEpicAccountId"); StartCoroutine("FetchEpicAccountId");
if (ClientActive()) if (ClientActive()) {
{
Debug.LogError("Transport already running as client!"); Debug.LogError("Transport already running as client!");
return; return;
} }
if (!ServerActive()) if (!ServerActive()) {
{
Debug.Log("Starting server."); Debug.Log("Starting server.");
server = Server.CreateServer(this, NetworkManager.singleton.maxConnections); server = Server.CreateServer(this, NetworkManager.singleton.maxConnections);
activeNode = server; activeNode = server;
if (EOSSDKComponent.CollectPlayerMetrics) if (EOSSDKComponent.CollectPlayerMetrics) {
{
// Start Metrics colletion session // Start Metrics colletion session
var sessionOptions = new BeginPlayerSessionOptions(); BeginPlayerSessionOptions sessionOptions = new BeginPlayerSessionOptions();
sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId; sessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
sessionOptions.ControllerType = UserControllerType.Unknown; sessionOptions.ControllerType = UserControllerType.Unknown;
sessionOptions.DisplayName = EOSSDKComponent.DisplayName; sessionOptions.DisplayName = EOSSDKComponent.DisplayName;
sessionOptions.GameSessionId = null; sessionOptions.GameSessionId = null;
sessionOptions.ServerIp = null; sessionOptions.ServerIp = null;
var result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions); Result result = EOSSDKComponent.GetMetricsInterface().BeginPlayerSession(sessionOptions);
if (result == Result.Success) if (result == Result.Success) {
{
Debug.Log("Started Metric Session"); Debug.Log("Started Metric Session");
} }
} }
} } else {
else
{
Debug.LogError("Server already started!"); Debug.LogError("Server already started!");
} }
} }
public override Uri ServerUri() public override Uri ServerUri() {
{ UriBuilder epicBuilder = new UriBuilder {
var epicBuilder = new UriBuilder
{
Scheme = EPIC_SCHEME, Scheme = EPIC_SCHEME,
Host = EOSSDKComponent.LocalUserProductIdString Host = EOSSDKComponent.LocalUserProductIdString
}; };
@ -261,37 +216,32 @@ public class EosTransport : Transport
return epicBuilder.Uri; return epicBuilder.Uri;
} }
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId) public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId) {
{ if (ServerActive()) {
if (ServerActive()) Send( channelId, segment, connectionId);
{
Send(channelId, segment, connectionId);
} }
} }
public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId); public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId);
public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty; public override string ServerGetClientAddress(int connectionId) => ServerActive() ? server.ServerGetClientAddress(connectionId) : string.Empty;
public override void ServerStop() {
public override void ServerStop() if (ServerActive()) {
{
if (ServerActive())
{
Shutdown(); Shutdown();
} }
} }
private void Send(int channelId, ArraySegment<byte> segment, int connectionId = int.MinValue) private void Send(int channelId, ArraySegment<byte> segment, int connectionId = int.MinValue) {
{ Packet[] packets = GetPacketArray(channelId, segment);
var packets = GetPacketArray(channelId, segment);
for (var i = 0; i < packets.Length; i++) for(int i = 0; i < packets.Length; i++) {
if (connectionId == int.MinValue) {
if (client == null)
{ {
if (connectionId == int.MinValue) OnClientDisconnected.Invoke();
{ return;
client.Send(packets[i].ToBytes(), channelId);
} }
else
{ client.Send(packets[i].ToBytes(), channelId);
} else {
server.SendAll(connectionId, packets[i].ToBytes(), channelId); server.SendAll(connectionId, packets[i].ToBytes(), channelId);
} }
} }
@ -299,19 +249,17 @@ public class EosTransport : Transport
packetId++; packetId++;
} }
private Packet[] GetPacketArray(int channelId, ArraySegment<byte> segment) private Packet[] GetPacketArray(int channelId, ArraySegment<byte> segment) {
{ int packetCount = Mathf.CeilToInt((float) segment.Count / (float)GetMaxSinglePacketSize(channelId));
var packetCount = Mathf.CeilToInt((float)segment.Count / (float)GetMaxSinglePacketSize(channelId)); Packet[] packets = new Packet[packetCount];
var packets = new Packet[packetCount];
for (var i = 0; i < segment.Count; i += GetMaxSinglePacketSize(channelId)) for (int i = 0; i < segment.Count; i += GetMaxSinglePacketSize(channelId)) {
{ int fragment = i / GetMaxSinglePacketSize(channelId);
var fragment = i / GetMaxSinglePacketSize(channelId);
packets[fragment] = new Packet(); packets[fragment] = new Packet();
packets[fragment].id = packetId; packets[fragment].id = packetId;
packets[fragment].fragment = fragment; packets[fragment].fragment = fragment;
packets[fragment].moreFragments = segment.Count - i > GetMaxSinglePacketSize(channelId); packets[fragment].moreFragments = (segment.Count - i) > GetMaxSinglePacketSize(channelId);
packets[fragment].data = new byte[segment.Count - i > GetMaxSinglePacketSize(channelId) ? GetMaxSinglePacketSize(channelId) : segment.Count - i]; 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); Array.Copy(segment.Array, i, packets[fragment].data, 0, packets[fragment].data.Length);
} }
@ -319,17 +267,14 @@ public class EosTransport : Transport
return packets; return packets;
} }
public override void Shutdown() public override void Shutdown() {
{ if (EOSSDKComponent.CollectPlayerMetrics) {
if (EOSSDKComponent.CollectPlayerMetrics)
{
// Stop Metrics collection session // Stop Metrics collection session
var endSessionOptions = new EndPlayerSessionOptions(); EndPlayerSessionOptions endSessionOptions = new EndPlayerSessionOptions();
endSessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId; endSessionOptions.AccountId = EOSSDKComponent.LocalUserAccountId;
var result = EOSSDKComponent.GetMetricsInterface().EndPlayerSession(endSessionOptions); Result result = EOSSDKComponent.GetMetricsInterface().EndPlayerSession(endSessionOptions);
if (result == Result.Success) if (result == Result.Success) {
{
Debug.LogError("Stopped Metric Session"); Debug.LogError("Stopped Metric Session");
} }
} }
@ -349,51 +294,41 @@ public class EosTransport : Transport
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 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 bool Available() {
{ try {
try
{
return EOSSDKComponent.Initialized; return EOSSDKComponent.Initialized;
} } catch {
catch
{
return false; return false;
} }
} }
private IEnumerator FetchEpicAccountId() private IEnumerator FetchEpicAccountId() {
{ while (!EOSSDKComponent.Initialized) {
while (!EOSSDKComponent.Initialized)
{
yield return null; yield return null;
} }
productUserId = EOSSDKComponent.LocalUserProductId; productUserId = EOSSDKComponent.LocalUserProductId;
} }
private IEnumerator ChangeRelayStatus() private IEnumerator ChangeRelayStatus() {
{ while (!EOSSDKComponent.Initialized) {
while (!EOSSDKComponent.Initialized)
{
yield return null; yield return null;
} }
var setRelayControlOptions = new SetRelayControlOptions(); SetRelayControlOptions setRelayControlOptions = new SetRelayControlOptions();
setRelayControlOptions.RelayControl = relayControl; setRelayControlOptions.RelayControl = relayControl;
EOSSDKComponent.GetP2PInterface().SetRelayControl(setRelayControlOptions); EOSSDKComponent.GetP2PInterface().SetRelayControl(setRelayControlOptions);
} }
public void ResetIgnoreMessagesAtStartUpTimer() public void ResetIgnoreMessagesAtStartUpTimer() {
{
ignoreCachedMessagesTimer = 0; ignoreCachedMessagesTimer = 0;
} }
private void OnDestroy() private void OnDestroy() {
{ if (activeNode != null) {
if (activeNode != null)
{
Shutdown(); Shutdown();
} }
} }
}
} }

View File

@ -1,15 +1,14 @@
using Epic.OnlineServices.Logging; using Epic.OnlineServices.Logging;
using System; using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace EpicTransport; namespace EpicTransport {
public static class Logger {
public static class Logger public static void EpicDebugLog(LogMessage message) {
{ switch (message.Level) {
public static void EpicDebugLog(LogMessage message)
{
switch (message.Level)
{
case LogLevel.Info: case LogLevel.Info:
Debug.Log($"Epic Manager: Category - {message.Category} Message - {message.Message}"); Debug.Log($"Epic Manager: Category - {message.Category} Message - {message.Message}");
break; break;
@ -27,4 +26,5 @@ public static class Logger
break; break;
} }
} }
}
} }

View File

@ -1,9 +1,7 @@
using System; using System;
namespace EpicTransport; namespace EpicTransport {
public struct Packet {
public struct Packet
{
public const int headerSize = sizeof(uint) + sizeof(uint) + 1; public const int headerSize = sizeof(uint) + sizeof(uint) + 1;
public int size => headerSize + data.Length; public int size => headerSize + data.Length;
@ -15,21 +13,20 @@ public struct Packet
// body // body
public byte[] data; public byte[] data;
public byte[] ToBytes() public byte[] ToBytes() {
{ byte[] array = new byte[size];
var array = new byte[size];
// Copy id // Copy id
array[0] = (byte)id; array[0] = (byte) id;
array[1] = (byte)(id >> 8); array[1] = (byte) (id >> 8);
array[2] = (byte)(id >> 0x10); array[2] = (byte) (id >> 0x10);
array[3] = (byte)(id >> 0x18); array[3] = (byte) (id >> 0x18);
// Copy fragment // Copy fragment
array[4] = (byte)fragment; array[4] = (byte) fragment;
array[5] = (byte)(fragment >> 8); array[5] = (byte) (fragment >> 8);
array[6] = (byte)(fragment >> 0x10); array[6] = (byte) (fragment >> 0x10);
array[7] = (byte)(fragment >> 0x18); array[7] = (byte) (fragment >> 0x18);
array[8] = moreFragments ? (byte)1 : (byte)0; array[8] = moreFragments ? (byte)1 : (byte)0;
@ -38,8 +35,7 @@ public struct Packet
return array; return array;
} }
public void FromBytes(byte[] array) public void FromBytes(byte[] array) {
{
id = BitConverter.ToInt32(array, 0); id = BitConverter.ToInt32(array, 0);
fragment = BitConverter.ToInt32(array, 4); fragment = BitConverter.ToInt32(array, 4);
moreFragments = array[8] == 1; moreFragments = array[8] == 1;
@ -47,4 +43,5 @@ public struct Packet
data = new byte[array.Length - 9]; data = new byte[array.Length - 9];
Array.Copy(array, 9, data, 0, data.Length); Array.Copy(array, 9, data, 0, data.Length);
} }
}
} }

View File

@ -1,14 +1,13 @@
using System; using System;
using System.Text; using System.Text;
public class RandomString public class RandomString {
{
// Generates a random string with a given size. // Generates a random string with a given size.
public static string Generate(int size) public static string Generate(int size) {
{
var builder = new StringBuilder(size); var builder = new StringBuilder(size);
var random = new Random(); Random random = new Random();
// Unicode/ASCII Letters are divided into two blocks // Unicode/ASCII Letters are divided into two blocks
// (Letters 6590 / 97122): // (Letters 6590 / 97122):
@ -16,23 +15,19 @@ public class RandomString
// the second group containing the lowercase. // the second group containing the lowercase.
// char is a single Unicode character // char is a single Unicode character
var offsetLowerCase = 'a'; char offsetLowerCase = 'a';
var offsetUpperCase = 'A'; char offsetUpperCase = 'A';
const int lettersOffset = 26; // A...Z or a..z: length=26 const int lettersOffset = 26; // A...Z or a..z: length=26
for (var i = 0; i < size; i++) for (var i = 0; i < size; i++) {
{
char offset; char offset;
if (random.Next(0, 2) == 0) if(random.Next(0,2) == 0) {
{
offset = offsetLowerCase; offset = offsetLowerCase;
} } else {
else
{
offset = offsetUpperCase; offset = offsetUpperCase;
} }
var @char = (char)random.Next(offset, offset + lettersOffset); var @char = (char) random.Next(offset, offset + lettersOffset);
builder.Append(@char); builder.Append(@char);
} }

View File

@ -4,10 +4,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace EpicTransport; namespace EpicTransport {
public class Server : Common {
public class Server : Common
{
private event Action<int> OnConnected; private event Action<int> OnConnected;
private event Action<int, byte[], int> OnReceivedData; private event Action<int, byte[], int> OnReceivedData;
private event Action<int> OnDisconnected; private event Action<int> OnDisconnected;
@ -18,65 +16,55 @@ public class Server : Common
private int maxConnections; private int maxConnections;
private int nextConnectionID; private int nextConnectionID;
public static Server CreateServer(EosTransport transport, int maxConnections) public static Server CreateServer(EosTransport transport, int maxConnections) {
{ Server s = new Server(transport, maxConnections);
var s = new Server(transport, maxConnections);
s.OnConnected += (id) => transport.OnServerConnected.Invoke(id); s.OnConnected += (id) => transport.OnServerConnected.Invoke(id);
s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id); s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id);
s.OnReceivedData += (id, data, channel) => transport.OnServerDataReceived.Invoke(id, new ArraySegment<byte>(data), channel); s.OnReceivedData += (id, data, channel) => transport.OnServerDataReceived.Invoke(id, new ArraySegment<byte>(data), channel);
s.OnReceivedError += (id, exception) => transport.OnServerError.Invoke(id, exception); // CHANGED
s.OnReceivedError += (id, exception) => transport.OnServerError?.Invoke(id, Mirror.TransportError.Unexpected, exception.ToString());
if (!EOSSDKComponent.Initialized) if (!EOSSDKComponent.Initialized) {
{
Debug.LogError("EOS not initialized."); Debug.LogError("EOS not initialized.");
} }
return s; return s;
} }
private Server(EosTransport transport, int maxConnections) : base(transport) private Server(EosTransport transport, int maxConnections) : base(transport) {
{
this.maxConnections = maxConnections; this.maxConnections = maxConnections;
epicToMirrorIds = new BidirectionalDictionary<ProductUserId, int>(); epicToMirrorIds = new BidirectionalDictionary<ProductUserId, int>();
epicToSocketIds = new Dictionary<ProductUserId, SocketId>(); epicToSocketIds = new Dictionary<ProductUserId, SocketId>();
nextConnectionID = 1; nextConnectionID = 1;
} }
protected override void OnNewConnection(OnIncomingConnectionRequestInfo result) protected override void OnNewConnection(OnIncomingConnectionRequestInfo result) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
if (deadSockets.Contains(result.SocketId.SocketName)) if (deadSockets.Contains(result.SocketId.SocketName)) {
{
Debug.LogError("Received incoming connection request from dead socket"); Debug.LogError("Received incoming connection request from dead socket");
return; return;
} }
EOSSDKComponent.GetP2PInterface().AcceptConnection( EOSSDKComponent.GetP2PInterface().AcceptConnection(
new AcceptConnectionOptions() new AcceptConnectionOptions() {
{
LocalUserId = EOSSDKComponent.LocalUserProductId, LocalUserId = EOSSDKComponent.LocalUserProductId,
RemoteUserId = result.RemoteUserId, RemoteUserId = result.RemoteUserId,
SocketId = result.SocketId SocketId = result.SocketId
}); });
} }
protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId) protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
switch (type) switch (type) {
{
case InternalMessages.CONNECT: case InternalMessages.CONNECT:
if (epicToMirrorIds.Count >= maxConnections) if (epicToMirrorIds.Count >= maxConnections) {
{
Debug.LogError("Reached max connections"); Debug.LogError("Reached max connections");
//CloseP2PSessionWithUser(clientUserId, socketId); //CloseP2PSessionWithUser(clientUserId, socketId);
SendInternal(clientUserId, socketId, InternalMessages.DISCONNECT); SendInternal(clientUserId, socketId, InternalMessages.DISCONNECT);
@ -85,7 +73,7 @@ public class Server : Common
SendInternal(clientUserId, socketId, InternalMessages.ACCEPT_CONNECT); SendInternal(clientUserId, socketId, InternalMessages.ACCEPT_CONNECT);
var connectionId = nextConnectionID++; int connectionId = nextConnectionID++;
epicToMirrorIds.Add(clientUserId, connectionId); epicToMirrorIds.Add(clientUserId, connectionId);
epicToSocketIds.Add(clientUserId, socketId); epicToSocketIds.Add(clientUserId, socketId);
OnConnected.Invoke(connectionId); OnConnected.Invoke(connectionId);
@ -95,16 +83,13 @@ public class Server : Common
Debug.Log($"Client with Product User ID {clientUserIdString} connected. Assigning connection id {connectionId}"); Debug.Log($"Client with Product User ID {clientUserIdString} connected. Assigning connection id {connectionId}");
break; break;
case InternalMessages.DISCONNECT: case InternalMessages.DISCONNECT:
if (epicToMirrorIds.TryGetValue(clientUserId, out var connId)) if (epicToMirrorIds.TryGetValue(clientUserId, out int connId)) {
{
OnDisconnected.Invoke(connId); OnDisconnected.Invoke(connId);
//CloseP2PSessionWithUser(clientUserId, socketId); //CloseP2PSessionWithUser(clientUserId, socketId);
epicToMirrorIds.Remove(clientUserId); epicToMirrorIds.Remove(clientUserId);
epicToSocketIds.Remove(clientUserId); epicToSocketIds.Remove(clientUserId);
Debug.Log($"Client with Product User ID {clientUserId} disconnected."); Debug.Log($"Client with Product User ID {clientUserId} disconnected.");
} } else {
else
{
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown Product User ID")); OnReceivedError.Invoke(-1, new Exception("ERROR Unknown Product User ID"));
} }
@ -115,19 +100,14 @@ public class Server : Common
} }
} }
protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel) protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
if (epicToMirrorIds.TryGetValue(clientUserId, out var connectionId)) if (epicToMirrorIds.TryGetValue(clientUserId, out int connectionId)) {
{
OnReceivedData.Invoke(connectionId, data, channel); OnReceivedData.Invoke(connectionId, data, channel);
} } else {
else
{
SocketId socketId; SocketId socketId;
epicToSocketIds.TryGetValue(clientUserId, out socketId); epicToSocketIds.TryGetValue(clientUserId, out socketId);
CloseP2PSessionWithUser(clientUserId, socketId); CloseP2PSessionWithUser(clientUserId, socketId);
@ -140,26 +120,20 @@ public class Server : Common
} }
} }
public void Disconnect(int connectionId) public void Disconnect(int connectionId) {
{ if (epicToMirrorIds.TryGetValue(connectionId, out ProductUserId userId)) {
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
{
SocketId socketId; SocketId socketId;
epicToSocketIds.TryGetValue(userId, out socketId); epicToSocketIds.TryGetValue(userId, out socketId);
SendInternal(userId, socketId, InternalMessages.DISCONNECT); SendInternal(userId, socketId, InternalMessages.DISCONNECT);
epicToMirrorIds.Remove(userId); epicToMirrorIds.Remove(userId);
epicToSocketIds.Remove(userId); epicToSocketIds.Remove(userId);
} } else {
else
{
Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId); Debug.LogWarning("Trying to disconnect unknown connection id: " + connectionId);
} }
} }
public void Shutdown() public void Shutdown() {
{ foreach (KeyValuePair<ProductUserId, int> client in epicToMirrorIds) {
foreach (KeyValuePair<ProductUserId, int> client in epicToMirrorIds)
{
Disconnect(client.Value); Disconnect(client.Value);
SocketId socketId; SocketId socketId;
epicToSocketIds.TryGetValue(client.Key, out socketId); epicToSocketIds.TryGetValue(client.Key, out socketId);
@ -172,49 +146,41 @@ public class Server : Common
Dispose(); Dispose();
} }
public void SendAll(int connectionId, byte[] data, int channelId) public void SendAll(int connectionId, byte[] data, int channelId) {
{ if (epicToMirrorIds.TryGetValue(connectionId, out ProductUserId userId)) {
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
{
SocketId socketId; SocketId socketId;
epicToSocketIds.TryGetValue(userId, out socketId); epicToSocketIds.TryGetValue(userId, out socketId);
Send(userId, socketId, data, (byte)channelId); Send(userId, socketId, data, (byte)channelId);
} } else {
else
{
Debug.LogError("Trying to send on unknown connection: " + connectionId); Debug.LogError("Trying to send on unknown connection: " + connectionId);
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection")); OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
} }
} }
public string ServerGetClientAddress(int connectionId) public string ServerGetClientAddress(int connectionId) {
{ if (epicToMirrorIds.TryGetValue(connectionId, out ProductUserId userId)) {
if (epicToMirrorIds.TryGetValue(connectionId, out var userId))
{
string userIdString; string userIdString;
userId.ToString(out userIdString); userId.ToString(out userIdString);
return userIdString; return userIdString;
} } else {
else
{
Debug.LogError("Trying to get info on unknown connection: " + connectionId); Debug.LogError("Trying to get info on unknown connection: " + connectionId);
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection")); OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
return string.Empty; return string.Empty;
} }
} }
protected override void OnConnectionFailed(ProductUserId remoteId) protected override void OnConnectionFailed(ProductUserId remoteId) {
{ if (ignoreAllMessages) {
if (ignoreAllMessages)
{
return; return;
} }
var connectionId = epicToMirrorIds.TryGetValue(remoteId, out var connId) ? connId : nextConnectionID++; int connectionId = epicToMirrorIds.TryGetValue(remoteId, out int connId) ? connId : nextConnectionID++;
OnDisconnected.Invoke(connectionId); OnDisconnected.Invoke(connectionId);
Debug.LogError("Connection Failed, removing user"); Debug.LogError("Connection Failed, removing user");
epicToMirrorIds.Remove(remoteId); epicToMirrorIds.Remove(remoteId);
epicToSocketIds.Remove(remoteId); epicToSocketIds.Remove(remoteId);
} }
}
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -12,8 +12,18 @@ namespace Mirror.Weaver
? td.GetElementType().FullName == type.FullName ? td.GetElementType().FullName == type.FullName
: td.FullName == type.FullName; : td.FullName == type.FullName;
// check if 'td' is exactly of type T.
// it does not check if any base type is of <T>, only the specific type.
// for example:
// NetworkConnection Is NetworkConnection: true
// NetworkConnectionToClient Is NetworkConnection: false
public static bool Is<T>(this TypeReference td) => Is(td, typeof(T)); public static bool Is<T>(this TypeReference td) => Is(td, typeof(T));
// check if 'tr' is derived from T.
// it does not check if 'tr' is exactly T.
// for example:
// NetworkConnection IsDerivedFrom<NetworkConnection>: false
// NetworkConnectionToClient IsDerivedFrom<NetworkConnection>: true
public static bool IsDerivedFrom<T>(this TypeReference tr) => IsDerivedFrom(tr, typeof(T)); public static bool IsDerivedFrom<T>(this TypeReference tr) => IsDerivedFrom(tr, typeof(T));
public static bool IsDerivedFrom(this TypeReference tr, Type baseClass) public static bool IsDerivedFrom(this TypeReference tr, Type baseClass)
@ -79,7 +89,10 @@ namespace Mirror.Weaver
public static bool IsNetworkIdentityField(this TypeReference tr) => public static bool IsNetworkIdentityField(this TypeReference tr) =>
tr.Is<UnityEngine.GameObject>() || tr.Is<UnityEngine.GameObject>() ||
tr.Is<NetworkIdentity>() || tr.Is<NetworkIdentity>() ||
tr.IsDerivedFrom<NetworkBehaviour>(); // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
tr.IsDerivedFrom<NetworkBehaviour>() ||
tr.Is<NetworkBehaviour>();
public static bool CanBeResolved(this TypeReference parent) public static bool CanBeResolved(this TypeReference parent)
{ {

View File

@ -10,10 +10,11 @@ namespace Mirror.Weaver
// generates code like: // generates code like:
public void CmdThrust(float thrusting, int spin) public void CmdThrust(float thrusting, int spin)
{ {
NetworkWriter networkWriter = new NetworkWriter(); NetworkWriterPooled writer = NetworkWriterPool.Get();
networkWriter.Write(thrusting); writer.Write(thrusting);
networkWriter.WritePackedUInt32((uint)spin); writer.WritePackedUInt32((uint)spin);
base.SendCommandInternal(cmdName, networkWriter, channel); base.SendCommandInternal(cmdName, cmdHash, writer, channel);
NetworkWriterPool.Return(writer);
} }
public void CallCmdThrust(float thrusting, int spin) public void CallCmdThrust(float thrusting, int spin)
@ -38,7 +39,7 @@ namespace Mirror.Weaver
NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes);
// NetworkWriter writer = new NetworkWriter(); // NetworkWriter writer = new NetworkWriter();
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the Cmd call // write all the arguments that the user passed to the Cmd call
if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed))
@ -52,6 +53,11 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, channel); worker.Emit(OpCodes.Ldc_I4, channel);
@ -59,7 +65,7 @@ namespace Mirror.Weaver
worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
return cmd; return cmd;

View File

@ -137,21 +137,21 @@ namespace Mirror.Weaver
public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes)
{ {
worker.Body.InitLocals = true; worker.Body.InitLocals = true;
worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import<PooledNetworkWriter>())); worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import<NetworkWriterPooled>()));
} }
public static void WriteCreateWriter(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteGetWriter(ILProcessor worker, WeaverTypes weaverTypes)
{ {
// create writer // create writer
worker.Emit(OpCodes.Call, weaverTypes.GetPooledWriterReference); worker.Emit(OpCodes.Call, weaverTypes.GetWriterReference);
worker.Emit(OpCodes.Stloc_0); worker.Emit(OpCodes.Stloc_0);
} }
public static void WriteRecycleWriter(ILProcessor worker, WeaverTypes weaverTypes) public static void WriteReturnWriter(ILProcessor worker, WeaverTypes weaverTypes)
{ {
// NetworkWriterPool.Recycle(writer); // NetworkWriterPool.Recycle(writer);
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Call, weaverTypes.RecycleWriterReference); worker.Emit(OpCodes.Call, weaverTypes.ReturnWriterReference);
} }
public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed)
@ -397,7 +397,7 @@ namespace Mirror.Weaver
MethodDefinition serialize = new MethodDefinition(SerializeMethodName, MethodDefinition serialize = new MethodDefinition(SerializeMethodName,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
weaverTypes.Import<bool>()); weaverTypes.Import(typeof(void)));
serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import<NetworkWriter>())); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import<NetworkWriter>()));
serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import<bool>())); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import<bool>()));
@ -405,10 +405,7 @@ namespace Mirror.Weaver
serialize.Body.InitLocals = true; serialize.Body.InitLocals = true;
// loc_0, this local variable is to determine if any variable was dirty // base.SerializeSyncVars(writer, forceAll);
VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import<bool>());
serialize.Body.Variables.Add(dirtyLocal);
MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName);
if (baseSerialize != null) if (baseSerialize != null)
{ {
@ -419,16 +416,20 @@ namespace Mirror.Weaver
// forceAll // forceAll
worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Ldarg_2);
worker.Emit(OpCodes.Call, baseSerialize); worker.Emit(OpCodes.Call, baseSerialize);
// set dirtyLocal to result of base.OnSerialize()
worker.Emit(OpCodes.Stloc_0);
} }
// Generates: if (forceAll); // Generates:
// if (forceAll)
// {
// writer.WriteInt(health);
// ...
// }
Instruction initialStateLabel = worker.Create(OpCodes.Nop); Instruction initialStateLabel = worker.Create(OpCodes.Nop);
// forceAll // forceAll
worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Ldarg_2); // load 'forceAll' flag
worker.Emit(OpCodes.Brfalse, initialStateLabel); worker.Emit(OpCodes.Brfalse, initialStateLabel); // start the 'if forceAll' branch
// generates write.Write(syncVar) for each SyncVar in forceAll case
foreach (FieldDefinition syncVarDef in syncVars) foreach (FieldDefinition syncVarDef in syncVars)
{ {
FieldReference syncVar = syncVarDef; FieldReference syncVar = syncVarDef;
@ -442,7 +443,21 @@ namespace Mirror.Weaver
// this // this
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
worker.Emit(OpCodes.Ldfld, syncVar); worker.Emit(OpCodes.Ldfld, syncVar);
MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); MethodReference writeFunc;
// For NBs we always need to use the default NetworkBehaviour write func
// since the reader counter part uses that exact layout which is not easy to change
// without introducing more edge cases
// effectively this disallows custom NB-type writers/readers on SyncVars
// see: https://github.com/MirrorNetworking/Mirror/issues/2680
if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{
writeFunc = writers.GetWriteFunc(weaverTypes.Import<NetworkBehaviour>(), ref WeavingFailed);
}
else
{
writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed);
}
if (writeFunc != null) if (writeFunc != null)
{ {
worker.Emit(OpCodes.Call, writeFunc); worker.Emit(OpCodes.Call, writeFunc);
@ -455,15 +470,14 @@ namespace Mirror.Weaver
} }
} }
// always return true if forceAll // if (forceAll) then always return at the end of the 'if' case
// Generates: return true
worker.Emit(OpCodes.Ldc_I4_1);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
// Generates: end if (forceAll); // end the 'if' case for "if (forceAll)"
worker.Append(initialStateLabel); worker.Append(initialStateLabel);
////////////////////////////////////////////////////////////////////
// write dirty bits before the data fields // write dirty bits before the data fields
// Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ());
// writer // writer
@ -480,7 +494,6 @@ namespace Mirror.Weaver
int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName);
foreach (FieldDefinition syncVarDef in syncVars) foreach (FieldDefinition syncVarDef in syncVars)
{ {
FieldReference syncVar = syncVarDef; FieldReference syncVar = syncVarDef;
if (netBehaviourSubclass.HasGenericParameters) if (netBehaviourSubclass.HasGenericParameters)
{ {
@ -504,7 +517,21 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
worker.Emit(OpCodes.Ldfld, syncVar); worker.Emit(OpCodes.Ldfld, syncVar);
MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); MethodReference writeFunc;
// For NBs we always need to use the default NetworkBehaviour write func
// since the reader counter part uses that exact layout which is not easy to change
// without introducing more edge cases
// effectively this disallows custom NB-type writers/readers on SyncVars
// see: https://github.com/MirrorNetworking/Mirror/issues/2680
if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{
writeFunc = writers.GetWriteFunc(weaverTypes.Import<NetworkBehaviour>(), ref WeavingFailed);
}
else
{
writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed);
}
if (writeFunc != null) if (writeFunc != null)
{ {
worker.Emit(OpCodes.Call, writeFunc); worker.Emit(OpCodes.Call, writeFunc);
@ -516,11 +543,6 @@ namespace Mirror.Weaver
return; return;
} }
// something was dirty
worker.Emit(OpCodes.Ldc_I4_1);
// set dirtyLocal to true
worker.Emit(OpCodes.Stloc_0);
worker.Append(varLabel); worker.Append(varLabel);
dirtyBit += 1; dirtyBit += 1;
} }
@ -529,8 +551,7 @@ namespace Mirror.Weaver
//worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}");
//worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference);
// generate: return dirtyLocal // generate: return
worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
netBehaviourSubclass.Methods.Add(serialize); netBehaviourSubclass.Methods.Add(serialize);
} }
@ -589,10 +610,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldflda, netIdField); worker.Emit(OpCodes.Ldflda, netIdField);
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity); worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity);
} }
// TODO this only uses the persistent netId for types DERIVED FROM NB. // handle both NetworkBehaviour and inheritors.
// not if the type is just 'NetworkBehaviour'. // fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
// this is what original implementation did too. fix it after. else if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>() || syncVar.FieldType.Is<NetworkBehaviour>())
else if (syncVar.FieldType.IsDerivedFrom<NetworkBehaviour>())
{ {
// reader // reader
worker.Emit(OpCodes.Ldarg_1); worker.Emit(OpCodes.Ldarg_1);

View File

@ -68,7 +68,7 @@ namespace Mirror.Weaver
//worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}"); //worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}");
//worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference);
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the Rpc call // write all the arguments that the user passed to the Rpc call
if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed)) if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed))
@ -82,6 +82,11 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_0);
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, channel); worker.Emit(OpCodes.Ldc_I4, channel);
@ -89,7 +94,7 @@ namespace Mirror.Weaver
worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal); worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);

View File

@ -16,7 +16,7 @@ namespace Mirror.Weaver
foreach (FieldDefinition fd in td.Fields) foreach (FieldDefinition fd in td.Fields)
{ {
if (fd.FieldType.IsGenericParameter) if (fd.FieldType.IsGenericParameter || fd.ContainsGenericParameter)
{ {
// can't call .Resolve on generic ones // can't call .Resolve on generic ones
continue; continue;

View File

@ -203,7 +203,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference); worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);
} }
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
{ {
// return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId); // return this.GetSyncVarNetworkBehaviour<T>(ref field, uint netId);
// this. // this.
@ -331,10 +333,9 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Ldflda, netIdFieldReference); worker.Emit(OpCodes.Ldflda, netIdFieldReference);
worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_NetworkIdentity); worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_NetworkIdentity);
} }
// TODO this only uses the persistent netId for types DERIVED FROM NB. // handle both NetworkBehaviour and inheritors.
// not if the type is just 'NetworkBehaviour'. // fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
// this is what original implementation did too. fix it after. else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
else if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>())
{ {
// NetworkIdentity setter needs one more parameter: netId field ref // NetworkIdentity setter needs one more parameter: netId field ref
// (actually its a NetworkBehaviourSyncVar type) // (actually its a NetworkBehaviourSyncVar type)
@ -368,11 +369,13 @@ namespace Mirror.Weaver
// GameObject/NetworkIdentity SyncVars have a new field for netId // GameObject/NetworkIdentity SyncVars have a new field for netId
FieldDefinition netIdField = null; FieldDefinition netIdField = null;
// NetworkBehaviour has different field type than other NetworkIdentityFields // NetworkBehaviour has different field type than other NetworkIdentityFields
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
if (fd.FieldType.IsDerivedFrom<NetworkBehaviour>() || fd.FieldType.Is<NetworkBehaviour>())
{ {
netIdField = new FieldDefinition($"___{fd.Name}NetId", netIdField = new FieldDefinition($"___{fd.Name}NetId",
FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed
weaverTypes.Import<NetworkBehaviour.NetworkBehaviourSyncVar>()); weaverTypes.Import<NetworkBehaviourSyncVar>());
netIdField.DeclaringType = td; netIdField.DeclaringType = td;
syncVarNetIds[fd] = netIdField; syncVarNetIds[fd] = netIdField;
@ -475,7 +478,11 @@ namespace Mirror.Weaver
{ {
td.Fields.Add(fd); td.Fields.Add(fd);
} }
syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count);
// include parent class syncvars
// fixes: https://github.com/MirrorNetworking/Mirror/issues/3457
int parentSyncVarCount = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName);
syncVarAccessLists.SetNumSyncVars(td.FullName, parentSyncVarCount + syncVars.Count);
return (syncVars, syncVarNetIds); return (syncVars, syncVarNetIds);
} }

View File

@ -9,8 +9,16 @@ namespace Mirror.Weaver
// helper functions to check if the method has a NetworkConnection parameter // helper functions to check if the method has a NetworkConnection parameter
public static bool HasNetworkConnectionParameter(MethodDefinition md) public static bool HasNetworkConnectionParameter(MethodDefinition md)
{ {
return md.Parameters.Count > 0 && if (md.Parameters.Count > 0)
md.Parameters[0].ParameterType.Is<NetworkConnection>(); {
// we need to allow both NetworkConnection, and inheriting types.
// NetworkBehaviour.SendTargetRpc takes a NetworkConnection parameter.
// fixes https://github.com/vis2k/Mirror/issues/3290
TypeReference type = md.Parameters[0].ParameterType;
return type.Is<NetworkConnection>() ||
type.IsDerivedFrom<NetworkConnection>();
}
return false;
} }
public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed)
@ -34,16 +42,25 @@ namespace Mirror.Weaver
// NetworkConnection parameter is optional // NetworkConnection parameter is optional
if (HasNetworkConnectionParameter(md)) if (HasNetworkConnectionParameter(md))
{ {
// on server, the NetworkConnection parameter is a connection to client. // TargetRpcs are sent from server to client.
// when the rpc is invoked on the client, it still has the same // on server, we currently support two types:
// function signature. we pass in the connection to server, // TargetRpc(NetworkConnection)
// which is cleaner than just passing null) // TargetRpc(NetworkConnectionToClient)
//NetworkClient.readyconnection // however, it's always a connection to client.
// in the future, only NetworkConnectionToClient will be supported.
// explicit typing helps catch issues at compile time.
// //
// TODO // on client, InvokeTargetRpc calls the original code.
// a) .connectionToServer = best solution. no doubt. // we need to fill in the NetworkConnection parameter.
// b) NetworkClient.connection for now. add TODO to not use static later. // NetworkClient.connection is always a connection to server.
worker.Emit(OpCodes.Call, weaverTypes.NetworkClientConnectionReference); //
// we used to pass NetworkClient.connection as the TargetRpc parameter.
// which caused: https://github.com/MirrorNetworking/Mirror/issues/3455
// when the parameter is defined as a NetworkConnectionToClient.
//
// a client's connection never fits into a NetworkConnectionToClient.
// we need to always pass null here.
worker.Emit(OpCodes.Ldnull);
} }
// process reader parameters and skip first one if first one is NetworkConnection // process reader parameters and skip first one if first one is NetworkConnection
@ -100,7 +117,7 @@ namespace Mirror.Weaver
NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes);
NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes);
// write all the arguments that the user passed to the TargetRpc call // write all the arguments that the user passed to the TargetRpc call
// (skip first one if first one is NetworkConnection) // (skip first one if first one is NetworkConnection)
@ -122,12 +139,17 @@ namespace Mirror.Weaver
} }
// pass full function name to avoid ClassA.Func <-> ClassB.Func collisions // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions
worker.Emit(OpCodes.Ldstr, md.FullName); worker.Emit(OpCodes.Ldstr, md.FullName);
// pass the function hash so we don't have to compute it at runtime
// otherwise each GetStableHash call requires O(N) complexity.
// noticeable for long function names:
// https://github.com/MirrorNetworking/Mirror/issues/3375
worker.Emit(OpCodes.Ldc_I4, md.FullName.GetStableHashCode());
// writer // writer
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0)); worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0));
worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal); worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal);
NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes);
worker.Emit(OpCodes.Ret); worker.Emit(OpCodes.Ret);

View File

@ -19,6 +19,7 @@ namespace Mirror.Weaver
AssemblyDefinition assembly; AssemblyDefinition assembly;
WeaverTypes weaverTypes; WeaverTypes weaverTypes;
TypeDefinition GeneratedCodeClass; TypeDefinition GeneratedCodeClass;
// CHANGED
internal Logger Log; internal Logger Log;
Dictionary<TypeReference, MethodReference> readFuncs = Dictionary<TypeReference, MethodReference> readFuncs =
@ -114,7 +115,9 @@ namespace Mirror.Weaver
return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed);
} }
else if (variableReference.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
else if (variableReference.IsDerivedFrom<NetworkBehaviour>() || variableReference.Is<NetworkBehaviour>())
{ {
return GetNetworkBehaviourReader(variableReference); return GetNetworkBehaviourReader(variableReference);
} }
@ -138,6 +141,7 @@ namespace Mirror.Weaver
WeavingFailed = true; WeavingFailed = true;
return null; return null;
} }
// CHANGED
/* /*
if (variableDefinition.HasGenericParameters) if (variableDefinition.HasGenericParameters)
{ {
@ -270,6 +274,7 @@ namespace Mirror.Weaver
GenerateNullCheck(worker, ref WeavingFailed); GenerateNullCheck(worker, ref WeavingFailed);
CreateNew(variable, worker, td, ref WeavingFailed); CreateNew(variable, worker, td, ref WeavingFailed);
// CHANGED
this.ReadAllFieldsGeneric(variable, worker, ref WeavingFailed); this.ReadAllFieldsGeneric(variable, worker, ref WeavingFailed);
worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldloc_0);

View File

@ -25,6 +25,12 @@ namespace Mirror.Weaver
AssemblyDefinition CurrentAssembly; AssemblyDefinition CurrentAssembly;
Writers writers; Writers writers;
Readers readers; Readers readers;
// in case of weaver errors, we don't stop immediately.
// we log all errors and then eventually return false if
// weaving has failed.
// this way the user can fix multiple errors at once, instead of having
// to fix -> recompile -> fix -> recompile for one error at a time.
bool WeavingFailed; bool WeavingFailed;
// logger functions can be set from the outside. // logger functions can be set from the outside.
@ -200,6 +206,7 @@ namespace Mirror.Weaver
ModuleDefinition moduleDefinition = CurrentAssembly.MainModule; ModuleDefinition moduleDefinition = CurrentAssembly.MainModule;
Console.WriteLine($"Script Module: {moduleDefinition.Name}"); Console.WriteLine($"Script Module: {moduleDefinition.Name}");
// CHANGED
QSBReaderWriterProcessor.Process(moduleDefinition, writers, readers, ref WeavingFailed); QSBReaderWriterProcessor.Process(moduleDefinition, writers, readers, ref WeavingFailed);
modified |= WeaveModule(moduleDefinition); modified |= WeaveModule(moduleDefinition);

View File

@ -11,8 +11,8 @@ namespace Mirror.Weaver
public MethodReference ScriptableObjectCreateInstanceMethod; public MethodReference ScriptableObjectCreateInstanceMethod;
public MethodReference NetworkBehaviourDirtyBitsReference; public MethodReference NetworkBehaviourDirtyBitsReference;
public MethodReference GetPooledWriterReference; public MethodReference GetWriterReference;
public MethodReference RecycleWriterReference; public MethodReference ReturnWriterReference;
public MethodReference NetworkClientConnectionReference; public MethodReference NetworkClientConnectionReference;
@ -77,28 +77,14 @@ namespace Mirror.Weaver
TypeReference NetworkServerType = Import(typeof(NetworkServer)); TypeReference NetworkServerType = Import(typeof(NetworkServer));
NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed); NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed);
TypeReference NetworkClientType = Import(typeof(NetworkClient)); TypeReference NetworkClientType = Import(typeof(NetworkClient));
NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed); NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed);
NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed);
TypeReference RemoteCallDelegateType = Import<RemoteCalls.RemoteCallDelegate>();
RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed);
TypeReference NetworkBehaviourType = Import<NetworkBehaviour>(); TypeReference NetworkBehaviourType = Import<NetworkBehaviour>();
TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls));
TypeReference ScriptableObjectType = Import<ScriptableObject>();
ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod(
ScriptableObjectType, assembly, Log,
md => md.Name == "CreateInstance" && md.HasGenericParameters,
ref WeavingFailed);
NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits"); NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits");
TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool));
GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "GetWriter", ref WeavingFailed);
RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Recycle", ref WeavingFailed);
NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed);
generatedSyncVarSetter = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter", ref WeavingFailed); generatedSyncVarSetter = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter", ref WeavingFailed);
generatedSyncVarSetter_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_GameObject", ref WeavingFailed); generatedSyncVarSetter_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_GameObject", ref WeavingFailed);
@ -114,9 +100,25 @@ namespace Mirror.Weaver
getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed); getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed);
getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed); getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed);
sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed);
sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed);
sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed);
InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed);
TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls));
registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed); registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed);
registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed); registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed);
TypeReference RemoteCallDelegateType = Import<RemoteCalls.RemoteCallDelegate>();
RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed);
TypeReference ScriptableObjectType = Import<ScriptableObject>();
ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod(
ScriptableObjectType, assembly, Log,
md => md.Name == "CreateInstance" && md.HasGenericParameters,
ref WeavingFailed);
TypeReference unityDebug = Import(typeof(UnityEngine.Debug)); TypeReference unityDebug = Import(typeof(UnityEngine.Debug));
// these have multiple methods with same name, so need to check parameters too // these have multiple methods with same name, so need to check parameters too
logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md =>
@ -133,11 +135,10 @@ namespace Mirror.Weaver
TypeReference typeType = Import(typeof(Type)); TypeReference typeType = Import(typeof(Type));
getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed); getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed);
sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed);
sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed);
sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed);
InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed); TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool));
GetWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Get", ref WeavingFailed);
ReturnWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Return", ref WeavingFailed);
TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions)); TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions));
readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md => readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md =>
@ -147,6 +148,7 @@ namespace Mirror.Weaver
}), }),
ref WeavingFailed); ref WeavingFailed);
// CHANGED
/* /*
// [InitializeOnLoadMethod] // [InitializeOnLoadMethod]
// 'UnityEditor' is not available in builds. // 'UnityEditor' is not available in builds.

View File

@ -116,7 +116,9 @@ namespace Mirror.Weaver
return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed); return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed);
} }
if (variableReference.IsDerivedFrom<NetworkBehaviour>()) // handle both NetworkBehaviour and inheritors.
// fixes: https://github.com/MirrorNetworking/Mirror/issues/2939
if (variableReference.IsDerivedFrom<NetworkBehaviour>() || variableReference.Is<NetworkBehaviour>())
{ {
return GetNetworkBehaviourWriter(variableReference); return GetNetworkBehaviourWriter(variableReference);
} }
@ -139,6 +141,7 @@ namespace Mirror.Weaver
{ {
throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference); throw new GenerateWriterException($"Cannot generate writer for {variableReference.Name}. Use a supported type or provide a custom writer", variableReference);
} }
// CHANGED
/* /*
if (variableDefinition.HasGenericParameters) if (variableDefinition.HasGenericParameters)
{ {
@ -219,6 +222,7 @@ namespace Mirror.Weaver
if (!variable.Resolve().IsValueType) if (!variable.Resolve().IsValueType)
WriteNullCheck(worker, ref WeavingFailed); WriteNullCheck(worker, ref WeavingFailed);
// CHANGED
if (!this.WriteAllFieldsGeneric(variable, worker, ref WeavingFailed)) if (!this.WriteAllFieldsGeneric(variable, worker, ref WeavingFailed))
return null; return null;

View File

@ -67,7 +67,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
private string _lastTransportError; private string _lastTransportError;
private static readonly string[] _kcpErrorLogs = private static readonly string[] _kcpErrorLogs =
{ {
"KCP: received disconnect message", "KcpPeer: received disconnect message",
"Failed to resolve host: .*" "Failed to resolve host: .*"
}; };
@ -110,7 +110,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
QSBCore.ProfileManager.OnProfileSignInComplete += _ => InitPlayerName(); QSBCore.ProfileManager.OnProfileSignInComplete += _ => InitPlayerName();
playerPrefab = QSBCore.NetworkAssetBundle.LoadAsset<GameObject>("Assets/Prefabs/NETWORK_Player_Body.prefab"); playerPrefab = QSBCore.NetworkAssetBundle.LoadAsset<GameObject>("Assets/Prefabs/NETWORK_Player_Body.prefab");
playerPrefab.GetRequiredComponent<NetworkIdentity>().SetValue("m_AssetId", 1.ToGuid().ToString("N")); playerPrefab.GetRequiredComponent<NetworkIdentity>().SetValue("_assetId", (uint)1);
ShipPrefab = MakeNewNetworkObject(2, "NetworkShip", typeof(ShipTransformSync)); ShipPrefab = MakeNewNetworkObject(2, "NetworkShip", typeof(ShipTransformSync));
var shipVector3Sync = ShipPrefab.AddComponent<Vector3VariableSyncer>(); var shipVector3Sync = ShipPrefab.AddComponent<Vector3VariableSyncer>();
@ -202,7 +202,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
/// this works by calling Unload(false) and then reloading the AssetBundle, /// this works by calling Unload(false) and then reloading the AssetBundle,
/// which makes LoadAsset give you a new resource. /// which makes LoadAsset give you a new resource.
/// see https://docs.unity3d.com/Manual/AssetBundles-Native.html. /// see https://docs.unity3d.com/Manual/AssetBundles-Native.html.
private static GameObject MakeNewNetworkObject(int assetId, string name, Type networkBehaviourType) private static GameObject MakeNewNetworkObject(uint assetId, string name, Type networkBehaviourType)
{ {
var bundle = QSBCore.Helper.Assets.LoadBundle("AssetBundles/qsb_empty"); var bundle = QSBCore.Helper.Assets.LoadBundle("AssetBundles/qsb_empty");
@ -216,7 +216,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
bundle.Unload(false); bundle.Unload(false);
template.name = name; template.name = name;
template.AddComponent<NetworkIdentity>().SetValue("m_AssetId", assetId.ToGuid().ToString("N")); template.AddComponent<NetworkIdentity>().SetValue("_assetId", assetId);
template.AddComponent(networkBehaviourType); template.AddComponent(networkBehaviourType);
return template; return template;
} }

View File

@ -59,7 +59,7 @@ public abstract class QSBNetworkBehaviour : NetworkBehaviour
return; return;
} }
using var writer = NetworkWriterPool.GetWriter(); using var writer = NetworkWriterPool.Get();
Serialize(writer); Serialize(writer);
UpdatePrevData(); UpdatePrevData();
@ -127,7 +127,7 @@ public abstract class QSBNetworkBehaviour : NetworkBehaviour
Array.Copy(data.Array!, data.Offset, _lastKnownData, 0, data.Count); Array.Copy(data.Array!, data.Offset, _lastKnownData, 0, data.Count);
} }
using var reader = NetworkReaderPool.GetReader(data); using var reader = NetworkReaderPool.Get(data);
Deserialize(reader); Deserialize(reader);
} }
} }

Binary file not shown.