version 1.3

This commit is contained in:
2023-01-02 00:08:18 +05:30
parent 8faf4476ed
commit af487ad62d
581 changed files with 145027 additions and 809 deletions

View File

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

View File

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

View File

@@ -0,0 +1,355 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
using System;
using System.Collections.Concurrent;
using System.Threading;
using ENet;
using IgnoranceThirdparty;
using UnityEngine;
using Event = ENet.Event; // fixes CS0104 ambigous reference between the same thing in UnityEngine
using EventType = ENet.EventType; // fixes CS0104 ambigous reference between the same thing in UnityEngine
using Object = System.Object; // fixes CS0104 ambigous reference between the same thing in UnityEngine
namespace IgnoranceCore
{
public class IgnoranceClient
{
// Client connection address and port
public string ConnectAddress = "127.0.0.1";
public int ConnectPort = 7777;
// How many channels are expected
public int ExpectedChannels = 2;
// Native poll waiting time
public int PollTime = 1;
// Maximum Packet Size
public int MaximumPacketSize = 33554432;
// General Verbosity by default.
public int Verbosity = 1;
// Maximum ring buffer capacity.
public int IncomingOutgoingBufferSize = 5000;
public int ConnectionEventBufferSize = 100;
// Queues
public RingBuffer<IgnoranceIncomingPacket> Incoming;
public RingBuffer<IgnoranceOutgoingPacket> Outgoing;
public RingBuffer<IgnoranceCommandPacket> Commands;
public RingBuffer<IgnoranceConnectionEvent> ConnectionEvents;
public RingBuffer<IgnoranceClientStats> StatusUpdates;
public bool IsAlive => WorkerThread != null && WorkerThread.IsAlive;
private volatile bool CeaseOperation = false;
private Thread WorkerThread;
public void Start()
{
if (WorkerThread != null && WorkerThread.IsAlive)
{
// Cannot do that.
Debug.LogError("Ignorance: A client worker thread is already running. Cannot start another.");
return;
}
// Setup the ring buffers.
SetupRingBuffersIfNull();
CeaseOperation = false;
ThreadParamInfo threadParams = new ThreadParamInfo()
{
Address = ConnectAddress,
Port = ConnectPort,
Channels = ExpectedChannels,
PollTime = PollTime,
PacketSizeLimit = MaximumPacketSize,
Verbosity = Verbosity
};
// Drain queues.
if (Incoming != null) while (Incoming.TryDequeue(out _)) ;
if (Outgoing != null) while (Outgoing.TryDequeue(out _)) ;
if (Commands != null) while (Commands.TryDequeue(out _)) ;
if (ConnectionEvents != null) while (ConnectionEvents.TryDequeue(out _)) ;
if (StatusUpdates != null) while (StatusUpdates.TryDequeue(out _)) ;
WorkerThread = new Thread(ThreadWorker);
WorkerThread.Start(threadParams);
if(Verbosity > 0)
Debug.Log("Ignorance: Client instance has dispatched worker thread.");
}
public void Stop()
{
if (WorkerThread != null && !CeaseOperation)
{
Debug.Log("Ignorance: Client stop request acknowledged. This may take a while depending on network load...");
CeaseOperation = true;
}
}
#region The meat and potatoes.
// This runs in a seperate thread, be careful accessing anything outside of it's thread
// or you may get an AccessViolation/crash.
private void ThreadWorker(Object parameters)
{
if (Verbosity > 0)
Debug.Log("Ignorance: Client instance worker thread initializing. Please stand by...");
ThreadParamInfo setupInfo;
Address clientAddress = new Address();
Peer clientPeer; // The peer object that represents the client's connection.
Host clientHost; // NOT related to Mirror "Client Host". This is the client's ENet Host Object.
Event clientEvent; // Used when clients get events on the network.
IgnoranceClientStats icsu = default;
bool alreadyNotifiedAboutDisconnect = false;
// Grab the setup information.
if (parameters.GetType() == typeof(ThreadParamInfo))
setupInfo = (ThreadParamInfo)parameters;
else
{
Debug.LogError("Ignorance: Client instance worker thread startup failure; Invalid thread parameters. Aborting.");
return;
}
// Attempt to initialize ENet inside the thread.
if (Library.Initialize())
Debug.Log("Ignorance: Client instance worker thread successfully initialized ENet.");
else
{
Debug.LogError("Ignorance: Client instance worker thread failed to initialize ENet. Aborting.");
return;
}
// Attempt to connect to our target.
clientAddress.SetHost(setupInfo.Address);
clientAddress.Port = (ushort)setupInfo.Port;
using (clientHost = new Host())
{
try
{
clientHost.Create();
Debug.Log($"Ignorance: Client worker thread attempting connection to '{setupInfo.Address}:{setupInfo.Port}'.");
clientPeer = clientHost.Connect(clientAddress, setupInfo.Channels);
}
catch (Exception ex)
{
// Oops, something failed.
Debug.LogError($"Ignorance: Client instance worker thread reports that something went wrong. While attempting to create client object, we caught an exception:\n{ex.Message}\n");
Debug.LogError($"You could try the debug-enabled version of the native ENet library which creates a logfile, or alternatively you could try restart " +
$"your device to ensure jank is cleared out of memory. If problems persist, please file a support ticket explaining what happened.");
Library.Deinitialize();
return;
}
// Process network events as long as we're not ceasing operation.
while (!CeaseOperation)
{
bool pollComplete = false;
while (Commands.TryDequeue(out IgnoranceCommandPacket ignoranceCommandPacket))
{
switch (ignoranceCommandPacket.Type)
{
case IgnoranceCommandType.ClientStatusRequest:
// Respond with statistics so far.
if (!clientPeer.IsSet)
break;
icsu.RTT = clientPeer.RoundTripTime;
icsu.BytesReceived = clientPeer.BytesReceived;
icsu.BytesSent = clientPeer.BytesSent;
icsu.PacketsReceived = clientHost.PacketsReceived;
icsu.PacketsSent = clientPeer.PacketsSent;
icsu.PacketsLost = clientPeer.PacketsLost;
StatusUpdates.Enqueue(icsu);
break;
case IgnoranceCommandType.ClientWantsToStop:
CeaseOperation = true;
break;
}
}
// If something outside the thread has told us to stop execution, then we need to break out of this while loop.
if (CeaseOperation)
break;
// Step 1: Sending to Server
while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket))
{
// TODO: Revise this, could we tell the Peer to disconnect right here?
// Stop early if we get a client stop packet.
// if (outgoingPacket.Type == IgnorancePacketType.ClientWantsToStop) break;
int ret = clientPeer.Send(outgoingPacket.Channel, ref outgoingPacket.Payload);
if (ret < 0 && setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: ENet error {ret} while sending packet to Server via Peer {outgoingPacket.NativePeerId}.");
}
// If something outside the thread has told us to stop execution, then we need to break out of this while loop.
// while loop to break out of is while(!CeaseOperation).
if (CeaseOperation)
break;
// Step 2: Receive Data packets
// This loops until polling is completed. It may take a while, if it's
// a slow networking day.
while (!pollComplete)
{
Packet incomingPacket;
Peer incomingPeer;
int incomingPacketLength;
// Any events worth checking out?
if (clientHost.CheckEvents(out clientEvent) <= 0)
{
// If service time is met, break out of it.
if (clientHost.Service(setupInfo.PollTime, out clientEvent) <= 0) break;
// Poll is done.
pollComplete = true;
}
// Setup the packet references.
incomingPeer = clientEvent.Peer;
// Now, let's handle those events.
switch (clientEvent.Type)
{
case EventType.None:
default:
break;
case EventType.Connect:
if (setupInfo.Verbosity > 0)
Debug.Log($"Ignorance: Client worker thread has connected to '{incomingPeer.IP}:{incomingPeer.Port}'.");
ConnectionEvents.Enqueue(new IgnoranceConnectionEvent
{
EventType = 0x00,
NativePeerId = incomingPeer.ID,
IP = incomingPeer.IP,
Port = incomingPeer.Port
});
break;
case EventType.Disconnect:
case EventType.Timeout:
if (setupInfo.Verbosity > 0)
Debug.Log($"Ignorance: Client worker thread has disconnected from '{incomingPeer.IP}:{incomingPeer.Port}'.");
ConnectionEvents.Enqueue(new IgnoranceConnectionEvent { EventType = 0x01 });
CeaseOperation = true;
alreadyNotifiedAboutDisconnect = true;
break;
case EventType.Receive:
// Receive event type usually includes a packet; so cache its reference.
incomingPacket = clientEvent.Packet;
if (!incomingPacket.IsSet)
{
if (setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: Client receive event did not supply us with a packet to work with. This should never happen.");
break;
}
incomingPacketLength = incomingPacket.Length;
// Never consume more than we can have capacity for.
if (incomingPacketLength > setupInfo.PacketSizeLimit)
{
if (setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: Client incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes.");
incomingPacket.Dispose();
break;
}
IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket
{
Channel = clientEvent.ChannelID,
NativePeerId = incomingPeer.ID,
Payload = incomingPacket
};
Incoming.Enqueue(incomingQueuePacket);
break;
}
}
// If something outside the thread has told us to stop execution, then we need to break out of this while loop.
// while loop to break out of is while(!CeaseOperation).
if (CeaseOperation)
break;
}
if (Verbosity > 0)
Debug.Log("Ignorance: Client worker thread shutdown commencing. Disconnecting and flushing connection.");
// Flush the client and disconnect.
clientPeer.Disconnect(0);
clientHost.Flush();
// Fix for client stuck in limbo, since the disconnection event may not be fired until next loop.
if (!alreadyNotifiedAboutDisconnect)
{
ConnectionEvents.Enqueue(new IgnoranceConnectionEvent { EventType = 0x01 });
alreadyNotifiedAboutDisconnect = true;
}
}
// Fix for client stuck in limbo, since the disconnection event may not be fired until next loop, again.
if (!alreadyNotifiedAboutDisconnect)
ConnectionEvents.Enqueue(new IgnoranceConnectionEvent { EventType = 0x01 });
// Deinitialize
Library.Deinitialize();
if (setupInfo.Verbosity > 0)
Debug.Log("Ignorance: Client worker thread shutdown complete.");
}
#endregion
private void SetupRingBuffersIfNull()
{
Debug.Log($"Ignorance: Setting up the ring buffers if they're not already created. " +
$"If they are already, this step will be skipped.");
if (Incoming == null)
Incoming = new RingBuffer<IgnoranceIncomingPacket>(IncomingOutgoingBufferSize);
if (Outgoing == null)
Outgoing = new RingBuffer<IgnoranceOutgoingPacket>(IncomingOutgoingBufferSize);
if (Commands == null)
Commands = new RingBuffer<IgnoranceCommandPacket>(100);
if (ConnectionEvents == null)
ConnectionEvents = new RingBuffer<IgnoranceConnectionEvent>(ConnectionEventBufferSize);
if (StatusUpdates == null)
StatusUpdates = new RingBuffer<IgnoranceClientStats>(10);
}
private struct ThreadParamInfo
{
public int Channels;
public int PollTime;
public int Port;
public int PacketSizeLimit;
public int Verbosity;
public string Address;
}
}
}

View File

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

View File

@@ -0,0 +1,437 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
using System;
using System.Collections.Generic;
using System.Threading;
using ENet;
using IgnoranceThirdparty;
using UnityEngine;
using Event = ENet.Event; // fixes CS0104 ambigous reference between the same thing in UnityEngine
using EventType = ENet.EventType; // fixes CS0104 ambigous reference between the same thing in UnityEngine
using Object = System.Object; // fixes CS0104 ambigous reference between the same thing in UnityEngine
namespace IgnoranceCore
{
public class IgnoranceServer
{
// Server Properties
// - Bind Settings
public string BindAddress = "127.0.0.1";
public int BindPort = 7777;
// - Maximum allowed channels, peers, etc.
public int MaximumChannels = 2;
public int MaximumPeers = 100;
public int MaximumPacketSize = 33554432; // ENet.cs: uint maxPacketSize = 32 * 1024 * 1024 = 33554432
// - Native poll waiting time
public int PollTime = 1;
// - Verbosity.
public int Verbosity = 1;
// - Queue Sizing
public int IncomingOutgoingBufferSize = 5000;
public int ConnectionEventBufferSize = 100;
// - Fruity devices
public bool IsFruityDevice;
public bool BindAllInterfaces;
public bool IsAlive => WorkerThread != null && WorkerThread.IsAlive;
private volatile bool CeaseOperation = false;
// Queues
// v1.4.0b9: Replace the queues with RingBuffers.
public RingBuffer<IgnoranceIncomingPacket> Incoming;
public RingBuffer<IgnoranceOutgoingPacket> Outgoing;
public RingBuffer<IgnoranceCommandPacket> Commands;
public RingBuffer<IgnoranceConnectionEvent> ConnectionEvents;
public RingBuffer<IgnoranceConnectionEvent> DisconnectionEvents;
public RingBuffer<IgnoranceServerStats> StatusUpdates;
public RingBuffer<IgnoranceServerStats> RecycledServerStatBlocks = new RingBuffer<IgnoranceServerStats>(100);
// Thread
private Thread WorkerThread;
public void Start()
{
if (WorkerThread != null && WorkerThread.IsAlive)
{
// Cannot do that.
Debug.LogError("Ignorance Server: A worker thread is already running. Cannot start another.");
return;
}
// Setup the ring buffers.
SetupRingBuffersIfNull();
CeaseOperation = false;
ThreadParamInfo threadParams = new ThreadParamInfo()
{
IsFruityDevice = IsFruityDevice,
BindAllInterfaces = BindAllInterfaces,
Address = BindAddress,
Port = BindPort,
Peers = MaximumPeers,
Channels = MaximumChannels,
PollTime = PollTime,
PacketSizeLimit = MaximumPacketSize,
Verbosity = Verbosity
};
// Drain queues.
if (Incoming != null) while (Incoming.TryDequeue(out _)) ;
if (Outgoing != null) while (Outgoing.TryDequeue(out _)) ;
if (Commands != null) while (Commands.TryDequeue(out _)) ;
if (ConnectionEvents != null) while (ConnectionEvents.TryDequeue(out _)) ;
if (DisconnectionEvents != null) while (DisconnectionEvents.TryDequeue(out _)) ;
if (StatusUpdates != null) while (StatusUpdates.TryDequeue(out _)) ;
WorkerThread = new Thread(ThreadWorker);
WorkerThread.Start(threadParams);
// Announce
if (Verbosity > 0)
Debug.Log("Ignorance Server: Dispatched worker thread.");
}
public void Stop()
{
// v1.4.0b7: Mirror may call this; if the worker thread isn't alive then don't announce it.
if (WorkerThread != null && WorkerThread.IsAlive)
{
if (Verbosity > 0)
Debug.Log("Ignorance Server: Server stop acknowledged. Depending on network load, this may take a moment or two...");
CeaseOperation = true;
}
}
#region The meat and potatoes.
private void ThreadWorker(Object parameters)
{
if (Verbosity > 0)
Debug.Log("Ignorance: Server instance worker thread is initializing. Please stand by...");
// Thread cache items
ThreadParamInfo setupInfo;
Address serverAddress = new Address();
Host serverENetHost;
Event serverENetEvent;
Peer[] serverPeerArray;
IgnoranceClientStats peerStats = default;
// Grab the setup information.
if (parameters.GetType() == typeof(ThreadParamInfo))
{
setupInfo = (ThreadParamInfo)parameters;
}
else
{
Debug.LogError("Ignorance: Server instance worker thread reports startup failure; Invalid thread parameters. Aborting.");
return;
}
// Attempt to initialize ENet inside the thread.
if (Library.Initialize())
{
Debug.Log("Ignorance: Server instance worker thread successfully initialized ENet.");
}
else
{
Debug.LogError("Ignorance Server: Failed to initialize ENet Native. Aborting.");
return;
}
// Configure the server address.
// So... if we're running on a Mac, we have an issue where if we attempt to bind to all devices which usually is either
// 0.0.0.0 or ::0. In older versions of Mac OS, this worked since those addresses are "broadcast" or "listen all"...
// but on newer versions this broke the server functionality. So the fix was rather easy but the debugging was not.
// The fix was to NOT set any IP/Host if we're binding to all addresses on MacOS. Apparently that works. Go figure.
// Afterthought: Maybe it's something funky in ENet...?
if (setupInfo.IsFruityDevice)
{
if (!setupInfo.BindAllInterfaces)
serverAddress.SetHost(setupInfo.Address);
}
else
{
if (setupInfo.BindAllInterfaces)
serverAddress.SetIP(IgnoranceInternals.BindAnyAddress);
else
serverAddress.SetHost(setupInfo.Address);
}
// Set the port too.
serverAddress.Port = (ushort)setupInfo.Port;
serverPeerArray = new Peer[setupInfo.Peers];
using (serverENetHost = new Host())
{
// Create the server object.
try
{
serverENetHost.Create(serverAddress, setupInfo.Peers, setupInfo.Channels);
}
catch (Exception ex)
{
Debug.LogError($"Ignorance: Server instance worker thread reports that something went wrong. While attempting to create server host object, we caught an exception:\n{ex.Message}");
Debug.LogError($"If you are getting a \"Host creation call failed\" exception, please ensure you don't have a server already running on the same IP and Port.\n" +
$"Multiple server instances running on the same port are not supported. Also check to see if ports are not in-use by another application. In the worse case scenario, " +
$"restart your device to ensure no random background ENet threads are active that haven't been cleaned up correctly. If problems persist, please file a support ticket.");
Library.Deinitialize();
return;
}
// Loop until we're told to cease operations.
while (!CeaseOperation)
{
// Intermission: Command Handling
while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket))
{
switch (commandPacket.Type)
{
default:
break;
// Boot a Peer off the Server.
case IgnoranceCommandType.ServerKickPeer:
uint targetPeer = commandPacket.PeerId;
if (!serverPeerArray[targetPeer].IsSet) continue;
if (setupInfo.Verbosity > 0)
Debug.Log($"Ignorance: Server instance is disconnecting peer {targetPeer}.");
IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent
{
EventType = 0x01,
NativePeerId = targetPeer
};
DisconnectionEvents.Enqueue(iced);
// Disconnect and reset the peer array's entry for that peer.
serverPeerArray[targetPeer].DisconnectNow(0);
serverPeerArray[targetPeer] = default;
break;
case IgnoranceCommandType.ServerStatusRequest:
IgnoranceServerStats serverStats;
if (!RecycledServerStatBlocks.TryDequeue(out serverStats))
serverStats.PeerStats = new Dictionary<int, IgnoranceClientStats>(setupInfo.Peers);
serverStats.PeerStats.Clear();
serverStats.BytesReceived = serverENetHost.BytesReceived;
serverStats.BytesSent = serverENetHost.BytesSent;
serverStats.PacketsReceived = serverENetHost.PacketsReceived;
serverStats.PacketsSent = serverENetHost.PacketsSent;
serverStats.PeersCount = serverENetHost.PeersCount;
for (int i = 0; i < serverPeerArray.Length; i++)
{
if (!serverPeerArray[i].IsSet) continue;
peerStats.RTT = serverPeerArray[i].RoundTripTime;
peerStats.BytesReceived = serverPeerArray[i].BytesReceived;
peerStats.BytesSent = serverPeerArray[i].BytesSent;
peerStats.PacketsSent = serverPeerArray[i].PacketsSent;
peerStats.PacketsLost = serverPeerArray[i].PacketsLost;
serverStats.PeerStats.Add(i, peerStats);
}
StatusUpdates.Enqueue(serverStats);
break;
}
}
// Step One:
// ---> Sending to peers
while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket))
{
// Only create a packet if the server knows the peer.
if (serverPeerArray[outgoingPacket.NativePeerId].IsSet)
{
int ret = serverPeerArray[outgoingPacket.NativePeerId].Send(outgoingPacket.Channel, ref outgoingPacket.Payload);
if (ret < 0 && setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: Server instance ENet error {ret} while sending packet to Peer {outgoingPacket.NativePeerId}.");
}
else
{
// A peer might have disconnected, this is OK - just log the packet if set to paranoid.
if (setupInfo.Verbosity > 1)
Debug.LogWarning("Ignorance: Server instance can't send packet, a native peer object is not set. This may be normal if the Peer has disconnected before this send cycle.");
}
}
// Step 2
// <--- Receiving from peers
bool pollComplete = false;
while (!pollComplete)
{
Packet incomingPacket;
Peer incomingPeer;
int incomingPacketLength;
// Any events happening?
if (serverENetHost.CheckEvents(out serverENetEvent) <= 0)
{
// If service time is met, break out of it.
if (serverENetHost.Service(setupInfo.PollTime, out serverENetEvent) <= 0) break;
pollComplete = true;
}
// Setup the packet references.
incomingPeer = serverENetEvent.Peer;
// What type are you?
switch (serverENetEvent.Type)
{
// Idle.
case EventType.None:
default:
break;
// Connection Event.
case EventType.Connect:
if (setupInfo.Verbosity > 1)
Debug.Log($"Ignorance: Server instance says that Peer {incomingPeer.ID} says Hi.");
IgnoranceConnectionEvent ice = new IgnoranceConnectionEvent()
{
NativePeerId = incomingPeer.ID,
IP = incomingPeer.IP,
Port = incomingPeer.Port
};
ConnectionEvents.Enqueue(ice);
// Assign a reference to the Peer.
serverPeerArray[incomingPeer.ID] = incomingPeer;
break;
// Disconnect/Timeout. Mirror doesn't care if it's either, so we lump them together.
case EventType.Disconnect:
case EventType.Timeout:
if (!serverPeerArray[incomingPeer.ID].IsSet) break;
if (setupInfo.Verbosity > 1)
Debug.Log($"Ignorance: Server instance says that Peer {incomingPeer.ID} has disconnected.");
IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent
{
EventType = 0x01,
NativePeerId = incomingPeer.ID
};
DisconnectionEvents.Enqueue(iced);
// Reset the peer array's entry for that peer.
serverPeerArray[incomingPeer.ID] = default;
break;
case EventType.Receive:
// Receive event type usually includes a packet; so cache its reference.
incomingPacket = serverENetEvent.Packet;
if (!incomingPacket.IsSet)
{
if (setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: Server instance receive event did not supply us with a packet to work with. This should never happen.");
break;
}
incomingPacketLength = incomingPacket.Length;
// Firstly check if the packet is too big. If it is, do not process it - drop it.
if (incomingPacketLength > setupInfo.PacketSizeLimit)
{
if (setupInfo.Verbosity > 0)
Debug.LogWarning($"Ignorance: Server incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes.");
incomingPacket.Dispose();
break;
}
IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket
{
Channel = serverENetEvent.ChannelID,
NativePeerId = incomingPeer.ID,
Payload = incomingPacket,
};
// Enqueue.
Incoming.Enqueue(incomingQueuePacket);
break;
}
}
}
if (Verbosity > 0)
Debug.Log("Ignorance: Server instance thread shutdown commencing. Flushing connections.");
// Cleanup and flush everything.
serverENetHost.Flush();
// Kick everyone.
for (int i = 0; i < serverPeerArray.Length; i++)
{
if (!serverPeerArray[i].IsSet) continue;
serverPeerArray[i].DisconnectNow(0);
}
}
if (setupInfo.Verbosity > 0)
Debug.Log("Ignorance: Server instance thread shutdown complete.");
Library.Deinitialize();
}
#endregion
private void SetupRingBuffersIfNull()
{
Debug.Log($"Ignorance: Setting up ring buffers if they're not already created. " +
$"If they are already, this step will be skipped.");
if (Incoming == null)
Incoming = new RingBuffer<IgnoranceIncomingPacket>(IncomingOutgoingBufferSize);
if (Outgoing == null)
Outgoing = new RingBuffer<IgnoranceOutgoingPacket>(IncomingOutgoingBufferSize);
if (Commands == null)
Commands = new RingBuffer<IgnoranceCommandPacket>(100);
if (ConnectionEvents == null)
ConnectionEvents = new RingBuffer<IgnoranceConnectionEvent>(ConnectionEventBufferSize);
if (DisconnectionEvents == null)
DisconnectionEvents = new RingBuffer<IgnoranceConnectionEvent>(ConnectionEventBufferSize);
if (StatusUpdates == null)
StatusUpdates = new RingBuffer<IgnoranceServerStats>(10);
}
private struct ThreadParamInfo
{
public bool IsFruityDevice;
public bool BindAllInterfaces;
public int Channels;
public int Peers;
public int PollTime;
public int Port;
public int PacketSizeLimit;
public int Verbosity;
public string Address;
}
}
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,279 @@
// The following dependency was taken from https://github.com/dave-hillier/disruptor-unity3d
// The Apache License 2.0 this dependency follows is located at https://github.com/dave-hillier/disruptor-unity3d/blob/master/LICENSE.
// Modifications were made by SoftwareGuy (Coburn).
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace IgnoranceThirdparty
{
/// <summary>
/// Implementation of the Disruptor pattern
/// </summary>
/// <typeparam name="T">the type of item to be stored</typeparam>
public class RingBuffer<T>
{
private readonly T[] _entries;
private readonly int _modMask;
private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong();
private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong();
/// <summary>
/// Creates a new RingBuffer with the given capacity
/// </summary>
/// <param name="capacity">The capacity of the buffer</param>
/// <remarks>Only a single thread may attempt to consume at any one time</remarks>
public RingBuffer(int capacity)
{
capacity = NextPowerOfTwo(capacity);
_modMask = capacity - 1;
_entries = new T[capacity];
}
/// <summary>
/// The maximum number of items that can be stored
/// </summary>
public int Capacity
{
get { return _entries.Length; }
}
public T this[long index]
{
get { unchecked { return _entries[index & _modMask]; } }
set { unchecked { _entries[index & _modMask] = value; } }
}
/// <summary>
/// Removes an item from the buffer.
/// </summary>
/// <returns>The next available item</returns>
public T Dequeue()
{
var next = _consumerCursor.ReadAcquireFence() + 1;
while (_producerCursor.ReadAcquireFence() < next) // makes sure we read the data from _entries after we have read the producer cursor
{
Thread.SpinWait(1);
}
var result = this[next];
_consumerCursor.WriteReleaseFence(next); // makes sure we read the data from _entries before we update the consumer cursor
return result;
}
/// <summary>
/// Attempts to remove an items from the queue
/// </summary>
/// <param name="obj">the items</param>
/// <returns>True if successful</returns>
public bool TryDequeue(out T obj)
{
var next = _consumerCursor.ReadAcquireFence() + 1;
if (_producerCursor.ReadAcquireFence() < next)
{
obj = default(T);
return false;
}
obj = Dequeue();
return true;
}
/// <summary>
/// Add an item to the buffer
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
var next = _producerCursor.ReadAcquireFence() + 1;
long wrapPoint = next - _entries.Length;
long min = _consumerCursor.ReadAcquireFence();
while (wrapPoint > min)
{
min = _consumerCursor.ReadAcquireFence();
Thread.SpinWait(1);
}
this[next] = item;
_producerCursor.WriteReleaseFence(next); // makes sure we write the data in _entries before we update the producer cursor
}
/// <summary>
/// The number of items in the buffer
/// </summary>
/// <remarks>for indicative purposes only, may contain stale data</remarks>
public int Count { get { return (int)(_producerCursor.ReadFullFence() - _consumerCursor.ReadFullFence()); } }
private static int NextPowerOfTwo(int x)
{
var result = 2;
while (result < x)
{
result <<= 1;
}
return result;
}
}
public static class Volatile
{
private const int CacheLineSize = 64;
[StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)]
public struct PaddedLong
{
[FieldOffset(CacheLineSize)]
private long _value;
/// <summary>
/// Create a new <see cref="PaddedLong"/> with the given initial value.
/// </summary>
/// <param name="value">Initial value</param>
public PaddedLong(long value)
{
_value = value;
}
/// <summary>
/// Read the value without applying any fence
/// </summary>
/// <returns>The current value</returns>
public long ReadUnfenced()
{
return _value;
}
/// <summary>
/// Read the value applying acquire fence semantic
/// </summary>
/// <returns>The current value</returns>
public long ReadAcquireFence()
{
var value = _value;
Thread.MemoryBarrier();
return value;
}
/// <summary>
/// Read the value applying full fence semantic
/// </summary>
/// <returns>The current value</returns>
public long ReadFullFence()
{
Thread.MemoryBarrier();
return _value;
}
/// <summary>
/// Read the value applying a compiler only fence, no CPU fence is applied
/// </summary>
/// <returns>The current value</returns>
[MethodImpl(MethodImplOptions.NoOptimization)]
public long ReadCompilerOnlyFence()
{
return _value;
}
/// <summary>
/// Write the value applying release fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteReleaseFence(long newValue)
{
Thread.MemoryBarrier();
_value = newValue;
}
/// <summary>
/// Write the value applying full fence semantic
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteFullFence(long newValue)
{
Thread.MemoryBarrier();
_value = newValue;
}
/// <summary>
/// Write the value applying a compiler fence only, no CPU fence is applied
/// </summary>
/// <param name="newValue">The new value</param>
[MethodImpl(MethodImplOptions.NoOptimization)]
public void WriteCompilerOnlyFence(long newValue)
{
_value = newValue;
}
/// <summary>
/// Write without applying any fence
/// </summary>
/// <param name="newValue">The new value</param>
public void WriteUnfenced(long newValue)
{
_value = newValue;
}
/// <summary>
/// Atomically set the value to the given updated value if the current value equals the comparand
/// </summary>
/// <param name="newValue">The new value</param>
/// <param name="comparand">The comparand (expected value)</param>
/// <returns></returns>
public bool AtomicCompareExchange(long newValue, long comparand)
{
return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
}
/// <summary>
/// Atomically set the value to the given updated value
/// </summary>
/// <param name="newValue">The new value</param>
/// <returns>The original value</returns>
public long AtomicExchange(long newValue)
{
return Interlocked.Exchange(ref _value, newValue);
}
/// <summary>
/// Atomically add the given value to the current value and return the sum
/// </summary>
/// <param name="delta">The value to be added</param>
/// <returns>The sum of the current value and the given value</returns>
public long AtomicAddAndGet(long delta)
{
return Interlocked.Add(ref _value, delta);
}
/// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The incremented value.</returns>
public long AtomicIncrementAndGet()
{
return Interlocked.Increment(ref _value);
}
/// <summary>
/// Atomically increment the current value and return the new value
/// </summary>
/// <returns>The decremented value.</returns>
public long AtomicDecrementAndGet()
{
return Interlocked.Decrement(ref _value);
}
/// <summary>
/// Returns the string representation of the current value.
/// </summary>
/// <returns>the string representation of the current value.</returns>
public override string ToString()
{
var value = ReadFullFence();
return value.ToString();
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,102 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace IgnoranceTransport
{
/// <summary>
/// Adds the given define symbols to PlayerSettings define symbols.
/// Just add your own define symbols to the Symbols property at the below.
/// </summary>
[InitializeOnLoad]
public class AddIgnoranceDefine : Editor
{
private static bool debugMode = false;
private static string existingDefines = string.Empty;
/// <summary>
/// Symbols that will be added to the editor
/// </summary>
public static readonly string[] Symbols = new string[] {
"IGNORANCE", // Ignorance exists
"IGNORANCE_1", // Major version
"IGNORANCE_1_4" // Major and minor version
};
/// <summary>
/// Do not remove these symbols
/// </summary>
public static readonly string[] DoNotRemoveTheseSymbols = new string[]
{
"IGNORANCE",
"IGNORANCE_1",
"IGNORANCE_1_4",
"IGNORANCE_MIRROR_POLLING"
};
/// <summary>
/// Add define symbols as soon as Unity gets done compiling.
/// </summary>
static AddIgnoranceDefine()
{
// Get the current scripting defines
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
if (existingDefines == definesString)
{
// 1.2.6: There is no need to apply the changes, return.
return;
}
// Convert the string to a list
List<string> allDefines = definesString.Split(';').ToList();
// Remove any old version defines from previous installs
allDefines.RemoveAll(IsSafeToRemove);
// x => x.StartsWith("IGNORANCE") && !DoesSymbolExistInBlacklist(x));
// Add any symbols that weren't already in the list
allDefines.AddRange(Symbols.Except(allDefines));
string newDefines = string.Join(";", allDefines.ToArray());
PlayerSettings.SetScriptingDefineSymbolsForGroup(
EditorUserBuildSettings.selectedBuildTargetGroup,
newDefines
);
existingDefines = newDefines;
}
// 1.2.4: Workaround to stop things from eating custom IGNORANCE_ symbols
static bool DoesSymbolExistInBlacklist(string symbol)
{
foreach(string s in DoNotRemoveTheseSymbols)
{
if(debugMode)
UnityEngine.Debug.Log($"{s.Trim()} matches blacklist");
if (s == symbol.Trim()) return true;
}
return false;
}
static bool IsSafeToRemove (string input)
{
if (input.StartsWith("IGNORANCE") && !DoesSymbolExistInBlacklist(input))
{
if (debugMode)
UnityEngine.Debug.Log($"{input.Trim()} is safe to remove");
return true;
}
return false;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,43 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
#if UNITY_EDITOR
using UnityEditor;
namespace IgnoranceTransport
{
public class Toolbox
{
#pragma warning disable IDE0051
[MenuItem("Ignorance/Debug/Native Library Name")]
public static void RevealEnetLibraryName()
{
EditorUtility.DisplayDialog("ENet Library Name", $"Your platform expects the native library to be called: {ENet.Native.nativeLibraryName}.\n\n" +
$"This info is very useful when trying to diagnose issues with DLL loading.", "Got it");
}
[MenuItem("Ignorance/RTFM/Github Repo")]
private static void LaunchGithubRepo()
{
UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/Ignorance");
}
[MenuItem("Ignorance/RTFM/Issue Tracker")]
private static void LaunchGithubIssueTracker()
{
UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/Ignorance/issues");
}
[MenuItem("Ignorance/RTFM/ENet-C# Repo")]
private static void LaunchENetCSharpForkRepo()
{
UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/ENet-CSharp");
}
#pragma warning restore
}
}
#endif

View File

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

View File

@@ -0,0 +1,731 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
using ENet;
using IgnoranceCore;
using Mirror;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace IgnoranceTransport
{
[DisallowMultipleComponent]
public class Ignorance : Transport
{
#region Inspector options
[Header("Essentials")]
public int port = 7777;
[Header("Debug & Logging")]
[Tooltip("Uses OnGUI to present you with statistics for Server and Client backend instances.")]
public bool DebugDisplay = false;
[Tooltip("How verbose do you want Ignorance to be?")]
public IgnoranceLogType LogType = IgnoranceLogType.Standard;
[Header("Server Configuration")]
[Tooltip("Should the server bind to all interfaces?")]
public bool serverBindsAll = true;
[Tooltip("This is only used if Server Binds All is unticked.")]
public string serverBindAddress = string.Empty;
[Tooltip("How many peers native ENet will support. Low sane numbers help performance, avoids looping over huge native arrays. Recommended: maximum Mirror players, rounded to nearest 10. (Example: 16 -> 20).")]
public int serverMaxPeerCapacity = 64;
[Tooltip("Server network performance/CPU utilization trade off. Lower numbers = Better performance, more CPU. Higher numbers = Potentially lower performance, less CPU. (Value is in milliseconds)")]
public int serverMaxNativeWaitTime = 1;
[Tooltip("Interval between asking ENet for server status updates. Set to <= 0 to disable.")]
public int serverStatusUpdateInterval = 0;
[Header("Client Configuration")]
[Tooltip("Client network performance/CPU utilization trade off. Lower numbers = Better performance, more CPU. Higher numbers = Potentially lower performance, less CPU. (Value is in milliseconds)")]
public int clientMaxNativeWaitTime = 3;
[Tooltip("Interval between asking ENet for client status updates. Set to <= 0 to disable.")]
public int clientStatusUpdateInterval = 0;
[Header("Channel Configuration")]
[Tooltip("You must define your channels in the array shown here, otherwise ENet will not know what channel delivery type to use.")]
public IgnoranceChannelTypes[] Channels;
[Header("Ring Buffer Tweaking")]
[Tooltip("[Client Only] Capacity of the incoming and outgoing ring buffers. If the ring buffer is full, it will spin waiting for a free slot in the buffer. Test and increase as required. This value translates to packets per second under a worse-case scenario.")]
public int ClientDataBufferSize = 1000;
[Tooltip("[Client Only] Capacity of the connection event buffer. This is probably best to keep small as connection events are literally minimal in Mirror.")]
public int ClientConnEventBufferSize = 10;
[Tooltip("[Server Only] Capacity of Server Incoming/Outgoing ring buffers. If the ring buffer is full, it will spin waiting for a free slot in the RingBuffer. Test and increase as required. This value translates to packets per second under a worse-case scenario.\n\n" +
"Unlike the client value, it is recommended that you keep this resonably high as servers process more network IO than clients.")]
public int ServerDataBufferSize = 5000;
[Tooltip("[Server Only] Defines the capacity of server connection event buffer. This is probably best to keep moderately small unless you expect to have a large influx of users connecting/disconnecting at once.")]
public int ServerConnEventBufferSize = 100;
[Header("Danger: I hope you know what you're doing!")]
[Tooltip("Used internally to keep allocations to a minimum. This is how much memory will be consumed by the packet buffer on startup, and then reused. If you have large packets, change this to something larger. Default is 4096 bytes (4KB).")]
public int PacketBufferCapacity = 4096;
[Tooltip("For UDP based protocols, it's best to keep your data under the safe MTU of 1200 bytes. This is the maximum allowed packet size, however note that internally ENet can only go to 32MB.")]
public int MaxAllowedPacketSize = 33554432;
#endregion
#region Public Statistics
public IgnoranceClientStats ClientStatistics;
public IgnoranceServerStats ServerStatistics;
#endregion
public override bool Available()
{
// Ignorance is not available for Unity WebGL, the PS4 (no dev kit to confirm) or Switch (port exists but I have no access to said code).
// Ignorance is available for most other operating systems.
#if UNITY_WEBGL || UNITY_PS4 || UNITY_PS5 || UNITY_SWITCH
return false;
#else
return true;
#endif
}
public void Awake()
{
if (LogType != IgnoranceLogType.Quiet)
print($"Ignorance {IgnoranceInternals.Version} has arrived. Keep up to date, report bugs and support the developer at https://github.com/SoftwareGuy/Ignorance!");
}
public override string ToString()
{
return $"Ignorance v{IgnoranceInternals.Version}";
}
public override void ClientConnect(string address)
{
if (LogType != IgnoranceLogType.Quiet)
print($"Ignorance: Requesting client thread dispatch for a connection to '{address}'");
ClientState = ConnectionState.Connecting;
cachedConnectionAddress = address;
// Initialize.
InitializeClientBackend();
// Get going.
ignoreDataPackets = false;
// Start!
Client.Start();
}
public override void ClientConnect(Uri uri)
{
if (uri.Scheme != IgnoranceInternals.Scheme)
throw new ArgumentException($"You used an invalid URI: {uri}. Please use {IgnoranceInternals.Scheme}://host:port instead", nameof(uri));
if (!uri.IsDefaultPort)
// Set the communication port to the one specified.
port = uri.Port;
// Pass onwards to the proper handler.
ClientConnect(uri.Host);
}
public override bool ClientConnected() => ClientState == ConnectionState.Connected;
public override void ClientDisconnect()
{
// 2022-01-17 Fix issue ticket #83
if (Client != null)
{
// Ugh this feels like a ugly hack
// If we're host client for example, we don't need to run these routines.
if (!Client.IsAlive) return;
ClientState = ConnectionState.Disconnecting;
// Fix for the Client commands RingBuffer not being initialized if we're in host mode.
if (Client.Commands != null)
Client.Commands.Enqueue(new IgnoranceCommandPacket { Type = IgnoranceCommandType.ClientWantsToStop });
Client.Stop();
}
else
{
throw new InvalidOperationException("Cannot disconnect the client instance. The Ignorance Client instance is null.");
}
}
public override void ClientSend(ArraySegment<byte> segment, int channelId)
{
if (Client == null)
{
Debug.LogError("Client object is null, this shouldn't really happen but it did...");
return;
}
if (channelId < 0 || channelId > Channels.Length)
{
Debug.LogError("Channel ID is out of bounds.");
return;
}
// Create our struct...
Packet clientOutgoingPacket = default;
int byteCount = segment.Count;
int byteOffset = segment.Offset;
// Set our desired flags...
PacketFlags desiredFlags = (PacketFlags)Channels[channelId];
// Warn if over recommended MTU...
bool flagsSet = (desiredFlags & ReliableOrUnreliableFragmented) > 0;
if (LogType != IgnoranceLogType.Quiet && byteCount > 1200 && !flagsSet)
Debug.LogWarning($"Ignorance: Client tried to send a Unreliable packet bigger than the recommended ENet 1200 byte MTU ({byteCount} > 1200). ENet will force Reliable Fragmented delivery.");
// Create the packet.
clientOutgoingPacket.Create(segment.Array, byteOffset, byteCount + byteOffset, desiredFlags);
// Enqueue the packet.
IgnoranceOutgoingPacket dispatchPacket = new IgnoranceOutgoingPacket
{
Channel = (byte)channelId,
Payload = clientOutgoingPacket
};
// Pass the packet onto the thread for dispatch.
Client.Outgoing.Enqueue(dispatchPacket);
}
public override bool ServerActive()
{
// Very simple check.
return Server != null && Server.IsAlive;
}
// Current version Mirror server disconnection routine.
public override void ServerDisconnect(int connectionId)
{
if (Server == null)
{
Debug.LogError("Cannot enqueue kick packet; our Server object is null. Something has gone wrong.");
// Return here because otherwise we will get a NRE when trying to enqueue the kick packet.
return;
}
// Enqueue the kick packet.
IgnoranceCommandPacket kickPacket = new IgnoranceCommandPacket
{
Type = IgnoranceCommandType.ServerKickPeer,
PeerId = (uint)connectionId - 1 // ENet's native peer ID will be ConnID - 1
};
// Pass the packet onto the thread for dispatch.
Server.Commands.Enqueue(kickPacket);
}
public override string ServerGetClientAddress(int connectionId)
{
if (peerConnectionData == null)
return "(unavailable)";
// Need to adjust the string...
if (!string.IsNullOrEmpty(peerConnectionData[connectionId - 1].IP))
return $"{peerConnectionData[connectionId - 1].IP}:{peerConnectionData[connectionId - 1].Port}";
else
return "(unavailable)";
}
// v1.4.0b6: Mirror rearranged the ServerSend params, so we need to apply a fix for that or
// we end up using the obsoleted version. The obsolete version isn't a fatal error, but
// it's best to stick with the new structures.
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
{
if (Server == null)
{
Debug.LogError("Ignorance Server: Cannot enqueue data packet; our Server object is null. Something has gone wrong.");
return;
}
if (channelId < 0 || channelId > Channels.Length)
{
Debug.LogError("Ignorance Server: Channel ID is out of bounds.");
return;
}
// Packet Struct
Packet serverOutgoingPacket = default;
int byteCount = segment.Count;
int byteOffset = segment.Offset;
PacketFlags desiredFlags = (PacketFlags)Channels[channelId];
// Warn if over recommended MTU
bool flagsSet = (desiredFlags & ReliableOrUnreliableFragmented) > 0;
if (LogType != IgnoranceLogType.Quiet && byteCount > 1200 && !flagsSet)
Debug.LogWarning($"Ignorance Server: Trying to send a Unreliable packet bigger than the recommended ENet 1200 byte MTU ({byteCount} > 1200). ENet will force Reliable Fragmented delivery.");
// Create the packet.
serverOutgoingPacket.Create(segment.Array, byteOffset, byteCount + byteOffset, (PacketFlags)Channels[channelId]);
// Enqueue the packet.
IgnoranceOutgoingPacket dispatchPacket = new IgnoranceOutgoingPacket
{
Channel = (byte)channelId,
NativePeerId = (uint)connectionId - 1, // ENet's native peer ID will be ConnID - 1
Payload = serverOutgoingPacket
};
Server.Outgoing.Enqueue(dispatchPacket);
}
public override void ServerStart()
{
if (LogType != IgnoranceLogType.Quiet)
Debug.Log("Ignorance Server: Instance starting up...");
InitializeServerBackend();
Server.Start();
}
public override void ServerStop()
{
if (Server != null)
{
if (LogType != IgnoranceLogType.Quiet)
Debug.Log("Ignorance Server: Instance shutting down...");
Server.Stop();
}
peerConnectionData = null;
}
public override Uri ServerUri()
{
UriBuilder builder = new UriBuilder
{
Scheme = IgnoranceInternals.Scheme,
Host = serverBindAddress,
Port = port
};
return builder.Uri;
}
public override void Shutdown()
{
// TODO: Nothing needed here?
}
// Check to ensure channels 0 and 1 mimic LLAPI. Override this at your own risk.
private void OnValidate()
{
if (Channels != null && Channels.Length >= 2)
{
// Check to make sure that Channel 0 and 1 are correct.
if (Channels[0] != IgnoranceChannelTypes.Reliable)
{
Debug.LogWarning("Please do not modify Ignorance Channel 0. The channel will be reset to Reliable delivery. If you need a channel with a different delivery, define and use it instead.");
Channels[0] = IgnoranceChannelTypes.Reliable;
}
if (Channels[1] != IgnoranceChannelTypes.Unreliable)
{
Debug.LogWarning("Please do not modify Ignorance Channel 1. The channel will be reset to Unreliable delivery. If you need a channel with a different delivery, define and use it instead.");
Channels[1] = IgnoranceChannelTypes.Unreliable;
}
}
else
{
Debug.LogWarning("Invalid Channels setting, fixing. If you've just added Ignorance to your NetworkManager GameObject, seeing this message is normal.");
Channels = new IgnoranceChannelTypes[2]
{
IgnoranceChannelTypes.Reliable,
IgnoranceChannelTypes.Unreliable
};
}
// ENet only supports a maximum of 32MB packet size.
if (MaxAllowedPacketSize > 33554432)
MaxAllowedPacketSize = 33554432;
}
private void InitializeServerBackend()
{
if (Server == null)
{
Debug.LogWarning("Ignorance Server: Reference for Server mode was null. This shouldn't happen, but to be safe we'll attempt to reinitialize it.");
Server = new IgnoranceServer();
}
// Set up the new IgnoranceServer reference.
if (serverBindsAll)
{
// MacOS is special. It's also a massive thorn in my backside.
Server.IsFruityDevice = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX;
Server.BindAddress = IgnoranceInternals.BindAnyAddress;
Server.BindAllInterfaces = true;
}
else
// Use the supplied bind address.
Server.BindAddress = serverBindAddress;
// Sets port, maximum peers, max channels, the server poll time, maximum packet size and verbosity.
Server.BindPort = port;
Server.MaximumPeers = serverMaxPeerCapacity;
Server.MaximumChannels = Channels.Length;
Server.PollTime = serverMaxNativeWaitTime;
Server.MaximumPacketSize = MaxAllowedPacketSize;
Server.Verbosity = (int)LogType;
Server.IncomingOutgoingBufferSize = ServerDataBufferSize;
Server.ConnectionEventBufferSize = ServerConnEventBufferSize;
// Initializes the packet buffer.
// Allocates once, that's it.
if (InternalPacketBuffer == null)
InternalPacketBuffer = new byte[PacketBufferCapacity];
// This is required to ensure that ServerStatistics peer stats are initialised before first update
if (ServerStatistics.PeerStats == null)
ServerStatistics.PeerStats = new Dictionary<int, IgnoranceClientStats>(serverMaxPeerCapacity);
// Setup the peer connection array.
peerConnectionData = new PeerConnectionData[serverMaxPeerCapacity];
}
private void InitializeClientBackend()
{
if (Client == null)
{
Debug.LogWarning("Ignorance: Client backend instance reference was null. This shouldn't happen, but to be safe we'll reinitialize it.");
Client = new IgnoranceClient();
}
// Sets address, port, channels to expect, verbosity, the server poll time and maximum packet size.
Client.ConnectAddress = cachedConnectionAddress;
Client.ConnectPort = port;
Client.ExpectedChannels = Channels.Length;
Client.PollTime = clientMaxNativeWaitTime;
Client.MaximumPacketSize = MaxAllowedPacketSize;
Client.Verbosity = (int)LogType;
Client.IncomingOutgoingBufferSize = ClientDataBufferSize;
Client.ConnectionEventBufferSize = ClientConnEventBufferSize;
// Initializes the packet buffer. Allocates once, that's it.
if (InternalPacketBuffer == null)
InternalPacketBuffer = new byte[PacketBufferCapacity];
}
#region Main Thread Processing and Polling
public void ServerPump()
{
// Process Server Events...
if (Server.IsAlive)
{
ProcessServerPackets();
if (serverStatusUpdateInterval > 0)
{
serverStatusUpdateTimer += Time.deltaTime;
if (serverStatusUpdateTimer >= serverStatusUpdateInterval)
{
Server.Commands.Enqueue(new IgnoranceCommandPacket { Type = IgnoranceCommandType.ServerStatusRequest });
serverStatusUpdateTimer = 0f;
}
}
}
}
public void ClientPump()
{
// Only process client packets if we're not disconnected...
if (ClientState != ConnectionState.Disconnected)
ProcessClientPackets();
// Used if we're connected and the client status update interval is above 0.
if (ClientState == ConnectionState.Connected && clientStatusUpdateInterval > 0)
{
clientStatusUpdateTimer += Time.deltaTime;
if (clientStatusUpdateTimer >= clientStatusUpdateInterval)
{
Client.Commands.Enqueue(new IgnoranceCommandPacket { Type = IgnoranceCommandType.ClientStatusRequest });
clientStatusUpdateTimer = 0f;
}
}
}
private void ProcessServerPackets()
{
IgnoranceIncomingPacket incomingPacket;
IgnoranceConnectionEvent connectionEvent;
int adjustedConnectionId;
Packet payload;
IgnoranceServerStats serverStats;
// Incoming connection events.
while (Server.ConnectionEvents.TryDequeue(out connectionEvent))
{
adjustedConnectionId = (int)connectionEvent.NativePeerId + 1;
if (LogType == IgnoranceLogType.Verbose)
Debug.Log($"Processing a server connection event from ENet native peer {connectionEvent.NativePeerId}. This peer would be Mirror ConnID {adjustedConnectionId}.");
// Cache that peer.
// NOTE: We cache the peers native id and do some magic later.
peerConnectionData[(int)connectionEvent.NativePeerId] = new PeerConnectionData
{
IP = connectionEvent.IP,
NativePeerId = connectionEvent.NativePeerId,
Port = connectionEvent.Port
};
OnServerConnected?.Invoke(adjustedConnectionId);
}
// Handle incoming data packets.
// Console.WriteLine($"Server Incoming Queue is {Server.Incoming.Count}");
while (Server.Incoming.TryDequeue(out incomingPacket))
{
adjustedConnectionId = (int)incomingPacket.NativePeerId + 1;
payload = incomingPacket.Payload;
int length = payload.Length;
ArraySegment<byte> dataSegment;
// Copy to working buffer and dispose of it.
if (length > InternalPacketBuffer.Length)
{
byte[] oneFreshNTastyGcAlloc = new byte[length];
payload.CopyTo(oneFreshNTastyGcAlloc);
dataSegment = new ArraySegment<byte>(oneFreshNTastyGcAlloc, 0, length);
}
else
{
payload.CopyTo(InternalPacketBuffer);
dataSegment = new ArraySegment<byte>(InternalPacketBuffer, 0, length);
}
payload.Dispose();
OnServerDataReceived?.Invoke(adjustedConnectionId, dataSegment, incomingPacket.Channel);
}
// Disconnection events.
while (Server.DisconnectionEvents.TryDequeue(out IgnoranceConnectionEvent disconnectionEvent))
{
adjustedConnectionId = (int)disconnectionEvent.NativePeerId + 1;
// The array is no longer occupied.
peerConnectionData[(int)connectionEvent.NativePeerId] = default;
if (LogType == IgnoranceLogType.Verbose)
Debug.Log($"Ignorance Server: Handling disconnection event from native peer {disconnectionEvent.NativePeerId}.");
// Invoke Mirror handler.
OnServerDisconnected?.Invoke(adjustedConnectionId);
}
// Handle status updates.
if (Server.StatusUpdates.TryDequeue(out serverStats))
{
Server.RecycledServerStatBlocks.Enqueue(ServerStatistics);
ServerStatistics = serverStats;
}
}
private void ProcessClientPackets()
{
IgnoranceIncomingPacket incomingPacket;
IgnoranceClientStats clientStats;
Packet payload;
// Handle connection events.
while (Client.ConnectionEvents.TryDequeue(out IgnoranceConnectionEvent connectionEvent))
{
if (LogType == IgnoranceLogType.Verbose)
Debug.Log($"Ignorance Debug: Client processing a ConnectionEvents queue item. Type: {connectionEvent.EventType.ToString("{0:X2}")}");
switch (connectionEvent.EventType)
{
case 0x00:
// Connected to server.
ClientState = ConnectionState.Connected;
if (LogType != IgnoranceLogType.Quiet)
Debug.Log($"Ignorance: Client has successfully connected to server at {connectionEvent.IP}:{connectionEvent.Port}");
ignoreDataPackets = false;
OnClientConnected?.Invoke();
break;
case 0x01:
// Disconnected from server.
ClientState = ConnectionState.Disconnected;
if (LogType != IgnoranceLogType.Quiet)
Debug.Log($"Ignorance: Client has been disconnected from server.");
ignoreDataPackets = true;
OnClientDisconnected?.Invoke();
break;
default:
// Unknown type.
if (LogType != IgnoranceLogType.Quiet)
Debug.LogWarning($"Ignorance: Client has unknown connection event type {connectionEvent.EventType.ToString("{0:X2}")}.");
break;
}
}
// Handle the incoming messages.
while (Client.Incoming.TryDequeue(out incomingPacket))
{
// Temporary fix: if ENet thread is too fast for Mirror, then ignore the packet.
// This is seen sometimes if you stop the client and there's still stuff in the queue.
if (ignoreDataPackets)
{
if (LogType == IgnoranceLogType.Verbose)
Debug.Log("Ignorance: Client ProcessClientPackets cycle skipped; ignoring data packet");
break;
}
// Otherwise client recieved data, advise Mirror.
// print($"Byte array: {incomingPacket.RentedByteArray.Length}. Packet Length: {incomingPacket.Length}");
payload = incomingPacket.Payload;
int length = payload.Length;
ArraySegment<byte> dataSegment;
// Copy to working buffer and dispose of it.
if (length > InternalPacketBuffer.Length)
{
// Unity's favourite: A fresh 'n' tasty GC Allocation!
byte[] oneFreshNTastyGcAlloc = new byte[length];
payload.CopyTo(oneFreshNTastyGcAlloc);
dataSegment = new ArraySegment<byte>(oneFreshNTastyGcAlloc, 0, length);
}
else
{
payload.CopyTo(InternalPacketBuffer);
dataSegment = new ArraySegment<byte>(InternalPacketBuffer, 0, length);
}
payload.Dispose();
OnClientDataReceived?.Invoke(dataSegment, incomingPacket.Channel);
}
// Step 3: Handle status updates.
if (Client.StatusUpdates.TryDequeue(out clientStats))
ClientStatistics = clientStats;
}
#endregion
#region Main Thread Processing and Polling - Ignorance Flavour
#if !IGNORANCE_MIRROR_POLLING
// FixedUpdate can be called many times per frame.
// Once we've handled stuff, we set a flag so that we don't poll again for this frame.
private bool fixedUpdateCompletedWork;
private void FixedUpdate()
{
if (fixedUpdateCompletedWork) return;
ServerPump();
ClientPump();
// Flip the bool to signal we've done our work.
fixedUpdateCompletedWork = true;
}
private new void Update()
{
// Process what FixedUpdate missed, only if the boolean is not set.
if (!fixedUpdateCompletedWork)
{
ServerPump();
ClientPump();
}
// Flip back the bool, so it can be reset.
fixedUpdateCompletedWork = false;
}
#endif
#endregion
#region Main Thread Processing and Polling - Mirror Flavour
#if IGNORANCE_MIRROR_POLLING
public override void ClientEarlyUpdate()
{
ClientPump();
}
public override void ServerEarlyUpdate()
{
ServerPump();
}
#endif
#endregion
#region Debug
private void OnGUI()
{
if (DebugDisplay)
{
if (Client != null)
GUI.Box(new Rect(
new Vector2(32, Screen.height - 220), new Vector2(240, 100)),
"-- CLIENT --\n" +
$"State: {ClientState} ({(Client.IsAlive ? "Alive" : "Dead")}) \n" +
$"Round Trip Time: {ClientStatistics.RTT} \n" +
$"Bytes In/Out: {ClientStatistics.BytesReceived} / {ClientStatistics.BytesSent} \n" +
$"Queue In/Out: {(Client.Incoming != null ? $"{Client.Incoming.Count}" : "0")} / {(Client.Outgoing != null ? $"{Client.Outgoing.Count}" : "0")} \n" +
$"ConnEvents: {(Client.ConnectionEvents != null ? $"{Client.ConnectionEvents.Count}" : "0")}"
);
if (Server != null)
GUI.Box(new Rect(
new Vector2(32, Screen.height - 110), new Vector2(240, 100)),
"-- SERVER --\n" +
$"State: {(Server.IsAlive ? "Alive" : "Dead")} \n" +
$"Bytes In/Out: {ServerStatistics.BytesReceived} / {ServerStatistics.BytesSent} \n" +
$"Queue In/Out: {(Server.Incoming != null ? $"{Server.Incoming.Count}" : "0")} / {(Server.Outgoing != null ? $"{Server.Outgoing.Count}" : "0")} \n" +
$"ConnEvents: {(Server.ConnectionEvents != null ? $"{Server.ConnectionEvents.Count}" : "0")}"
);
}
}
#endregion
// Mirror 46 (Mirror LTS) work arounds
public override int GetMaxPacketSize(int channelId = 0)
{
bool isFragmentedAlready = ((PacketFlags)Channels[channelId] & ReliableOrUnreliableFragmented) > 0;
return isFragmentedAlready ? MaxAllowedPacketSize : 1200;
}
#region Internals
private bool ignoreDataPackets;
private string cachedConnectionAddress = string.Empty;
private IgnoranceServer Server = new IgnoranceServer();
private IgnoranceClient Client = new IgnoranceClient();
private PeerConnectionData[] peerConnectionData;
private enum ConnectionState { Connecting, Connected, Disconnecting, Disconnected }
private ConnectionState ClientState = ConnectionState.Disconnected;
private byte[] InternalPacketBuffer;
private const PacketFlags ReliableOrUnreliableFragmented = PacketFlags.Reliable | PacketFlags.UnreliableFragmented;
private float clientStatusUpdateTimer = 0f;
private float serverStatusUpdateTimer = 0f;
#endregion
}
}

View File

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

View File

@@ -0,0 +1,109 @@
// Ignorance 1.4.x LTS (Long Term Support)
// https://github.com/SoftwareGuy/Ignorance
// -----------------
// Copyright (c) 2019 - 2021 Matt Coburn (SoftwareGuy/Coburn64)
// Ignorance is licensed under the MIT license. Refer
// to the LICENSE file for more information.
using System;
using System.Collections.Generic;
using ENet;
namespace IgnoranceCore
{
// Snipped from the transport files, as this will help
// me keep things up to date.
[Serializable]
public enum IgnoranceChannelTypes
{
Reliable = PacketFlags.Reliable, // Reliable UDP (TCP-like emulation)
ReliableUnsequenced = PacketFlags.Reliable | PacketFlags.Unsequenced, // Reliable UDP (TCP-like emulation w/o sequencing)
Unreliable = PacketFlags.Unsequenced, // Pure UDP, high velocity packet action.
UnreliableFragmented = PacketFlags.UnreliableFragmented, // Pure UDP, but fragmented.
UnreliableSequenced = PacketFlags.None, // Pure UDP, but sequenced.
Unthrottled = PacketFlags.Unthrottled, // Pure UDP. Literally turbo mode.
}
public class IgnoranceInternals
{
public const string Version = "1.4.0r2 (LTS)";
public const string Scheme = "enet";
public const string BindAnyAddress = "::0";
}
public enum IgnoranceLogType
{
Quiet,
Standard,
Verbose
}
public struct IgnoranceIncomingPacket
{
public byte Channel;
public uint NativePeerId;
public Packet Payload;
}
public struct IgnoranceOutgoingPacket
{
public byte Channel;
public uint NativePeerId;
public Packet Payload;
}
public struct IgnoranceConnectionEvent
{
public byte EventType;
public ushort Port;
public uint NativePeerId;
public string IP;
}
public struct IgnoranceCommandPacket
{
public IgnoranceCommandType Type;
public uint PeerId;
}
// Stats only - may not always be used!
public struct IgnoranceClientStats
{
public uint RTT;
public ulong BytesReceived;
public ulong BytesSent;
public ulong PacketsReceived;
public ulong PacketsSent;
public ulong PacketsLost;
}
public enum IgnoranceCommandType
{
// Client
ClientWantsToStop,
ClientStatusRequest,
// Server
ServerKickPeer,
ServerStatusRequest
}
// Stats only - may not always be used!
public struct IgnoranceServerStats
{
public ulong BytesReceived;
public ulong BytesSent;
public ulong PacketsReceived;
public ulong PacketsSent;
public ulong PeersCount;
public Dictionary<int, IgnoranceClientStats> PeerStats;
}
public struct PeerConnectionData
{
public ushort Port;
public uint NativePeerId;
public string IP;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,111 @@
fileFormatVersion: 2
guid: 4b3abd2268dea254da11190b00d16051
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARM64
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 0
settings: {}
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,106 @@
fileFormatVersion: 2
guid: e3ce4b8600e09d74ead46137ba184d01
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: ba78b07719f786c4cae4c52b9528903f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: x86
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,6 @@
Rename this debug library without the "_debug" suffix and replace (maybe backup the original first) the library.
For example, to replace the x64 ENet Library with the Debug version, you'd first backup/move "libenet.so", then extract and rename "libenet_debug.so" to "libenet.so".
If you have trouble, file a GitHub support ticket at https://github.com/SoftwareGuy/Ignorance.
- Coburn

View File

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

View File

@@ -0,0 +1,97 @@
fileFormatVersion: 2
guid: f2bd341c8082ddd47b4b5cc9bfdf2332
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 0
Exclude Win64: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: x86_64
DefaultValueInitialized: true
OS: Linux
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
This is a Windows x64 build of ENet-CSharp.
If you require a version of ENet for 32 Bit computer systems (ie. Windows 7/8/10 32Bit) then please get in touch, or you can install the requirements yourself and compile it using ENet-CSharp's MSBuild-based build system. Get in touch if you want me to compile them for you, but keep in mind that I do not support them when reporting bugs.

View File

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

View File

@@ -0,0 +1,111 @@
fileFormatVersion: 2
guid: cc98033c6bc234a419ccd053e7173781
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 0
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 0
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 0
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: None
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 1
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 0
settings: {}
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: 1485de7d76884a64bac00df01454081e
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CPU: ARM64
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: fa239cb4c47fc9447816815f80667fea
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CPU: ARMv7
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: 385bb48fb124b5f40a79bdecf421671f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CPU: X64
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: 5317859893ad2cf48a6df1d585ebdd2c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
ENET Pre-compiled Binary Library Blobs
==========================
This folder contains pre-compiled binaries for a variety of different platforms.
A brief summary of these folders are as follows:
- Windows, Mac, Linux
-- 64bit (x64)
- Android (Kitkat 4.4 minimum target OS)
-- ARMv7 (armeabi-v7a), ARMv8/AArch64 (arm64-v8a)
- iOS
-- FAT Library (armv7 + arm64). Targeted for iOS 8 minimum. Unsigned library.
DEBUG VERSIONS
===============
Debug versions of the libraries can be obtained at https://github.com/SoftwareGuy/ENet-CSharp/releases.
Otherwise you can also compile the library yourself with Debug enabled.
DOT POINTS
===========
1. 32bit Support for Ignorance has been removed. Originally, I did not want to support 32bit operating systems,
however due to some countries in the world still stuck in the 32bit era (Brasil, some Russian areas, etc) I added them as a
goodwill gesture. However, since those who needed the libraries have now vanished, I have stopped building 32bit versions of ENet.
COMPILE THE CODE YOURSELF
=========================
If you don't trust the above binaries then git clone the ENET-CSharp repository (http://github.com/SoftwareGuy/ENet-CSharp) and read the readme.
EXCLUSION INSTRUCTIONS
======================
No need, the meta data will cover that for you.
Still don't know what to do with these? Drop by the Mirror discord and post in the Ignorance channel.

View File

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

View File

@@ -0,0 +1 @@
1.4.0r1 LTS

View File

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