提交修改
This commit is contained in:
@@ -1,344 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供字节操作辅助方法的静态类。
|
||||
/// </summary>
|
||||
public static class ByteHelper
|
||||
{
|
||||
private static readonly string[] Suffix = { "Byte", "KB", "MB", "GB", "TB" };
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的文件流中读取一个 64 位整数。
|
||||
/// </summary>
|
||||
public static long ReadInt64(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的文件流中读取一个 32 位整数。
|
||||
/// </summary>
|
||||
public static int ReadInt32(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的内存流中读取一个 64 位整数。
|
||||
/// </summary>
|
||||
public static long ReadInt64(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的内存流中读取一个 32 位整数。
|
||||
/// </summary>
|
||||
public static int ReadInt32(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte b)
|
||||
{
|
||||
return b.ToString("X2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("X2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组按指定格式转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes, string format)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString(format));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按十六进制格式转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes, int offset, int count)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
for (var i = offset; i < offset + count; ++i)
|
||||
{
|
||||
stringBuilder.Append(bytes[i].ToString("X2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为默认编码的字符串表示。
|
||||
/// </summary>
|
||||
public static string ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按默认编码转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为 UTF-8 编码的字符串表示。
|
||||
/// </summary>
|
||||
public static string Utf8ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按 UTF-8 编码转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string Utf8ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将无符号整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, uint num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
bytes[offset + 2] = (byte)((num & 0xff0000) >> 16);
|
||||
bytes[offset + 3] = (byte)((num & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将有符号整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, int num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
bytes[offset + 2] = (byte)((num & 0xff0000) >> 16);
|
||||
bytes[offset + 3] = (byte)((num & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, byte num)
|
||||
{
|
||||
bytes[offset] = num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将有符号短整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, short num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将无符号短整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, ushort num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数转换为可读的速度表示。
|
||||
/// </summary>
|
||||
/// <param name="byteCount">字节数</param>
|
||||
/// <returns>可读的速度表示</returns>
|
||||
public static string ToReadableSpeed(this long byteCount)
|
||||
{
|
||||
var i = 0;
|
||||
double dblSByte = byteCount;
|
||||
if (byteCount <= 1024)
|
||||
{
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
for (i = 0; byteCount / 1024 > 0; i++, byteCount /= 1024)
|
||||
{
|
||||
dblSByte = byteCount / 1024.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数转换为可读的速度表示。
|
||||
/// </summary>
|
||||
/// <param name="byteCount">字节数</param>
|
||||
/// <returns>可读的速度表示</returns>
|
||||
public static string ToReadableSpeed(this ulong byteCount)
|
||||
{
|
||||
var i = 0;
|
||||
double dblSByte = byteCount;
|
||||
|
||||
if (byteCount <= 1024)
|
||||
{
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
for (i = 0; byteCount / 1024 > 0; i++, byteCount /= 1024)
|
||||
{
|
||||
dblSByte = byteCount / 1024.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并两个字节数组。
|
||||
/// </summary>
|
||||
/// <param name="bytes">第一个字节数组</param>
|
||||
/// <param name="otherBytes">第二个字节数组</param>
|
||||
/// <returns>合并后的字节数组</returns>
|
||||
public static byte[] MergeBytes(byte[] bytes, byte[] otherBytes)
|
||||
{
|
||||
var result = new byte[bytes.Length + otherBytes.Length];
|
||||
bytes.CopyTo(result, 0);
|
||||
otherBytes.CopyTo(result, bytes.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(this int value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 4)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, int value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(4);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(ref this uint value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 4)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, uint value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(4);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(this long value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 8)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, long value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(8);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d992f9fd88d64d9085ab2945d98c3d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 287852db62f5047f2a0400646628e51d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public abstract class AUnityDownload : IDisposable
|
||||
{
|
||||
private long _timeId;
|
||||
private ulong _totalDownloadedBytes;
|
||||
private Download _download;
|
||||
protected UnityWebRequest UnityWebRequest;
|
||||
private FCancellationToken _cancellationToken;
|
||||
private Scene Scene;
|
||||
|
||||
protected AUnityDownload(Scene scene,Download download)
|
||||
{
|
||||
Scene = scene;
|
||||
_download = download;
|
||||
_download.Tasks.Add(this);
|
||||
}
|
||||
|
||||
protected UnityWebRequestAsyncOperation Start(UnityWebRequest unityWebRequest, bool monitor)
|
||||
{
|
||||
UnityWebRequest = unityWebRequest;
|
||||
_timeId = Scene.TimerComponent.Unity.RepeatedTimer(33, Update);
|
||||
return UnityWebRequest.SendWebRequest();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var downloadSpeed = UnityWebRequest.downloadedBytes - _totalDownloadedBytes;
|
||||
_download.DownloadSpeed += downloadSpeed;
|
||||
_download.TotalDownloadedBytes += downloadSpeed;
|
||||
_totalDownloadedBytes = UnityWebRequest.downloadedBytes;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Update();
|
||||
_totalDownloadedBytes = 0;
|
||||
UnityWebRequest?.Dispose();
|
||||
_download.Tasks.Remove(this);
|
||||
Scene.TimerComponent.Unity.Remove(ref _timeId);
|
||||
_download = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5743903d34d474a818b5c2bafa31459
|
||||
timeCreated: 1726021902
|
||||
@@ -1,70 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class Download
|
||||
{
|
||||
public Scene Scene;
|
||||
public ulong DownloadSpeed;
|
||||
public ulong TotalDownloadedBytes;
|
||||
public readonly HashSet<AUnityDownload> Tasks = new();
|
||||
|
||||
public static Download Create(Scene scene) => new Download(scene);
|
||||
|
||||
private Download(Scene scene)
|
||||
{
|
||||
Scene = scene;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
DownloadSpeed = 0;
|
||||
TotalDownloadedBytes = 0;
|
||||
|
||||
if (Tasks.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var aUnityDownload in Tasks.ToArray())
|
||||
{
|
||||
aUnityDownload.Dispose();
|
||||
}
|
||||
|
||||
Tasks.Clear();
|
||||
}
|
||||
|
||||
public FTask<AssetBundle> DownloadAssetBundle(string url, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadAssetBundle(Scene, this).StartDownload(url, monitor, cancellationToken);
|
||||
}
|
||||
|
||||
public FTask<AudioClip> DownloadAudioClip(string url, AudioType audioType, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadAudioClip(Scene, this).StartDownload(url, audioType, monitor, cancellationToken);
|
||||
}
|
||||
|
||||
public FTask<Sprite> DownloadSprite(string url, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadSprite(Scene, this).StartDownload(url, monitor, cancellationToken);
|
||||
}
|
||||
|
||||
public FTask<Texture> DownloadTexture(string url, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadTexture(Scene, this).StartDownload(url, monitor, cancellationToken);
|
||||
}
|
||||
|
||||
public FTask<string> DownloadText(string url, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadText(Scene, this).StartDownload(url, monitor, cancellationToken);
|
||||
}
|
||||
|
||||
public FTask<byte[]> DownloadByte(string url, bool monitor = false, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return new DownloadByte(Scene, this).StartDownload(url, monitor, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5715816370e84842aaab799c9776a5e4
|
||||
timeCreated: 1726023436
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadAssetBundle : AUnityDownload
|
||||
{
|
||||
public DownloadAssetBundle(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<AssetBundle> StartDownload(string url, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<AssetBundle>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequestAssetBundle.GetAssetBundle(Uri.EscapeUriString(url)), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var assetBundle = DownloadHandlerAssetBundle.GetContent(UnityWebRequest);
|
||||
task.SetResult(assetBundle);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07cbb9a010ed4fe1b90919f81847b9ea
|
||||
timeCreated: 1726023471
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadAudioClip : AUnityDownload
|
||||
{
|
||||
public DownloadAudioClip(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<AudioClip> StartDownload(string url, AudioType audioType, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<AudioClip>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequestMultimedia.GetAudioClip(Uri.EscapeUriString(url), audioType), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var audioClip = DownloadHandlerAudioClip.GetContent(UnityWebRequest);
|
||||
task.SetResult(audioClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66d3739ec33845148534e6ecaf134b73
|
||||
timeCreated: 1726023476
|
||||
@@ -1,50 +0,0 @@
|
||||
using NBC.Async;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadByte : AUnityDownload
|
||||
{
|
||||
public DownloadByte(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<byte[]> StartDownload(string url, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<byte[]>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequest.Get(url), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var bytes = UnityWebRequest.downloadHandler.data;
|
||||
task.SetResult(bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae87f3ea9f4e4c9ebabedf45b0bb83b1
|
||||
timeCreated: 1726023481
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadSprite : AUnityDownload
|
||||
{
|
||||
public DownloadSprite(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<Sprite> StartDownload(string url, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<Sprite>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequestTexture.GetTexture(Uri.EscapeUriString(url)), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var texture = DownloadHandlerTexture.GetContent(UnityWebRequest);
|
||||
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 5, 1f);
|
||||
task.SetResult(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c2a0f442e974169b7d8b7a5878fe0e6
|
||||
timeCreated: 1726023487
|
||||
@@ -1,50 +0,0 @@
|
||||
using NBC.Async;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadText : AUnityDownload
|
||||
{
|
||||
public DownloadText(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<string> StartDownload(string url, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<string>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequest.Get(url), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var text = UnityWebRequest.downloadHandler.text;
|
||||
task.SetResult(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4284aafa8572453cb75920d2d58e9c50
|
||||
timeCreated: 1726023491
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity.Download
|
||||
{
|
||||
public sealed class DownloadTexture : AUnityDownload
|
||||
{
|
||||
public DownloadTexture(Scene scene, Download download) : base(scene, download)
|
||||
{
|
||||
}
|
||||
|
||||
public FTask<Texture> StartDownload(string url, bool monitor, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<Texture>.Create(false);
|
||||
var unityWebRequestAsyncOperation = Start(UnityWebRequestTexture.GetTexture(Uri.EscapeUriString(url)), monitor);
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
Dispose();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UnityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var texture = DownloadHandlerTexture.GetContent(UnityWebRequest);
|
||||
task.SetResult(texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(UnityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5eaa6023378844ebb51e4b80425d8a4e
|
||||
timeCreated: 1726023496
|
||||
@@ -1,59 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供计算 MD5 散列值的辅助方法。
|
||||
/// </summary>
|
||||
public static partial class EncryptHelper
|
||||
{
|
||||
private static readonly SHA256 Sha256Hash = SHA256.Create();
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定字节数组的Sha256。
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ComputeSha256Hash(byte[] bytes)
|
||||
{
|
||||
using var sha256Hash = SHA256.Create();
|
||||
return sha256Hash.ComputeHash(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定文件的 MD5 散列值。
|
||||
/// </summary>
|
||||
/// <param name="filePath">要计算散列值的文件路径。</param>
|
||||
/// <returns>表示文件的 MD5 散列值的字符串。</returns>
|
||||
public static string FileMD5(string filePath)
|
||||
{
|
||||
using var file = new FileStream(filePath, FileMode.Open);
|
||||
return FileMD5(file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算给定文件流的 MD5 散列值。
|
||||
/// </summary>
|
||||
/// <param name="fileStream">要计算散列值的文件流。</param>
|
||||
/// <returns>表示文件流的 MD5 散列值的字符串。</returns>
|
||||
public static string FileMD5(FileStream fileStream)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
return md5.ComputeHash(fileStream).ToHex("x2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算给定字节数组的 MD5 散列值。
|
||||
/// </summary>
|
||||
/// <param name="bytes">要计算散列值的字节数组。</param>
|
||||
/// <returns>表示字节数组的 MD5 散列值的字符串。</returns>
|
||||
public static string BytesMD5(byte[] bytes)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
bytes = md5.ComputeHash(bytes);
|
||||
return bytes.ToHex("x2");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c09f5dc7247c4396b293e9c91b42361
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,186 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件操作助手类,提供了各种文件操作方法。
|
||||
/// </summary>
|
||||
public static partial class FileHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取相对路径的完整路径。
|
||||
/// </summary>
|
||||
/// <param name="relativePath">相对路径。</param>
|
||||
/// <returns>完整路径。</returns>
|
||||
public static string GetFullPath(string relativePath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取相对路径的完整路径。
|
||||
/// </summary>
|
||||
/// <param name="relativePath">相对于指定的目录的相对路径。</param>
|
||||
/// <param name="srcDir">指定的目录</param>
|
||||
/// <returns>完整路径。</returns>
|
||||
public static string GetFullPath(string relativePath, string srcDir)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(srcDir, relativePath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取相对路径的的文本信息。
|
||||
/// </summary>
|
||||
/// <param name="relativePath"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetTextByRelativePath(string relativePath)
|
||||
{
|
||||
var fullPath = GetFullPath(relativePath);
|
||||
return await File.ReadAllTextAsync(fullPath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取绝对路径的的文本信息。
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetText(string fullPath)
|
||||
{
|
||||
return await File.ReadAllTextAsync(fullPath, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据文件夹路径创建文件夹,如果文件夹不存在会自动创建文件夹。
|
||||
/// </summary>
|
||||
/// <param name="directoryPath"></param>
|
||||
public static void CreateDirectory(string directoryPath)
|
||||
{
|
||||
if (directoryPath.LastIndexOf('/') != directoryPath.Length - 1)
|
||||
{
|
||||
directoryPath += "/";
|
||||
}
|
||||
|
||||
var directoriesByFilePath = GetDirectoriesByFilePath(directoryPath);
|
||||
|
||||
foreach (var dir in directoriesByFilePath)
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将文件复制到目标路径,如果目标目录不存在会自动创建目录。
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">源文件路径。</param>
|
||||
/// <param name="destinationFile">目标文件路径。</param>
|
||||
/// <param name="overwrite">是否覆盖已存在的目标文件。</param>
|
||||
public static void Copy(string sourceFile, string destinationFile, bool overwrite)
|
||||
{
|
||||
CreateDirectory(destinationFile);
|
||||
File.Copy(sourceFile, destinationFile, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件路径内的所有文件夹路径。
|
||||
/// </summary>
|
||||
/// <param name="filePath">文件路径。</param>
|
||||
/// <returns>文件夹路径列表。</returns>
|
||||
public static IEnumerable<string> GetDirectoriesByFilePath(string filePath)
|
||||
{
|
||||
var dir = "";
|
||||
var fileDirectories = filePath.Split('/');
|
||||
|
||||
for (var i = 0; i < fileDirectories.Length - 1; i++)
|
||||
{
|
||||
dir = $"{dir}{fileDirectories[i]}/";
|
||||
yield return dir;
|
||||
}
|
||||
|
||||
if (fileDirectories.Length == 1)
|
||||
{
|
||||
yield return filePath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将文件夹内的所有内容复制到目标位置。
|
||||
/// </summary>
|
||||
/// <param name="sourceDirectory">源文件夹路径。</param>
|
||||
/// <param name="destinationDirectory">目标文件夹路径。</param>
|
||||
/// <param name="overwrite">是否覆盖已存在的文件。</param>
|
||||
public static void CopyDirectory(string sourceDirectory, string destinationDirectory, bool overwrite)
|
||||
{
|
||||
// 创建目标文件夹
|
||||
|
||||
if (!Directory.Exists(destinationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDirectory);
|
||||
}
|
||||
|
||||
// 获取当前文件夹中的所有文件
|
||||
|
||||
var files = Directory.GetFiles(sourceDirectory);
|
||||
|
||||
// 拷贝文件到目标文件夹
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileName = Path.GetFileName(file);
|
||||
var destinationPath = Path.Combine(destinationDirectory, fileName);
|
||||
File.Copy(file, destinationPath, overwrite);
|
||||
}
|
||||
|
||||
// 获取源文件夹中的所有子文件夹
|
||||
|
||||
var directories = Directory.GetDirectories(sourceDirectory);
|
||||
|
||||
// 递归方式拷贝文件夹
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
var directoryName = Path.GetFileName(directory);
|
||||
var destinationPath = Path.Combine(destinationDirectory, directoryName);
|
||||
CopyDirectory(directory, destinationPath, overwrite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取目录下的所有文件
|
||||
/// </summary>
|
||||
/// <param name="folderPath">文件夹路径。</param>
|
||||
/// <param name="searchPattern">需要查找的文件通配符</param>
|
||||
/// <param name="searchOption">查找的类型</param>
|
||||
/// <returns></returns>
|
||||
public static string[] GetDirectoryFile(string folderPath, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
return Directory.GetFiles(folderPath, searchPattern, searchOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空文件夹内的所有文件。
|
||||
/// </summary>
|
||||
/// <param name="folderPath">文件夹路径。</param>
|
||||
public static void ClearDirectoryFile(string folderPath)
|
||||
{
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(folderPath);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb7aa7f0698f4409da7f0cdb32e8d8ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,143 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// HashCode算法帮助类
|
||||
/// </summary>
|
||||
public static partial class HashCodeHelper
|
||||
{
|
||||
private static readonly SHA256 Sha256Hash = SHA256.Create();
|
||||
|
||||
/// <summary>
|
||||
/// 计算两个字符串的HashCode
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetHashCode(string a, string b)
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 31 + a.GetHashCode();
|
||||
hash = hash * 31 + b.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用bkdr算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe long GetBKDRHashCode(string str)
|
||||
{
|
||||
ulong hash = 0;
|
||||
// 如果要修改这个种子、建议选择一个质数来做种子
|
||||
const uint seed = 13131; // 31 131 1313 13131 131313 etc..
|
||||
fixed (char* p = str)
|
||||
{
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var c = p[i];
|
||||
var high = (byte)(c >> 8);
|
||||
var low = (byte)(c & byte.MaxValue);
|
||||
hash = hash * seed + high;
|
||||
hash = hash * seed + low;
|
||||
}
|
||||
}
|
||||
return (long)hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个uint的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe uint MurmurHash3(string str)
|
||||
{
|
||||
const uint seed = 0xc58f1a7b;
|
||||
uint hash = seed;
|
||||
uint c1 = 0xcc9e2d51;
|
||||
uint c2 = 0x1b873593;
|
||||
|
||||
fixed (char* p = str)
|
||||
{
|
||||
var current = p;
|
||||
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var k1 = (uint)(*current);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 15) | (k1 >> (32 - 15));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 13) | (hash >> (32 - 13));
|
||||
hash = hash * 5 + 0xe6546b64;
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
hash ^= (uint)str.Length;
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85ebca6b;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 0xc2b2ae35;
|
||||
hash ^= hash >> 16;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用MurmurHash3算法生成一个long的值
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe long ComputeHash64(string str)
|
||||
{
|
||||
const ulong seed = 0xc58f1a7bc58f1a7bUL; // 64-bit seed
|
||||
var hash = seed;
|
||||
var c1 = 0x87c37b91114253d5UL;
|
||||
var c2 = 0x4cf5ad432745937fUL;
|
||||
|
||||
fixed (char* p = str)
|
||||
{
|
||||
var current = p;
|
||||
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
var k1 = (ulong)(*current);
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 31) | (k1 >> (64 - 31));
|
||||
k1 *= c2;
|
||||
|
||||
hash ^= k1;
|
||||
hash = (hash << 27) | (hash >> (64 - 27));
|
||||
hash = hash * 5 + 0x52dce729;
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
hash ^= (ulong)str.Length;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xff51afd7ed558ccdUL;
|
||||
hash ^= hash >> 33;
|
||||
hash *= 0xc4ceb9fe1a85ec53UL;
|
||||
hash ^= hash >> 33;
|
||||
return (long)hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据字符串计算一个Hash值
|
||||
/// </summary>
|
||||
/// <param name="rawData"></param>
|
||||
/// <returns></returns>
|
||||
public static int ComputeSha256HashAsInt(string rawData)
|
||||
{
|
||||
var bytes = Sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
|
||||
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ec2f7d0b1e3d4605bc7224099e156a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 724626f87e31e4ac4a3098336100c034
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,145 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using NBC.Helper;
|
||||
using NBC.Async;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace NBC.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP帮助类
|
||||
/// </summary>
|
||||
public static partial class HttpClientHelper
|
||||
{
|
||||
private static readonly HttpClient Client = new HttpClient(new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 用Post方式请求string数据
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async FTask<string> CallNotDeserializeByPost(string url, HttpContent content)
|
||||
{
|
||||
var response = await Client.PostAsync(url, content);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"Unable to connect to server url {(object)url} HttpStatusCode:{(object)response.StatusCode}");
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用Get方式请求string数据
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async FTask<string> CallNotDeserializeByGet(string url)
|
||||
{
|
||||
var response = await Client.GetAsync(url);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"Unable to connect to server url {(object)url} HttpStatusCode:{(object)response.StatusCode}");
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用Post方式请求JSON数据,并自动把JSON转换为对象。
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async FTask<T> CallByPost<T>(string url, HttpContent content)
|
||||
{
|
||||
return await Deserialize<T>(url, await Client.PostAsync(url, content));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用Post方式请求JSON数据,并自动把JSON转换为对象。
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async FTask<T> CallByPost<T>(string url, HttpMethod method)
|
||||
{
|
||||
return await Deserialize<T>(url, await Client.SendAsync(new HttpRequestMessage(method, url)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用Get方式请求JSON数据,并自动把JSON转换为对象。
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async FTask<T> CallByGet<T>(string url)
|
||||
{
|
||||
return await Deserialize<T>(url, await Client.GetAsync(url));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用Post方式请求JSON数据,并自动把JSON转换为对象。
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="authentication"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="params"></param>
|
||||
/// <typeparam name="TRequest"></typeparam>
|
||||
/// <typeparam name="TResponse"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static async FTask<TResponse> Call<TRequest, TResponse>(string url, int id, AuthenticationHeaderValue authentication, string method, params object[] @params) where TRequest : class, IJsonRpcRequest, new()
|
||||
{
|
||||
var request = Pool<TRequest>.Rent();
|
||||
using var httpClientPool = HttpClientPool.Create();
|
||||
var client = httpClientPool.Client;
|
||||
client.DefaultRequestHeaders.Authorization = authentication;
|
||||
|
||||
try
|
||||
{
|
||||
request.Init(method, id, @params);
|
||||
var content = new StringContent(request.ToJson(), Encoding.UTF8, "application/json");
|
||||
var response = await Deserialize<TResponse>(url, await client.PostAsync(url, content));
|
||||
return response;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Pool<TRequest>.Return(request);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private static async FTask<T> Deserialize<T>(string url, HttpResponseMessage response)
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"Unable to connect to server url {(object)url} HttpStatusCode:{(object)response.StatusCode}");
|
||||
}
|
||||
|
||||
return (await response.Content.ReadAsStringAsync()).Deserialize<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8005f3a1a1945a2929442f82832e765
|
||||
timeCreated: 1726023741
|
||||
@@ -1,44 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace NBC.Http
|
||||
{
|
||||
internal class HttpClientPool : IDisposable
|
||||
{
|
||||
private bool IsDispose { get; set; }
|
||||
public HttpClient Client { get; private set; }
|
||||
private static readonly Queue<HttpClientPool> Pools = new Queue<HttpClientPool>();
|
||||
private static readonly HttpClientHandler ClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||
};
|
||||
|
||||
public static HttpClientPool Create()
|
||||
{
|
||||
if (Pools.TryDequeue(out var httpClientPool))
|
||||
{
|
||||
httpClientPool.IsDispose = false;
|
||||
return httpClientPool;
|
||||
}
|
||||
|
||||
httpClientPool = new HttpClientPool();
|
||||
httpClientPool.Client = new HttpClient(ClientHandler);
|
||||
return httpClientPool;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDispose = true;
|
||||
Pools.Enqueue(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a78c441357d244d5ba490a13c89e1c50
|
||||
timeCreated: 1726023895
|
||||
@@ -1,20 +0,0 @@
|
||||
using NBC.Pool;
|
||||
|
||||
#if !FANTASY_WEBGL
|
||||
namespace NBC.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 一个JsonRPC的接口
|
||||
/// </summary>
|
||||
public interface IJsonRpcRequest : IPool
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于初始化这个Json对象
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="params"></param>
|
||||
void Init(string method, int id, params object[] @params);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72a03580c619417b9f8f92d99938e371
|
||||
timeCreated: 1726023900
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供操作 JSON 数据的辅助方法。
|
||||
/// </summary>
|
||||
public static partial class JsonHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 将对象序列化为 JSON 字符串。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要序列化的对象类型。</typeparam>
|
||||
/// <param name="t">要序列化的对象。</param>
|
||||
/// <returns>表示序列化对象的 JSON 字符串。</returns>
|
||||
public static string ToJson<T>(this T t)
|
||||
{
|
||||
return JsonConvert.SerializeObject(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化 JSON 字符串为指定类型的对象。
|
||||
/// </summary>
|
||||
/// <param name="json">要反序列化的 JSON 字符串。</param>
|
||||
/// <param name="type">目标对象的类型。</param>
|
||||
/// <param name="reflection">是否使用反射进行反序列化(默认为 true)。</param>
|
||||
/// <returns>反序列化后的对象。</returns>
|
||||
public static object Deserialize(this string json, Type type, bool reflection = true)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化 JSON 字符串为指定类型的对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标对象的类型。</typeparam>
|
||||
/// <param name="json">要反序列化的 JSON 字符串。</param>
|
||||
/// <returns>反序列化后的对象。</returns>
|
||||
public static T Deserialize<T>(this string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 克隆对象,通过将对象序列化为 JSON,然后再进行反序列化。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要克隆的对象类型。</typeparam>
|
||||
/// <param name="t">要克隆的对象。</param>
|
||||
/// <returns>克隆后的对象。</returns>
|
||||
public static T Clone<T>(T t)
|
||||
{
|
||||
return t.ToJson().Deserialize<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f25483965eb6459583e1d328adc8f05
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,443 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供网络操作相关的帮助方法。
|
||||
/// </summary>
|
||||
public static partial class NetworkHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据字符串获取一个IPEndPoint
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IPEndPoint GetIPEndPoint(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
var addressSplit = address.Split(':');
|
||||
if (addressSplit.Length != 2)
|
||||
{
|
||||
throw new FormatException("Invalid format");
|
||||
}
|
||||
|
||||
var ipString = addressSplit[0];
|
||||
var portString = addressSplit[1];
|
||||
|
||||
if (!IPAddress.TryParse(ipString, out var ipAddress))
|
||||
{
|
||||
throw new FormatException("Invalid IP address");
|
||||
}
|
||||
|
||||
if (!int.TryParse(portString, out var port) || port < 0 || port > 65535)
|
||||
{
|
||||
throw new FormatException("Invalid port number");
|
||||
}
|
||||
|
||||
return new IPEndPoint(ipAddress, port);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error parsing IP and Port:{e.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 克隆一个IPEndPoint
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IPEndPoint Clone(this EndPoint endPoint)
|
||||
{
|
||||
var ip = (IPEndPoint)endPoint;
|
||||
return new IPEndPoint(ip.Address, ip.Port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较两个IPEndPoint是否相等
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <param name="ipEndPoint"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IPEndPointEquals(this EndPoint endPoint, IPEndPoint ipEndPoint)
|
||||
{
|
||||
var ip = (IPEndPoint)endPoint;
|
||||
return ip.Address.Equals(ipEndPoint.Address) && ip.Port == ipEndPoint.Port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较两个IPEndPoint是否相等
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <param name="ipEndPoint"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IPEndPointEquals(this IPEndPoint endPoint, IPEndPoint ipEndPoint)
|
||||
{
|
||||
return endPoint.Address.Equals(ipEndPoint.Address) && endPoint.Port == ipEndPoint.Port;
|
||||
}
|
||||
|
||||
#if !FANTASY_WEBGL
|
||||
/// <summary>
|
||||
/// 将SocketAddress写入到Byte[]中
|
||||
/// </summary>
|
||||
/// <param name="socketAddress"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void SocketAddressToByte(this SocketAddress socketAddress, byte[] buffer, int offset)
|
||||
{
|
||||
if (socketAddress == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(socketAddress), "The SocketAddress cannot be null.");
|
||||
}
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer), "The buffer cannot be null.");
|
||||
}
|
||||
|
||||
if (buffer.Length < socketAddress.Size + offset + 8)
|
||||
{
|
||||
throw new ArgumentException("The buffer length is insufficient. It must be at least the size of the SocketAddress plus 8 bytes.", nameof(buffer));
|
||||
}
|
||||
|
||||
fixed (byte* pBuffer = buffer)
|
||||
{
|
||||
var pOffsetBuffer = pBuffer + offset;
|
||||
var addressFamilyValue = (int)socketAddress.Family;
|
||||
var socketAddressSizeValue = socketAddress.Size;
|
||||
Buffer.MemoryCopy(&addressFamilyValue, pOffsetBuffer, buffer.Length - offset, sizeof(int));
|
||||
Buffer.MemoryCopy(&socketAddressSizeValue, pOffsetBuffer + 4, buffer.Length - offset -4, sizeof(int));
|
||||
for (var i = 0; i < socketAddress.Size - 2; i++)
|
||||
{
|
||||
pOffsetBuffer[8 + i] = socketAddress[i + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将byre[]转换为SocketAddress
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="socketAddress"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe int ByteToSocketAddress(byte[] buffer, int offset, out SocketAddress socketAddress)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer), "The buffer cannot be null.");
|
||||
}
|
||||
|
||||
if (buffer.Length < 8)
|
||||
{
|
||||
throw new ArgumentException("Buffer length is insufficient. It must be at least 8 bytes.", nameof(buffer));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
fixed (byte* pBuffer = buffer)
|
||||
{
|
||||
var pOffsetBuffer = pBuffer + offset;
|
||||
var addressFamily = (AddressFamily)Marshal.ReadInt32((IntPtr)pOffsetBuffer);
|
||||
var socketAddressSize = Marshal.ReadInt32((IntPtr)(pOffsetBuffer + 4));
|
||||
|
||||
if (buffer.Length < offset + 8 + socketAddressSize)
|
||||
{
|
||||
throw new ArgumentException("Buffer length is insufficient for the given SocketAddress size.", nameof(buffer));
|
||||
}
|
||||
|
||||
socketAddress = new SocketAddress(addressFamily, socketAddressSize);
|
||||
|
||||
for (var i = 0; i < socketAddressSize - 2; i++)
|
||||
{
|
||||
socketAddress[i + 2] = *(pOffsetBuffer + 8 + i);
|
||||
}
|
||||
|
||||
return 8 + offset + socketAddressSize;
|
||||
}
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
throw new InvalidOperationException("An argument provided to the method is null.", ex);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
throw new InvalidOperationException("An argument provided to the method is invalid.", ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException("An unexpected error occurred while processing the buffer.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将ReadOnlyMemory转换为SocketAddress
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="socketAddress"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe int ByteToSocketAddress(ReadOnlyMemory<byte> buffer, int offset, out SocketAddress socketAddress)
|
||||
{
|
||||
if (buffer.Length < 8)
|
||||
{
|
||||
throw new ArgumentException("Buffer length is insufficient. It must be at least 8 bytes.", nameof(buffer));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
fixed (byte* pBuffer = buffer.Span)
|
||||
{
|
||||
var pOffsetBuffer = pBuffer + offset;
|
||||
var addressFamily = (AddressFamily)Marshal.ReadInt32((IntPtr)pOffsetBuffer);
|
||||
var socketAddressSize = Marshal.ReadInt32((IntPtr)(pOffsetBuffer + 4));
|
||||
|
||||
if (buffer.Length < offset + 8 + socketAddressSize)
|
||||
{
|
||||
throw new ArgumentException("Buffer length is insufficient for the given SocketAddress size.", nameof(buffer));
|
||||
}
|
||||
|
||||
socketAddress = new SocketAddress(addressFamily, socketAddressSize);
|
||||
|
||||
for (var i = 0; i < socketAddressSize - 2; i++)
|
||||
{
|
||||
socketAddress[i + 2] = *(pOffsetBuffer + 8 + i);
|
||||
}
|
||||
|
||||
return 8 + offset + socketAddressSize;
|
||||
}
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
throw new InvalidOperationException("An argument provided to the method is null.", ex);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
throw new InvalidOperationException("An argument provided to the method is invalid.", ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException("An unexpected error occurred while processing the buffer.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据SocketAddress获得IPEndPoint
|
||||
/// </summary>
|
||||
/// <param name="socketAddress"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static unsafe IPEndPoint GetIPEndPoint(this SocketAddress socketAddress)
|
||||
{
|
||||
switch (socketAddress.Family)
|
||||
{
|
||||
case AddressFamily.InterNetwork:
|
||||
{
|
||||
var ipBytes = new byte[4];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
ipBytes[i] = socketAddress[4 + i];
|
||||
}
|
||||
var port = (socketAddress[2] << 8) + socketAddress[3];
|
||||
var ip = new IPAddress(ipBytes);
|
||||
return new IPEndPoint(ip, port);
|
||||
}
|
||||
case AddressFamily.InterNetworkV6:
|
||||
{
|
||||
var ipBytes = new byte[16];
|
||||
Span<byte> socketAddressSpan = stackalloc byte[28];
|
||||
|
||||
for (var i = 0; i < 28; i++)
|
||||
{
|
||||
socketAddressSpan[i] = socketAddress[i];
|
||||
}
|
||||
|
||||
fixed (byte* pSocketAddress = socketAddressSpan)
|
||||
{
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
ipBytes[i] = *(pSocketAddress + 8 + i);
|
||||
}
|
||||
|
||||
var port = (*(pSocketAddress + 2) << 8) + *(pSocketAddress + 3);
|
||||
var scopeId = Marshal.ReadInt64((IntPtr)(pSocketAddress + 24));
|
||||
var ip = new IPAddress(ipBytes, scopeId);
|
||||
return new IPEndPoint(ip, port);
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("Address family not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 获取本机所有网络适配器的IP地址。
|
||||
/// </summary>
|
||||
/// <returns>IP地址数组。</returns>
|
||||
public static string[] GetAddressIPs()
|
||||
{
|
||||
var list = new List<string>();
|
||||
foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
// 仅考虑以太网类型的网络适配器
|
||||
if (networkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var add in networkInterface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
list.Add(add.Address.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将主机名和端口号转换为 <see cref="IPEndPoint"/> 实例。
|
||||
/// </summary>
|
||||
/// <param name="host">主机名。</param>
|
||||
/// <param name="port">端口号。</param>
|
||||
/// <returns><see cref="IPEndPoint"/> 实例。</returns>
|
||||
public static IPEndPoint ToIPEndPoint(string host, int port)
|
||||
{
|
||||
return new IPEndPoint(IPAddress.Parse(host), port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将地址字符串转换为 <see cref="IPEndPoint"/> 实例。
|
||||
/// </summary>
|
||||
/// <param name="address">地址字符串,格式为 "主机名:端口号"。</param>
|
||||
/// <returns><see cref="IPEndPoint"/> 实例。</returns>
|
||||
public static IPEndPoint ToIPEndPoint(string address)
|
||||
{
|
||||
var index = address.LastIndexOf(':');
|
||||
var host = address.Substring(0, index);
|
||||
var p = address.Substring(index + 1);
|
||||
var port = int.Parse(p);
|
||||
return ToIPEndPoint(host, port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 <see cref="IPEndPoint"/> 实例转换为字符串表示形式。
|
||||
/// </summary>
|
||||
/// <param name="self"><see cref="IPEndPoint"/> 实例。</param>
|
||||
/// <returns>表示 <see cref="IPEndPoint"/> 的字符串。</returns>
|
||||
public static string IPEndPointToStr(this IPEndPoint self)
|
||||
{
|
||||
return $"{self.Address}:{self.Port}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 针对 Windows 平台设置UDP连接重置选项。
|
||||
/// </summary>
|
||||
/// <param name="socket">要设置选项的 <see cref="Socket"/> 实例。</param>
|
||||
public static void SetSioUdpConnReset(this Socket socket)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
目前这个问题只有Windows下才会出现。
|
||||
服务器端在发送数据时捕获到了一个异常,
|
||||
这个异常导致原因应该是远程客户端的UDP监听已停止导致数据发送出错。
|
||||
按理说UDP是无连接的,报这个异常是不合理的
|
||||
这个异常让整UDP的服务监听也停止了。
|
||||
这样就因为一个客户端的数据发送无法到达而导致了服务挂了,所有客户端都无法与服务器通信了
|
||||
想详细了解看下https://blog.csdn.net/sunzhen6251/article/details/124168805*/
|
||||
const uint IOC_IN = 0x80000000;
|
||||
const uint IOC_VENDOR = 0x18000000;
|
||||
const int SIO_UDP_CONNRESET = unchecked((int) (IOC_IN | IOC_VENDOR | 12));
|
||||
|
||||
socket.IOControl(SIO_UDP_CONNRESET, new[] {Convert.ToByte(false)}, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Socket 缓冲区大小设置为操作系统限制。
|
||||
/// </summary>
|
||||
/// <param name="socket">要设置缓冲区大小的 Socket。</param>
|
||||
public static void SetSocketBufferToOsLimit(this Socket socket)
|
||||
{
|
||||
socket.SetReceiveBufferToOSLimit();
|
||||
socket.SetSendBufferToOSLimit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Socket 接收缓冲区大小设置为操作系统限制。
|
||||
/// 尝试增加接收缓冲区大小的次数 = 默认 + 最大增加 100 MB。
|
||||
/// </summary>
|
||||
/// <param name="socket">要设置接收缓冲区大小的 Socket。</param>
|
||||
/// <param name="stepSize">每次增加的步长大小。</param>
|
||||
/// <param name="attempts">尝试增加缓冲区大小的次数。</param>
|
||||
public static void SetReceiveBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000)
|
||||
{
|
||||
// setting a too large size throws a socket exception.
|
||||
// so let's keep increasing until we encounter it.
|
||||
for (int i = 0; i < attempts; ++i)
|
||||
{
|
||||
// increase in 1 KB steps
|
||||
try
|
||||
{
|
||||
socket.ReceiveBufferSize += stepSize;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将 Socket 发送缓冲区大小设置为操作系统限制。
|
||||
/// 尝试增加发送缓冲区大小的次数 = 默认 + 最大增加 100 MB。
|
||||
/// </summary>
|
||||
/// <param name="socket">要设置发送缓冲区大小的 Socket。</param>
|
||||
/// <param name="stepSize">每次增加的步长大小。</param>
|
||||
/// <param name="attempts">尝试增加缓冲区大小的次数。</param>
|
||||
public static void SetSendBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000)
|
||||
{
|
||||
// setting a too large size throws a socket exception.
|
||||
// so let's keep increasing until we encounter it.
|
||||
for (var i = 0; i < attempts; ++i)
|
||||
{
|
||||
// increase in 1 KB steps
|
||||
try
|
||||
{
|
||||
socket.SendBufferSize += stepSize;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f35b5f3e69dae431982e69500c1c97c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,310 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NBC.LowLevel;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 随机数操作助手类,提供各种随机数生成和操作方法。
|
||||
/// </summary>
|
||||
public static partial class RandomHelper
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static Random _random;
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 64 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 64 位整数。</returns>
|
||||
public static ulong RandUInt64()
|
||||
{
|
||||
var byte8 = new FixedBytes8().AsSpan();
|
||||
var random = _random ??= new Random();
|
||||
random.NextBytes(byte8);
|
||||
return BitConverter.ToUInt64(byte8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的 64 位整数。
|
||||
/// </summary>
|
||||
/// <returns>64 位整数。</returns>
|
||||
public static long RandInt64()
|
||||
{
|
||||
var byte8 = new FixedBytes8().AsSpan();
|
||||
var random = _random ??= new Random();
|
||||
random.NextBytes(byte8);
|
||||
return BitConverter.ToInt64(byte8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 32 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 32 位整数。</returns>
|
||||
public static uint RandUInt32()
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
return (uint) random.Next();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 16 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 16 位整数。</returns>
|
||||
public static ushort RandUInt16()
|
||||
{
|
||||
var byte2 = new FixedBytes2().AsSpan();
|
||||
var random = _random ??= new Random();
|
||||
random.NextBytes(byte2);
|
||||
return BitConverter.ToUInt16(byte2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定范围内生成一个随机整数(包含下限,不包含上限)。
|
||||
/// </summary>
|
||||
/// <param name="lower">下限。</param>
|
||||
/// <param name="upper">上限。</param>
|
||||
/// <returns>生成的随机整数。</returns>
|
||||
public static int RandomNumber(int lower, int upper)
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
return random.Next(lower, upper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的布尔值。
|
||||
/// </summary>
|
||||
/// <returns>随机的布尔值。</returns>
|
||||
public static bool RandomBool()
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
return (random.Next() & 1) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数组中随机选择一个元素。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素的类型。</typeparam>
|
||||
/// <param name="array">要选择的数组。</param>
|
||||
/// <returns>随机选择的数组元素。</returns>
|
||||
public static T RandomArray<T>(this T[] array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count())];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中随机选择一个元素。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表元素的类型。</typeparam>
|
||||
/// <param name="array">要选择的列表。</param>
|
||||
/// <returns>随机选择的列表元素。</returns>
|
||||
public static T RandomArray<T>(this List<T> array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count())];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打乱列表中元素的顺序。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表元素的类型。</typeparam>
|
||||
/// <param name="arr">要打乱顺序的列表。</param>
|
||||
public static void BreakRank<T>(List<T> arr)
|
||||
{
|
||||
if (arr == null || arr.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var random = _random ??= new Random();
|
||||
for (var i = 0; i < arr.Count / 2; i++)
|
||||
{
|
||||
var index = random.Next(0, arr.Count);
|
||||
(arr[index], arr[arr.Count - index - 1]) = (arr[arr.Count - index - 1], arr[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个介于 0 和 1 之间的随机单精度浮点数。
|
||||
/// </summary>
|
||||
/// <returns>随机单精度浮点数。</returns>
|
||||
public static float RandFloat01()
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
var value = random.NextDouble();
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
private static int Rand(int n)
|
||||
{
|
||||
var rd = new Random();
|
||||
// 注意,返回值是左闭右开,所以maxValue要加1
|
||||
return rd.Next(1, n + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据权重随机选择一个索引。
|
||||
/// </summary>
|
||||
/// <param name="weights">权重数组,每个元素表示相应索引的权重。</param>
|
||||
/// <returns>随机选择的索引值。</returns>
|
||||
public static int RandomByWeight(int[] weights)
|
||||
{
|
||||
var sum = weights.Sum();
|
||||
var numberRand = Rand(sum);
|
||||
var sumTemp = 0;
|
||||
for (var i = 0; i < weights.Length; i++)
|
||||
{
|
||||
sumTemp += weights[i];
|
||||
if (numberRand <= sumTemp)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据固定概率随机选择一个索引,即某个数值上限内随机多少次。
|
||||
/// </summary>
|
||||
/// <param name="args">概率数组,每个元素表示相应索引的概率。</param>
|
||||
/// <returns>随机选择的索引值。</returns>
|
||||
public static int RandomByFixedProbability(int[] args)
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
var argCount = args.Length;
|
||||
var sum = args.Sum();
|
||||
var value = random.NextDouble() * sum;
|
||||
while (sum > value)
|
||||
{
|
||||
sum -= args[argCount - 1];
|
||||
argCount--;
|
||||
}
|
||||
|
||||
return argCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回随机数。
|
||||
/// </summary>
|
||||
/// <param name="containNegative">是否包含负数。</param>
|
||||
/// <returns>返回一个随机的单精度浮点数。</returns>
|
||||
public static float NextFloat(bool containNegative = false)
|
||||
{
|
||||
var random = _random ??= new Random();
|
||||
float f;
|
||||
var buffer = new FixedBytes4().AsSpan();
|
||||
if (containNegative)
|
||||
{
|
||||
do
|
||||
{
|
||||
random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer);
|
||||
} while ((f >= float.MinValue && f < float.MaxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer);
|
||||
} while ((f >= 0 && f < float.MaxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个小于所指定最大值的非负随机数。
|
||||
/// </summary>
|
||||
/// <param name="maxValue">要生成的随机数的上限(随机数不能取该上限值)。 maxValue 必须大于或等于零。</param>
|
||||
/// <returns>大于等于零且小于 maxValue 的单精度浮点数,即:返回值的范围通常包括零但不包括 maxValue。 不过,如果 maxValue 等于零,则返回 maxValue。</returns>
|
||||
public static float NextFloat(float maxValue)
|
||||
{
|
||||
if (maxValue.Equals(0))
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
if (maxValue < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("“maxValue”必须大于 0。", "maxValue");
|
||||
}
|
||||
|
||||
var random = _random ??= new Random();
|
||||
float f;
|
||||
var buffer = new FixedBytes4().AsSpan();
|
||||
|
||||
do
|
||||
{
|
||||
random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer);
|
||||
} while ((f >= 0 && f < maxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个指定范围内的随机数。
|
||||
/// </summary>
|
||||
/// <param name="minValue">返回的随机数的下界(随机数可取该下界值)。</param>
|
||||
/// <param name="maxValue">返回的随机数的上界(随机数不能取该上界值)。 maxValue 必须大于或等于 minValue。</param>
|
||||
/// <returns>一个大于等于 minValue 且小于 maxValue 的单精度浮点数,即:返回的值范围包括 minValue 但不包括 maxValue。 如果 minValue 等于 maxValue,则返回 minValue。</returns>
|
||||
public static float NextFloat(float minValue, float maxValue)
|
||||
{
|
||||
if (minValue.Equals(maxValue))
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
if (minValue > maxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("“minValue”不能大于 maxValue。", "minValue");
|
||||
}
|
||||
|
||||
var random = _random ??= new Random();
|
||||
var buffer = new FixedBytes4().AsSpan();
|
||||
float f;
|
||||
|
||||
do
|
||||
{
|
||||
random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer);
|
||||
} while ((f >= minValue && f < maxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定的矩形区域内随机生成一个二维向量位置。
|
||||
/// </summary>
|
||||
/// <param name="minX">X轴最小值。</param>
|
||||
/// <param name="maxX">X轴最大值。</param>
|
||||
/// <param name="minY">Y轴最小值。</param>
|
||||
/// <param name="maxY">Y轴最大值。</param>
|
||||
/// <returns>随机生成的二维向量位置。</returns>
|
||||
public static Vector2 NextVector2(float minX, float maxX, float minY, float maxY)
|
||||
{
|
||||
return new Vector2(NextFloat(minX, maxX), NextFloat(minY, maxY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成指定长度的随机数字代码。
|
||||
/// </summary>
|
||||
/// <param name="len">数字代码的长度。</param>
|
||||
/// <returns>生成的随机数字代码。</returns>
|
||||
public static string RandomNumberCode(int len = 6)
|
||||
{
|
||||
int num = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int number = RandomNumber(0, 10);
|
||||
num = num * 10 + number;
|
||||
}
|
||||
|
||||
return num.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ca1a2a2ac7a7472ab9c07166a4e471a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,295 +0,0 @@
|
||||
#if FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Fantasy.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 随机数操作助手类,提供各种随机数生成和操作方法。
|
||||
/// </summary>
|
||||
public static partial class RandomHelper
|
||||
{
|
||||
private static readonly Random Random = new Random();
|
||||
private static readonly byte[] Byte8 = new byte[8];
|
||||
private static readonly byte[] Byte2 = new byte[2];
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 64 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 64 位整数。</returns>
|
||||
public static ulong RandUInt64()
|
||||
{
|
||||
Random.NextBytes(Byte8);
|
||||
return BitConverter.ToUInt64(Byte8, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的 64 位整数。
|
||||
/// </summary>
|
||||
/// <returns>64 位整数。</returns>
|
||||
public static long RandInt64()
|
||||
{
|
||||
Random.NextBytes(Byte8);
|
||||
return BitConverter.ToInt64(Byte8, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 32 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 32 位整数。</returns>
|
||||
public static uint RandUInt32()
|
||||
{
|
||||
return (uint) Random.Next();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的无符号 16 位整数。
|
||||
/// </summary>
|
||||
/// <returns>无符号 16 位整数。</returns>
|
||||
public static ushort RandUInt16()
|
||||
{
|
||||
Random.NextBytes(Byte2);
|
||||
return BitConverter.ToUInt16(Byte2, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定范围内生成一个随机整数(包含下限,不包含上限)。
|
||||
/// </summary>
|
||||
/// <param name="lower">下限。</param>
|
||||
/// <param name="upper">上限。</param>
|
||||
/// <returns>生成的随机整数。</returns>
|
||||
public static int RandomNumber(int lower, int upper)
|
||||
{
|
||||
return Random.Next(lower, upper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个随机的布尔值。
|
||||
/// </summary>
|
||||
/// <returns>随机的布尔值。</returns>
|
||||
public static bool RandomBool()
|
||||
{
|
||||
return Random.Next(2) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数组中随机选择一个元素。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素的类型。</typeparam>
|
||||
/// <param name="array">要选择的数组。</param>
|
||||
/// <returns>随机选择的数组元素。</returns>
|
||||
public static T RandomArray<T>(this T[] array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count())];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中随机选择一个元素。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表元素的类型。</typeparam>
|
||||
/// <param name="array">要选择的列表。</param>
|
||||
/// <returns>随机选择的列表元素。</returns>
|
||||
public static T RandomArray<T>(this List<T> array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count())];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打乱列表中元素的顺序。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表元素的类型。</typeparam>
|
||||
/// <param name="arr">要打乱顺序的列表。</param>
|
||||
public static void BreakRank<T>(List<T> arr)
|
||||
{
|
||||
if (arr == null || arr.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < arr.Count / 2; i++)
|
||||
{
|
||||
var index = Random.Next(0, arr.Count);
|
||||
(arr[index], arr[arr.Count - index - 1]) = (arr[arr.Count - index - 1], arr[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个介于 0 和 1 之间的随机单精度浮点数。
|
||||
/// </summary>
|
||||
/// <returns>随机单精度浮点数。</returns>
|
||||
public static float RandFloat01()
|
||||
{
|
||||
var value = Random.NextDouble();
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
private static int Rand(int n)
|
||||
{
|
||||
var rd = new Random();
|
||||
// 注意,返回值是左闭右开,所以maxValue要加1
|
||||
return rd.Next(1, n + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据权重随机选择一个索引。
|
||||
/// </summary>
|
||||
/// <param name="weights">权重数组,每个元素表示相应索引的权重。</param>
|
||||
/// <returns>随机选择的索引值。</returns>
|
||||
public static int RandomByWeight(int[] weights)
|
||||
{
|
||||
var sum = weights.Sum();
|
||||
var numberRand = Rand(sum);
|
||||
var sumTemp = 0;
|
||||
for (var i = 0; i < weights.Length; i++)
|
||||
{
|
||||
sumTemp += weights[i];
|
||||
if (numberRand <= sumTemp)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据固定概率随机选择一个索引,即某个数值上限内随机多少次。
|
||||
/// </summary>
|
||||
/// <param name="args">概率数组,每个元素表示相应索引的概率。</param>
|
||||
/// <returns>随机选择的索引值。</returns>
|
||||
public static int RandomByFixedProbability(int[] args)
|
||||
{
|
||||
var argCount = args.Length;
|
||||
var sum = args.Sum();
|
||||
var random = Random.NextDouble() * sum;
|
||||
while (sum > random)
|
||||
{
|
||||
sum -= args[argCount - 1];
|
||||
argCount--;
|
||||
}
|
||||
|
||||
return argCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回随机数。
|
||||
/// </summary>
|
||||
/// <param name="containNegative">是否包含负数。</param>
|
||||
/// <returns>返回一个随机的单精度浮点数。</returns>
|
||||
public static float NextFloat(bool containNegative = false)
|
||||
{
|
||||
float f;
|
||||
var buffer = new byte[4];
|
||||
if (containNegative)
|
||||
{
|
||||
do
|
||||
{
|
||||
Random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer, 0);
|
||||
} while ((f >= float.MinValue && f < float.MaxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer, 0);
|
||||
} while ((f >= 0 && f < float.MaxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个小于所指定最大值的非负随机数。
|
||||
/// </summary>
|
||||
/// <param name="maxValue">要生成的随机数的上限(随机数不能取该上限值)。 maxValue 必须大于或等于零。</param>
|
||||
/// <returns>大于等于零且小于 maxValue 的单精度浮点数,即:返回值的范围通常包括零但不包括 maxValue。 不过,如果 maxValue 等于零,则返回 maxValue。</returns>
|
||||
public static float NextFloat(float maxValue)
|
||||
{
|
||||
if (maxValue.Equals(0))
|
||||
{
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
if (maxValue < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("“maxValue”必须大于 0。", "maxValue");
|
||||
}
|
||||
|
||||
float f;
|
||||
var buffer = new byte[4];
|
||||
|
||||
do
|
||||
{
|
||||
Random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer, 0);
|
||||
} while ((f >= 0 && f < maxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个指定范围内的随机数。
|
||||
/// </summary>
|
||||
/// <param name="minValue">返回的随机数的下界(随机数可取该下界值)。</param>
|
||||
/// <param name="maxValue">返回的随机数的上界(随机数不能取该上界值)。 maxValue 必须大于或等于 minValue。</param>
|
||||
/// <returns>一个大于等于 minValue 且小于 maxValue 的单精度浮点数,即:返回的值范围包括 minValue 但不包括 maxValue。 如果 minValue 等于 maxValue,则返回 minValue。</returns>
|
||||
public static float NextFloat(float minValue, float maxValue)
|
||||
{
|
||||
if (minValue.Equals(maxValue))
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
if (minValue > maxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("“minValue”不能大于 maxValue。", "minValue");
|
||||
}
|
||||
|
||||
float f;
|
||||
var buffer = new byte[4];
|
||||
|
||||
do
|
||||
{
|
||||
Random.NextBytes(buffer);
|
||||
f = BitConverter.ToSingle(buffer, 0);
|
||||
} while ((f >= minValue && f < maxValue) == false);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定的矩形区域内随机生成一个二维向量位置。
|
||||
/// </summary>
|
||||
/// <param name="minX">X轴最小值。</param>
|
||||
/// <param name="maxX">X轴最大值。</param>
|
||||
/// <param name="minY">Y轴最小值。</param>
|
||||
/// <param name="maxY">Y轴最大值。</param>
|
||||
/// <returns>随机生成的二维向量位置。</returns>
|
||||
public static Vector2 NextVector2(float minX, float maxX, float minY, float maxY)
|
||||
{
|
||||
return new Vector2(NextFloat(minX, maxX), NextFloat(minY, maxY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成指定长度的随机数字代码。
|
||||
/// </summary>
|
||||
/// <param name="len">数字代码的长度。</param>
|
||||
/// <returns>生成的随机数字代码。</returns>
|
||||
public static string RandomNumberCode(int len = 6)
|
||||
{
|
||||
int num = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
int number = RandomNumber(0, 10);
|
||||
num = num * 10 + number;
|
||||
}
|
||||
|
||||
return num.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74eea4430a34f4086a0f6e09f1e4ccfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,70 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Socket帮助类
|
||||
/// </summary>
|
||||
public static partial class SocketHelper
|
||||
{
|
||||
// always pass the same IPEndPointNonAlloc instead of allocating a new
|
||||
// one each time.
|
||||
//
|
||||
// use IPEndPointNonAlloc.temp to get the latest SocketAdddress written
|
||||
// by ReceiveFrom_Internal!
|
||||
//
|
||||
// IMPORTANT: .temp will be overwritten in next call!
|
||||
// hash or manually copy it if you need to store it, e.g.
|
||||
// when adding a new connection.
|
||||
public static int ReceiveFrom_NonAlloc(
|
||||
this Socket socket,
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int size,
|
||||
SocketFlags socketFlags,
|
||||
EndPoint remoteEndPoint)
|
||||
{
|
||||
// call ReceiveFrom with IPEndPointNonAlloc.
|
||||
// need to wrap this in ReceiveFrom_NonAlloc because it's not
|
||||
// obvious that IPEndPointNonAlloc.Create does NOT create a new
|
||||
// IPEndPoint. it saves the result in IPEndPointNonAlloc.temp!
|
||||
EndPoint casted = remoteEndPoint;
|
||||
return socket.ReceiveFrom(buffer, offset, size, socketFlags, ref casted);
|
||||
}
|
||||
|
||||
// same as above, different parameters
|
||||
public static int ReceiveFrom_NonAlloc(this Socket socket, byte[] buffer, ref EndPoint remoteEndPoint)
|
||||
{
|
||||
#if UNITY
|
||||
EndPoint casted = remoteEndPoint;
|
||||
return socket.ReceiveFrom(buffer, ref casted);
|
||||
#else
|
||||
return socket.ReceiveFrom(buffer, ref remoteEndPoint);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// SendTo allocates too:
|
||||
// https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L2240
|
||||
// -> the allocation is in EndPoint.Serialize()
|
||||
// NOTE: technically this function isn't necessary.
|
||||
// could just pass IPEndPointNonAlloc.
|
||||
// still good for strong typing.
|
||||
//public static int SendTo_NonAlloc(
|
||||
// this Socket socket,
|
||||
// byte[] buffer,
|
||||
// int offset,
|
||||
// int size,
|
||||
// SocketFlags socketFlags,
|
||||
// IPEndPointNonAlloc remoteEndPoint)
|
||||
//{
|
||||
// EndPoint casted = remoteEndPoint;
|
||||
// return socket.SendTo(buffer, offset, size, socketFlags, casted);
|
||||
//}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54d75f1a06d9144e482ec46a67f2cfeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供与时间相关的帮助方法。
|
||||
/// </summary>
|
||||
public static partial class TimeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 一小时的毫秒值。
|
||||
/// </summary>
|
||||
public const long Hour = 3600000;
|
||||
/// <summary>
|
||||
/// 一分钟的毫秒值。
|
||||
/// </summary>
|
||||
public const long Minute = 60000;
|
||||
/// <summary>
|
||||
/// 一天的毫秒值。
|
||||
/// </summary>
|
||||
public const long OneDay = 86400000;
|
||||
// 1970年1月1日的Ticks
|
||||
private const long Epoch = 621355968000000000L;
|
||||
/// <summary>
|
||||
/// 获取当前时间的毫秒数,从1970年1月1日开始计算。
|
||||
/// </summary>
|
||||
public static long Now => (DateTime.UtcNow.Ticks - Epoch) / 10000;
|
||||
|
||||
/// <summary>
|
||||
/// 与服务器时间的偏差。
|
||||
/// </summary>
|
||||
public static long TimeDiff;
|
||||
/// <summary>
|
||||
/// 获取当前服务器时间的毫秒数,加上与服务器时间的偏差。
|
||||
/// </summary>
|
||||
public static long ServerNow => Now + TimeDiff;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前Unity运行的总时间的毫秒数。
|
||||
/// </summary>
|
||||
public static long UnityNow => (long) (Time.time * 1000);
|
||||
|
||||
/// <summary>
|
||||
/// 根据时间获取时间戳
|
||||
/// </summary>
|
||||
public static long Transition(DateTime dateTime)
|
||||
{
|
||||
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据时间获取 时间戳
|
||||
/// </summary>
|
||||
public static long TransitionToSeconds(DateTime dateTime)
|
||||
{
|
||||
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将毫秒数转换为日期时间。
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">要转换的毫秒数。</param>
|
||||
/// <returns>转换后的日期时间。</returns>
|
||||
public static DateTime Transition(this long timeStamp)
|
||||
{
|
||||
return new DateTime(Epoch + timeStamp * 10000, DateTimeKind.Utc).ToUniversalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将毫秒数转换为本地时间的日期时间。
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">要转换的毫秒数。</param>
|
||||
/// <returns>转换后的本地时间的日期时间。</returns>
|
||||
public static DateTime TransitionLocal(this long timeStamp)
|
||||
{
|
||||
return new DateTime(Epoch + timeStamp * 10000, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3937c5cea56304a79b138c980c91b79b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d9aa49407518439ebc2527068436501
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,242 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace NBC.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// UnityWebRequest的帮助类
|
||||
/// </summary>
|
||||
public static class UnityWebRequestHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取一个文本
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<string> GetText(string url, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<string>.Create(false);
|
||||
var unityWebRequest = UnityWebRequest.Get(url);
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var text = unityWebRequest.downloadHandler.text;
|
||||
task.SetResult(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个Sprite
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<Sprite> GetSprite(string url, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<Sprite>.Create(false);
|
||||
var unityWebRequest = UnityWebRequestTexture.GetTexture(Uri.EscapeUriString(url));
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var texture = DownloadHandlerTexture.GetContent(unityWebRequest);
|
||||
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 5, 1f);
|
||||
task.SetResult(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个Texture
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<Texture> GetTexture(string url, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<Texture>.Create(false);
|
||||
var unityWebRequest = UnityWebRequestTexture.GetTexture(Uri.EscapeUriString(url));
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var texture = DownloadHandlerTexture.GetContent(unityWebRequest);
|
||||
task.SetResult(texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Bytes
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<byte[]> GetBytes(string url, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<byte[]>.Create(false);
|
||||
var unityWebRequest = UnityWebRequest.Get(url);
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var bytes = unityWebRequest.downloadHandler.data;
|
||||
task.SetResult(bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AssetBundle
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<AssetBundle> GetAssetBundle(string url, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<AssetBundle>.Create(false);
|
||||
var unityWebRequest = UnityWebRequestAssetBundle.GetAssetBundle(Uri.EscapeUriString(url));
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var assetBundle = DownloadHandlerAssetBundle.GetContent(unityWebRequest);
|
||||
task.SetResult(assetBundle);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取AudioClip
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="audioType"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<AudioClip> GetAudioClip(string url, AudioType audioType, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var task = FTask<AudioClip>.Create(false);
|
||||
var unityWebRequest = UnityWebRequestMultimedia.GetAudioClip(Uri.EscapeUriString(url), audioType);
|
||||
var unityWebRequestAsyncOperation = unityWebRequest.SendWebRequest();
|
||||
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.Add(() =>
|
||||
{
|
||||
unityWebRequest.Abort();
|
||||
task.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
||||
unityWebRequestAsyncOperation.completed += operation =>
|
||||
{
|
||||
if (unityWebRequest.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
var audioClip = DownloadHandlerAudioClip.GetContent(unityWebRequest);
|
||||
task.SetResult(audioClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(unityWebRequest.error);
|
||||
task.SetResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a679bf05117455388666f6d8cc35d7d
|
||||
timeCreated: 1726022012
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket帮助类
|
||||
/// </summary>
|
||||
public static partial class WebSocketHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据字符串获取WebSocket的连接地址
|
||||
/// </summary>
|
||||
/// <param name="address">目标服务器地址格式为:127.0.0.1:2000</param>
|
||||
/// <param name="isHttps">目标服务器是否为加密连接也就是https</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FormatException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetWebSocketAddress(string address, bool isHttps)
|
||||
{
|
||||
var addressSplit = address.Split(':');
|
||||
if (addressSplit.Length != 2)
|
||||
{
|
||||
throw new FormatException("Invalid format");
|
||||
}
|
||||
|
||||
var ipString = addressSplit[0];
|
||||
var portString = addressSplit[1];
|
||||
|
||||
if (!int.TryParse(portString, out var port) || port < 0 || port > 65535)
|
||||
{
|
||||
throw new FormatException("Invalid port number");
|
||||
}
|
||||
|
||||
return isHttps ? $"wss://{ipString}:{portString}" : $"ws://{ipString}:{portString}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43d82d55edae640d69ed83a832d4dd3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 精度设置
|
||||
/// </summary>
|
||||
public static partial class WinPeriod
|
||||
{
|
||||
// 一般默认的精度不止1毫秒(不同操作系统有所不同),需要调用timeBeginPeriod与timeEndPeriod来设置精度
|
||||
[DllImport("winmm")]
|
||||
private static extern void timeBeginPeriod(int t);
|
||||
/// <summary>
|
||||
/// 针对Windows平台设置精度
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20f6f771c52ad42f0a0e74df662f45cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user