ball 项目提交
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af3b822355b50854d85fb6b4fb58ba1b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
|
||||
org.gradle.parallel=true
|
||||
android.enableR8=**MINIFY_WITH_R_EIGHT**
|
||||
unityStreamingAssets=**STREAMING_ASSETS**
|
||||
**ADDITIONAL_PROPERTIES**
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc96f3f48ea52c445af29f357b4ec42c
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
apply plugin: 'com.android.library'
|
||||
**APPLY_PLUGINS**
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
**DEPS**}
|
||||
|
||||
android {
|
||||
compileSdkVersion **APIVERSION**
|
||||
buildToolsVersion '**BUILDTOOLS**'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion **MINSDKVERSION**
|
||||
targetSdkVersion **TARGETSDKVERSION**
|
||||
ndk {
|
||||
abiFilters **ABIFILTERS**
|
||||
}
|
||||
versionCode **VERSIONCODE**
|
||||
versionName '**VERSIONNAME**'
|
||||
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
|
||||
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
|
||||
}**PACKAGING_OPTIONS**
|
||||
}**REPOSITORIES**
|
||||
**IL_CPP_BUILD_SETUP**
|
||||
**SOURCE_BUILD_SETUP**
|
||||
**EXTERNAL_SOURCES**
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a66065698c9436344b3d18daccbdb027
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff14fb2c30ffa6d41bc03ad58abc4f05
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
public static class ConsoleProDebug
|
||||
{
|
||||
// Clear the console and the native console
|
||||
public static void Clear()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(ConsoleClearMethod != null)
|
||||
{
|
||||
ConsoleClearMethod.Invoke(null, null);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send a log to a specific filter regardless of contents
|
||||
// Ex: ConsoleProDebug.LogToFilter("Hi", "CustomFilter");
|
||||
public static void LogToFilter(string inLog, string inFilterName)
|
||||
{
|
||||
Debug.Log(inLog + "\nCPAPI:{\"cmd\":\"Filter\" \"name\":\"" + inFilterName + "\"}");
|
||||
}
|
||||
|
||||
// Send a log as a regular log but change its type in ConsolePro
|
||||
// Ex: ConsoleProDebug.LogAsType("Hi", "Error");
|
||||
public static void LogAsType(string inLog, string inTypeName)
|
||||
{
|
||||
Debug.Log(inLog + "\nCPAPI:{\"cmd\":\"LogType\" \"name\":\"" + inTypeName + "\"}");
|
||||
}
|
||||
|
||||
// Watch a variable. This will only produce one log entry regardless of how many times it is logged, allowing you to track variables without spam.
|
||||
// Ex:
|
||||
// void Update() {
|
||||
// ConsoleProDebug.Watch("Player X Position", transform.position.x);
|
||||
// }
|
||||
public static void Watch(string inName, string inValue)
|
||||
{
|
||||
Debug.Log(inName + " : " + inValue + "\nCPAPI:{\"cmd\":\"Watch\" \"name\":\"" + inName + "\"}");
|
||||
}
|
||||
|
||||
public static void Search(string inText)
|
||||
{
|
||||
Debug.Log("\nCPAPI:{\"cmd\":\"Search\" \"text\":\"" + inText + "\"}");
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Reflection calls to access Console Pro from runtime
|
||||
private static bool _checkedConsoleClearMethod = false;
|
||||
private static MethodInfo _consoleClearMethod = null;
|
||||
private static MethodInfo ConsoleClearMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_consoleClearMethod == null || !_checkedConsoleClearMethod)
|
||||
{
|
||||
_checkedConsoleClearMethod = true;
|
||||
if(ConsoleWindowType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_consoleClearMethod = ConsoleWindowType.GetMethod("ClearEntries", BindingFlags.Static | BindingFlags.Public);
|
||||
}
|
||||
|
||||
return _consoleClearMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool _checkedConsoleWindowType = false;
|
||||
private static Type _consoleWindowType = null;
|
||||
private static Type ConsoleWindowType
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_consoleWindowType == null || !_checkedConsoleWindowType)
|
||||
{
|
||||
_checkedConsoleWindowType = true;
|
||||
Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||
for(int iAssembly = 0; iAssembly < assemblies.Length; iAssembly++)
|
||||
{
|
||||
Type[] types = assemblies[iAssembly].GetTypes();
|
||||
for(int iType = 0; iType < types.Length; iType++)
|
||||
{
|
||||
if(types[iType].Name == "ConsolePro3Window")
|
||||
{
|
||||
_consoleWindowType = types[iType];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _consoleWindowType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f42e8a12eb842acbe9a63057fb00e4
|
||||
timeCreated: 1469329295
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5de782a9528f04b41a8ba70afba32a61
|
||||
timeCreated: 1498113024
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be7aee03972b847e4a8b81ef8ce46a8e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2284c517ee274c19a6ba4c1a8c96fb6
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1d51a9b386eb4992af4cb193629d854
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,237 @@
|
||||
// Uncomment to use in Editor
|
||||
// #define USECONSOLEPROREMOTESERVERINEDITOR
|
||||
|
||||
#if (!UNITY_EDITOR && DEBUG) || (UNITY_EDITOR && USECONSOLEPROREMOTESERVERINEDITOR)
|
||||
#define USECONSOLEPROREMOTESERVER
|
||||
#endif
|
||||
|
||||
#if (UNITY_WP_8_1 || UNITY_WSA)
|
||||
#define UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR && !USECONSOLEPROREMOTESERVER
|
||||
#elif UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
#elif !USECONSOLEPROREMOTESERVER
|
||||
#else
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#if USECONSOLEPROREMOTESERVER
|
||||
using FlyingWormConsole3.LiteNetLib;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3
|
||||
{
|
||||
#if USECONSOLEPROREMOTESERVER
|
||||
public class ConsoleProRemoteServer : MonoBehaviour, INetEventListener
|
||||
#else
|
||||
public class ConsoleProRemoteServer : MonoBehaviour
|
||||
#endif
|
||||
{
|
||||
public bool useNATPunch = false;
|
||||
public int port = 51000;
|
||||
|
||||
#if UNITY_EDITOR && !USECONSOLEPROREMOTESERVER
|
||||
|
||||
#elif UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Debug.Log("Console Pro Remote Server is not supported on this platform");
|
||||
}
|
||||
|
||||
#elif !USECONSOLEPROREMOTESERVER
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Debug.Log("Console Pro Remote Server is disabled in release mode, please use a Development build or define DEBUG to use it");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private NetManager _netServer;
|
||||
private NetPeer _ourPeer;
|
||||
private NetDataWriter _dataWriter;
|
||||
|
||||
[System.SerializableAttribute]
|
||||
public class QueuedLog
|
||||
{
|
||||
public string message;
|
||||
public string stackTrace;
|
||||
public LogType type;
|
||||
}
|
||||
|
||||
|
||||
[NonSerializedAttribute]
|
||||
public List<QueuedLog> logs = new List<QueuedLog>();
|
||||
|
||||
private static ConsoleProRemoteServer instance = null;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
instance = this;
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
Debug.Log("Starting Console Pro Server on port : " + port);
|
||||
|
||||
_dataWriter = new NetDataWriter();
|
||||
_netServer = new NetManager(this, 100, "ConsolePro");
|
||||
_netServer.Start(port);
|
||||
_netServer.DiscoveryEnabled = true;
|
||||
_netServer.UpdateTime = 15;
|
||||
_netServer.MergeEnabled = true;
|
||||
_netServer.NatPunchEnabled = useNATPunch;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if(_netServer != null)
|
||||
{
|
||||
_netServer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPeerConnected(NetPeer peer)
|
||||
{
|
||||
Debug.Log("Connected to " + peer.EndPoint);
|
||||
_ourPeer = peer;
|
||||
}
|
||||
|
||||
public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||
{
|
||||
Debug.Log("Disconnected from " + peer.EndPoint + ", info: " + disconnectInfo.Reason);
|
||||
if (peer == _ourPeer)
|
||||
{
|
||||
_ourPeer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNetworkReceive(NetPeer peer, NetDataReader reader)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPeerDisconnected(NetPeer peer, DisconnectReason reason, int socketErrorCode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnNetworkError(NetEndPoint endPoint, int socketErrorCode)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType)
|
||||
{
|
||||
if (messageType == UnconnectedMessageType.DiscoveryRequest)
|
||||
{
|
||||
// Debug.Log("[SERVER] Received discovery request. Send discovery response");
|
||||
_netServer.SendDiscoveryResponse(new byte[] {1}, remoteEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNetworkLatencyUpdate(NetPeer peer, int latency)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Application.RegisterLogCallback(LogCallback);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Application.RegisterLogCallback(LogCallback);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Application.RegisterLogCallback(null);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Application.logMessageReceived += LogCallback;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Application.logMessageReceived -= LogCallback;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public void LogCallback(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if(!logString.StartsWith("CPIGNORE"))
|
||||
{
|
||||
QueueLog(logString, stackTrace, type);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueLog(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if(logs.Count > 200)
|
||||
{
|
||||
while(logs.Count > 200)
|
||||
{
|
||||
logs.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
logs.Add(new QueuedLog() { message = logString, stackTrace = stackTrace, type = type } );
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(_netServer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_netServer.PollEvents();
|
||||
|
||||
if(_ourPeer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(logs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string cMessage = "";
|
||||
|
||||
for(int i = 0; i < logs.Count; i++)
|
||||
{
|
||||
cMessage = "";
|
||||
|
||||
QueuedLog cLog = logs[i];
|
||||
cMessage = "::::" + cLog.type + "::::" + cLog.message + "\n" + cLog.stackTrace;
|
||||
_dataWriter.Reset();
|
||||
_dataWriter.Put(cMessage);
|
||||
_ourPeer.Send(_dataWriter, SendOptions.ReliableOrdered);
|
||||
}
|
||||
|
||||
logs.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6bcfaced529e418bb75980b297fda2a
|
||||
timeCreated: 1437614101
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c6bd635eeaa04c228b6d342c4758ad7
|
||||
folderAsset: yes
|
||||
timeCreated: 1494014730
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public static class FastBitConverter
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct ConverterHelperDouble
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ulong Along;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public double Adouble;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct ConverterHelperFloat
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int Aint;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public float Afloat;
|
||||
}
|
||||
|
||||
private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 7] = (byte)(data);
|
||||
buffer[offset + 6] = (byte)(data >> 8);
|
||||
buffer[offset + 5] = (byte)(data >> 16);
|
||||
buffer[offset + 4] = (byte)(data >> 24);
|
||||
buffer[offset + 3] = (byte)(data >> 32);
|
||||
buffer[offset + 2] = (byte)(data >> 40);
|
||||
buffer[offset + 1] = (byte)(data >> 48);
|
||||
buffer[offset ] = (byte)(data >> 56);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
buffer[offset + 2] = (byte)(data >> 16);
|
||||
buffer[offset + 3] = (byte)(data >> 24);
|
||||
buffer[offset + 4] = (byte)(data >> 32);
|
||||
buffer[offset + 5] = (byte)(data >> 40);
|
||||
buffer[offset + 6] = (byte)(data >> 48);
|
||||
buffer[offset + 7] = (byte)(data >> 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void WriteLittleEndian(byte[] buffer, int offset, int data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 3] = (byte)(data);
|
||||
buffer[offset + 2] = (byte)(data >> 8);
|
||||
buffer[offset + 1] = (byte)(data >> 16);
|
||||
buffer[offset ] = (byte)(data >> 24);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
buffer[offset + 2] = (byte)(data >> 16);
|
||||
buffer[offset + 3] = (byte)(data >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void WriteLittleEndian(byte[] buffer, int offset, short data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 1] = (byte)(data);
|
||||
buffer[offset ] = (byte)(data >> 8);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, double value)
|
||||
{
|
||||
ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Along);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, float value)
|
||||
{
|
||||
ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Aint);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, short value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (short)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, int value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, uint value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (int)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, long value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (ulong)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55df0de9a58e74c9395dfe5ffdab9a5a
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum UnconnectedMessageType
|
||||
{
|
||||
Default,
|
||||
DiscoveryRequest,
|
||||
DiscoveryResponse
|
||||
}
|
||||
|
||||
public enum DisconnectReason
|
||||
{
|
||||
SocketReceiveError,
|
||||
ConnectionFailed,
|
||||
Timeout,
|
||||
SocketSendError,
|
||||
RemoteConnectionClose,
|
||||
DisconnectPeerCalled
|
||||
}
|
||||
|
||||
public struct DisconnectInfo
|
||||
{
|
||||
public DisconnectReason Reason;
|
||||
public int SocketErrorCode;
|
||||
public NetDataReader AdditionalData;
|
||||
}
|
||||
|
||||
public interface INetEventListener
|
||||
{
|
||||
/// <summary>
|
||||
/// New remote peer connected to host, or client connected to remote host
|
||||
/// </summary>
|
||||
/// <param name="peer">Connected peer object</param>
|
||||
void OnPeerConnected(NetPeer peer);
|
||||
|
||||
/// <summary>
|
||||
/// Peer disconnected
|
||||
/// </summary>
|
||||
/// <param name="peer">disconnected peer</param>
|
||||
/// <param name="disconnectInfo">additional info about reason, errorCode or data received with disconnect message</param>
|
||||
void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Network error (on send or receive)
|
||||
/// </summary>
|
||||
/// <param name="endPoint">From endPoint (can be null)</param>
|
||||
/// <param name="socketErrorCode">Socket error code</param>
|
||||
void OnNetworkError(NetEndPoint endPoint, int socketErrorCode);
|
||||
|
||||
/// <summary>
|
||||
/// Received some data
|
||||
/// </summary>
|
||||
/// <param name="peer">From peer</param>
|
||||
/// <param name="reader">DataReader containing all received data</param>
|
||||
void OnNetworkReceive(NetPeer peer, NetDataReader reader);
|
||||
|
||||
/// <summary>
|
||||
/// Received unconnected message
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint">From address (IP and Port)</param>
|
||||
/// <param name="reader">Message data</param>
|
||||
/// <param name="messageType">Message type (simple, discovery request or responce)</param>
|
||||
void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType);
|
||||
|
||||
/// <summary>
|
||||
/// Latency information updated
|
||||
/// </summary>
|
||||
/// <param name="peer">Peer with updated latency</param>
|
||||
/// <param name="latency">latency value in milliseconds</param>
|
||||
void OnNetworkLatencyUpdate(NetPeer peer, int latency);
|
||||
}
|
||||
|
||||
public class EventBasedNetListener : INetEventListener
|
||||
{
|
||||
public delegate void OnPeerConnected(NetPeer peer);
|
||||
public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
|
||||
public delegate void OnNetworkError(NetEndPoint endPoint, int socketErrorCode);
|
||||
public delegate void OnNetworkReceive(NetPeer peer, NetDataReader reader);
|
||||
public delegate void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType);
|
||||
public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency);
|
||||
|
||||
public event OnPeerConnected PeerConnectedEvent;
|
||||
public event OnPeerDisconnected PeerDisconnectedEvent;
|
||||
public event OnNetworkError NetworkErrorEvent;
|
||||
public event OnNetworkReceive NetworkReceiveEvent;
|
||||
public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent;
|
||||
public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent;
|
||||
|
||||
void INetEventListener.OnPeerConnected(NetPeer peer)
|
||||
{
|
||||
if (PeerConnectedEvent != null)
|
||||
PeerConnectedEvent(peer);
|
||||
}
|
||||
|
||||
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||
{
|
||||
if (PeerDisconnectedEvent != null)
|
||||
PeerDisconnectedEvent(peer, disconnectInfo);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkError(NetEndPoint endPoint, int socketErrorCode)
|
||||
{
|
||||
if (NetworkErrorEvent != null)
|
||||
NetworkErrorEvent(endPoint, socketErrorCode);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkReceive(NetPeer peer, NetDataReader reader)
|
||||
{
|
||||
if (NetworkReceiveEvent != null)
|
||||
NetworkReceiveEvent(peer, reader);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType)
|
||||
{
|
||||
if (NetworkReceiveUnconnectedEvent != null)
|
||||
NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
|
||||
{
|
||||
if (NetworkLatencyUpdateEvent != null)
|
||||
NetworkLatencyUpdateEvent(peer, latency);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d391f9565d58e44a798d680ec5c11906
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
//Some code parts taked from lidgren-network-gen3
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public interface INatPunchListener
|
||||
{
|
||||
void OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token);
|
||||
void OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token);
|
||||
}
|
||||
|
||||
public class EventBasedNatPunchListener : INatPunchListener
|
||||
{
|
||||
public delegate void OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token);
|
||||
public delegate void OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token);
|
||||
|
||||
public event OnNatIntroductionRequest NatIntroductionRequest;
|
||||
public event OnNatIntroductionSuccess NatIntroductionSuccess;
|
||||
|
||||
void INatPunchListener.OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token)
|
||||
{
|
||||
if(NatIntroductionRequest != null)
|
||||
NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
|
||||
}
|
||||
|
||||
void INatPunchListener.OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token)
|
||||
{
|
||||
if (NatIntroductionSuccess != null)
|
||||
NatIntroductionSuccess(targetEndPoint, token);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NatPunchModule
|
||||
{
|
||||
struct RequestEventData
|
||||
{
|
||||
public NetEndPoint LocalEndPoint;
|
||||
public NetEndPoint RemoteEndPoint;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
struct SuccessEventData
|
||||
{
|
||||
public NetEndPoint TargetEndPoint;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
private readonly NetManager _netBase;
|
||||
private readonly Queue<RequestEventData> _requestEvents;
|
||||
private readonly Queue<SuccessEventData> _successEvents;
|
||||
private const byte HostByte = 1;
|
||||
private const byte ClientByte = 0;
|
||||
public const int MaxTokenLength = 256;
|
||||
|
||||
private INatPunchListener _natPunchListener;
|
||||
|
||||
internal NatPunchModule(NetManager netBase)
|
||||
{
|
||||
_netBase = netBase;
|
||||
_requestEvents = new Queue<RequestEventData>();
|
||||
_successEvents = new Queue<SuccessEventData>();
|
||||
}
|
||||
|
||||
public void Init(INatPunchListener listener)
|
||||
{
|
||||
_natPunchListener = listener;
|
||||
}
|
||||
|
||||
public void NatIntroduce(
|
||||
NetEndPoint hostInternal,
|
||||
NetEndPoint hostExternal,
|
||||
NetEndPoint clientInternal,
|
||||
NetEndPoint clientExternal,
|
||||
string additionalInfo)
|
||||
{
|
||||
NetDataWriter dw = new NetDataWriter();
|
||||
|
||||
//First packet (server)
|
||||
//send to client
|
||||
dw.Put(ClientByte);
|
||||
dw.Put(hostInternal);
|
||||
dw.Put(hostExternal);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroduction, dw);
|
||||
_netBase.SendRawAndRecycle(packet, clientExternal);
|
||||
|
||||
//Second packet (client)
|
||||
//send to server
|
||||
dw.Reset();
|
||||
dw.Put(HostByte);
|
||||
dw.Put(clientInternal);
|
||||
dw.Put(clientExternal);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroduction, dw);
|
||||
_netBase.SendRawAndRecycle(packet, hostExternal);
|
||||
}
|
||||
|
||||
public void PollEvents()
|
||||
{
|
||||
if (_natPunchListener == null)
|
||||
return;
|
||||
lock (_successEvents)
|
||||
{
|
||||
while (_successEvents.Count > 0)
|
||||
{
|
||||
var evt = _successEvents.Dequeue();
|
||||
_natPunchListener.OnNatIntroductionSuccess(evt.TargetEndPoint, evt.Token);
|
||||
}
|
||||
}
|
||||
lock (_requestEvents)
|
||||
{
|
||||
while (_requestEvents.Count > 0)
|
||||
{
|
||||
var evt = _requestEvents.Dequeue();
|
||||
_natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendNatIntroduceRequest(NetEndPoint masterServerEndPoint, string additionalInfo)
|
||||
{
|
||||
if (!_netBase.IsRunning)
|
||||
return;
|
||||
|
||||
//prepare outgoing data
|
||||
NetDataWriter dw = new NetDataWriter();
|
||||
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
|
||||
if (string.IsNullOrEmpty(networkIp))
|
||||
{
|
||||
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
|
||||
}
|
||||
int networkPort = _netBase.LocalEndPoint.Port;
|
||||
NetEndPoint localEndPoint = new NetEndPoint(networkIp, networkPort);
|
||||
dw.Put(localEndPoint);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
//prepare packet
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroductionRequest, dw);
|
||||
_netBase.SendRawAndRecycle(packet, masterServerEndPoint);
|
||||
}
|
||||
|
||||
private void HandleNatPunch(NetEndPoint senderEndPoint, NetDataReader dr)
|
||||
{
|
||||
byte fromHostByte = dr.GetByte();
|
||||
if (fromHostByte != HostByte && fromHostByte != ClientByte)
|
||||
{
|
||||
//garbage
|
||||
return;
|
||||
}
|
||||
|
||||
//Read info
|
||||
string additionalInfo = dr.GetString(MaxTokenLength);
|
||||
NetUtils.DebugWrite(ConsoleColor.Green, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, additionalInfo);
|
||||
|
||||
//Release punch success to client; enabling him to Connect() to msg.Sender if token is ok
|
||||
lock (_successEvents)
|
||||
{
|
||||
_successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Token = additionalInfo });
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleNatIntroduction(NetDataReader dr)
|
||||
{
|
||||
// read intro
|
||||
byte hostByte = dr.GetByte();
|
||||
NetEndPoint remoteInternal = dr.GetNetEndPoint();
|
||||
NetEndPoint remoteExternal = dr.GetNetEndPoint();
|
||||
string token = dr.GetString(MaxTokenLength);
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] introduction received; we are designated " + (hostByte == HostByte ? "host" : "client"));
|
||||
NetDataWriter writer = new NetDataWriter();
|
||||
|
||||
// send internal punch
|
||||
writer.Put(hostByte);
|
||||
writer.Put(token);
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatPunchMessage, writer);
|
||||
_netBase.SendRawAndRecycle(packet, remoteInternal);
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] internal punch sent to " + remoteInternal);
|
||||
|
||||
// send external punch
|
||||
writer.Reset();
|
||||
writer.Put(hostByte);
|
||||
writer.Put(token);
|
||||
packet = _netBase.PacketPool.GetWithData(PacketProperty.NatPunchMessage, writer);
|
||||
_netBase.SendRawAndRecycle(packet, remoteExternal);
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] external punch sent to " + remoteExternal);
|
||||
}
|
||||
|
||||
private void HandleNatIntroductionRequest(NetEndPoint senderEndPoint, NetDataReader dr)
|
||||
{
|
||||
NetEndPoint localEp = dr.GetNetEndPoint();
|
||||
string token = dr.GetString(MaxTokenLength);
|
||||
lock (_requestEvents)
|
||||
{
|
||||
_requestEvents.Enqueue(new RequestEventData
|
||||
{
|
||||
LocalEndPoint = localEp,
|
||||
RemoteEndPoint = senderEndPoint,
|
||||
Token = token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal void ProcessMessage(NetEndPoint senderEndPoint, NetPacket packet)
|
||||
{
|
||||
var dr = new NetDataReader(packet.RawData, NetConstants.HeaderSize, packet.Size);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.NatIntroductionRequest:
|
||||
//We got request and must introduce
|
||||
HandleNatIntroductionRequest(senderEndPoint, dr);
|
||||
break;
|
||||
case PacketProperty.NatIntroduction:
|
||||
//We got introduce and must punch
|
||||
HandleNatIntroduction(dr);
|
||||
break;
|
||||
case PacketProperty.NatPunchMessage:
|
||||
//We got punch and can connect
|
||||
HandleNatPunch(senderEndPoint, dr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11f3508667cc14e3797a49d4695ffdd8
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum SendOptions
|
||||
{
|
||||
Unreliable,
|
||||
ReliableUnordered,
|
||||
Sequenced,
|
||||
ReliableOrdered
|
||||
}
|
||||
|
||||
public static class NetConstants
|
||||
{
|
||||
public const int HeaderSize = 1;
|
||||
public const int SequencedHeaderSize = 3;
|
||||
public const int FragmentHeaderSize = 6;
|
||||
public const int DefaultWindowSize = 64;
|
||||
public const ushort MaxSequence = 32768;
|
||||
public const ushort HalfMaxSequence = MaxSequence / 2;
|
||||
|
||||
//socket
|
||||
public const string MulticastGroupIPv4 = "224.0.0.1";
|
||||
public const string MulticastGroupIPv6 = "FF02:0:0:0:0:0:0:1";
|
||||
public const int SocketBufferSize = 1024*1024; //2mb
|
||||
public const int SocketTTL = 255;
|
||||
|
||||
//protocol
|
||||
public const int ProtocolId = 1;
|
||||
public const int MaxUdpHeaderSize = 68;
|
||||
public const int PacketSizeLimit = ushort.MaxValue - MaxUdpHeaderSize;
|
||||
public const int MinPacketSize = 576 - MaxUdpHeaderSize;
|
||||
public const int MinPacketDataSize = MinPacketSize - HeaderSize;
|
||||
public const int MinSequencedPacketDataSize = MinPacketSize - SequencedHeaderSize;
|
||||
|
||||
public static readonly int[] PossibleMtu =
|
||||
{
|
||||
576 - MaxUdpHeaderSize, //Internet Path MTU for X.25 (RFC 879)
|
||||
1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042)
|
||||
1500 - MaxUdpHeaderSize, //Ethernet II (RFC 1191)
|
||||
4352 - MaxUdpHeaderSize, //FDDI
|
||||
4464 - MaxUdpHeaderSize, //Token ring
|
||||
7981 - MaxUdpHeaderSize //WLAN
|
||||
};
|
||||
|
||||
public static int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1];
|
||||
|
||||
//peer specific
|
||||
public const int FlowUpdateTime = 1000;
|
||||
public const int FlowIncreaseThreshold = 4;
|
||||
public const int DefaultPingInterval = 1000;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02abb4740bff94f28bdd538839339932
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,444 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public class NetDataReader
|
||||
{
|
||||
protected byte[] _data;
|
||||
protected int _position;
|
||||
protected int _dataSize;
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get { return _data; }
|
||||
}
|
||||
|
||||
public int Position
|
||||
{
|
||||
get { return _position; }
|
||||
}
|
||||
|
||||
public bool EndOfData
|
||||
{
|
||||
get { return _position == _dataSize; }
|
||||
}
|
||||
|
||||
public int AvailableBytes
|
||||
{
|
||||
get { return _dataSize - _position; }
|
||||
}
|
||||
|
||||
public void SetSource(NetDataWriter dataWriter)
|
||||
{
|
||||
_data = dataWriter.Data;
|
||||
_position = 0;
|
||||
_dataSize = dataWriter.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source)
|
||||
{
|
||||
_data = source;
|
||||
_position = 0;
|
||||
_dataSize = source.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source, int offset)
|
||||
{
|
||||
_data = source;
|
||||
_position = offset;
|
||||
_dataSize = source.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source, int offset, int dataSize)
|
||||
{
|
||||
_data = source;
|
||||
_position = offset;
|
||||
_dataSize = dataSize;
|
||||
}
|
||||
|
||||
public NetDataReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source)
|
||||
{
|
||||
SetSource(source);
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source, int offset)
|
||||
{
|
||||
SetSource(source, offset);
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source, int offset, int maxSize)
|
||||
{
|
||||
SetSource(source, offset, maxSize);
|
||||
}
|
||||
|
||||
#region GetMethods
|
||||
public NetEndPoint GetNetEndPoint()
|
||||
{
|
||||
string host = GetString(1000);
|
||||
int port = GetInt();
|
||||
return new NetEndPoint(host, port);
|
||||
}
|
||||
|
||||
public byte GetByte()
|
||||
{
|
||||
byte res = _data[_position];
|
||||
_position += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
public sbyte GetSByte()
|
||||
{
|
||||
var b = (sbyte)_data[_position];
|
||||
_position++;
|
||||
return b;
|
||||
}
|
||||
|
||||
public bool[] GetBoolArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new bool[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetBool();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ushort[] GetUShortArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new ushort[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetUShort();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public short[] GetShortArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new short[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetShort();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public long[] GetLongArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new long[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetLong();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ulong[] GetULongArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new ulong[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetULong();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public int[] GetIntArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new int[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetInt();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public uint[] GetUIntArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new uint[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetUInt();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public float[] GetFloatArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new float[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetFloat();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public double[] GetDoubleArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new double[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetDouble();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public string[] GetStringArray(int maxLength)
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new string[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetString(maxLength);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public bool GetBool()
|
||||
{
|
||||
bool res = _data[_position] > 0;
|
||||
_position += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
public ushort GetUShort()
|
||||
{
|
||||
ushort result = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public short GetShort()
|
||||
{
|
||||
short result = BitConverter.ToInt16(_data, _position);
|
||||
_position += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long GetLong()
|
||||
{
|
||||
long result = BitConverter.ToInt64(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ulong GetULong()
|
||||
{
|
||||
ulong result = BitConverter.ToUInt64(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetInt()
|
||||
{
|
||||
int result = BitConverter.ToInt32(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint GetUInt()
|
||||
{
|
||||
uint result = BitConverter.ToUInt32(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetFloat()
|
||||
{
|
||||
float result = BitConverter.ToSingle(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double GetDouble()
|
||||
{
|
||||
double result = BitConverter.ToDouble(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetString(int maxLength)
|
||||
{
|
||||
int bytesCount = GetInt();
|
||||
if (bytesCount <= 0 || bytesCount > maxLength*2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int charCount = Encoding.UTF8.GetCharCount(_data, _position, bytesCount);
|
||||
if (charCount > maxLength)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
|
||||
_position += bytesCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetString()
|
||||
{
|
||||
int bytesCount = GetInt();
|
||||
if (bytesCount <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
|
||||
_position += bytesCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] GetRemainingBytes()
|
||||
{
|
||||
byte[] outgoingData = new byte[AvailableBytes];
|
||||
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
|
||||
_position = _data.Length;
|
||||
return outgoingData;
|
||||
}
|
||||
|
||||
public void GetRemainingBytes(byte[] destination)
|
||||
{
|
||||
Buffer.BlockCopy(_data, _position, destination, 0, AvailableBytes);
|
||||
_position = _data.Length;
|
||||
}
|
||||
|
||||
public void GetBytes(byte[] destination, int lenght)
|
||||
{
|
||||
Buffer.BlockCopy(_data, _position, destination, 0, lenght);
|
||||
_position += lenght;
|
||||
}
|
||||
|
||||
public byte[] GetBytesWithLength()
|
||||
{
|
||||
int length = GetInt();
|
||||
byte[] outgoingData = new byte[length];
|
||||
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
|
||||
_position += length;
|
||||
return outgoingData;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PeekMethods
|
||||
|
||||
public byte PeekByte()
|
||||
{
|
||||
return _data[_position];
|
||||
}
|
||||
|
||||
public sbyte PeekSByte()
|
||||
{
|
||||
return (sbyte)_data[_position];
|
||||
}
|
||||
|
||||
public bool PeekBool()
|
||||
{
|
||||
return _data[_position] > 0;
|
||||
}
|
||||
|
||||
public ushort PeekUShort()
|
||||
{
|
||||
return BitConverter.ToUInt16(_data, _position);
|
||||
}
|
||||
|
||||
public short PeekShort()
|
||||
{
|
||||
return BitConverter.ToInt16(_data, _position);
|
||||
}
|
||||
|
||||
public long PeekLong()
|
||||
{
|
||||
return BitConverter.ToInt64(_data, _position);
|
||||
}
|
||||
|
||||
public ulong PeekULong()
|
||||
{
|
||||
return BitConverter.ToUInt64(_data, _position);
|
||||
}
|
||||
|
||||
public int PeekInt()
|
||||
{
|
||||
return BitConverter.ToInt32(_data, _position);
|
||||
}
|
||||
|
||||
public uint PeekUInt()
|
||||
{
|
||||
return BitConverter.ToUInt32(_data, _position);
|
||||
}
|
||||
|
||||
public float PeekFloat()
|
||||
{
|
||||
return BitConverter.ToSingle(_data, _position);
|
||||
}
|
||||
|
||||
public double PeekDouble()
|
||||
{
|
||||
return BitConverter.ToDouble(_data, _position);
|
||||
}
|
||||
|
||||
public string PeekString(int maxLength)
|
||||
{
|
||||
int bytesCount = BitConverter.ToInt32(_data, _position);
|
||||
if (bytesCount <= 0 || bytesCount > maxLength * 2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int charCount = Encoding.UTF8.GetCharCount(_data, _position + 4, bytesCount);
|
||||
if (charCount > maxLength)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
public string PeekString()
|
||||
{
|
||||
int bytesCount = BitConverter.ToInt32(_data, _position);
|
||||
if (bytesCount <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_position = 0;
|
||||
_dataSize = 0;
|
||||
_data = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37bb0204fc22b499690c4032caf14811
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,375 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public class NetDataWriter
|
||||
{
|
||||
protected byte[] _data;
|
||||
protected int _position;
|
||||
|
||||
private int _maxLength;
|
||||
private readonly bool _autoResize;
|
||||
|
||||
public NetDataWriter()
|
||||
{
|
||||
_maxLength = 64;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = true;
|
||||
}
|
||||
|
||||
public NetDataWriter(bool autoResize)
|
||||
{
|
||||
_maxLength = 64;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = autoResize;
|
||||
}
|
||||
|
||||
public NetDataWriter(bool autoResize, int initialSize)
|
||||
{
|
||||
_maxLength = initialSize;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = autoResize;
|
||||
}
|
||||
|
||||
public void ResizeIfNeed(int newSize)
|
||||
{
|
||||
if (_maxLength < newSize)
|
||||
{
|
||||
while (_maxLength < newSize)
|
||||
{
|
||||
_maxLength *= 2;
|
||||
}
|
||||
Array.Resize(ref _data, _maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset(int size)
|
||||
{
|
||||
ResizeIfNeed(size);
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public byte[] CopyData()
|
||||
{
|
||||
byte[] resultData = new byte[_position];
|
||||
Buffer.BlockCopy(_data, 0, resultData, 0, _position);
|
||||
return resultData;
|
||||
}
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get { return _data; }
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _position; }
|
||||
}
|
||||
|
||||
public void Put(float value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(double value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(long value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(ulong value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(int value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(uint value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(ushort value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 2);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 2;
|
||||
}
|
||||
|
||||
public void Put(short value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 2);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 2;
|
||||
}
|
||||
|
||||
public void Put(sbyte value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = (byte)value;
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void Put(byte value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = value;
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void Put(byte[] data, int offset, int length)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + length);
|
||||
Buffer.BlockCopy(data, offset, _data, _position, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
public void Put(byte[] data)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + data.Length);
|
||||
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
public void PutBytesWithLength(byte[] data, int offset, int length)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + length);
|
||||
Put(length);
|
||||
Buffer.BlockCopy(data, offset, _data, _position, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
public void PutBytesWithLength(byte[] data)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + data.Length);
|
||||
Put(data.Length);
|
||||
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
public void Put(bool value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = (byte)(value ? 1 : 0);
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void PutArray(float[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(double[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(long[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(ulong[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(int[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(uint[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(ushort[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 2 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(short[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 2 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(bool[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(string[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
Put(len);
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(string[] value, int maxLength)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i], maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
public void Put(NetEndPoint endPoint)
|
||||
{
|
||||
Put(endPoint.Host);
|
||||
Put(endPoint.Port);
|
||||
}
|
||||
|
||||
public void Put(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Put(0);
|
||||
return;
|
||||
}
|
||||
|
||||
//put bytes count
|
||||
int bytesCount = Encoding.UTF8.GetByteCount(value);
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + bytesCount + 4);
|
||||
Put(bytesCount);
|
||||
|
||||
//put string
|
||||
Encoding.UTF8.GetBytes(value, 0, value.Length, _data, _position);
|
||||
_position += bytesCount;
|
||||
}
|
||||
|
||||
public void Put(string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Put(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int length = value.Length > maxLength ? maxLength : value.Length;
|
||||
//calculate max count
|
||||
int bytesCount = Encoding.UTF8.GetByteCount(value);
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + bytesCount + 4);
|
||||
|
||||
//put bytes count
|
||||
Put(bytesCount);
|
||||
|
||||
//put string
|
||||
Encoding.UTF8.GetBytes(value, 0, length, _data, _position);
|
||||
|
||||
_position += bytesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ecf42c9cc394fc395942030e71bddd
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public interface INetLogger
|
||||
{
|
||||
void WriteNet(ConsoleColor color, string str, params object[] args);
|
||||
}
|
||||
|
||||
public static class NetDebug
|
||||
{
|
||||
public static INetLogger Logger = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78dc00ceb66ac4fdfa8c3957763522ba
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,221 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetEndPoint
|
||||
{
|
||||
public string Host { get { return EndPoint.Address.ToString(); } }
|
||||
public int Port { get { return EndPoint.Port; } }
|
||||
|
||||
internal readonly IPEndPoint EndPoint;
|
||||
|
||||
internal NetEndPoint(IPEndPoint ipEndPoint)
|
||||
{
|
||||
EndPoint = ipEndPoint;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is NetEndPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return EndPoint.Equals(((NetEndPoint)obj).EndPoint);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return EndPoint.ToString();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return EndPoint.GetHashCode();
|
||||
}
|
||||
|
||||
public NetEndPoint(string hostStr, int port)
|
||||
{
|
||||
IPAddress ipAddress;
|
||||
if (!IPAddress.TryParse(hostStr, out ipAddress))
|
||||
{
|
||||
if (Socket.OSSupportsIPv6)
|
||||
{
|
||||
if (hostStr == "localhost")
|
||||
{
|
||||
ipAddress = IPAddress.IPv6Loopback;
|
||||
}
|
||||
else
|
||||
{
|
||||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6);
|
||||
}
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork);
|
||||
}
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
throw new Exception("Invalid address: " + hostStr);
|
||||
}
|
||||
EndPoint = new IPEndPoint(ipAddress, port);
|
||||
}
|
||||
|
||||
private IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily)
|
||||
{
|
||||
#if NETCORE
|
||||
var hostTask = Dns.GetHostEntryAsync(hostStr);
|
||||
hostTask.Wait();
|
||||
var host = hostTask.Result;
|
||||
#else
|
||||
var host = Dns.GetHostEntry(hostStr);
|
||||
#endif
|
||||
foreach (IPAddress ip in host.AddressList)
|
||||
{
|
||||
if (ip.AddressFamily == addressFamily)
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal long GetId()
|
||||
{
|
||||
byte[] addr = EndPoint.Address.GetAddressBytes();
|
||||
long id = 0;
|
||||
|
||||
if (addr.Length == 4) //IPv4
|
||||
{
|
||||
id = addr[0];
|
||||
id |= (long)addr[1] << 8;
|
||||
id |= (long)addr[2] << 16;
|
||||
id |= (long)addr[3] << 24;
|
||||
id |= (long)EndPoint.Port << 32;
|
||||
}
|
||||
else if (addr.Length == 16) //IPv6
|
||||
{
|
||||
id = addr[0] ^ addr[8];
|
||||
id |= (long)(addr[1] ^ addr[9]) << 8;
|
||||
id |= (long)(addr[2] ^ addr[10]) << 16;
|
||||
|
||||
|
||||
id |= (long)(addr[3] ^ addr[11]) << 24;
|
||||
id |= (long)(addr[4] ^ addr[12]) << 32;
|
||||
id |= (long)(addr[5] ^ addr[13]) << 40;
|
||||
id |= (long)(addr[6] ^ addr[14]) << 48;
|
||||
id |= (long)(Port ^ addr[7] ^ addr[15]) << 56;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
using System;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetEndPoint
|
||||
{
|
||||
public string Host { get { return HostName.DisplayName; } }
|
||||
public int Port { get; private set; }
|
||||
internal readonly HostName HostName;
|
||||
internal readonly string PortStr;
|
||||
|
||||
internal NetEndPoint(int port)
|
||||
{
|
||||
HostName = null;
|
||||
PortStr = port.ToString();
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is NetEndPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NetEndPoint other = (NetEndPoint) obj;
|
||||
return HostName.IsEqual(other.HostName) && PortStr.Equals(other.PortStr);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HostName.CanonicalName.GetHashCode() ^ PortStr.GetHashCode();
|
||||
}
|
||||
|
||||
internal long GetId()
|
||||
{
|
||||
//Check locals
|
||||
if (HostName == null)
|
||||
{
|
||||
return ParseIpToId("0.0.0.0");
|
||||
}
|
||||
|
||||
if (HostName.DisplayName == "localhost")
|
||||
{
|
||||
return ParseIpToId("127.0.0.1");
|
||||
}
|
||||
|
||||
//Check remote
|
||||
string hostIp = string.Empty;
|
||||
var task = DatagramSocket.GetEndpointPairsAsync(HostName, "0").AsTask();
|
||||
task.Wait();
|
||||
|
||||
//IPv4
|
||||
foreach (var endpointPair in task.Result)
|
||||
{
|
||||
hostIp = endpointPair.RemoteHostName.CanonicalName;
|
||||
if (endpointPair.RemoteHostName.Type == HostNameType.Ipv4)
|
||||
{
|
||||
return ParseIpToId(hostIp);
|
||||
}
|
||||
}
|
||||
|
||||
//Else
|
||||
return hostIp.GetHashCode() ^ Port;
|
||||
}
|
||||
|
||||
private long ParseIpToId(string hostIp)
|
||||
{
|
||||
long id = 0;
|
||||
string[] ip = hostIp.Split('.');
|
||||
id |= long.Parse(ip[0]);
|
||||
id |= long.Parse(ip[1]) << 8;
|
||||
id |= long.Parse(ip[2]) << 16;
|
||||
id |= long.Parse(ip[3]) << 24;
|
||||
id |= (long)Port << 32;
|
||||
return id;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return HostName.CanonicalName + ":" + PortStr;
|
||||
}
|
||||
|
||||
public NetEndPoint(string hostName, int port)
|
||||
{
|
||||
var task = DatagramSocket.GetEndpointPairsAsync(new HostName(hostName), port.ToString()).AsTask();
|
||||
task.Wait();
|
||||
HostName = task.Result[0].RemoteHostName;
|
||||
Port = port;
|
||||
PortStr = port.ToString();
|
||||
}
|
||||
|
||||
internal NetEndPoint(HostName hostName, string port)
|
||||
{
|
||||
HostName = hostName;
|
||||
Port = int.Parse(port);
|
||||
PortStr = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33fb66c3ba5b8429fbbb0a2f5e7ceb57
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0197124da84847d8855d5f690b8c653
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,163 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal enum PacketProperty : byte
|
||||
{
|
||||
Unreliable, //0
|
||||
Reliable, //1
|
||||
Sequenced, //2
|
||||
ReliableOrdered, //3
|
||||
AckReliable, //4
|
||||
AckReliableOrdered, //5
|
||||
Ping, //6
|
||||
Pong, //7
|
||||
ConnectRequest, //8
|
||||
ConnectAccept, //9
|
||||
Disconnect, //10
|
||||
UnconnectedMessage, //11
|
||||
NatIntroductionRequest, //12
|
||||
NatIntroduction, //13
|
||||
NatPunchMessage, //14
|
||||
MtuCheck, //15
|
||||
MtuOk, //16
|
||||
DiscoveryRequest, //17
|
||||
DiscoveryResponse, //18
|
||||
Merged //19
|
||||
}
|
||||
|
||||
internal sealed class NetPacket
|
||||
{
|
||||
private const int LastProperty = 19;
|
||||
|
||||
//Header
|
||||
public PacketProperty Property
|
||||
{
|
||||
get { return (PacketProperty)(RawData[0] & 0x7F); }
|
||||
set { RawData[0] = (byte)((RawData[0] & 0x80) | ((byte)value & 0x7F)); }
|
||||
}
|
||||
|
||||
public ushort Sequence
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 1); }
|
||||
set { FastBitConverter.GetBytes(RawData, 1, value); }
|
||||
}
|
||||
|
||||
public bool IsFragmented
|
||||
{
|
||||
get { return (RawData[0] & 0x80) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
RawData[0] |= 0x80; //set first bit
|
||||
else
|
||||
RawData[0] &= 0x7F; //unset first bit
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FragmentId
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 3); }
|
||||
set { FastBitConverter.GetBytes(RawData, 3, value); }
|
||||
}
|
||||
|
||||
public ushort FragmentPart
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 5); }
|
||||
set { FastBitConverter.GetBytes(RawData, 5, value); }
|
||||
}
|
||||
|
||||
public ushort FragmentsTotal
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 7); }
|
||||
set { FastBitConverter.GetBytes(RawData, 7, value); }
|
||||
}
|
||||
|
||||
//Data
|
||||
public readonly byte[] RawData;
|
||||
public int Size;
|
||||
|
||||
public NetPacket(int size)
|
||||
{
|
||||
RawData = new byte[size];
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
public static bool GetPacketProperty(byte[] data, out PacketProperty property)
|
||||
{
|
||||
byte properyByte = (byte)(data[0] & 0x7F);
|
||||
if (properyByte > LastProperty)
|
||||
{
|
||||
property = PacketProperty.Unreliable;
|
||||
return false;
|
||||
}
|
||||
property = (PacketProperty)properyByte;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int GetHeaderSize(PacketProperty property)
|
||||
{
|
||||
return IsSequenced(property)
|
||||
? NetConstants.SequencedHeaderSize
|
||||
: NetConstants.HeaderSize;
|
||||
}
|
||||
|
||||
public int GetHeaderSize()
|
||||
{
|
||||
return GetHeaderSize(Property);
|
||||
}
|
||||
|
||||
public byte[] GetPacketData()
|
||||
{
|
||||
int headerSize = GetHeaderSize(Property);
|
||||
int dataSize = Size - headerSize;
|
||||
byte[] data = new byte[dataSize];
|
||||
Buffer.BlockCopy(RawData, headerSize, data, 0, dataSize);
|
||||
return data;
|
||||
}
|
||||
|
||||
public bool IsClientData()
|
||||
{
|
||||
var property = Property;
|
||||
return property == PacketProperty.Reliable ||
|
||||
property == PacketProperty.ReliableOrdered ||
|
||||
property == PacketProperty.Unreliable ||
|
||||
property == PacketProperty.Sequenced;
|
||||
}
|
||||
|
||||
public static bool IsSequenced(PacketProperty property)
|
||||
{
|
||||
return property == PacketProperty.ReliableOrdered ||
|
||||
property == PacketProperty.Reliable ||
|
||||
property == PacketProperty.Sequenced ||
|
||||
property == PacketProperty.Ping ||
|
||||
property == PacketProperty.Pong ||
|
||||
property == PacketProperty.AckReliable ||
|
||||
property == PacketProperty.AckReliableOrdered;
|
||||
}
|
||||
|
||||
//Packet contstructor from byte array
|
||||
public bool FromBytes(byte[] data, int start, int packetSize)
|
||||
{
|
||||
//Reading property
|
||||
byte property = (byte)(data[start] & 0x7F);
|
||||
bool fragmented = (data[start] & 0x80) != 0;
|
||||
int headerSize = GetHeaderSize((PacketProperty) property);
|
||||
|
||||
if (property > LastProperty ||
|
||||
packetSize > NetConstants.PacketSizeLimit ||
|
||||
packetSize < headerSize ||
|
||||
(fragmented && packetSize < headerSize + NetConstants.FragmentHeaderSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(data, start, RawData, 0, packetSize);
|
||||
Size = packetSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7aa9ed55f53fa48569ccd0963c50d8da
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal class NetPacketPool
|
||||
{
|
||||
private readonly Stack<NetPacket> _pool;
|
||||
|
||||
public NetPacketPool()
|
||||
{
|
||||
_pool = new Stack<NetPacket>();
|
||||
}
|
||||
|
||||
public NetPacket GetWithData(PacketProperty property, NetDataWriter writer)
|
||||
{
|
||||
var packet = Get(property, writer.Length);
|
||||
Buffer.BlockCopy(writer.Data, 0, packet.RawData, NetPacket.GetHeaderSize(property), writer.Length);
|
||||
return packet;
|
||||
}
|
||||
|
||||
public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length)
|
||||
{
|
||||
var packet = Get(property, length);
|
||||
Buffer.BlockCopy(data, start, packet.RawData, NetPacket.GetHeaderSize(property), length);
|
||||
return packet;
|
||||
}
|
||||
|
||||
//Get packet just for read
|
||||
public NetPacket GetAndRead(byte[] data, int start, int count)
|
||||
{
|
||||
NetPacket packet = null;
|
||||
lock (_pool)
|
||||
{
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
packet = _pool.Pop();
|
||||
}
|
||||
}
|
||||
if (packet == null)
|
||||
{
|
||||
//allocate new packet of max size or bigger
|
||||
packet = new NetPacket(NetConstants.MaxPacketSize);
|
||||
}
|
||||
if (!packet.FromBytes(data, start, count))
|
||||
{
|
||||
Recycle(packet);
|
||||
return null;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
//Get packet with size
|
||||
public NetPacket Get(PacketProperty property, int size)
|
||||
{
|
||||
NetPacket packet = null;
|
||||
size += NetPacket.GetHeaderSize(property);
|
||||
if (size <= NetConstants.MaxPacketSize)
|
||||
{
|
||||
lock (_pool)
|
||||
{
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
packet = _pool.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packet == null)
|
||||
{
|
||||
//allocate new packet of max size or bigger
|
||||
packet = new NetPacket(size > NetConstants.MaxPacketSize ? size : NetConstants.MaxPacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(packet.RawData, 0, size);
|
||||
}
|
||||
packet.Property = property;
|
||||
packet.Size = size;
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void Recycle(NetPacket packet)
|
||||
{
|
||||
if (packet.Size > NetConstants.MaxPacketSize)
|
||||
{
|
||||
//Dont pool big packets. Save memory
|
||||
return;
|
||||
}
|
||||
|
||||
//Clean fragmented flag
|
||||
packet.IsFragmented = false;
|
||||
lock (_pool)
|
||||
{
|
||||
_pool.Push(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3637381933a4745b996d4dd48cd71efe
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,857 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum ConnectionState
|
||||
{
|
||||
InProgress,
|
||||
Connected,
|
||||
Disconnected
|
||||
}
|
||||
|
||||
public sealed class NetPeer
|
||||
{
|
||||
//Flow control
|
||||
private int _currentFlowMode;
|
||||
private int _sendedPacketsCount;
|
||||
private int _flowTimer;
|
||||
|
||||
//Ping and RTT
|
||||
private int _ping;
|
||||
private int _rtt;
|
||||
private int _avgRtt;
|
||||
private int _rttCount;
|
||||
private int _goodRttCount;
|
||||
private ushort _pingSequence;
|
||||
private ushort _remotePingSequence;
|
||||
private double _resendDelay = 27.0;
|
||||
|
||||
private int _pingSendTimer;
|
||||
private const int RttResetDelay = 1000;
|
||||
private int _rttResetTimer;
|
||||
|
||||
private DateTime _pingTimeStart;
|
||||
private int _timeSinceLastPacket;
|
||||
|
||||
//Common
|
||||
private readonly NetEndPoint _remoteEndPoint;
|
||||
private readonly NetManager _peerListener;
|
||||
private readonly NetPacketPool _packetPool;
|
||||
private readonly object _flushLock = new object();
|
||||
|
||||
//Channels
|
||||
private readonly ReliableChannel _reliableOrderedChannel;
|
||||
private readonly ReliableChannel _reliableUnorderedChannel;
|
||||
private readonly SequencedChannel _sequencedChannel;
|
||||
private readonly SimpleChannel _simpleChannel;
|
||||
|
||||
private int _windowSize = NetConstants.DefaultWindowSize;
|
||||
|
||||
//MTU
|
||||
private int _mtu = NetConstants.PossibleMtu[0];
|
||||
private int _mtuIdx;
|
||||
private bool _finishMtu;
|
||||
private int _mtuCheckTimer;
|
||||
private int _mtuCheckAttempts;
|
||||
private const int MtuCheckDelay = 1000;
|
||||
private const int MaxMtuCheckAttempts = 4;
|
||||
private readonly object _mtuMutex = new object();
|
||||
|
||||
//Fragment
|
||||
private class IncomingFragments
|
||||
{
|
||||
public NetPacket[] Fragments;
|
||||
public int ReceivedCount;
|
||||
public int TotalSize;
|
||||
}
|
||||
private ushort _fragmentId;
|
||||
private readonly Dictionary<ushort, IncomingFragments> _holdedFragments;
|
||||
|
||||
//Merging
|
||||
private readonly NetPacket _mergeData;
|
||||
private int _mergePos;
|
||||
private int _mergeCount;
|
||||
|
||||
//Connection
|
||||
private int _connectAttempts;
|
||||
private int _connectTimer;
|
||||
private long _connectId;
|
||||
private ConnectionState _connectionState;
|
||||
|
||||
public ConnectionState ConnectionState
|
||||
{
|
||||
get { return _connectionState; }
|
||||
}
|
||||
|
||||
public long ConnectId
|
||||
{
|
||||
get { return _connectId; }
|
||||
}
|
||||
|
||||
public NetEndPoint EndPoint
|
||||
{
|
||||
get { return _remoteEndPoint; }
|
||||
}
|
||||
|
||||
public int Ping
|
||||
{
|
||||
get { return _ping; }
|
||||
}
|
||||
|
||||
public int CurrentFlowMode
|
||||
{
|
||||
get { return _currentFlowMode; }
|
||||
}
|
||||
|
||||
public int Mtu
|
||||
{
|
||||
get { return _mtu; }
|
||||
}
|
||||
|
||||
public int TimeSinceLastPacket
|
||||
{
|
||||
get { return _timeSinceLastPacket; }
|
||||
}
|
||||
|
||||
public NetManager NetManager
|
||||
{
|
||||
get { return _peerListener; }
|
||||
}
|
||||
|
||||
public int PacketsCountInReliableQueue
|
||||
{
|
||||
get { return _reliableUnorderedChannel.PacketsInQueue; }
|
||||
}
|
||||
|
||||
public int PacketsCountInReliableOrderedQueue
|
||||
{
|
||||
get { return _reliableOrderedChannel.PacketsInQueue; }
|
||||
}
|
||||
|
||||
internal double ResendDelay
|
||||
{
|
||||
get { return _resendDelay; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application defined object containing data about the connection
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
internal NetPeer(NetManager peerListener, NetEndPoint remoteEndPoint, long connectId)
|
||||
{
|
||||
_packetPool = peerListener.PacketPool;
|
||||
_peerListener = peerListener;
|
||||
_remoteEndPoint = remoteEndPoint;
|
||||
|
||||
_avgRtt = 0;
|
||||
_rtt = 0;
|
||||
_pingSendTimer = 0;
|
||||
|
||||
_reliableOrderedChannel = new ReliableChannel(this, true, _windowSize);
|
||||
_reliableUnorderedChannel = new ReliableChannel(this, false, _windowSize);
|
||||
_sequencedChannel = new SequencedChannel(this);
|
||||
_simpleChannel = new SimpleChannel(this);
|
||||
|
||||
_holdedFragments = new Dictionary<ushort, IncomingFragments>();
|
||||
|
||||
_mergeData = _packetPool.Get(PacketProperty.Merged, NetConstants.MaxPacketSize);
|
||||
|
||||
//if ID != 0 then we already connected
|
||||
_connectAttempts = 0;
|
||||
if (connectId == 0)
|
||||
{
|
||||
_connectId = DateTime.UtcNow.Ticks;
|
||||
SendConnectRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectId = connectId;
|
||||
_connectionState = ConnectionState.Connected;
|
||||
SendConnectAccept();
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[CC] ConnectId: {0}", _connectId);
|
||||
}
|
||||
|
||||
private void SendConnectRequest()
|
||||
{
|
||||
//Get connect key bytes
|
||||
byte[] keyData = Encoding.UTF8.GetBytes(_peerListener.ConnectKey);
|
||||
|
||||
//Make initial packet
|
||||
var connectPacket = _packetPool.Get(PacketProperty.ConnectRequest, 12 + keyData.Length);
|
||||
|
||||
//Add data
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 1, NetConstants.ProtocolId);
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 5, _connectId);
|
||||
Buffer.BlockCopy(keyData, 0, connectPacket.RawData, 13, keyData.Length);
|
||||
|
||||
//Send raw
|
||||
_peerListener.SendRawAndRecycle(connectPacket, _remoteEndPoint);
|
||||
}
|
||||
|
||||
private void SendConnectAccept()
|
||||
{
|
||||
//Reset connection timer
|
||||
_timeSinceLastPacket = 0;
|
||||
|
||||
//Make initial packet
|
||||
var connectPacket = _packetPool.Get(PacketProperty.ConnectAccept, 8);
|
||||
|
||||
//Add data
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 1, _connectId);
|
||||
|
||||
//Send raw
|
||||
_peerListener.SendRawAndRecycle(connectPacket, _remoteEndPoint);
|
||||
}
|
||||
|
||||
internal bool ProcessConnectAccept(NetPacket packet)
|
||||
{
|
||||
if (_connectionState != ConnectionState.InProgress)
|
||||
return false;
|
||||
|
||||
//check connection id
|
||||
if (BitConverter.ToInt64(packet.RawData, 1) != _connectId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NC] Received connection accept");
|
||||
_timeSinceLastPacket = 0;
|
||||
_connectionState = ConnectionState.Connected;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PacketProperty SendOptionsToProperty(SendOptions options)
|
||||
{
|
||||
switch (options)
|
||||
{
|
||||
case SendOptions.ReliableUnordered:
|
||||
return PacketProperty.Reliable;
|
||||
case SendOptions.Sequenced:
|
||||
return PacketProperty.Sequenced;
|
||||
case SendOptions.ReliableOrdered:
|
||||
return PacketProperty.ReliableOrdered;
|
||||
default:
|
||||
return PacketProperty.Unreliable;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetMaxSinglePacketSize(SendOptions options)
|
||||
{
|
||||
return _mtu - NetPacket.GetHeaderSize(SendOptionsToProperty(options));
|
||||
}
|
||||
|
||||
public void Send(byte[] data, SendOptions options)
|
||||
{
|
||||
Send(data, 0, data.Length, options);
|
||||
}
|
||||
|
||||
public void Send(NetDataWriter dataWriter, SendOptions options)
|
||||
{
|
||||
Send(dataWriter.Data, 0, dataWriter.Length, options);
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int start, int length, SendOptions options)
|
||||
{
|
||||
//Prepare
|
||||
PacketProperty property = SendOptionsToProperty(options);
|
||||
int headerSize = NetPacket.GetHeaderSize(property);
|
||||
|
||||
//Check fragmentation
|
||||
if (length + headerSize > _mtu)
|
||||
{
|
||||
if (options == SendOptions.Sequenced || options == SendOptions.Unreliable)
|
||||
{
|
||||
throw new Exception("Unreliable packet size > allowed (" + (_mtu - headerSize) + ")");
|
||||
}
|
||||
|
||||
int packetFullSize = _mtu - headerSize;
|
||||
int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize;
|
||||
|
||||
int fullPacketsCount = length / packetDataSize;
|
||||
int lastPacketSize = length % packetDataSize;
|
||||
int totalPackets = fullPacketsCount + (lastPacketSize == 0 ? 0 : 1);
|
||||
|
||||
NetUtils.DebugWrite("FragmentSend:\n" +
|
||||
" MTU: {0}\n" +
|
||||
" headerSize: {1}\n" +
|
||||
" packetFullSize: {2}\n" +
|
||||
" packetDataSize: {3}\n" +
|
||||
" fullPacketsCount: {4}\n" +
|
||||
" lastPacketSize: {5}\n" +
|
||||
" totalPackets: {6}",
|
||||
_mtu, headerSize, packetFullSize, packetDataSize, fullPacketsCount, lastPacketSize, totalPackets);
|
||||
|
||||
if (totalPackets > ushort.MaxValue)
|
||||
{
|
||||
throw new Exception("Too many fragments: " + totalPackets + " > " + ushort.MaxValue);
|
||||
}
|
||||
|
||||
int dataOffset = headerSize + NetConstants.FragmentHeaderSize;
|
||||
for (ushort i = 0; i < fullPacketsCount; i++)
|
||||
{
|
||||
NetPacket p = _packetPool.Get(property, packetFullSize);
|
||||
p.FragmentId = _fragmentId;
|
||||
p.FragmentPart = i;
|
||||
p.FragmentsTotal = (ushort)totalPackets;
|
||||
p.IsFragmented = true;
|
||||
Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize);
|
||||
SendPacket(p);
|
||||
}
|
||||
|
||||
if (lastPacketSize > 0)
|
||||
{
|
||||
NetPacket p = _packetPool.Get(property, lastPacketSize + NetConstants.FragmentHeaderSize);
|
||||
p.FragmentId = _fragmentId;
|
||||
p.FragmentPart = (ushort)fullPacketsCount; //last
|
||||
p.FragmentsTotal = (ushort)totalPackets;
|
||||
p.IsFragmented = true;
|
||||
Buffer.BlockCopy(data, fullPacketsCount * packetDataSize, p.RawData, dataOffset, lastPacketSize);
|
||||
SendPacket(p);
|
||||
}
|
||||
|
||||
_fragmentId++;
|
||||
return;
|
||||
}
|
||||
|
||||
//Else just send
|
||||
NetPacket packet = _packetPool.GetWithData(property, data, start, length);
|
||||
SendPacket(packet);
|
||||
}
|
||||
|
||||
private void CreateAndSend(PacketProperty property, ushort sequence)
|
||||
{
|
||||
NetPacket packet = _packetPool.Get(property, 0);
|
||||
packet.Sequence = sequence;
|
||||
SendPacket(packet);
|
||||
}
|
||||
|
||||
//from user thread, our thread, or recv?
|
||||
private void SendPacket(NetPacket packet)
|
||||
{
|
||||
NetUtils.DebugWrite("[RS]Packet: " + packet.Property);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.Reliable:
|
||||
_reliableUnorderedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.Sequenced:
|
||||
_sequencedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.ReliableOrdered:
|
||||
_reliableOrderedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.Unreliable:
|
||||
_simpleChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.MtuCheck:
|
||||
//Must check result for MTU fix
|
||||
if (!_peerListener.SendRawAndRecycle(packet, _remoteEndPoint))
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
break;
|
||||
case PacketProperty.AckReliable:
|
||||
case PacketProperty.AckReliableOrdered:
|
||||
case PacketProperty.Ping:
|
||||
case PacketProperty.Pong:
|
||||
case PacketProperty.Disconnect:
|
||||
case PacketProperty.MtuOk:
|
||||
SendRawData(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown packet property: " + packet.Property);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRoundTripTime(int roundTripTime)
|
||||
{
|
||||
//Calc average round trip time
|
||||
_rtt += roundTripTime;
|
||||
_rttCount++;
|
||||
_avgRtt = _rtt/_rttCount;
|
||||
|
||||
//flowmode 0 = fastest
|
||||
//flowmode max = lowest
|
||||
|
||||
if (_avgRtt < _peerListener.GetStartRtt(_currentFlowMode - 1))
|
||||
{
|
||||
if (_currentFlowMode <= 0)
|
||||
{
|
||||
//Already maxed
|
||||
return;
|
||||
}
|
||||
|
||||
_goodRttCount++;
|
||||
if (_goodRttCount > NetConstants.FlowIncreaseThreshold)
|
||||
{
|
||||
_goodRttCount = 0;
|
||||
_currentFlowMode--;
|
||||
|
||||
NetUtils.DebugWrite("[PA]Increased flow speed, RTT: {0}, PPS: {1}", _avgRtt, _peerListener.GetPacketsPerSecond(_currentFlowMode));
|
||||
}
|
||||
}
|
||||
else if(_avgRtt > _peerListener.GetStartRtt(_currentFlowMode))
|
||||
{
|
||||
_goodRttCount = 0;
|
||||
if (_currentFlowMode < _peerListener.GetMaxFlowMode())
|
||||
{
|
||||
_currentFlowMode++;
|
||||
NetUtils.DebugWrite("[PA]Decreased flow speed, RTT: {0}, PPS: {1}", _avgRtt, _peerListener.GetPacketsPerSecond(_currentFlowMode));
|
||||
}
|
||||
}
|
||||
|
||||
//recalc resend delay
|
||||
double avgRtt = _avgRtt;
|
||||
if (avgRtt <= 0.0)
|
||||
avgRtt = 0.1;
|
||||
_resendDelay = 25 + (avgRtt * 2.1); // 25 ms + double rtt
|
||||
}
|
||||
|
||||
internal void AddIncomingPacket(NetPacket p)
|
||||
{
|
||||
if (p.IsFragmented)
|
||||
{
|
||||
NetUtils.DebugWrite("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal);
|
||||
//Get needed array from dictionary
|
||||
ushort packetFragId = p.FragmentId;
|
||||
IncomingFragments incomingFragments;
|
||||
if (!_holdedFragments.TryGetValue(packetFragId, out incomingFragments))
|
||||
{
|
||||
incomingFragments = new IncomingFragments
|
||||
{
|
||||
Fragments = new NetPacket[p.FragmentsTotal]
|
||||
};
|
||||
_holdedFragments.Add(packetFragId, incomingFragments);
|
||||
}
|
||||
|
||||
//Cache
|
||||
var fragments = incomingFragments.Fragments;
|
||||
|
||||
//Error check
|
||||
if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null)
|
||||
{
|
||||
_packetPool.Recycle(p);
|
||||
NetUtils.DebugWriteError("Invalid fragment packet");
|
||||
return;
|
||||
}
|
||||
//Fill array
|
||||
fragments[p.FragmentPart] = p;
|
||||
|
||||
//Increase received fragments count
|
||||
incomingFragments.ReceivedCount++;
|
||||
|
||||
//Increase total size
|
||||
int dataOffset = p.GetHeaderSize() + NetConstants.FragmentHeaderSize;
|
||||
incomingFragments.TotalSize += p.Size - dataOffset;
|
||||
|
||||
//Check for finish
|
||||
if (incomingFragments.ReceivedCount != fragments.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite("Received all fragments!");
|
||||
NetPacket resultingPacket = _packetPool.Get( p.Property, incomingFragments.TotalSize );
|
||||
|
||||
int resultingPacketOffset = resultingPacket.GetHeaderSize();
|
||||
int firstFragmentSize = fragments[0].Size - dataOffset;
|
||||
for (int i = 0; i < incomingFragments.ReceivedCount; i++)
|
||||
{
|
||||
//Create resulting big packet
|
||||
int fragmentSize = fragments[i].Size - dataOffset;
|
||||
Buffer.BlockCopy(
|
||||
fragments[i].RawData,
|
||||
dataOffset,
|
||||
resultingPacket.RawData,
|
||||
resultingPacketOffset + firstFragmentSize * i,
|
||||
fragmentSize);
|
||||
|
||||
//Free memory
|
||||
_packetPool.Recycle(fragments[i]);
|
||||
fragments[i] = null;
|
||||
}
|
||||
|
||||
//Send to process
|
||||
_peerListener.ReceiveFromPeer(resultingPacket, _remoteEndPoint);
|
||||
|
||||
//Clear memory
|
||||
_packetPool.Recycle(resultingPacket);
|
||||
_holdedFragments.Remove(packetFragId);
|
||||
}
|
||||
else //Just simple packet
|
||||
{
|
||||
_peerListener.ReceiveFromPeer(p, _remoteEndPoint);
|
||||
_packetPool.Recycle(p);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMtuPacket(NetPacket packet)
|
||||
{
|
||||
if (packet.Size == 1 ||
|
||||
packet.RawData[1] >= NetConstants.PossibleMtu.Length)
|
||||
return;
|
||||
|
||||
//MTU auto increase
|
||||
if (packet.Property == PacketProperty.MtuCheck)
|
||||
{
|
||||
if (packet.Size != NetConstants.PossibleMtu[packet.RawData[1]])
|
||||
{
|
||||
return;
|
||||
}
|
||||
_mtuCheckAttempts = 0;
|
||||
NetUtils.DebugWrite("MTU check. Resend: " + packet.RawData[1]);
|
||||
var mtuOkPacket = _packetPool.Get(PacketProperty.MtuOk, 1);
|
||||
mtuOkPacket.RawData[1] = packet.RawData[1];
|
||||
SendPacket(mtuOkPacket);
|
||||
}
|
||||
else if(packet.RawData[1] > _mtuIdx) //MtuOk
|
||||
{
|
||||
lock (_mtuMutex)
|
||||
{
|
||||
_mtuIdx = packet.RawData[1];
|
||||
_mtu = NetConstants.PossibleMtu[_mtuIdx];
|
||||
}
|
||||
//if maxed - finish.
|
||||
if (_mtuIdx == NetConstants.PossibleMtu.Length - 1)
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
NetUtils.DebugWrite("MTU ok. Increase to: " + _mtu);
|
||||
}
|
||||
}
|
||||
|
||||
//Process incoming packet
|
||||
internal void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
_timeSinceLastPacket = 0;
|
||||
|
||||
NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.ConnectRequest:
|
||||
//response with connect
|
||||
long newId = BitConverter.ToInt64(packet.RawData, 1);
|
||||
if (newId > _connectId)
|
||||
{
|
||||
_connectId = newId;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", ConnectId, newId, _remoteEndPoint);
|
||||
SendConnectAccept();
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.Merged:
|
||||
int pos = NetConstants.HeaderSize;
|
||||
while (pos < packet.Size)
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(packet.RawData, pos);
|
||||
pos += 2;
|
||||
NetPacket mergedPacket = _packetPool.GetAndRead(packet.RawData, pos, size);
|
||||
if (mergedPacket == null)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
pos += size;
|
||||
ProcessPacket(mergedPacket);
|
||||
}
|
||||
break;
|
||||
//If we get ping, send pong
|
||||
case PacketProperty.Ping:
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remotePingSequence) < 0)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
NetUtils.DebugWrite("[PP]Ping receive, send pong");
|
||||
_remotePingSequence = packet.Sequence;
|
||||
_packetPool.Recycle(packet);
|
||||
|
||||
//send
|
||||
CreateAndSend(PacketProperty.Pong, _remotePingSequence);
|
||||
break;
|
||||
|
||||
//If we get pong, calculate ping time and rtt
|
||||
case PacketProperty.Pong:
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pingSequence) < 0)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
_pingSequence = packet.Sequence;
|
||||
int rtt = (int)(DateTime.UtcNow - _pingTimeStart).TotalMilliseconds;
|
||||
UpdateRoundTripTime(rtt);
|
||||
NetUtils.DebugWrite("[PP]Ping: {0}", rtt);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
//Process ack
|
||||
case PacketProperty.AckReliable:
|
||||
_reliableUnorderedChannel.ProcessAck(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.AckReliableOrdered:
|
||||
_reliableOrderedChannel.ProcessAck(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
//Process in order packets
|
||||
case PacketProperty.Sequenced:
|
||||
_sequencedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.Reliable:
|
||||
_reliableUnorderedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.ReliableOrdered:
|
||||
_reliableOrderedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
//Simple packet without acks
|
||||
case PacketProperty.Unreliable:
|
||||
AddIncomingPacket(packet);
|
||||
return;
|
||||
|
||||
case PacketProperty.MtuCheck:
|
||||
case PacketProperty.MtuOk:
|
||||
ProcessMtuPacket(packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanMerge(PacketProperty property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case PacketProperty.ConnectAccept:
|
||||
case PacketProperty.ConnectRequest:
|
||||
case PacketProperty.MtuOk:
|
||||
case PacketProperty.Pong:
|
||||
case PacketProperty.Disconnect:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendRawData(NetPacket packet)
|
||||
{
|
||||
//2 - merge byte + minimal packet size + datalen(ushort)
|
||||
if (_peerListener.MergeEnabled &&
|
||||
CanMerge(packet.Property) &&
|
||||
_mergePos + packet.Size + NetConstants.HeaderSize*2 + 2 < _mtu)
|
||||
{
|
||||
FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size);
|
||||
Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size);
|
||||
_mergePos += packet.Size + 2;
|
||||
_mergeCount++;
|
||||
|
||||
//DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount);
|
||||
return;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.DarkYellow, "[P]SendingPacket: " + packet.Property);
|
||||
_peerListener.SendRaw(packet.RawData, 0, packet.Size, _remoteEndPoint);
|
||||
}
|
||||
|
||||
private void SendQueuedPackets(int currentMaxSend)
|
||||
{
|
||||
int currentSended = 0;
|
||||
while (currentSended < currentMaxSend)
|
||||
{
|
||||
//Get one of packets
|
||||
if (_reliableOrderedChannel.SendNextPacket() ||
|
||||
_reliableUnorderedChannel.SendNextPacket() ||
|
||||
_sequencedChannel.SendNextPacket() ||
|
||||
_simpleChannel.SendNextPacket())
|
||||
{
|
||||
currentSended++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//no outgoing packets
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Increase counter
|
||||
_sendedPacketsCount += currentSended;
|
||||
|
||||
//If merging enabled
|
||||
if (_mergePos > 0)
|
||||
{
|
||||
if (_mergeCount > 1)
|
||||
{
|
||||
NetUtils.DebugWrite("Send merged: " + _mergePos + ", count: " + _mergeCount);
|
||||
_peerListener.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, _remoteEndPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Send without length information and merging
|
||||
_peerListener.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, _remoteEndPoint);
|
||||
}
|
||||
_mergePos = 0;
|
||||
_mergeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush all queued packets
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
lock (_flushLock)
|
||||
{
|
||||
SendQueuedPackets(int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(int deltaTime)
|
||||
{
|
||||
if (_connectionState == ConnectionState.Disconnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeSinceLastPacket += deltaTime;
|
||||
if (_connectionState == ConnectionState.InProgress)
|
||||
{
|
||||
_connectTimer += deltaTime;
|
||||
if (_connectTimer > _peerListener.ReconnectDelay)
|
||||
{
|
||||
_connectTimer = 0;
|
||||
_connectAttempts++;
|
||||
if (_connectAttempts > _peerListener.MaxConnectAttempts)
|
||||
{
|
||||
_connectionState = ConnectionState.Disconnected;
|
||||
return;
|
||||
}
|
||||
|
||||
//else send connect again
|
||||
SendConnectRequest();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Get current flow mode
|
||||
int maxSendPacketsCount = _peerListener.GetPacketsPerSecond(_currentFlowMode);
|
||||
int currentMaxSend;
|
||||
|
||||
if (maxSendPacketsCount > 0)
|
||||
{
|
||||
int availableSendPacketsCount = maxSendPacketsCount - _sendedPacketsCount;
|
||||
currentMaxSend = Math.Min(availableSendPacketsCount, (maxSendPacketsCount*deltaTime)/NetConstants.FlowUpdateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentMaxSend = int.MaxValue;
|
||||
}
|
||||
|
||||
//DebugWrite("[UPDATE]Delta: {0}ms, MaxSend: {1}", deltaTime, currentMaxSend);
|
||||
|
||||
//Pending acks
|
||||
_reliableOrderedChannel.SendAcks();
|
||||
_reliableUnorderedChannel.SendAcks();
|
||||
|
||||
//ResetFlowTimer
|
||||
_flowTimer += deltaTime;
|
||||
if (_flowTimer >= NetConstants.FlowUpdateTime)
|
||||
{
|
||||
NetUtils.DebugWrite("[UPDATE]Reset flow timer, _sendedPackets - {0}", _sendedPacketsCount);
|
||||
_sendedPacketsCount = 0;
|
||||
_flowTimer = 0;
|
||||
}
|
||||
|
||||
//Send ping
|
||||
_pingSendTimer += deltaTime;
|
||||
if (_pingSendTimer >= _peerListener.PingInterval)
|
||||
{
|
||||
NetUtils.DebugWrite("[PP] Send ping...");
|
||||
|
||||
//reset timer
|
||||
_pingSendTimer = 0;
|
||||
|
||||
//send ping
|
||||
CreateAndSend(PacketProperty.Ping, _pingSequence);
|
||||
|
||||
//reset timer
|
||||
_pingTimeStart = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
//RTT - round trip time
|
||||
_rttResetTimer += deltaTime;
|
||||
if (_rttResetTimer >= RttResetDelay)
|
||||
{
|
||||
_rttResetTimer = 0;
|
||||
//Rtt update
|
||||
_rtt = _avgRtt;
|
||||
_ping = _avgRtt;
|
||||
_peerListener.ConnectionLatencyUpdated(this, _ping);
|
||||
_rttCount = 1;
|
||||
}
|
||||
|
||||
//MTU - Maximum transmission unit
|
||||
if (!_finishMtu)
|
||||
{
|
||||
_mtuCheckTimer += deltaTime;
|
||||
if (_mtuCheckTimer >= MtuCheckDelay)
|
||||
{
|
||||
_mtuCheckTimer = 0;
|
||||
_mtuCheckAttempts++;
|
||||
if (_mtuCheckAttempts >= MaxMtuCheckAttempts)
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_mtuMutex)
|
||||
{
|
||||
//Send increased packet
|
||||
if (_mtuIdx < NetConstants.PossibleMtu.Length - 1)
|
||||
{
|
||||
int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1] - NetConstants.HeaderSize;
|
||||
var p = _packetPool.Get(PacketProperty.MtuCheck, newMtu);
|
||||
p.RawData[1] = (byte)(_mtuIdx + 1);
|
||||
SendPacket(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//MTU - end
|
||||
|
||||
//Pending send
|
||||
lock (_flushLock)
|
||||
{
|
||||
SendQueuedPackets(currentMaxSend);
|
||||
}
|
||||
}
|
||||
|
||||
//For channels
|
||||
internal void Recycle(NetPacket packet)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
}
|
||||
|
||||
internal NetPacket GetPacketFromPool(PacketProperty property, int bytesCount)
|
||||
{
|
||||
return _packetPool.Get(property, bytesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39ca2e83856ea4ba0a0100a00089e695
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetPeerCollection
|
||||
{
|
||||
private readonly Dictionary<NetEndPoint, NetPeer> _peersDict;
|
||||
private readonly NetPeer[] _peersArray;
|
||||
private int _count;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _count; }
|
||||
}
|
||||
|
||||
public NetPeer this[int index]
|
||||
{
|
||||
get { return _peersArray[index]; }
|
||||
}
|
||||
|
||||
public NetPeerCollection(int maxPeers)
|
||||
{
|
||||
_peersArray = new NetPeer[maxPeers];
|
||||
_peersDict = new Dictionary<NetEndPoint, NetPeer>();
|
||||
}
|
||||
|
||||
public bool TryGetValue(NetEndPoint endPoint, out NetPeer peer)
|
||||
{
|
||||
return _peersDict.TryGetValue(endPoint, out peer);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_peersArray, 0, _count);
|
||||
_peersDict.Clear();
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public void Add(NetEndPoint endPoint, NetPeer peer)
|
||||
{
|
||||
_peersArray[_count] = peer;
|
||||
_peersDict.Add(endPoint, peer);
|
||||
_count++;
|
||||
}
|
||||
|
||||
public bool ContainsAddress(NetEndPoint endPoint)
|
||||
{
|
||||
return _peersDict.ContainsKey(endPoint);
|
||||
}
|
||||
|
||||
public NetPeer[] ToArray()
|
||||
{
|
||||
NetPeer[] result = new NetPeer[_count];
|
||||
Array.Copy(_peersArray, 0, result, 0, _count);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void RemoveAt(int idx)
|
||||
{
|
||||
_peersDict.Remove(_peersArray[idx].EndPoint);
|
||||
_peersArray[idx] = _peersArray[_count - 1];
|
||||
_peersArray[_count - 1] = null;
|
||||
_count--;
|
||||
}
|
||||
|
||||
public void Remove(NetEndPoint endPoint)
|
||||
{
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (_peersArray[i].EndPoint.Equals(endPoint))
|
||||
{
|
||||
RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca9b271291848481a86c9b3cb7f07451
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,709 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
#if WINRT || NETCORE
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public interface INetSerializable
|
||||
{
|
||||
void Serialize(NetDataWriter writer);
|
||||
void Desereialize(NetDataReader reader);
|
||||
}
|
||||
|
||||
public abstract class NetSerializerHasher
|
||||
{
|
||||
public abstract ulong GetHash(string type);
|
||||
public abstract void WriteHash(ulong hash, NetDataWriter writer);
|
||||
public abstract ulong ReadHash(NetDataReader reader);
|
||||
}
|
||||
|
||||
public sealed class FNVHasher : NetSerializerHasher
|
||||
{
|
||||
private readonly Dictionary<string, ulong> _hashCache = new Dictionary<string, ulong>();
|
||||
private readonly char[] _hashBuffer = new char[1024];
|
||||
|
||||
public override ulong GetHash(string type)
|
||||
{
|
||||
ulong hash;
|
||||
if (_hashCache.TryGetValue(type, out hash))
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
hash = 14695981039346656037UL; //offset
|
||||
int len = type.Length;
|
||||
type.CopyTo(0, _hashBuffer, 0, len);
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
hash = hash ^ _hashBuffer[i];
|
||||
hash *= 1099511628211UL; //prime
|
||||
}
|
||||
_hashCache.Add(type, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override ulong ReadHash(NetDataReader reader)
|
||||
{
|
||||
return reader.GetULong();
|
||||
}
|
||||
|
||||
public override void WriteHash(ulong hash, NetDataWriter writer)
|
||||
{
|
||||
writer.Put(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NetSerializer
|
||||
{
|
||||
private sealed class CustomType
|
||||
{
|
||||
public readonly CustomTypeWrite WriteDelegate;
|
||||
public readonly CustomTypeRead ReadDelegate;
|
||||
|
||||
public CustomType(CustomTypeWrite writeDelegate, CustomTypeRead readDelegate)
|
||||
{
|
||||
WriteDelegate = writeDelegate;
|
||||
ReadDelegate = readDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void CustomTypeWrite(NetDataWriter writer, object customObj);
|
||||
private delegate object CustomTypeRead(NetDataReader reader);
|
||||
|
||||
private sealed class StructInfo
|
||||
{
|
||||
public readonly Action<NetDataWriter>[] WriteDelegate;
|
||||
public readonly Action<NetDataReader>[] ReadDelegate;
|
||||
public readonly Type[] FieldTypes;
|
||||
public object Reference;
|
||||
public Func<object> CreatorFunc;
|
||||
public Action<object, object> OnReceive;
|
||||
public readonly ulong Hash;
|
||||
public readonly int MembersCount;
|
||||
|
||||
public StructInfo(ulong hash, int membersCount)
|
||||
{
|
||||
Hash = hash;
|
||||
MembersCount = membersCount;
|
||||
WriteDelegate = new Action<NetDataWriter>[membersCount];
|
||||
ReadDelegate = new Action<NetDataReader>[membersCount];
|
||||
FieldTypes = new Type[membersCount];
|
||||
}
|
||||
|
||||
public void Write(NetDataWriter writer, object obj)
|
||||
{
|
||||
Reference = obj;
|
||||
for (int i = 0; i < MembersCount; i++)
|
||||
{
|
||||
WriteDelegate[i](writer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(NetDataReader reader)
|
||||
{
|
||||
for (int i = 0; i < MembersCount; i++)
|
||||
{
|
||||
ReadDelegate[i](reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<ulong, StructInfo> _cache;
|
||||
private readonly Dictionary<Type, CustomType> _registeredCustomTypes;
|
||||
|
||||
private static readonly HashSet<Type> BasicTypes = new HashSet<Type>
|
||||
{
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(string),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(bool)
|
||||
};
|
||||
|
||||
private readonly NetDataWriter _writer;
|
||||
private readonly NetSerializerHasher _hasher;
|
||||
private const int MaxStringLenght = 1024;
|
||||
|
||||
public NetSerializer() : this(new FNVHasher())
|
||||
{
|
||||
}
|
||||
|
||||
public NetSerializer(NetSerializerHasher hasher)
|
||||
{
|
||||
_hasher = hasher;
|
||||
_cache = new Dictionary<ulong, StructInfo>();
|
||||
_registeredCustomTypes = new Dictionary<Type, CustomType>();
|
||||
_writer = new NetDataWriter();
|
||||
}
|
||||
|
||||
private bool RegisterCustomTypeInternal<T>(Func<T> constructor) where T : INetSerializable
|
||||
{
|
||||
var t = typeof(T);
|
||||
if (_registeredCustomTypes.ContainsKey(t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rwDelegates = new CustomType(
|
||||
(writer, obj) =>
|
||||
{
|
||||
((T)obj).Serialize(writer);
|
||||
},
|
||||
reader =>
|
||||
{
|
||||
var instance = constructor();
|
||||
instance.Desereialize(reader);
|
||||
return instance;
|
||||
});
|
||||
_registeredCustomTypes.Add(t, rwDelegates);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">INetSerializable structure</typeparam>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>() where T : struct, INetSerializable
|
||||
{
|
||||
return RegisterCustomTypeInternal(() => new T());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">INetSerializable class</typeparam>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>(Func<T> constructor) where T : class, INetSerializable
|
||||
{
|
||||
return RegisterCustomTypeInternal(constructor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <param name="writeDelegate"></param>
|
||||
/// <param name="readDelegate"></param>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate)
|
||||
{
|
||||
var t = typeof(T);
|
||||
if (BasicTypes.Contains(t) || _registeredCustomTypes.ContainsKey(t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rwDelegates = new CustomType(
|
||||
(writer, obj) => writeDelegate(writer, (T)obj),
|
||||
reader => readDelegate(reader));
|
||||
|
||||
_registeredCustomTypes.Add(t, rwDelegates);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Delegate CreateDelegate(Type type, MethodInfo info)
|
||||
{
|
||||
#if WINRT || NETCORE
|
||||
return info.CreateDelegate(type);
|
||||
#else
|
||||
return Delegate.CreateDelegate(type, info);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Func<TClass, TProperty> ExtractGetDelegate<TClass, TProperty>(MethodInfo info)
|
||||
{
|
||||
return (Func<TClass, TProperty>)CreateDelegate(typeof(Func<TClass, TProperty>), info);
|
||||
}
|
||||
|
||||
private static Action<TClass, TProperty> ExtractSetDelegate<TClass, TProperty>(MethodInfo info)
|
||||
{
|
||||
return (Action<TClass, TProperty>)CreateDelegate(typeof(Action<TClass, TProperty>), info);
|
||||
}
|
||||
|
||||
private StructInfo RegisterInternal<T>() where T : class
|
||||
{
|
||||
Type t = typeof(T);
|
||||
ulong nameHash = _hasher.GetHash(t.Name);
|
||||
|
||||
StructInfo info;
|
||||
if (_cache.TryGetValue(nameHash, out info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
#if WINRT || NETCORE
|
||||
var props = t.GetRuntimeProperties().ToArray();
|
||||
int propsCount = props.Count();
|
||||
#else
|
||||
var props = t.GetProperties(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.GetProperty |
|
||||
BindingFlags.SetProperty);
|
||||
int propsCount = props.Length;
|
||||
#endif
|
||||
if (props == null || propsCount < 0)
|
||||
{
|
||||
throw new ArgumentException("Type does not contain acceptable fields");
|
||||
}
|
||||
|
||||
info = new StructInfo(nameHash, propsCount);
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var property = props[i];
|
||||
var propertyType = property.PropertyType;
|
||||
|
||||
//Set field type
|
||||
info.FieldTypes[i] = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
|
||||
#if WINRT || NETCORE
|
||||
bool isEnum = propertyType.GetTypeInfo().IsEnum;
|
||||
var getMethod = property.GetMethod;
|
||||
var setMethod = property.SetMethod;
|
||||
#else
|
||||
bool isEnum = propertyType.IsEnum;
|
||||
var getMethod = property.GetGetMethod();
|
||||
var setMethod = property.GetSetMethod();
|
||||
#endif
|
||||
if (isEnum)
|
||||
{
|
||||
var underlyingType = Enum.GetUnderlyingType(propertyType);
|
||||
if (underlyingType == typeof(byte))
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetByte()), null);
|
||||
};
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
writer.Put((byte)property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
else if (underlyingType == typeof(int))
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetInt()), null);
|
||||
};
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
writer.Put((int)property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Not supported enum underlying type: " + underlyingType.Name);
|
||||
}
|
||||
}
|
||||
else if (propertyType == typeof(string))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, string>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, string>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetString(MaxStringLenght));
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference), MaxStringLenght);
|
||||
}
|
||||
else if (propertyType == typeof(bool))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, bool>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, bool>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBool());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(byte))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, byte>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, byte>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetByte());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(sbyte))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, sbyte>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, sbyte>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetSByte());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(short))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, short>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, short>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShort());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ushort))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ushort>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ushort>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShort());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(int))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, int>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, int>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetInt());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(uint))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, uint>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, uint>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUInt());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(long))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, long>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, long>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLong());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ulong))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ulong>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ulong>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULong());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(float))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, float>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, float>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloat());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(double))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, double>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, double>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDouble());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
// Array types
|
||||
else if (propertyType == typeof(string[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, string[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, string[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetStringArray(MaxStringLenght));
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference), MaxStringLenght);
|
||||
}
|
||||
else if (propertyType == typeof(byte[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, byte[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, byte[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBytesWithLength());
|
||||
info.WriteDelegate[i] = writer => writer.PutBytesWithLength(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(short[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, short[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, short[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShortArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ushort[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ushort[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ushort[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShortArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(int[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, int[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, int[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetIntArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(uint[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, uint[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, uint[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUIntArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(long[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, long[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, long[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLongArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ulong[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ulong[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ulong[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULongArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(float[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, float[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, float[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloatArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(double[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, double[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, double[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDoubleArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomType registeredCustomType;
|
||||
bool array = false;
|
||||
|
||||
if (propertyType.IsArray)
|
||||
{
|
||||
array = true;
|
||||
propertyType = propertyType.GetElementType();
|
||||
}
|
||||
|
||||
if (_registeredCustomTypes.TryGetValue(propertyType, out registeredCustomType))
|
||||
{
|
||||
if (array) //Array type serialize/deserialize
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
ushort arrLength = reader.GetUShort();
|
||||
Array arr = Array.CreateInstance(propertyType, arrLength);
|
||||
for (int k = 0; k < arrLength; k++)
|
||||
{
|
||||
arr.SetValue(registeredCustomType.ReadDelegate(reader), k);
|
||||
}
|
||||
|
||||
property.SetValue(info.Reference, arr, null);
|
||||
};
|
||||
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
Array arr = (Array)property.GetValue(info.Reference, null);
|
||||
writer.Put((ushort)arr.Length);
|
||||
for (int k = 0; k < arr.Length; k++)
|
||||
{
|
||||
registeredCustomType.WriteDelegate(writer, arr.GetValue(k));
|
||||
}
|
||||
};
|
||||
}
|
||||
else //Simple
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, registeredCustomType.ReadDelegate(reader), null);
|
||||
};
|
||||
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
registeredCustomType.WriteDelegate(writer, property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unknown property type: " + propertyType.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_cache.Add(nameHash, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all available data from NetDataReader and calls OnReceive delegates
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packets data</param>
|
||||
public void ReadAllPackets(NetDataReader reader)
|
||||
{
|
||||
while (reader.AvailableBytes > 0)
|
||||
{
|
||||
ReadPacket(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all available data from NetDataReader and calls OnReceive delegates
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packets data</param>
|
||||
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
|
||||
public void ReadAllPackets<T>(NetDataReader reader, T userData)
|
||||
{
|
||||
while (reader.AvailableBytes > 0)
|
||||
{
|
||||
ReadPacket(reader, userData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads one packet from NetDataReader and calls OnReceive delegate
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
public void ReadPacket(NetDataReader reader)
|
||||
{
|
||||
ReadPacket(reader, null);
|
||||
}
|
||||
|
||||
private StructInfo ReadInfo(NetDataReader reader)
|
||||
{
|
||||
ulong hash = _hasher.ReadHash(reader);
|
||||
StructInfo info;
|
||||
if (!_cache.TryGetValue(hash, out info))
|
||||
{
|
||||
throw new Exception("Undefined packet received");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads packet with known type
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <returns>Returns packet if packet in reader is matched type</returns>
|
||||
public T ReadKnownPacket<T>(NetDataReader reader) where T : class, new()
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
ulong typeHash = _hasher.GetHash(typeof(T).Name);
|
||||
if (typeHash != info.Hash)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
info.Reference = info.CreatorFunc != null ? info.CreatorFunc() : Activator.CreateInstance<T>();
|
||||
info.Read(reader);
|
||||
return (T)info.Reference;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads packet with known type (non alloc variant)
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <param name="target">Deserialization target</param>
|
||||
/// <returns>Returns true if packet in reader is matched type</returns>
|
||||
public bool ReadKnownPacket<T>(NetDataReader reader, T target) where T : class, new()
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
ulong typeHash = _hasher.GetHash(typeof(T).Name);
|
||||
if (typeHash != info.Hash)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
info.Reference = target;
|
||||
info.Read(reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads one packet from NetDataReader and calls OnReceive delegate
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
|
||||
public void ReadPacket(NetDataReader reader, object userData)
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
if (info.CreatorFunc != null)
|
||||
{
|
||||
info.Reference = info.CreatorFunc();
|
||||
}
|
||||
info.Read(reader);
|
||||
info.OnReceive(info.Reference, userData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register packet type for direct reading (ReadKnownPacket)
|
||||
/// </summary>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Register<T>(Func<T> packetConstructor = null) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
if (packetConstructor != null)
|
||||
{
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
}
|
||||
info.OnReceive = (o, userData) => { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event (with userData)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// This metod will overwrite last received packet class on receive (less garbage)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.Reference = new T();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// This metod will overwrite last received packet class on receive (less garbage)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.Reference = new T();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize struct to NetDataWriter (fast)
|
||||
/// </summary>
|
||||
/// <param name="writer">Serialization target NetDataWriter</param>
|
||||
/// <param name="obj">Struct to serialize</param>
|
||||
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
_hasher.WriteHash(info.Hash, writer);
|
||||
info.Write(writer, obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize struct to byte array
|
||||
/// </summary>
|
||||
/// <param name="obj">Struct to serialize</param>
|
||||
/// <returns>byte array with serialized data</returns>
|
||||
public byte[] Serialize<T>(T obj) where T : class, new()
|
||||
{
|
||||
_writer.Reset();
|
||||
Serialize(_writer, obj);
|
||||
return _writer.CopyData();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e26db3a8188754e07b42db31f32e20f7
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,455 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetSocket
|
||||
{
|
||||
private Socket _udpSocketv4;
|
||||
private Socket _udpSocketv6;
|
||||
private NetEndPoint _localEndPoint;
|
||||
private Thread _threadv4;
|
||||
private Thread _threadv6;
|
||||
private bool _running;
|
||||
private readonly NetManager.OnMessageReceived _onMessageReceived;
|
||||
|
||||
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse (NetConstants.MulticastGroupIPv6);
|
||||
private static readonly bool IPv6Support;
|
||||
private const int SocketReceivePollTime = 100000;
|
||||
private const int SocketSendPollTime = 5000;
|
||||
|
||||
public NetEndPoint LocalEndPoint
|
||||
{
|
||||
get { return _localEndPoint; }
|
||||
}
|
||||
|
||||
static NetSocket()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Unity3d .NET 2.0 throws exception.
|
||||
// IPv6Support = Socket.OSSupportsIPv6;
|
||||
IPv6Support = false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
IPv6Support = false;
|
||||
}
|
||||
}
|
||||
|
||||
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
|
||||
{
|
||||
_onMessageReceived = onMessageReceived;
|
||||
}
|
||||
|
||||
private void ReceiveLogic(object state)
|
||||
{
|
||||
Socket socket = (Socket)state;
|
||||
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
|
||||
NetEndPoint bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
|
||||
byte[] receiveBuffer = new byte[NetConstants.PacketSizeLimit];
|
||||
|
||||
while (_running)
|
||||
{
|
||||
//wait for data
|
||||
if (!socket.Poll(SocketReceivePollTime, SelectMode.SelectRead))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
//Reading data
|
||||
try
|
||||
{
|
||||
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint);
|
||||
if (!bufferNetEndPoint.EndPoint.Equals(bufferEndPoint))
|
||||
{
|
||||
bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode == SocketError.ConnectionReset ||
|
||||
ex.SocketErrorCode == SocketError.MessageSize)
|
||||
{
|
||||
//10040 - message too long
|
||||
//10054 - remote close (not error)
|
||||
//Just UDP
|
||||
NetUtils.DebugWrite(ConsoleColor.DarkRed, "[R] Ingored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString() );
|
||||
continue;
|
||||
}
|
||||
NetUtils.DebugWriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString());
|
||||
_onMessageReceived(null, 0, (int)ex.SocketErrorCode, bufferNetEndPoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
//All ok!
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[R]Recieved data from {0}, result: {1}", bufferNetEndPoint.ToString(), result);
|
||||
_onMessageReceived(receiveBuffer, result, 0, bufferNetEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Bind(int port, bool reuseAddress)
|
||||
{
|
||||
_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
_udpSocketv4.Blocking = false;
|
||||
_udpSocketv4.ReceiveBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv4.SendBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, NetConstants.SocketTTL);
|
||||
if(reuseAddress)
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
#if !NETCORE
|
||||
_udpSocketv4.DontFragment = true;
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
NetUtils.DebugWriteError("Broadcast error: {0}", e.ToString());
|
||||
}
|
||||
|
||||
if (!BindSocket(_udpSocketv4, new IPEndPoint(IPAddress.Any, port)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv4.LocalEndPoint);
|
||||
|
||||
_running = true;
|
||||
_threadv4 = new Thread(ReceiveLogic);
|
||||
_threadv4.Name = "SocketThreadv4(" + port + ")";
|
||||
_threadv4.IsBackground = true;
|
||||
_threadv4.Start(_udpSocketv4);
|
||||
|
||||
//Check IPv6 support
|
||||
if (!IPv6Support)
|
||||
return true;
|
||||
|
||||
//Use one port for two sockets
|
||||
port = _localEndPoint.Port;
|
||||
|
||||
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
_udpSocketv6.Blocking = false;
|
||||
_udpSocketv6.ReceiveBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv6.SendBufferSize = NetConstants.SocketBufferSize;
|
||||
if (reuseAddress)
|
||||
_udpSocketv6.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
|
||||
if (BindSocket(_udpSocketv6, new IPEndPoint(IPAddress.IPv6Any, port)))
|
||||
{
|
||||
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv6.LocalEndPoint);
|
||||
|
||||
try
|
||||
{
|
||||
_udpSocketv6.SetSocketOption(
|
||||
SocketOptionLevel.IPv6,
|
||||
SocketOptionName.AddMembership,
|
||||
new IPv6MulticastOption(MulticastAddressV6));
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// Unity3d throws exception - ignored
|
||||
}
|
||||
|
||||
_threadv6 = new Thread(ReceiveLogic);
|
||||
_threadv6.Name = "SocketThreadv6(" + port + ")";
|
||||
_threadv6.IsBackground = true;
|
||||
_threadv6.Start(_udpSocketv6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool BindSocket(Socket socket, IPEndPoint ep)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Bind(ep);
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[B]Succesfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
|
||||
//ODO: very temporary hack for iOS (Unity3D)
|
||||
if (ex.SocketErrorCode == SocketError.AddressFamilyNotSupported)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendBroadcast(byte[] data, int offset, int size, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
int result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port));
|
||||
if (result <= 0)
|
||||
return false;
|
||||
if (IPv6Support)
|
||||
{
|
||||
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port));
|
||||
if (result <= 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S][MCAST]" + ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SendTo(byte[] data, int offset, int size, NetEndPoint remoteEndPoint, ref int errorCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
int result = 0;
|
||||
if (remoteEndPoint.EndPoint.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
if (!_udpSocketv4.Poll(SocketSendPollTime, SelectMode.SelectWrite))
|
||||
return -1;
|
||||
result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
|
||||
}
|
||||
else if(IPv6Support)
|
||||
{
|
||||
if (!_udpSocketv6.Poll(SocketSendPollTime, SelectMode.SelectWrite))
|
||||
return -1;
|
||||
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[S]Send packet to {0}, result: {1}", remoteEndPoint.EndPoint, result);
|
||||
return result;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode != SocketError.MessageSize)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S]" + ex);
|
||||
}
|
||||
|
||||
errorCode = (int)ex.SocketErrorCode;
|
||||
return -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S]" + ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseSocket(Socket s)
|
||||
{
|
||||
#if NETCORE
|
||||
s.Dispose();
|
||||
#else
|
||||
s.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_running = false;
|
||||
|
||||
//Close IPv4
|
||||
if (Thread.CurrentThread != _threadv4)
|
||||
{
|
||||
_threadv4.Join();
|
||||
}
|
||||
_threadv4 = null;
|
||||
if (_udpSocketv4 != null)
|
||||
{
|
||||
CloseSocket(_udpSocketv4);
|
||||
_udpSocketv4 = null;
|
||||
}
|
||||
|
||||
//No ipv6
|
||||
if (_udpSocketv6 == null)
|
||||
return;
|
||||
|
||||
//Close IPv6
|
||||
if (Thread.CurrentThread != _threadv6)
|
||||
{
|
||||
_threadv6.Join();
|
||||
}
|
||||
_threadv6 = null;
|
||||
if (_udpSocketv6 != null)
|
||||
{
|
||||
CloseSocket(_udpSocketv6);
|
||||
_udpSocketv6 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetSocket
|
||||
{
|
||||
private DatagramSocket _datagramSocket;
|
||||
private readonly Dictionary<NetEndPoint, IOutputStream> _peers = new Dictionary<NetEndPoint, IOutputStream>();
|
||||
private readonly NetManager.OnMessageReceived _onMessageReceived;
|
||||
private readonly byte[] _byteBuffer = new byte[NetConstants.PacketSizeLimit];
|
||||
private readonly IBuffer _buffer;
|
||||
private NetEndPoint _bufferEndPoint;
|
||||
private NetEndPoint _localEndPoint;
|
||||
private static readonly HostName BroadcastAddress = new HostName("255.255.255.255");
|
||||
private static readonly HostName MulticastAddressV6 = new HostName(NetConstants.MulticastGroupIPv6);
|
||||
|
||||
public NetEndPoint LocalEndPoint
|
||||
{
|
||||
get { return _localEndPoint; }
|
||||
}
|
||||
|
||||
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
|
||||
{
|
||||
_onMessageReceived = onMessageReceived;
|
||||
_buffer = _byteBuffer.AsBuffer();
|
||||
}
|
||||
|
||||
private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
|
||||
{
|
||||
var result = args.GetDataStream().ReadAsync(_buffer, _buffer.Capacity, InputStreamOptions.None).AsTask().Result;
|
||||
int length = (int)result.Length;
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
if (_bufferEndPoint == null ||
|
||||
!_bufferEndPoint.HostName.IsEqual(args.RemoteAddress) ||
|
||||
!_bufferEndPoint.PortStr.Equals(args.RemotePort))
|
||||
{
|
||||
_bufferEndPoint = new NetEndPoint(args.RemoteAddress, args.RemotePort);
|
||||
}
|
||||
_onMessageReceived(_byteBuffer, length, 0, _bufferEndPoint);
|
||||
}
|
||||
|
||||
public bool Bind(int port, bool reuseAddress)
|
||||
{
|
||||
_datagramSocket = new DatagramSocket();
|
||||
_datagramSocket.Control.InboundBufferSizeInBytes = NetConstants.SocketBufferSize;
|
||||
_datagramSocket.Control.DontFragment = true;
|
||||
_datagramSocket.Control.OutboundUnicastHopLimit = NetConstants.SocketTTL;
|
||||
_datagramSocket.MessageReceived += OnMessageReceived;
|
||||
|
||||
try
|
||||
{
|
||||
_datagramSocket.BindServiceNameAsync(port.ToString()).AsTask().Wait();
|
||||
_datagramSocket.JoinMulticastGroup(MulticastAddressV6);
|
||||
_localEndPoint = new NetEndPoint(_datagramSocket.Information.LocalAddress, _datagramSocket.Information.LocalPort);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendBroadcast(byte[] data, int offset, int size, int port)
|
||||
{
|
||||
var portString = port.ToString();
|
||||
try
|
||||
{
|
||||
var outputStream =
|
||||
_datagramSocket.GetOutputStreamAsync(BroadcastAddress, portString)
|
||||
.AsTask()
|
||||
.Result;
|
||||
var writer = outputStream.AsStreamForWrite();
|
||||
writer.Write(data, offset, size);
|
||||
writer.Flush();
|
||||
|
||||
outputStream =
|
||||
_datagramSocket.GetOutputStreamAsync(MulticastAddressV6, portString)
|
||||
.AsTask()
|
||||
.Result;
|
||||
writer = outputStream.AsStreamForWrite();
|
||||
writer.Write(data, offset, size);
|
||||
writer.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S][MCAST]" + ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SendTo(byte[] data, int offset, int length, NetEndPoint remoteEndPoint, ref int errorCode)
|
||||
{
|
||||
Task<uint> task = null;
|
||||
try
|
||||
{
|
||||
IOutputStream writer;
|
||||
if (!_peers.TryGetValue(remoteEndPoint, out writer))
|
||||
{
|
||||
writer =
|
||||
_datagramSocket.GetOutputStreamAsync(remoteEndPoint.HostName, remoteEndPoint.PortStr)
|
||||
.AsTask()
|
||||
.Result;
|
||||
_peers.Add(remoteEndPoint, writer);
|
||||
}
|
||||
|
||||
task = writer.WriteAsync(data.AsBuffer(offset, length)).AsTask();
|
||||
return (int)task.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (task?.Exception?.InnerExceptions != null)
|
||||
{
|
||||
ex = task.Exception.InnerException;
|
||||
}
|
||||
var errorStatus = SocketError.GetStatus(ex.HResult);
|
||||
switch (errorStatus)
|
||||
{
|
||||
case SocketErrorStatus.MessageTooLong:
|
||||
errorCode = 10040;
|
||||
break;
|
||||
default:
|
||||
errorCode = (int)errorStatus;
|
||||
NetUtils.DebugWriteError("[S " + errorStatus + "(" + errorCode + ")]" + ex);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemovePeer(NetEndPoint ep)
|
||||
{
|
||||
_peers.Remove(ep);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_datagramSocket.Dispose();
|
||||
_datagramSocket = null;
|
||||
ClearPeers();
|
||||
}
|
||||
|
||||
internal void ClearPeers()
|
||||
{
|
||||
_peers.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c517b909a8c704eae91d4eccf06bc8a1
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
#define USE_WINRT
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
#if USE_WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.System.Threading;
|
||||
using Windows.System.Threading.Core;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetThread
|
||||
{
|
||||
#if USE_WINRT
|
||||
private readonly ManualResetEvent _updateWaiter = new ManualResetEvent(false);
|
||||
private readonly ManualResetEvent _joinWaiter = new ManualResetEvent(false);
|
||||
#else
|
||||
private Thread _thread;
|
||||
#endif
|
||||
|
||||
private readonly Action _callback;
|
||||
|
||||
public int SleepTime;
|
||||
private bool _running;
|
||||
private readonly string _name;
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return _running; }
|
||||
}
|
||||
|
||||
public NetThread(string name, int sleepTime, Action callback)
|
||||
{
|
||||
_callback = callback;
|
||||
SleepTime = sleepTime;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_running)
|
||||
return;
|
||||
_running = true;
|
||||
#if USE_WINRT
|
||||
var thread = new PreallocatedWorkItem(ThreadLogic, WorkItemPriority.Normal, WorkItemOptions.TimeSliced);
|
||||
thread.RunAsync().AsTask();
|
||||
#else
|
||||
_thread = new Thread(ThreadLogic)
|
||||
{
|
||||
Name = _name,
|
||||
IsBackground = true
|
||||
};
|
||||
_thread.Start();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!_running)
|
||||
return;
|
||||
_running = false;
|
||||
|
||||
#if USE_WINRT
|
||||
_joinWaiter.WaitOne();
|
||||
#else
|
||||
_thread.Join();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_WINRT
|
||||
private void ThreadLogic(IAsyncAction action)
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
_callback();
|
||||
_updateWaiter.WaitOne(SleepTime);
|
||||
}
|
||||
_joinWaiter.Set();
|
||||
}
|
||||
#else
|
||||
private void ThreadLogic()
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
_callback();
|
||||
Thread.Sleep(SleepTime);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1928476aac6242d29fab6849f102494
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,219 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#define UNITY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Connectivity;
|
||||
#else
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.NetworkInformation;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
public enum ConsoleColor
|
||||
{
|
||||
Gray,
|
||||
Yellow,
|
||||
Cyan,
|
||||
DarkCyan,
|
||||
DarkGreen,
|
||||
Blue,
|
||||
DarkRed,
|
||||
Red,
|
||||
Green,
|
||||
DarkYellow
|
||||
}
|
||||
#endif
|
||||
|
||||
[Flags]
|
||||
public enum LocalAddrType
|
||||
{
|
||||
IPv4 = 1,
|
||||
IPv6 = 2,
|
||||
All = 3
|
||||
}
|
||||
|
||||
public static class NetUtils
|
||||
{
|
||||
internal static int RelativeSequenceNumber(int number, int expected)
|
||||
{
|
||||
return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence;
|
||||
}
|
||||
|
||||
internal static int GetDividedPacketsCount(int size, int mtu)
|
||||
{
|
||||
return (size / mtu) + (size % mtu == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
public static void PrintInterfaceInfos()
|
||||
{
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
DebugWriteForce(ConsoleColor.Green, "IPv6Support: {0}", Socket.OSSupportsIPv6);
|
||||
try
|
||||
{
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (ip.Address.AddressFamily == AddressFamily.InterNetwork ||
|
||||
ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
DebugWriteForce(
|
||||
ConsoleColor.Green,
|
||||
"Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}",
|
||||
ni.Name,
|
||||
ni.NetworkInterfaceType.ToString(),
|
||||
ip.Address.ToString(),
|
||||
ni.OperationalStatus.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugWriteForce(ConsoleColor.Red, "Error while getting interface infos: {0}", e.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void GetLocalIpList(List<string> targetList, LocalAddrType addrType)
|
||||
{
|
||||
bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4;
|
||||
bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6;
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
foreach (HostName localHostName in NetworkInformation.GetHostNames())
|
||||
{
|
||||
if (localHostName.IPInformation != null &&
|
||||
((ipv4 && localHostName.Type == HostNameType.Ipv4) ||
|
||||
(ipv6 && localHostName.Type == HostNameType.Ipv6)))
|
||||
{
|
||||
targetList.Add(localHostName.ToString());
|
||||
}
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
//Skip loopback
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
|
||||
var ipProps = ni.GetIPProperties();
|
||||
|
||||
//Skip address without gateway
|
||||
if (ipProps.GatewayAddresses.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses)
|
||||
{
|
||||
var address = ip.Address;
|
||||
if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) ||
|
||||
(ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6))
|
||||
targetList.Add(address.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//ignored
|
||||
}
|
||||
|
||||
//Fallback mode (unity android)
|
||||
if (targetList.Count == 0)
|
||||
{
|
||||
#if NETCORE
|
||||
var hostTask = Dns.GetHostEntryAsync(Dns.GetHostName());
|
||||
hostTask.Wait();
|
||||
var host = hostTask.Result;
|
||||
#else
|
||||
var host = Dns.GetHostEntry(Dns.GetHostName());
|
||||
#endif
|
||||
foreach (IPAddress ip in host.AddressList)
|
||||
{
|
||||
if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) ||
|
||||
(ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6))
|
||||
targetList.Add(ip.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (targetList.Count == 0)
|
||||
{
|
||||
if(ipv4)
|
||||
targetList.Add("127.0.0.1");
|
||||
if(ipv6)
|
||||
targetList.Add("::1");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<string> IpList = new List<string>();
|
||||
public static string GetLocalIp(LocalAddrType addrType)
|
||||
{
|
||||
lock (IpList)
|
||||
{
|
||||
IpList.Clear();
|
||||
GetLocalIpList(IpList, addrType);
|
||||
return IpList.Count == 0 ? string.Empty : IpList[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly object DebugLogLock = new object();
|
||||
|
||||
private static void DebugWriteLogic(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
lock (DebugLogLock)
|
||||
{
|
||||
|
||||
if (NetDebug.Logger == null)
|
||||
{
|
||||
#if UNITY
|
||||
#if !UNITY_4_0
|
||||
UnityEngine.Debug.LogFormat(str, args);
|
||||
#endif
|
||||
#elif WINRT
|
||||
Debug.WriteLine(str, args);
|
||||
#else
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(str, args);
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NetDebug.Logger.WriteNet(color, str, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
internal static void DebugWrite(string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(ConsoleColor.DarkGreen, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
internal static void DebugWrite(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(color, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
|
||||
internal static void DebugWriteForce(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(color, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
|
||||
internal static void DebugWriteError(string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(ConsoleColor.Red, str, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b27974b2b4714973a8f5a3ea4036677
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public class NtpSyncModule
|
||||
{
|
||||
public DateTime? SyncedTime { get; private set; }
|
||||
private readonly NetSocket _socket;
|
||||
private readonly NetEndPoint _ntpEndPoint;
|
||||
private readonly ManualResetEvent _waiter = new ManualResetEvent(false);
|
||||
|
||||
public NtpSyncModule(string ntpServer)
|
||||
{
|
||||
_ntpEndPoint = new NetEndPoint(ntpServer, 123);
|
||||
_socket = new NetSocket(OnMessageReceived);
|
||||
_socket.Bind(0, false);
|
||||
SyncedTime = null;
|
||||
}
|
||||
|
||||
private void OnMessageReceived(byte[] data, int length, int errorCode, NetEndPoint remoteEndPoint)
|
||||
{
|
||||
if (errorCode != 0)
|
||||
{
|
||||
_waiter.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
ulong intPart = (ulong)data[40] << 24 | (ulong)data[41] << 16 | (ulong)data[42] << 8 | (ulong)data[43];
|
||||
ulong fractPart = (ulong)data[44] << 24 | (ulong)data[45] << 16 | (ulong)data[46] << 8 | (ulong)data[47];
|
||||
|
||||
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
|
||||
SyncedTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
|
||||
|
||||
_waiter.Set();
|
||||
}
|
||||
|
||||
public void GetNetworkTime()
|
||||
{
|
||||
if (SyncedTime != null)
|
||||
return;
|
||||
|
||||
var ntpData = new byte[48];
|
||||
//LeapIndicator = 0 (no warning)
|
||||
//VersionNum = 3
|
||||
//Mode = 3 (Client Mode)
|
||||
ntpData[0] = 0x1B;
|
||||
|
||||
//send
|
||||
int errorCode = 0;
|
||||
_socket.SendTo(ntpData, 0, ntpData.Length, _ntpEndPoint, ref errorCode);
|
||||
|
||||
if(errorCode == 0)
|
||||
_waiter.WaitOne(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91e481bf657774228854ba5923470b26
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,375 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class ReliableChannel
|
||||
{
|
||||
private class PendingPacket
|
||||
{
|
||||
public NetPacket Packet;
|
||||
public DateTime? TimeStamp;
|
||||
|
||||
public NetPacket GetAndClear()
|
||||
{
|
||||
var packet = Packet;
|
||||
Packet = null;
|
||||
TimeStamp = null;
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly bool[] _outgoingAcks; //for send acks
|
||||
private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates
|
||||
private readonly NetPacket[] _receivedPackets; //for order
|
||||
private readonly bool[] _earlyReceived; //for unordered
|
||||
|
||||
private int _localSeqence;
|
||||
private int _remoteSequence;
|
||||
private int _localWindowStart;
|
||||
private int _remoteWindowStart;
|
||||
|
||||
private readonly NetPeer _peer;
|
||||
private bool _mustSendAcks;
|
||||
|
||||
private readonly bool _ordered;
|
||||
private readonly int _windowSize;
|
||||
private const int BitsInByte = 8;
|
||||
|
||||
private int _queueIndex;
|
||||
|
||||
public int PacketsInQueue
|
||||
{
|
||||
get { return _outgoingPackets.Count; }
|
||||
}
|
||||
|
||||
public ReliableChannel(NetPeer peer, bool ordered, int windowSize)
|
||||
{
|
||||
_windowSize = windowSize;
|
||||
_peer = peer;
|
||||
_ordered = ordered;
|
||||
|
||||
_outgoingPackets = new Queue<NetPacket>(_windowSize);
|
||||
|
||||
_outgoingAcks = new bool[_windowSize];
|
||||
_pendingPackets = new PendingPacket[_windowSize];
|
||||
for (int i = 0; i < _pendingPackets.Length; i++)
|
||||
{
|
||||
_pendingPackets[i] = new PendingPacket();
|
||||
}
|
||||
|
||||
if (_ordered)
|
||||
_receivedPackets = new NetPacket[_windowSize];
|
||||
else
|
||||
_earlyReceived = new bool[_windowSize];
|
||||
|
||||
_localWindowStart = 0;
|
||||
_localSeqence = 0;
|
||||
_remoteSequence = 0;
|
||||
_remoteWindowStart = 0;
|
||||
}
|
||||
|
||||
//ProcessAck in packet
|
||||
public void ProcessAck(NetPacket packet)
|
||||
{
|
||||
int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize;
|
||||
if (packet.Size != validPacketSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Invalid acks packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
ushort ackWindowStart = packet.Sequence;
|
||||
if (ackWindowStart > NetConstants.MaxSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Bad window start");
|
||||
return;
|
||||
}
|
||||
|
||||
//check relevance
|
||||
if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Old acks");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] acksData = packet.RawData;
|
||||
NetUtils.DebugWrite("[PA]AcksStart: {0}", ackWindowStart);
|
||||
int startByte = NetConstants.SequencedHeaderSize;
|
||||
|
||||
Monitor.Enter(_pendingPackets);
|
||||
for (int i = 0; i < _windowSize; i++)
|
||||
{
|
||||
int ackSequence = (ackWindowStart + i) % NetConstants.MaxSequence;
|
||||
if (NetUtils.RelativeSequenceNumber(ackSequence, _localWindowStart) < 0)
|
||||
{
|
||||
//NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP OLD: " + ackSequence);
|
||||
//Skip old ack
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentByte = startByte + i / BitsInByte;
|
||||
int currentBit = i % BitsInByte;
|
||||
|
||||
if ((acksData[currentByte] & (1 << currentBit)) == 0)
|
||||
{
|
||||
//NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP FALSE: " + ackSequence);
|
||||
//Skip false ack
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ackSequence == _localWindowStart)
|
||||
{
|
||||
//Move window
|
||||
_localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
|
||||
NetPacket removed = _pendingPackets[ackSequence % _windowSize].GetAndClear();
|
||||
if (removed != null)
|
||||
{
|
||||
_peer.Recycle(removed);
|
||||
|
||||
NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", ackSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - false", ackSequence);
|
||||
}
|
||||
}
|
||||
Monitor.Exit(_pendingPackets);
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessQueuedPackets()
|
||||
{
|
||||
//get packets from queue
|
||||
while (_outgoingPackets.Count > 0)
|
||||
{
|
||||
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart);
|
||||
if (relate < _windowSize)
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
packet.Sequence = (ushort)_localSeqence;
|
||||
_pendingPackets[_localSeqence % _windowSize].Packet = packet;
|
||||
_localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
else //Queue filled
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
//check sending acks
|
||||
DateTime currentTime = DateTime.UtcNow;
|
||||
|
||||
Monitor.Enter(_pendingPackets);
|
||||
ProcessQueuedPackets();
|
||||
|
||||
//send
|
||||
PendingPacket currentPacket;
|
||||
bool packetFound = false;
|
||||
int startQueueIndex = _queueIndex;
|
||||
do
|
||||
{
|
||||
currentPacket = _pendingPackets[_queueIndex];
|
||||
if (currentPacket.Packet != null)
|
||||
{
|
||||
//check send time
|
||||
if(currentPacket.TimeStamp.HasValue)
|
||||
{
|
||||
double packetHoldTime = (currentTime - currentPacket.TimeStamp.Value).TotalMilliseconds;
|
||||
if (packetHoldTime > _peer.ResendDelay)
|
||||
{
|
||||
NetUtils.DebugWrite("[RC]Resend: {0} > {1}", (int)packetHoldTime, _peer.ResendDelay);
|
||||
packetFound = true;
|
||||
}
|
||||
}
|
||||
else //Never sended
|
||||
{
|
||||
packetFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
_queueIndex = (_queueIndex + 1) % _windowSize;
|
||||
} while (!packetFound && _queueIndex != startQueueIndex);
|
||||
|
||||
if (packetFound)
|
||||
{
|
||||
currentPacket.TimeStamp = DateTime.UtcNow;
|
||||
_peer.SendRawData(currentPacket.Packet);
|
||||
NetUtils.DebugWrite("[RR]Sended");
|
||||
}
|
||||
Monitor.Exit(_pendingPackets);
|
||||
return packetFound;
|
||||
}
|
||||
|
||||
public void SendAcks()
|
||||
{
|
||||
if (!_mustSendAcks)
|
||||
return;
|
||||
_mustSendAcks = false;
|
||||
|
||||
NetUtils.DebugWrite("[RR]SendAcks");
|
||||
|
||||
//Init packet
|
||||
int bytesCount = (_windowSize - 1) / BitsInByte + 1;
|
||||
PacketProperty property = _ordered ? PacketProperty.AckReliableOrdered : PacketProperty.AckReliable;
|
||||
var acksPacket = _peer.GetPacketFromPool(property, bytesCount);
|
||||
|
||||
//For quick access
|
||||
byte[] data = acksPacket.RawData; //window start + acks size
|
||||
|
||||
//Put window start
|
||||
Monitor.Enter(_outgoingAcks);
|
||||
acksPacket.Sequence = (ushort)_remoteWindowStart;
|
||||
|
||||
//Put acks
|
||||
int startAckIndex = _remoteWindowStart % _windowSize;
|
||||
int currentAckIndex = startAckIndex;
|
||||
int currentBit = 0;
|
||||
int currentByte = NetConstants.SequencedHeaderSize;
|
||||
do
|
||||
{
|
||||
if (_outgoingAcks[currentAckIndex])
|
||||
{
|
||||
data[currentByte] |= (byte)(1 << currentBit);
|
||||
}
|
||||
|
||||
currentBit++;
|
||||
if (currentBit == BitsInByte)
|
||||
{
|
||||
currentByte++;
|
||||
currentBit = 0;
|
||||
}
|
||||
currentAckIndex = (currentAckIndex + 1) % _windowSize;
|
||||
} while (currentAckIndex != startAckIndex);
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
|
||||
_peer.SendRawData(acksPacket);
|
||||
_peer.Recycle(acksPacket);
|
||||
}
|
||||
|
||||
//Process incoming packet
|
||||
public void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
if (packet.Sequence >= NetConstants.MaxSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]Bad sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
int relate = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteWindowStart);
|
||||
int relateSeq = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence);
|
||||
|
||||
if (relateSeq > _windowSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]Bad sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
//Drop bad packets
|
||||
if(relate < 0)
|
||||
{
|
||||
//Too old packet doesn't ack
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder too old");
|
||||
return;
|
||||
}
|
||||
if (relate >= _windowSize * 2)
|
||||
{
|
||||
//Some very new packet
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder too new");
|
||||
return;
|
||||
}
|
||||
|
||||
//If very new - move window
|
||||
Monitor.Enter(_outgoingAcks);
|
||||
if (relate >= _windowSize)
|
||||
{
|
||||
//New window position
|
||||
int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence;
|
||||
|
||||
//Clean old data
|
||||
while (_remoteWindowStart != newWindowStart)
|
||||
{
|
||||
_outgoingAcks[_remoteWindowStart % _windowSize] = false;
|
||||
_remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
|
||||
//Final stage - process valid packet
|
||||
//trigger acks send
|
||||
_mustSendAcks = true;
|
||||
|
||||
if (_outgoingAcks[packet.Sequence % _windowSize])
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder duplicate");
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
return;
|
||||
}
|
||||
|
||||
//save ack
|
||||
_outgoingAcks[packet.Sequence % _windowSize] = true;
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
|
||||
//detailed check
|
||||
if (packet.Sequence == _remoteSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder packet succes");
|
||||
_peer.AddIncomingPacket(packet);
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
|
||||
if (_ordered)
|
||||
{
|
||||
NetPacket p;
|
||||
while ( (p = _receivedPackets[_remoteSequence % _windowSize]) != null)
|
||||
{
|
||||
//process holded packet
|
||||
_receivedPackets[_remoteSequence % _windowSize] = null;
|
||||
_peer.AddIncomingPacket(p);
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_earlyReceived[_remoteSequence % _windowSize])
|
||||
{
|
||||
//process early packet
|
||||
_earlyReceived[_remoteSequence % _windowSize] = false;
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//holded packet
|
||||
if (_ordered)
|
||||
{
|
||||
_receivedPackets[packet.Sequence % _windowSize] = packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_earlyReceived[packet.Sequence % _windowSize] = true;
|
||||
_peer.AddIncomingPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 516bd901eb62f4bb391ef541f9effdec
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,4 @@
|
||||
for file in *.cs; do
|
||||
echo "#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA" | cat - $file > tempfile && mv tempfile $file
|
||||
echo '#endif' >> "$file"
|
||||
done
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f1f24482ff84a25a1188dd49ec4d86
|
||||
timeCreated: 1498001160
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class SequencedChannel
|
||||
{
|
||||
private ushort _localSequence;
|
||||
private ushort _remoteSequence;
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly NetPeer _peer;
|
||||
|
||||
public SequencedChannel(NetPeer peer)
|
||||
{
|
||||
_outgoingPackets = new Queue<NetPacket>();
|
||||
_peer = peer;
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
if (_outgoingPackets.Count == 0)
|
||||
return false;
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
_localSequence++;
|
||||
packet.Sequence = _localSequence;
|
||||
_peer.SendRawData(packet);
|
||||
_peer.Recycle(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence) > 0)
|
||||
{
|
||||
_remoteSequence = packet.Sequence;
|
||||
_peer.AddIncomingPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84bbbc6c6e45c4287917fc944650e4af
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class SimpleChannel
|
||||
{
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly NetPeer _peer;
|
||||
|
||||
public SimpleChannel(NetPeer peer)
|
||||
{
|
||||
_outgoingPackets = new Queue<NetPacket>();
|
||||
_peer = peer;
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
if (_outgoingPackets.Count == 0)
|
||||
return false;
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
_peer.SendRawData(packet);
|
||||
_peer.Recycle(packet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b4cdb673a0094d438a802844a8cc794
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90979fd73276eab448e2785362667c6a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74883e5d446142e4f8d83cafa8d77f35
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a50bd9a009c8dfc4ebd88cc8101225a7
|
||||
labels:
|
||||
- Tween
|
||||
- Tweening
|
||||
- Animation
|
||||
- HOTween
|
||||
- Paths
|
||||
- iTween
|
||||
- DFTween
|
||||
- LeanTween
|
||||
- Ease
|
||||
- Easing
|
||||
- Shake
|
||||
- Punch
|
||||
- 2DToolkit
|
||||
- TextMeshPro
|
||||
- Text
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34192c5e0d14aee43a0e86cc4823268a
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f007001a22b3d24dae350342c4d19c8
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a811bde74b26b53498b4f6d872b09b6d
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b27f58ae5d5c33a4bb2d1f4f34bd036d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>DOTweenEditor</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Start(System.Action)">
|
||||
<summary>
|
||||
Starts the update loop of tween in the editor. Has no effect during playMode.
|
||||
</summary>
|
||||
<param name="onPreviewUpdated">Eventual callback to call after every update</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Stop(System.Boolean)">
|
||||
<summary>
|
||||
Stops the update loop and clears the onPreviewUpdated callback.
|
||||
</summary>
|
||||
<param name="resetTweenTargets">If TRUE also resets the tweened objects to their original state</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.PrepareTweenForPreview(DG.Tweening.Tween,System.Boolean,System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Readies the tween for editor preview by setting its UpdateType to Manual plus eventual extra settings.
|
||||
</summary>
|
||||
<param name="t">The tween to ready</param>
|
||||
<param name="clearCallbacks">If TRUE (recommended) removes all callbacks (OnComplete/Rewind/etc)</param>
|
||||
<param name="preventAutoKill">If TRUE prevents the tween from being auto-killed at completion</param>
|
||||
<param name="andPlay">If TRUE starts playing the tween immediately</param>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.Version">
|
||||
<summary>Full major version + first minor version (ex: 2018.1f)</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.MajorVersion">
|
||||
<summary>Major version</summary>
|
||||
</member>
|
||||
<member name="F:DG.DOTweenEditor.EditorVersion.MinorVersion">
|
||||
<summary>First minor version (ex: in 2018.1 it would be 1)</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.SetEditorTexture(UnityEngine.Texture2D,UnityEngine.FilterMode,System.Int32)">
|
||||
<summary>
|
||||
Checks that the given editor texture use the correct import settings,
|
||||
and applies them if they're incorrect.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.DOTweenSetupRequired">
|
||||
<summary>
|
||||
Returns TRUE if setup is required
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.AssetExists(System.String)">
|
||||
<summary>
|
||||
Returns TRUE if the file/directory at the given path exists.
|
||||
</summary>
|
||||
<param name="adbPath">Path, relative to Unity's project folder</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.ADBPathToFullPath(System.String)">
|
||||
<summary>
|
||||
Converts the given project-relative path to a full path,
|
||||
with backward (\) slashes).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.FullPathToADBPath(System.String)">
|
||||
<summary>
|
||||
Converts the given full path to a path usable with AssetDatabase methods
|
||||
(relative to Unity's project folder, and with the correct Unity forward (/) slashes).
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.ConnectToSourceAsset``1(System.String,System.Boolean)">
|
||||
<summary>
|
||||
Connects to a <see cref="T:UnityEngine.ScriptableObject"/> asset.
|
||||
If the asset already exists at the given path, loads it and returns it.
|
||||
Otherwise, either returns NULL or automatically creates it before loading and returning it
|
||||
(depending on the given parameters).
|
||||
</summary>
|
||||
<typeparam name="T">Asset type</typeparam>
|
||||
<param name="adbFilePath">File path (relative to Unity's project folder)</param>
|
||||
<param name="createIfMissing">If TRUE and the requested asset doesn't exist, forces its creation</param>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.GetAssemblyFilePath(System.Reflection.Assembly)">
|
||||
<summary>
|
||||
Full path for the given loaded assembly, assembly file included
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.AddGlobalDefine(System.String)">
|
||||
<summary>
|
||||
Adds the given global define if it's not already present
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.RemoveGlobalDefine(System.String)">
|
||||
<summary>
|
||||
Removes the given global define if it's present
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DG.DOTweenEditor.EditorUtils.HasGlobalDefine(System.String,System.Nullable{UnityEditor.BuildTargetGroup})">
|
||||
<summary>
|
||||
Returns TRUE if the given global define is present in all the <see cref="T:UnityEditor.BuildTargetGroup"/>
|
||||
or only in the given <see cref="T:UnityEditor.BuildTargetGroup"/>, depending on passed parameters.<para/>
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<param name="buildTargetGroup"><see cref="T:UnityEditor.BuildTargetGroup"/>to use. Leave NULL to check in all of them.</param>
|
||||
</member>
|
||||
<member name="T:DG.DOTweenEditor.DOTweenDefines">
|
||||
<summary>
|
||||
Not used as menu item anymore, but as a utiity function
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e2c6224d345d9249acfa6e8ef40bb2d
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f46310a8b0a8f04a92993c37c713243
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45d5034162d6cf04dbe46da84fc7d074
|
||||
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:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0034ebae0c2a9344e897db1160d71b6d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,47 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8da095e39e9b4df488dfd436f81116d6
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
linearTexture: 1
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -3
|
||||
maxTextureSize: 128
|
||||
textureSettings:
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 1
|
||||
textureType: 2
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,47 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7051dba417b3d53409f2918f1ea4938d
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
linearTexture: 1
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -3
|
||||
maxTextureSize: 256
|
||||
textureSettings:
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 1
|
||||
textureType: 2
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,47 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 519694efe2bb2914788b151fbd8c01f4
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
linearTexture: 0
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -1
|
||||
maxTextureSize: 1024
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 0
|
||||
textureType: -1
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,47 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78a59ca99f8987941adb61f9e14a06a7
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
linearTexture: 1
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -3
|
||||
maxTextureSize: 512
|
||||
textureSettings:
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 1
|
||||
textureType: 2
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 143604b8bad857d47a6f7cc7a533e2dc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "DOTween.Modules"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d85850b2bd2239408107d29ad53c78e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,202 @@
|
||||
// Author: Daniele Giardini - http://www.demigiant.com
|
||||
// Created: 2018/07/13
|
||||
|
||||
#if true // MODULE_MARKER
|
||||
using System;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
using UnityEngine;
|
||||
#if UNITY_5 || UNITY_2017_1_OR_NEWER
|
||||
using UnityEngine.Audio; // Required for AudioMixer
|
||||
#endif
|
||||
|
||||
#pragma warning disable 1591
|
||||
namespace DG.Tweening
|
||||
{
|
||||
public static class DOTweenModuleAudio
|
||||
{
|
||||
#region Shortcuts
|
||||
|
||||
#region Audio
|
||||
|
||||
/// <summary>Tweens an AudioSource's volume to the given value.
|
||||
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOFade(this AudioSource target, float endValue, float duration)
|
||||
{
|
||||
if (endValue < 0) endValue = 0;
|
||||
else if (endValue > 1) endValue = 1;
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.volume, x => target.volume = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an AudioSource's pitch to the given value.
|
||||
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOPitch(this AudioSource target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.pitch, x => target.pitch = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#if UNITY_5 || UNITY_2017_1_OR_NEWER
|
||||
#region AudioMixer (Unity 5 or Newer)
|
||||
|
||||
/// <summary>Tweens an AudioMixer's exposed float to the given value.
|
||||
/// Also stores the AudioMixer as the tween's target so it can be used for filtered operations.
|
||||
/// Note that you need to manually expose a float in an AudioMixerGroup in order to be able to tween it from an AudioMixer.</summary>
|
||||
/// <param name="floatName">Name given to the exposed float to set</param>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOSetFloat(this AudioMixer target, string floatName, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(()=> {
|
||||
float currVal;
|
||||
target.GetFloat(floatName, out currVal);
|
||||
return currVal;
|
||||
}, x=> target.SetFloat(floatName, x), endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#region Operation Shortcuts
|
||||
|
||||
/// <summary>
|
||||
/// Completes all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens completed
|
||||
/// (meaning the tweens that don't have infinite loops and were not already complete)
|
||||
/// </summary>
|
||||
/// <param name="withCallbacks">For Sequences only: if TRUE also internal Sequence callbacks will be fired,
|
||||
/// otherwise they will be ignored</param>
|
||||
public static int DOComplete(this AudioMixer target, bool withCallbacks = false)
|
||||
{
|
||||
return DOTween.Complete(target, withCallbacks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens killed.
|
||||
/// </summary>
|
||||
/// <param name="complete">If TRUE completes the tween before killing it</param>
|
||||
public static int DOKill(this AudioMixer target, bool complete = false)
|
||||
{
|
||||
return DOTween.Kill(target, complete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flips the direction (backwards if it was going forward or viceversa) of all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens flipped.
|
||||
/// </summary>
|
||||
public static int DOFlip(this AudioMixer target)
|
||||
{
|
||||
return DOTween.Flip(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends to the given position all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens involved.
|
||||
/// </summary>
|
||||
/// <param name="to">Time position to reach
|
||||
/// (if higher than the whole tween duration the tween will simply reach its end)</param>
|
||||
/// <param name="andPlay">If TRUE will play the tween after reaching the given position, otherwise it will pause it</param>
|
||||
public static int DOGoto(this AudioMixer target, float to, bool andPlay = false)
|
||||
{
|
||||
return DOTween.Goto(target, to, andPlay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens paused.
|
||||
/// </summary>
|
||||
public static int DOPause(this AudioMixer target)
|
||||
{
|
||||
return DOTween.Pause(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens played.
|
||||
/// </summary>
|
||||
public static int DOPlay(this AudioMixer target)
|
||||
{
|
||||
return DOTween.Play(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays backwards all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens played.
|
||||
/// </summary>
|
||||
public static int DOPlayBackwards(this AudioMixer target)
|
||||
{
|
||||
return DOTween.PlayBackwards(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays forward all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens played.
|
||||
/// </summary>
|
||||
public static int DOPlayForward(this AudioMixer target)
|
||||
{
|
||||
return DOTween.PlayForward(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens restarted.
|
||||
/// </summary>
|
||||
public static int DORestart(this AudioMixer target)
|
||||
{
|
||||
return DOTween.Restart(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewinds all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens rewinded.
|
||||
/// </summary>
|
||||
public static int DORewind(this AudioMixer target)
|
||||
{
|
||||
return DOTween.Rewind(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly rewinds all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens rewinded.
|
||||
/// </summary>
|
||||
public static int DOSmoothRewind(this AudioMixer target)
|
||||
{
|
||||
return DOTween.SmoothRewind(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the paused state (plays if it was paused, pauses if it was playing) of all tweens that have this target as a reference
|
||||
/// (meaning tweens that were started from this target, or that had this target added as an Id)
|
||||
/// and returns the total number of tweens involved.
|
||||
/// </summary>
|
||||
public static int DOTogglePause(this AudioMixer target)
|
||||
{
|
||||
return DOTween.TogglePause(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b766d08851589514b97afb23c6f30a70
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,216 @@
|
||||
// Author: Daniele Giardini - http://www.demigiant.com
|
||||
// Created: 2018/07/13
|
||||
|
||||
#if true // MODULE_MARKER
|
||||
using System;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Core.Enums;
|
||||
using DG.Tweening.Plugins;
|
||||
using DG.Tweening.Plugins.Core.PathCore;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 1591
|
||||
namespace DG.Tweening
|
||||
{
|
||||
public static class DOTweenModulePhysics
|
||||
{
|
||||
#region Shortcuts
|
||||
|
||||
#region Rigidbody
|
||||
|
||||
/// <summary>Tweens a Rigidbody's position to the given value.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMove(this Rigidbody target, Vector3 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's X position to the given value.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveX(this Rigidbody target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue, 0, 0), duration);
|
||||
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's Y position to the given value.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveY(this Rigidbody target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, endValue, 0), duration);
|
||||
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's Z position to the given value.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveZ(this Rigidbody target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue), duration);
|
||||
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's rotation to the given value.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="mode">Rotation mode</param>
|
||||
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DORotate(this Rigidbody target, Vector3 endValue, float duration, RotateMode mode = RotateMode.Fast)
|
||||
{
|
||||
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
t.plugOptions.rotateMode = mode;
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's rotation so that it will look towards the given position.
|
||||
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="towards">The position to look at</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="axisConstraint">Eventual axis constraint for the rotation</param>
|
||||
/// <param name="up">The vector that defines in which direction up is (default: Vector3.up)</param>
|
||||
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DOLookAt(this Rigidbody target, Vector3 towards, float duration, AxisConstraint axisConstraint = AxisConstraint.None, Vector3? up = null)
|
||||
{
|
||||
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, towards, duration)
|
||||
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetLookAt);
|
||||
t.plugOptions.axisConstraint = axisConstraint;
|
||||
t.plugOptions.up = (up == null) ? Vector3.up : (Vector3)up;
|
||||
return t;
|
||||
}
|
||||
|
||||
#region Special
|
||||
|
||||
/// <summary>Tweens a Rigidbody's position to the given value, while also applying a jump effect along the Y axis.
|
||||
/// Returns a Sequence instead of a Tweener.
|
||||
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param>
|
||||
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
|
||||
/// <param name="numJumps">Total number of jumps</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Sequence DOJump(this Rigidbody target, Vector3 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
|
||||
{
|
||||
if (numJumps < 1) numJumps = 1;
|
||||
float startPosY = 0;
|
||||
float offsetY = -1;
|
||||
bool offsetYSet = false;
|
||||
Sequence s = DOTween.Sequence();
|
||||
Tween yTween = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, jumpPower, 0), duration / (numJumps * 2))
|
||||
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
|
||||
.SetLoops(numJumps * 2, LoopType.Yoyo)
|
||||
.OnStart(() => startPosY = target.position.y);
|
||||
s.Append(DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue.x, 0, 0), duration)
|
||||
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
|
||||
).Join(DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue.z), duration)
|
||||
.SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear)
|
||||
).Join(yTween)
|
||||
.SetTarget(target).SetEase(DOTween.defaultEaseType);
|
||||
yTween.OnUpdate(() => {
|
||||
if (!offsetYSet) {
|
||||
offsetYSet = true;
|
||||
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
|
||||
}
|
||||
Vector3 pos = target.position;
|
||||
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
|
||||
target.MovePosition(pos);
|
||||
});
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody's position through the given path waypoints, using the chosen path algorithm.
|
||||
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations.
|
||||
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
|
||||
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
|
||||
/// If you plan to publish there you should use a regular transform.DOPath.</para></summary>
|
||||
/// <param name="path">The waypoints to go through</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
|
||||
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
|
||||
/// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
|
||||
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
|
||||
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
|
||||
public static TweenerCore<Vector3, Path, PathOptions> DOPath(
|
||||
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
|
||||
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
|
||||
)
|
||||
{
|
||||
if (resolution < 1) resolution = 1;
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, new Path(pathType, path, resolution, gizmoColor), duration)
|
||||
.SetTarget(target).SetUpdate(UpdateType.Fixed);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a Rigidbody's localPosition through the given path waypoints, using the chosen path algorithm.
|
||||
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations
|
||||
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
|
||||
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
|
||||
/// If you plan to publish there you should use a regular transform.DOLocalPath.</para></summary>
|
||||
/// <param name="path">The waypoint to go through</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
|
||||
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
|
||||
/// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
|
||||
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
|
||||
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
|
||||
public static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
|
||||
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
|
||||
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
|
||||
)
|
||||
{
|
||||
if (resolution < 1) resolution = 1;
|
||||
Transform trans = target.transform;
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), new Path(pathType, path, resolution, gizmoColor), duration)
|
||||
.SetTarget(target).SetUpdate(UpdateType.Fixed);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
t.plugOptions.useLocalPosition = true;
|
||||
return t;
|
||||
}
|
||||
// Used by path editor when creating the actual tween, so it can pass a pre-compiled path
|
||||
internal static TweenerCore<Vector3, Path, PathOptions> DOPath(
|
||||
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
|
||||
)
|
||||
{
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, path, duration)
|
||||
.SetTarget(target);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
return t;
|
||||
}
|
||||
internal static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
|
||||
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
|
||||
)
|
||||
{
|
||||
Transform trans = target.transform;
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), path, duration)
|
||||
.SetTarget(target);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
t.plugOptions.useLocalPosition = true;
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae9aa560b4242648a3affa2bfabc365
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,168 @@
|
||||
// Author: Daniele Giardini - http://www.demigiant.com
|
||||
// Created: 2018/07/13
|
||||
|
||||
#if true && (UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
|
||||
using System;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Plugins;
|
||||
using DG.Tweening.Plugins.Core.PathCore;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable 1591
|
||||
namespace DG.Tweening
|
||||
{
|
||||
public static class DOTweenModulePhysics2D
|
||||
{
|
||||
#region Shortcuts
|
||||
|
||||
#region Rigidbody2D Shortcuts
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's position to the given value.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMove(this Rigidbody2D target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's X position to the given value.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveX(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(endValue, 0), duration);
|
||||
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's Y position to the given value.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveY(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(0, endValue), duration);
|
||||
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's rotation to the given value.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DORotate(this Rigidbody2D target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#region Special
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's position to the given value, while also applying a jump effect along the Y axis.
|
||||
/// Returns a Sequence instead of a Tweener.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations.
|
||||
/// <para>IMPORTANT: a rigidbody2D can't be animated in a jump arc using MovePosition, so the tween will directly set the position</para></summary>
|
||||
/// <param name="endValue">The end value to reach</param>
|
||||
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
|
||||
/// <param name="numJumps">Total number of jumps</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Sequence DOJump(this Rigidbody2D target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
|
||||
{
|
||||
if (numJumps < 1) numJumps = 1;
|
||||
float startPosY = 0;
|
||||
float offsetY = -1;
|
||||
bool offsetYSet = false;
|
||||
Sequence s = DOTween.Sequence();
|
||||
Tween yTween = DOTween.To(() => target.position, x => target.position = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
|
||||
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
|
||||
.SetLoops(numJumps * 2, LoopType.Yoyo)
|
||||
.OnStart(() => startPosY = target.position.y);
|
||||
s.Append(DOTween.To(() => target.position, x => target.position = x, new Vector2(endValue.x, 0), duration)
|
||||
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
|
||||
).Join(yTween)
|
||||
.SetTarget(target).SetEase(DOTween.defaultEaseType);
|
||||
yTween.OnUpdate(() => {
|
||||
if (!offsetYSet) {
|
||||
offsetYSet = true;
|
||||
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
|
||||
}
|
||||
Vector3 pos = target.position;
|
||||
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
|
||||
target.MovePosition(pos);
|
||||
});
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Rigidbody2D's position through the given path waypoints, using the chosen path algorithm.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations.
|
||||
/// <para>NOTE: to tween a Rigidbody2D correctly it should be set to kinematic at least while being tweened.</para>
|
||||
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
|
||||
/// If you plan to publish there you should use a regular transform.DOPath.</para></summary>
|
||||
/// <param name="path">The waypoints to go through</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
|
||||
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
|
||||
/// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
|
||||
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
|
||||
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
|
||||
public static TweenerCore<Vector3, Path, PathOptions> DOPath(
|
||||
this Rigidbody2D target, Vector2[] path, float duration, PathType pathType = PathType.Linear,
|
||||
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
|
||||
)
|
||||
{
|
||||
if (resolution < 1) resolution = 1;
|
||||
int len = path.Length;
|
||||
Vector3[] path3D = new Vector3[len];
|
||||
for (int i = 0; i < len; ++i) path3D[i] = path[i];
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, x => target.MovePosition(x), new Path(pathType, path3D, resolution, gizmoColor), duration)
|
||||
.SetTarget(target).SetUpdate(UpdateType.Fixed);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a Rigidbody2D's localPosition through the given path waypoints, using the chosen path algorithm.
|
||||
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations
|
||||
/// <para>NOTE: to tween a Rigidbody2D correctly it should be set to kinematic at least while being tweened.</para>
|
||||
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
|
||||
/// If you plan to publish there you should use a regular transform.DOLocalPath.</para></summary>
|
||||
/// <param name="path">The waypoint to go through</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
|
||||
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
|
||||
/// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
|
||||
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
|
||||
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
|
||||
public static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
|
||||
this Rigidbody2D target, Vector2[] path, float duration, PathType pathType = PathType.Linear,
|
||||
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
|
||||
)
|
||||
{
|
||||
if (resolution < 1) resolution = 1;
|
||||
int len = path.Length;
|
||||
Vector3[] path3D = new Vector3[len];
|
||||
for (int i = 0; i < len; ++i) path3D[i] = path[i];
|
||||
Transform trans = target.transform;
|
||||
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), new Path(pathType, path3D, resolution, gizmoColor), duration)
|
||||
.SetTarget(target).SetUpdate(UpdateType.Fixed);
|
||||
|
||||
t.plugOptions.isRigidbody = true;
|
||||
t.plugOptions.mode = pathMode;
|
||||
t.plugOptions.useLocalPosition = true;
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 230fe34542e175245ba74b4659dae700
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,93 @@
|
||||
// Author: Daniele Giardini - http://www.demigiant.com
|
||||
// Created: 2018/07/13
|
||||
|
||||
#if true && (UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
|
||||
#pragma warning disable 1591
|
||||
namespace DG.Tweening
|
||||
{
|
||||
public static class DOTweenModuleSprite
|
||||
{
|
||||
#region Shortcuts
|
||||
|
||||
#region SpriteRenderer
|
||||
|
||||
/// <summary>Tweens a SpriteRenderer's color to the given value.
|
||||
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOColor(this SpriteRenderer target, Color endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Material's alpha color to the given value.
|
||||
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOFade(this SpriteRenderer target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a SpriteRenderer's color using the given gradient
|
||||
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
|
||||
public static Sequence DOGradientColor(this SpriteRenderer target, Gradient gradient, float duration)
|
||||
{
|
||||
Sequence s = DOTween.Sequence();
|
||||
GradientColorKey[] colors = gradient.colorKeys;
|
||||
int len = colors.Length;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
GradientColorKey c = colors[i];
|
||||
if (i == 0 && c.time <= 0) {
|
||||
target.color = c.color;
|
||||
continue;
|
||||
}
|
||||
float colorDuration = i == len - 1
|
||||
? duration - s.Duration(false) // Verifies that total duration is correct
|
||||
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
|
||||
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
|
||||
}
|
||||
s.SetTarget(target);
|
||||
return s;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blendables
|
||||
|
||||
#region SpriteRenderer
|
||||
|
||||
/// <summary>Tweens a SpriteRenderer's color to the given value,
|
||||
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
|
||||
/// instead than fight each other as multiple DOColor would do.
|
||||
/// Also stores the SpriteRenderer as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
|
||||
public static Tweener DOBlendableColor(this SpriteRenderer target, Color endValue, float duration)
|
||||
{
|
||||
endValue = endValue - target.color;
|
||||
Color to = new Color(0, 0, 0, 0);
|
||||
return DOTween.To(() => to, x => {
|
||||
Color diff = x - to;
|
||||
to = x;
|
||||
target.color += diff;
|
||||
}, endValue, duration)
|
||||
.Blendable().SetTarget(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 188918ab119d93148aa0de59ccf5286b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,634 @@
|
||||
// Author: Daniele Giardini - http://www.demigiant.com
|
||||
// Created: 2018/07/13
|
||||
|
||||
#if true && (UNITY_4_6 || UNITY_5 || UNITY_2017_1_OR_NEWER) // MODULE_MARKER
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using DG.Tweening.Core;
|
||||
using DG.Tweening.Core.Enums;
|
||||
using DG.Tweening.Plugins.Options;
|
||||
|
||||
#pragma warning disable 1591
|
||||
namespace DG.Tweening
|
||||
{
|
||||
public static class DOTweenModuleUI
|
||||
{
|
||||
#region Shortcuts
|
||||
|
||||
#region CanvasGroup
|
||||
|
||||
/// <summary>Tweens a CanvasGroup's alpha color to the given value.
|
||||
/// Also stores the canvasGroup as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOFade(this CanvasGroup target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.alpha, x => target.alpha = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Graphic
|
||||
|
||||
/// <summary>Tweens an Graphic's color to the given value.
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Graphic target, Color endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an Graphic's alpha color to the given value.
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Graphic target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Image
|
||||
|
||||
/// <summary>Tweens an Image's color to the given value.
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Image target, Color endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an Image's alpha color to the given value.
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Image target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an Image's fillAmount to the given value.
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOFillAmount(this Image target, float endValue, float duration)
|
||||
{
|
||||
if (endValue > 1) endValue = 1;
|
||||
else if (endValue < 0) endValue = 0;
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.fillAmount, x => target.fillAmount = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an Image's colors using the given gradient
|
||||
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
|
||||
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
|
||||
public static Sequence DOGradientColor(this Image target, Gradient gradient, float duration)
|
||||
{
|
||||
Sequence s = DOTween.Sequence();
|
||||
GradientColorKey[] colors = gradient.colorKeys;
|
||||
int len = colors.Length;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
GradientColorKey c = colors[i];
|
||||
if (i == 0 && c.time <= 0) {
|
||||
target.color = c.color;
|
||||
continue;
|
||||
}
|
||||
float colorDuration = i == len - 1
|
||||
? duration - s.Duration(false) // Verifies that total duration is correct
|
||||
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
|
||||
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
|
||||
}
|
||||
s.SetTarget(target);
|
||||
return s;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LayoutElement
|
||||
|
||||
/// <summary>Tweens an LayoutElement's flexibleWidth/Height to the given value.
|
||||
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOFlexibleSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.flexibleWidth, target.flexibleHeight), x => {
|
||||
target.flexibleWidth = x.x;
|
||||
target.flexibleHeight = x.y;
|
||||
}, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an LayoutElement's minWidth/Height to the given value.
|
||||
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMinSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.minWidth, target.minHeight), x => {
|
||||
target.minWidth = x.x;
|
||||
target.minHeight = x.y;
|
||||
}, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens an LayoutElement's preferredWidth/Height to the given value.
|
||||
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPreferredSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.preferredWidth, target.preferredHeight), x => {
|
||||
target.preferredWidth = x.x;
|
||||
target.preferredHeight = x.y;
|
||||
}, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Outline
|
||||
|
||||
/// <summary>Tweens a Outline's effectColor to the given value.
|
||||
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Outline target, Color endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.effectColor, x => target.effectColor = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Outline's effectColor alpha to the given value.
|
||||
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Outline target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.effectColor, x => target.effectColor = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Outline's effectDistance to the given value.
|
||||
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOScale(this Outline target, Vector2 endValue, float duration)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.effectDistance, x => target.effectDistance = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RectTransform
|
||||
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPos(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition X to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosX(this RectTransform target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue, 0), duration);
|
||||
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition Y to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosY(this RectTransform target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, endValue), duration);
|
||||
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition3D to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3D(this RectTransform target, Vector3 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition3D X to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DX(this RectTransform target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(endValue, 0, 0), duration);
|
||||
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition3D Y to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DY(this RectTransform target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, endValue, 0), duration);
|
||||
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition3D Z to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DZ(this RectTransform target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, 0, endValue), duration);
|
||||
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a RectTransform's anchorMax to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMax(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMax, x => target.anchorMax = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a RectTransform's anchorMin to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMin(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMin, x => target.anchorMin = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a RectTransform's pivot to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivot(this RectTransform target, Vector2 endValue, float duration)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's pivot X to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotX(this RectTransform target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(endValue, 0), duration);
|
||||
t.SetOptions(AxisConstraint.X).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
/// <summary>Tweens a RectTransform's pivot Y to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotY(this RectTransform target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(0, endValue), duration);
|
||||
t.SetOptions(AxisConstraint.Y).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a RectTransform's sizeDelta to the given value.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<Vector2, Vector2, VectorOptions> DOSizeDelta(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.sizeDelta, x => target.sizeDelta = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Punches a RectTransform's anchoredPosition towards the given direction and then back to the starting one
|
||||
/// as if it was connected to the starting position via an elastic.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="punch">The direction and strength of the punch (added to the RectTransform's current position)</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="vibrato">Indicates how much will the punch vibrate</param>
|
||||
/// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting position when bouncing backwards.
|
||||
/// 1 creates a full oscillation between the punch direction and the opposite direction,
|
||||
/// while 0 oscillates only between the punch and the start position</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Tweener DOPunchAnchorPos(this RectTransform target, Vector2 punch, float duration, int vibrato = 10, float elasticity = 1, bool snapping = false)
|
||||
{
|
||||
return DOTween.Punch(() => target.anchoredPosition, x => target.anchoredPosition = x, punch, duration, vibrato, elasticity)
|
||||
.SetTarget(target).SetOptions(snapping);
|
||||
}
|
||||
|
||||
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="strength">The shake strength</param>
|
||||
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
|
||||
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
|
||||
/// Setting it to 0 will shake along a single direction.</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
|
||||
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, float strength = 100, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true)
|
||||
{
|
||||
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, true, fadeOut)
|
||||
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
|
||||
}
|
||||
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="strength">The shake strength on each axis</param>
|
||||
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
|
||||
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
|
||||
/// Setting it to 0 will shake along a single direction.</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
|
||||
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, Vector2 strength, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true)
|
||||
{
|
||||
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, fadeOut)
|
||||
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
|
||||
}
|
||||
|
||||
#region Special
|
||||
|
||||
/// <summary>Tweens a RectTransform's anchoredPosition to the given value, while also applying a jump effect along the Y axis.
|
||||
/// Returns a Sequence instead of a Tweener.
|
||||
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param>
|
||||
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
|
||||
/// <param name="numJumps">Total number of jumps</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Sequence DOJumpAnchorPos(this RectTransform target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
|
||||
{
|
||||
if (numJumps < 1) numJumps = 1;
|
||||
float startPosY = 0;
|
||||
float offsetY = -1;
|
||||
bool offsetYSet = false;
|
||||
|
||||
// Separate Y Tween so we can elaborate elapsedPercentage on that insted of on the Sequence
|
||||
// (in case users add a delay or other elements to the Sequence)
|
||||
Sequence s = DOTween.Sequence();
|
||||
Tween yTween = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
|
||||
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
|
||||
.SetLoops(numJumps * 2, LoopType.Yoyo)
|
||||
.OnStart(()=> startPosY = target.anchoredPosition.y);
|
||||
s.Append(DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue.x, 0), duration)
|
||||
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
|
||||
).Join(yTween)
|
||||
.SetTarget(target).SetEase(DOTween.defaultEaseType);
|
||||
s.OnUpdate(() => {
|
||||
if (!offsetYSet) {
|
||||
offsetYSet = true;
|
||||
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
|
||||
}
|
||||
Vector2 pos = target.anchoredPosition;
|
||||
pos.y += DOVirtual.EasedValue(0, offsetY, s.ElapsedDirectionalPercentage(), Ease.OutQuad);
|
||||
target.anchoredPosition = pos;
|
||||
});
|
||||
return s;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region ScrollRect
|
||||
|
||||
/// <summary>Tweens a ScrollRect's horizontal/verticalNormalizedPosition to the given value.
|
||||
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Tweener DONormalizedPos(this ScrollRect target, Vector2 endValue, float duration, bool snapping = false)
|
||||
{
|
||||
return DOTween.To(() => new Vector2(target.horizontalNormalizedPosition, target.verticalNormalizedPosition),
|
||||
x => {
|
||||
target.horizontalNormalizedPosition = x.x;
|
||||
target.verticalNormalizedPosition = x.y;
|
||||
}, endValue, duration)
|
||||
.SetOptions(snapping).SetTarget(target);
|
||||
}
|
||||
/// <summary>Tweens a ScrollRect's horizontalNormalizedPosition to the given value.
|
||||
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Tweener DOHorizontalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
return DOTween.To(() => target.horizontalNormalizedPosition, x => target.horizontalNormalizedPosition = x, endValue, duration)
|
||||
.SetOptions(snapping).SetTarget(target);
|
||||
}
|
||||
/// <summary>Tweens a ScrollRect's verticalNormalizedPosition to the given value.
|
||||
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static Tweener DOVerticalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
return DOTween.To(() => target.verticalNormalizedPosition, x => target.verticalNormalizedPosition = x, endValue, duration)
|
||||
.SetOptions(snapping).SetTarget(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Slider
|
||||
|
||||
/// <summary>Tweens a Slider's value to the given value.
|
||||
/// Also stores the Slider as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
|
||||
public static TweenerCore<float, float, FloatOptions> DOValue(this Slider target, float endValue, float duration, bool snapping = false)
|
||||
{
|
||||
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.value, x => target.value = x, endValue, duration);
|
||||
t.SetOptions(snapping).SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Text
|
||||
|
||||
/// <summary>Tweens a Text's color to the given value.
|
||||
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Text target, Color endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tweens a Text's text from one integer to another, with options for thousands separators
|
||||
/// </summary>
|
||||
/// <param name="fromValue">The value to start from</param>
|
||||
/// <param name="endValue">The end value to reach</param>
|
||||
/// <param name="duration">The duration of the tween</param>
|
||||
/// <param name="addThousandsSeparator">If TRUE (default) also adds thousands separators</param>
|
||||
/// <param name="culture">The <see cref="CultureInfo"/> to use (InvariantCulture if NULL)</param>
|
||||
public static TweenerCore<int, int, NoOptions> DOCounter(
|
||||
this Text target, int fromValue, int endValue, float duration, bool addThousandsSeparator = true, CultureInfo culture = null
|
||||
){
|
||||
int v = fromValue;
|
||||
CultureInfo cInfo = !addThousandsSeparator ? null : culture ?? CultureInfo.InvariantCulture;
|
||||
TweenerCore<int, int, NoOptions> t = DOTween.To(() => v, x => {
|
||||
v = x;
|
||||
target.text = addThousandsSeparator
|
||||
? v.ToString("N0", cInfo)
|
||||
: v.ToString();
|
||||
}, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Text's alpha color to the given value.
|
||||
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
|
||||
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Text target, float endValue, float duration)
|
||||
{
|
||||
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
|
||||
t.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>Tweens a Text's text to the given value.
|
||||
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The end string to tween to</param><param name="duration">The duration of the tween</param>
|
||||
/// <param name="richTextEnabled">If TRUE (default), rich text will be interpreted correctly while animated,
|
||||
/// otherwise all tags will be considered as normal text</param>
|
||||
/// <param name="scrambleMode">The type of scramble mode to use, if any</param>
|
||||
/// <param name="scrambleChars">A string containing the characters to use for scrambling.
|
||||
/// Use as many characters as possible (minimum 10) because DOTween uses a fast scramble mode which gives better results with more characters.
|
||||
/// Leave it to NULL (default) to use default ones</param>
|
||||
public static TweenerCore<string, string, StringOptions> DOText(this Text target, string endValue, float duration, bool richTextEnabled = true, ScrambleMode scrambleMode = ScrambleMode.None, string scrambleChars = null)
|
||||
{
|
||||
if (endValue == null) {
|
||||
if (Debugger.logPriority > 0) Debugger.LogWarning("You can't pass a NULL string to DOText: an empty string will be used instead to avoid errors");
|
||||
endValue = "";
|
||||
}
|
||||
TweenerCore<string, string, StringOptions> t = DOTween.To(() => target.text, x => target.text = x, endValue, duration);
|
||||
t.SetOptions(richTextEnabled, scrambleMode, scrambleChars)
|
||||
.SetTarget(target);
|
||||
return t;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blendables
|
||||
|
||||
#region Graphic
|
||||
|
||||
/// <summary>Tweens a Graphic's color to the given value,
|
||||
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
|
||||
/// instead than fight each other as multiple DOColor would do.
|
||||
/// Also stores the Graphic as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
|
||||
public static Tweener DOBlendableColor(this Graphic target, Color endValue, float duration)
|
||||
{
|
||||
endValue = endValue - target.color;
|
||||
Color to = new Color(0, 0, 0, 0);
|
||||
return DOTween.To(() => to, x => {
|
||||
Color diff = x - to;
|
||||
to = x;
|
||||
target.color += diff;
|
||||
}, endValue, duration)
|
||||
.Blendable().SetTarget(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Image
|
||||
|
||||
/// <summary>Tweens a Image's color to the given value,
|
||||
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
|
||||
/// instead than fight each other as multiple DOColor would do.
|
||||
/// Also stores the Image as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
|
||||
public static Tweener DOBlendableColor(this Image target, Color endValue, float duration)
|
||||
{
|
||||
endValue = endValue - target.color;
|
||||
Color to = new Color(0, 0, 0, 0);
|
||||
return DOTween.To(() => to, x => {
|
||||
Color diff = x - to;
|
||||
to = x;
|
||||
target.color += diff;
|
||||
}, endValue, duration)
|
||||
.Blendable().SetTarget(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Text
|
||||
|
||||
/// <summary>Tweens a Text's color BY the given value,
|
||||
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
|
||||
/// instead than fight each other as multiple DOColor would do.
|
||||
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
|
||||
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
|
||||
public static Tweener DOBlendableColor(this Text target, Color endValue, float duration)
|
||||
{
|
||||
endValue = endValue - target.color;
|
||||
Color to = new Color(0, 0, 0, 0);
|
||||
return DOTween.To(() => to, x => {
|
||||
Color diff = x - to;
|
||||
to = x;
|
||||
target.color += diff;
|
||||
}, endValue, duration)
|
||||
.Blendable().SetTarget(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
|
||||
// ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
|
||||
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the anchoredPosition of the first RectTransform to the second RectTransform,
|
||||
/// taking into consideration offset, anchors and pivot, and returns the new anchoredPosition
|
||||
/// </summary>
|
||||
public static Vector2 SwitchToRectTransform(RectTransform from, RectTransform to)
|
||||
{
|
||||
Vector2 localPoint;
|
||||
Vector2 fromPivotDerivedOffset = new Vector2(from.rect.width * 0.5f + from.rect.xMin, from.rect.height * 0.5f + from.rect.yMin);
|
||||
Vector2 screenP = RectTransformUtility.WorldToScreenPoint(null, from.position);
|
||||
screenP += fromPivotDerivedOffset;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(to, screenP, null, out localPoint);
|
||||
Vector2 pivotDerivedOffset = new Vector2(to.rect.width * 0.5f + to.rect.xMin, to.rect.height * 0.5f + to.rect.yMin);
|
||||
return to.anchoredPosition + localPoint - pivotDerivedOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a060394c03331a64392db53a10e7f2d1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user