mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] TEngineServer
[+] TEngineServer
This commit is contained in:
432
Assets/GameScripts/DotNet/Core/Helper/AudioHelper.cs
Normal file
432
Assets/GameScripts/DotNet/Core/Helper/AudioHelper.cs
Normal file
@@ -0,0 +1,432 @@
|
||||
#if TENGINE_UNITY
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class AudioHelper
|
||||
{
|
||||
// Force save as 16-bit .wav
|
||||
const int BlockSize_16Bit = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Load PCM format *.wav audio file (using Unity's Application data path) and convert to AudioClip.
|
||||
/// </summary>
|
||||
/// <returns>The AudioClip.</returns>
|
||||
/// <param name="filePath">Local file path to .wav file</param>
|
||||
public static AudioClip ToAudioClip(string filePath)
|
||||
{
|
||||
if (!filePath.StartsWith(UnityEngine.Application.persistentDataPath) &&
|
||||
!filePath.StartsWith(UnityEngine.Application.dataPath))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"This only supports files that are stored using Unity's Application data path. \nTo load bundled resources use 'Resources.Load(\"filename\") typeof(AudioClip)' method. \nhttps://docs.unity3d.com/ScriptReference/Resources.Load.html");
|
||||
return null;
|
||||
}
|
||||
|
||||
var fileBytes = File.ReadAllBytes(filePath);
|
||||
return ToAudioClip(fileBytes, 0);
|
||||
}
|
||||
|
||||
public static AudioClip ToAudioClip(byte[] fileBytes, int offsetSamples = 0, string name = "wav")
|
||||
{
|
||||
//string riff = Encoding.ASCII.GetString (fileBytes, 0, 4);
|
||||
//string wave = Encoding.ASCII.GetString (fileBytes, 8, 4);
|
||||
int subchunk1 = BitConverter.ToInt32(fileBytes, 16);
|
||||
UInt16 audioFormat = BitConverter.ToUInt16(fileBytes, 20);
|
||||
|
||||
// NB: Only uncompressed PCM wav files are supported.
|
||||
string formatCode = FormatCode(audioFormat);
|
||||
Debug.AssertFormat(audioFormat == 1 || audioFormat == 65534,
|
||||
"Detected format code '{0}' {1}, but only PCM and WaveFormatExtensable uncompressed formats are currently supported.",
|
||||
audioFormat, formatCode);
|
||||
|
||||
UInt16 channels = BitConverter.ToUInt16(fileBytes, 22);
|
||||
int sampleRate = BitConverter.ToInt32(fileBytes, 24);
|
||||
//int byteRate = BitConverter.ToInt32 (fileBytes, 28);
|
||||
//UInt16 blockAlign = BitConverter.ToUInt16 (fileBytes, 32);
|
||||
UInt16 bitDepth = BitConverter.ToUInt16(fileBytes, 34);
|
||||
|
||||
int headerOffset = 16 + 4 + subchunk1 + 4;
|
||||
int subchunk2 = BitConverter.ToInt32(fileBytes, headerOffset);
|
||||
//Debug.LogFormat ("riff={0} wave={1} subchunk1={2} format={3} channels={4} sampleRate={5} byteRate={6} blockAlign={7} bitDepth={8} headerOffset={9} subchunk2={10} filesize={11}", riff, wave, subchunk1, formatCode, channels, sampleRate, byteRate, blockAlign, bitDepth, headerOffset, subchunk2, fileBytes.Length);
|
||||
|
||||
float[] data;
|
||||
switch (bitDepth)
|
||||
{
|
||||
case 8:
|
||||
data = Convert8BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
|
||||
break;
|
||||
case 16:
|
||||
data = Convert16BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
|
||||
break;
|
||||
case 24:
|
||||
data = Convert24BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
|
||||
break;
|
||||
case 32:
|
||||
data = Convert32BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(bitDepth + " bit depth is not supported.");
|
||||
}
|
||||
|
||||
AudioClip audioClip = AudioClip.Create(name, data.Length, (int)channels, sampleRate, false);
|
||||
audioClip.SetData(data, 0);
|
||||
return audioClip;
|
||||
}
|
||||
|
||||
#region wav file bytes to Unity AudioClip conversion methods
|
||||
|
||||
private static float[] Convert8BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
|
||||
{
|
||||
int wavSize = BitConverter.ToInt32(source, headerOffset);
|
||||
headerOffset += sizeof(int);
|
||||
Debug.AssertFormat(wavSize > 0 && wavSize == dataSize,
|
||||
"Failed to get valid 8-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize,
|
||||
headerOffset);
|
||||
|
||||
float[] data = new float[wavSize];
|
||||
|
||||
sbyte maxValue = sbyte.MaxValue;
|
||||
|
||||
int i = 0;
|
||||
while (i < wavSize)
|
||||
{
|
||||
data[i] = (float)source[i] / maxValue;
|
||||
++i;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static float[] Convert16BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
|
||||
{
|
||||
int wavSize = BitConverter.ToInt32(source, headerOffset);
|
||||
headerOffset += sizeof(int);
|
||||
Debug.AssertFormat(wavSize > 0 && wavSize == dataSize,
|
||||
"Failed to get valid 16-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize,
|
||||
headerOffset);
|
||||
|
||||
int x = sizeof(Int16); // block size = 2
|
||||
int convertedSize = wavSize / x;
|
||||
|
||||
float[] data = new float[convertedSize];
|
||||
|
||||
Int16 maxValue = Int16.MaxValue;
|
||||
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
while (i < convertedSize)
|
||||
{
|
||||
offset = i * x + headerOffset;
|
||||
data[i] = (float)BitConverter.ToInt16(source, offset) / maxValue;
|
||||
++i;
|
||||
}
|
||||
|
||||
Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}",
|
||||
data.Length, convertedSize);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static float[] Convert24BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
|
||||
{
|
||||
int wavSize = BitConverter.ToInt32(source, headerOffset);
|
||||
headerOffset += sizeof(int);
|
||||
Debug.AssertFormat(wavSize > 0 && wavSize == dataSize,
|
||||
"Failed to get valid 24-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize,
|
||||
headerOffset);
|
||||
|
||||
int x = 3; // block size = 3
|
||||
int convertedSize = wavSize / x;
|
||||
|
||||
int maxValue = Int32.MaxValue;
|
||||
|
||||
float[] data = new float[convertedSize];
|
||||
|
||||
byte[]
|
||||
block = new byte[sizeof(int)]; // using a 4 byte block for copying 3 bytes, then copy bytes with 1 offset
|
||||
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
while (i < convertedSize)
|
||||
{
|
||||
offset = i * x + headerOffset;
|
||||
Buffer.BlockCopy(source, offset, block, 1, x);
|
||||
data[i] = (float)BitConverter.ToInt32(block, 0) / maxValue;
|
||||
++i;
|
||||
}
|
||||
|
||||
Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}",
|
||||
data.Length, convertedSize);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static float[] Convert32BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
|
||||
{
|
||||
int wavSize = BitConverter.ToInt32(source, headerOffset);
|
||||
headerOffset += sizeof(int);
|
||||
Debug.AssertFormat(wavSize > 0 && wavSize == dataSize,
|
||||
"Failed to get valid 32-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize,
|
||||
headerOffset);
|
||||
|
||||
int x = sizeof(float); // block size = 4
|
||||
int convertedSize = wavSize / x;
|
||||
|
||||
Int32 maxValue = Int32.MaxValue;
|
||||
|
||||
float[] data = new float[convertedSize];
|
||||
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
while (i < convertedSize)
|
||||
{
|
||||
offset = i * x + headerOffset;
|
||||
data[i] = (float)BitConverter.ToInt32(source, offset) / maxValue;
|
||||
++i;
|
||||
}
|
||||
|
||||
Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}",
|
||||
data.Length, convertedSize);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static byte[] FromAudioClip(AudioClip audioClip)
|
||||
{
|
||||
string file;
|
||||
return FromAudioClip(audioClip, out file, false);
|
||||
}
|
||||
|
||||
public static byte[] FromAudioClip(AudioClip audioClip, out string filepath, bool saveAsFile = true,
|
||||
string dirname = "recordings")
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
|
||||
const int headerSize = 44;
|
||||
|
||||
// get bit depth
|
||||
UInt16 bitDepth = 16; //BitDepth (audioClip);
|
||||
|
||||
// NB: Only supports 16 bit
|
||||
//Debug.AssertFormat (bitDepth == 16, "Only converting 16 bit is currently supported. The audio clip data is {0} bit.", bitDepth);
|
||||
|
||||
// total file size = 44 bytes for header format and audioClip.samples * factor due to float to Int16 / sbyte conversion
|
||||
int fileSize = audioClip.samples * BlockSize_16Bit + headerSize; // BlockSize (bitDepth)
|
||||
|
||||
// chunk descriptor (riff)
|
||||
WriteFileHeader(ref stream, fileSize);
|
||||
// file header (fmt)
|
||||
WriteFileFormat(ref stream, audioClip.channels, audioClip.frequency, bitDepth);
|
||||
// data chunks (data)
|
||||
WriteFileData(ref stream, audioClip, bitDepth);
|
||||
|
||||
byte[] bytes = stream.ToArray();
|
||||
|
||||
// Validate total bytes
|
||||
Debug.AssertFormat(bytes.Length == fileSize, "Unexpected AudioClip to wav format byte count: {0} == {1}",
|
||||
bytes.Length, fileSize);
|
||||
|
||||
// Save file to persistant storage location
|
||||
if (saveAsFile)
|
||||
{
|
||||
filepath = string.Format("{0}/{1}/{2}.{3}", UnityEngine.Application.persistentDataPath, dirname,
|
||||
DateTime.UtcNow.ToString("yyMMdd-HHmmss-fff"), "wav");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
|
||||
File.WriteAllBytes(filepath, bytes);
|
||||
//Debug.Log ("Auto-saved .wav file: " + filepath);
|
||||
}
|
||||
else
|
||||
{
|
||||
filepath = null;
|
||||
}
|
||||
|
||||
stream.Dispose();
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#region write .wav file functions
|
||||
|
||||
private static int WriteFileHeader(ref MemoryStream stream, int fileSize)
|
||||
{
|
||||
int count = 0;
|
||||
int total = 12;
|
||||
|
||||
// riff chunk id
|
||||
byte[] riff = Encoding.ASCII.GetBytes("RIFF");
|
||||
count += WriteBytesToMemoryStream(ref stream, riff, "ID");
|
||||
|
||||
// riff chunk size
|
||||
int chunkSize = fileSize - 8; // total size - 8 for the other two fields in the header
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(chunkSize), "CHUNK_SIZE");
|
||||
|
||||
byte[] wave = Encoding.ASCII.GetBytes("WAVE");
|
||||
count += WriteBytesToMemoryStream(ref stream, wave, "FORMAT");
|
||||
|
||||
// Validate header
|
||||
Debug.AssertFormat(count == total, "Unexpected wav descriptor byte count: {0} == {1}", count, total);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static int WriteFileFormat(ref MemoryStream stream, int channels, int sampleRate, UInt16 bitDepth)
|
||||
{
|
||||
int count = 0;
|
||||
int total = 24;
|
||||
|
||||
byte[] id = Encoding.ASCII.GetBytes("fmt ");
|
||||
count += WriteBytesToMemoryStream(ref stream, id, "FMT_ID");
|
||||
|
||||
int subchunk1Size = 16; // 24 - 8
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(subchunk1Size), "SUBCHUNK_SIZE");
|
||||
|
||||
UInt16 audioFormat = 1;
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(audioFormat), "AUDIO_FORMAT");
|
||||
|
||||
UInt16 numChannels = Convert.ToUInt16(channels);
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(numChannels), "CHANNELS");
|
||||
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(sampleRate), "SAMPLE_RATE");
|
||||
|
||||
int byteRate = sampleRate * channels * BytesPerSample(bitDepth);
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(byteRate), "BYTE_RATE");
|
||||
|
||||
UInt16 blockAlign = Convert.ToUInt16(channels * BytesPerSample(bitDepth));
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(blockAlign), "BLOCK_ALIGN");
|
||||
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(bitDepth), "BITS_PER_SAMPLE");
|
||||
|
||||
// Validate format
|
||||
Debug.AssertFormat(count == total, "Unexpected wav fmt byte count: {0} == {1}", count, total);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static int WriteFileData(ref MemoryStream stream, AudioClip audioClip, UInt16 bitDepth)
|
||||
{
|
||||
int count = 0;
|
||||
int total = 8;
|
||||
|
||||
// Copy float[] data from AudioClip
|
||||
float[] data = new float[audioClip.samples * audioClip.channels];
|
||||
audioClip.GetData(data, 0);
|
||||
|
||||
byte[] bytes = ConvertAudioClipDataToInt16ByteArray(data);
|
||||
|
||||
byte[] id = Encoding.ASCII.GetBytes("data");
|
||||
count += WriteBytesToMemoryStream(ref stream, id, "DATA_ID");
|
||||
|
||||
int subchunk2Size = Convert.ToInt32(audioClip.samples * BlockSize_16Bit); // BlockSize (bitDepth)
|
||||
count += WriteBytesToMemoryStream(ref stream, BitConverter.GetBytes(subchunk2Size), "SAMPLES");
|
||||
|
||||
// Validate header
|
||||
Debug.AssertFormat(count == total, "Unexpected wav data id byte count: {0} == {1}", count, total);
|
||||
|
||||
// Write bytes to stream
|
||||
count += WriteBytesToMemoryStream(ref stream, bytes, "DATA");
|
||||
|
||||
// Validate audio data
|
||||
Debug.AssertFormat(bytes.Length == subchunk2Size, "Unexpected AudioClip to wav subchunk2 size: {0} == {1}",
|
||||
bytes.Length, subchunk2Size);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static byte[] ConvertAudioClipDataToInt16ByteArray(float[] data)
|
||||
{
|
||||
MemoryStream dataStream = new MemoryStream();
|
||||
|
||||
int x = sizeof(Int16);
|
||||
|
||||
Int16 maxValue = Int16.MaxValue;
|
||||
|
||||
int i = 0;
|
||||
while (i < data.Length)
|
||||
{
|
||||
dataStream.Write(BitConverter.GetBytes(Convert.ToInt16(data[i] * maxValue)), 0, x);
|
||||
++i;
|
||||
}
|
||||
|
||||
byte[] bytes = dataStream.ToArray();
|
||||
|
||||
// Validate converted bytes
|
||||
Debug.AssertFormat(data.Length * x == bytes.Length,
|
||||
"Unexpected float[] to Int16 to byte[] size: {0} == {1}", data.Length * x, bytes.Length);
|
||||
|
||||
dataStream.Dispose();
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static int WriteBytesToMemoryStream(ref MemoryStream stream, byte[] bytes, string tag = "")
|
||||
{
|
||||
int count = bytes.Length;
|
||||
stream.Write(bytes, 0, count);
|
||||
//Debug.LogFormat ("WAV:{0} wrote {1} bytes.", tag, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the bit depth of an AudioClip
|
||||
/// </summary>
|
||||
/// <returns>The bit depth. Should be 8 or 16 or 32 bit.</returns>
|
||||
/// <param name="audioClip">Audio clip.</param>
|
||||
public static UInt16 BitDepth(AudioClip audioClip)
|
||||
{
|
||||
UInt16 bitDepth =
|
||||
Convert.ToUInt16(audioClip.samples * audioClip.channels * audioClip.length / audioClip.frequency);
|
||||
Debug.AssertFormat(bitDepth == 8 || bitDepth == 16 || bitDepth == 32,
|
||||
"Unexpected AudioClip bit depth: {0}. Expected 8 or 16 or 32 bit.", bitDepth);
|
||||
return bitDepth;
|
||||
}
|
||||
|
||||
private static int BytesPerSample(UInt16 bitDepth)
|
||||
{
|
||||
return bitDepth / 8;
|
||||
}
|
||||
|
||||
private static int BlockSize(UInt16 bitDepth)
|
||||
{
|
||||
switch (bitDepth)
|
||||
{
|
||||
case 32:
|
||||
return sizeof(Int32); // 32-bit -> 4 bytes (Int32)
|
||||
case 16:
|
||||
return sizeof(Int16); // 16-bit -> 2 bytes (Int16)
|
||||
case 8:
|
||||
return sizeof(sbyte); // 8-bit -> 1 byte (sbyte)
|
||||
default:
|
||||
throw new Exception(bitDepth + " bit depth is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatCode(UInt16 code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 1:
|
||||
return "PCM";
|
||||
case 2:
|
||||
return "ADPCM";
|
||||
case 3:
|
||||
return "IEEE";
|
||||
case 7:
|
||||
return "μ-law";
|
||||
case 65534:
|
||||
return "WaveFormatExtensable";
|
||||
default:
|
||||
Debug.LogWarning("Unknown wav code format:" + code);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
11
Assets/GameScripts/DotNet/Core/Helper/AudioHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/AudioHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84a1c6c8542d1814da8b5fba76c64e74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
179
Assets/GameScripts/DotNet/Core/Helper/ByteHelper.cs
Normal file
179
Assets/GameScripts/DotNet/Core/Helper/ByteHelper.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class ByteHelper
|
||||
{
|
||||
private static readonly string[] Suffix = { "Byte", "KB", "MB", "GB", "TB" };
|
||||
|
||||
public static long ReadInt64(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static int ReadInt32(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static long ReadInt64(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
public static int ReadInt32(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
public static string ToHex(this byte b)
|
||||
{
|
||||
return b.ToString("X2");
|
||||
}
|
||||
|
||||
public static string ToHex(this byte[] bytes)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("X2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static string ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes);
|
||||
}
|
||||
|
||||
public static string ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
public static string Utf8ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public static string Utf8ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void WriteTo(this byte[] bytes, int offset, byte num)
|
||||
{
|
||||
bytes[offset] = num;
|
||||
}
|
||||
|
||||
public static void WriteTo(this byte[] bytes, int offset, short num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
public static void WriteTo(this byte[] bytes, int offset, ushort num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
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]}";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/ByteHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/ByteHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33f44962744fcd64d98b9bcd0fc41bf9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
77
Assets/GameScripts/DotNet/Core/Helper/CryptHelper.cs
Normal file
77
Assets/GameScripts/DotNet/Core/Helper/CryptHelper.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class CryptHelper
|
||||
{
|
||||
// 对应的字符串SYUwQN360OPY1gaL
|
||||
// 在这个网站随机生成的 http://tool.c7sky.com/password/
|
||||
private static readonly byte[] Key =
|
||||
{
|
||||
0x53, 0x59, 0x55, 0x77, 0x51, 0x4e, 0x33, 0x36,
|
||||
0x30, 0x4f, 0x50, 0x59, 0x31, 0x67, 0x61, 0x4c
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的KEY
|
||||
/// </summary>
|
||||
/// <param name="keyStr">一个长度为16的字符串、如果超过只会截取前16位</param>
|
||||
/// <returns>返回的是一个十六进制的字符串、每个用,分割的、每个都是一个byte</returns>
|
||||
public static string CreateKey(string keyStr)
|
||||
{
|
||||
if (keyStr.Length > 16)
|
||||
{
|
||||
keyStr = keyStr.Substring(0, 15);
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(keyStr);
|
||||
return $"0x{BitConverter.ToString(bytes, 0).Replace("-", ", 0x").ToLower()}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AES 加密
|
||||
/// </summary>
|
||||
/// <param name="toEncryptArray"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] AesEncrypt(byte[] toEncryptArray)
|
||||
{
|
||||
var rm = Aes.Create();
|
||||
rm.Key = Key;
|
||||
rm.Mode = CipherMode.ECB;
|
||||
rm.Padding = PaddingMode.PKCS7;
|
||||
var cTransform = rm.CreateEncryptor();
|
||||
return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AES 解密
|
||||
/// </summary>
|
||||
/// <param name="toEncryptArray"></param>
|
||||
/// <returns></returns>
|
||||
public static MemoryStream AesDecryptReturnStream(byte[] toEncryptArray)
|
||||
{
|
||||
var bytes = AesDecrypt(toEncryptArray);
|
||||
|
||||
return new MemoryStream(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AES 解密
|
||||
/// </summary>
|
||||
/// <param name="toEncryptArray"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] AesDecrypt(byte[] toEncryptArray)
|
||||
{
|
||||
var rm = Aes.Create();
|
||||
rm.Key = Key;
|
||||
rm.Mode = CipherMode.ECB;
|
||||
rm.Padding = PaddingMode.PKCS7;
|
||||
|
||||
var cTransform = rm.CreateDecryptor();
|
||||
return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/CryptHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/CryptHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 060c741ce3ae4d54498be5f7a286801b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
112
Assets/GameScripts/DotNet/Core/Helper/FileHelper.cs
Normal file
112
Assets/GameScripts/DotNet/Core/Helper/FileHelper.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class FileHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 拷贝文件到目标路径、如果目标目录不存在会自动创建目录
|
||||
/// </summary>
|
||||
/// <param name="sourceFile"></param>
|
||||
/// <param name="destinationFile"></param>
|
||||
/// <param name="overwrite"></param>
|
||||
public static void Copy(string sourceFile, string destinationFile, bool overwrite)
|
||||
{
|
||||
var directoriesByFilePath = GetDirectoriesByFilePath(destinationFile);
|
||||
|
||||
foreach (var dir in directoriesByFilePath)
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
File.Copy(sourceFile, destinationFile, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件路径内的所有文件夹路径
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string> GetDirectoriesByFilePath(string filePath)
|
||||
{
|
||||
var dir = "";
|
||||
var directories = new List<string>();
|
||||
var fileDirectories = filePath.Split('/');
|
||||
|
||||
for (var i = 0; i < fileDirectories.Length - 1; i++)
|
||||
{
|
||||
dir = $"{dir}{fileDirectories[i]}/";
|
||||
directories.Add(dir);
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static void ClearDirectoryFile(string folderPath)
|
||||
{
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(folderPath);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/FileHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/FileHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74cc512af7309484fa066b16175c6331
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
29
Assets/GameScripts/DotNet/Core/Helper/JsonHelper.cs
Normal file
29
Assets/GameScripts/DotNet/Core/Helper/JsonHelper.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class JsonHelper
|
||||
{
|
||||
public static string ToJson<T>(this T t)
|
||||
{
|
||||
return JsonConvert.SerializeObject(t);
|
||||
}
|
||||
|
||||
public static object Deserialize(this string json, Type type, bool reflection = true)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(this string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
public static T Clone<T>(T t)
|
||||
{
|
||||
return t.ToJson().Deserialize<T>();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/JsonHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/JsonHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44fd4e119f9d29342a2e312d58f6a1fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
27
Assets/GameScripts/DotNet/Core/Helper/MD5Helper.cs
Normal file
27
Assets/GameScripts/DotNet/Core/Helper/MD5Helper.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class MD5Helper
|
||||
{
|
||||
public static string FileMD5(string filePath)
|
||||
{
|
||||
using var file = new FileStream(filePath, FileMode.Open);
|
||||
return FileMD5(file);
|
||||
}
|
||||
|
||||
public static string FileMD5(FileStream fileStream)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
return md5.ComputeHash(fileStream).ToHex("x2");
|
||||
}
|
||||
|
||||
public static string BytesMD5(byte[] bytes)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
bytes = md5.ComputeHash(bytes);
|
||||
return bytes.ToHex("x2");
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/MD5Helper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/MD5Helper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c37f13dde60869459e624143ae45a3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
15
Assets/GameScripts/DotNet/Core/Helper/MemoryStreamHelper.cs
Normal file
15
Assets/GameScripts/DotNet/Core/Helper/MemoryStreamHelper.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.IO;
|
||||
using TEngine.IO;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class MemoryStreamHelper
|
||||
{
|
||||
private static readonly RecyclableMemoryStreamManager Manager = new RecyclableMemoryStreamManager();
|
||||
|
||||
public static MemoryStream GetRecyclableMemoryStream()
|
||||
{
|
||||
return Manager.GetStream();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90f0f0ce0808b61458dbb17cdc1f3766
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Helper/Mongo.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Helper/Mongo.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90685e6f420bcae4f851975873f430c9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
118
Assets/GameScripts/DotNet/Core/Helper/Mongo/MongoHelper.cs
Normal file
118
Assets/GameScripts/DotNet/Core/Helper/Mongo/MongoHelper.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
#if TENGINE_NET
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Conventions;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace TEngine.Core;
|
||||
|
||||
public sealed class MongoHelper : Singleton<MongoHelper>
|
||||
{
|
||||
private readonly HashSet<int> _registerCount = new HashSet<int>(3);
|
||||
|
||||
static MongoHelper()
|
||||
{
|
||||
// 自动注册IgnoreExtraElements
|
||||
var conventionPack = new ConventionPack {new IgnoreExtraElementsConvention(true)};
|
||||
ConventionRegistry.Register("IgnoreExtraElements", conventionPack, type => true);
|
||||
BsonSerializer.TryRegisterSerializer(typeof(float2), new StructBsonSerialize<float2>());
|
||||
BsonSerializer.TryRegisterSerializer(typeof(float3), new StructBsonSerialize<float3>());
|
||||
BsonSerializer.TryRegisterSerializer(typeof(float4), new StructBsonSerialize<float4>());
|
||||
BsonSerializer.TryRegisterSerializer(typeof(quaternion), new StructBsonSerialize<quaternion>());
|
||||
}
|
||||
|
||||
protected override void OnLoad(int assemblyName)
|
||||
{
|
||||
if (_registerCount.Count == 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_registerCount.Add(assemblyName);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
foreach (var type in AssemblyManager.ForEach(assemblyName))
|
||||
{
|
||||
if (type.IsInterface || type.IsAbstract || !typeof(Entity).IsAssignableFrom(type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BsonClassMap.LookupClassMap(type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T Deserialize<T>(byte[] bytes)
|
||||
{
|
||||
return BsonSerializer.Deserialize<T>(bytes);
|
||||
}
|
||||
|
||||
public object Deserialize(byte[] bytes, Type type)
|
||||
{
|
||||
return BsonSerializer.Deserialize(bytes, type);
|
||||
}
|
||||
|
||||
public object Deserialize(byte[] bytes, string type)
|
||||
{
|
||||
return BsonSerializer.Deserialize(bytes, Type.GetType(type));
|
||||
}
|
||||
|
||||
public T Deserialize<T>(Stream stream)
|
||||
{
|
||||
return BsonSerializer.Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
public object Deserialize(Stream stream, Type type)
|
||||
{
|
||||
return BsonSerializer.Deserialize(stream, type);
|
||||
}
|
||||
|
||||
public object DeserializeFrom(Type type, MemoryStream stream)
|
||||
{
|
||||
return Deserialize(stream, type);
|
||||
}
|
||||
|
||||
public T DeserializeFrom<T>(MemoryStream stream)
|
||||
{
|
||||
return Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
public T DeserializeFrom<T>(byte[] bytes, int index, int count)
|
||||
{
|
||||
return BsonSerializer.Deserialize<T>(bytes.AsMemory(index, count).ToArray());
|
||||
}
|
||||
|
||||
public byte[] SerializeTo<T>(T t)
|
||||
{
|
||||
return t.ToBson();
|
||||
}
|
||||
|
||||
public void SerializeTo<T>(T t, MemoryStream stream)
|
||||
{
|
||||
var bytes = t.ToBson();
|
||||
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public T Clone<T>(T t)
|
||||
{
|
||||
return Deserialize<T>(t.ToBson());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if TENGINE_UNITY
|
||||
using System;
|
||||
using System.IO;
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public sealed class MongoHelper : Singleton<MongoHelper>
|
||||
{
|
||||
public object DeserializeFrom(Type type, MemoryStream stream)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8305336cdf88b72498d9c3365424ed49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
#if TENGINE_NET
|
||||
using System.Reflection;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Serializers;
|
||||
|
||||
namespace TEngine.Core;
|
||||
|
||||
public class StructBsonSerialize<TValue> : StructSerializerBase<TValue> where TValue : struct
|
||||
{
|
||||
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
|
||||
{
|
||||
var nominalType = args.NominalType;
|
||||
|
||||
var bsonWriter = context.Writer;
|
||||
|
||||
bsonWriter.WriteStartDocument();
|
||||
|
||||
var fields = nominalType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
bsonWriter.WriteName(field.Name);
|
||||
BsonSerializer.Serialize(bsonWriter, field.FieldType, field.GetValue(value));
|
||||
}
|
||||
|
||||
bsonWriter.WriteEndDocument();
|
||||
}
|
||||
|
||||
public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
|
||||
{
|
||||
//boxing is required for SetValue to work
|
||||
object obj = new TValue();
|
||||
var actualType = args.NominalType;
|
||||
var bsonReader = context.Reader;
|
||||
|
||||
bsonReader.ReadStartDocument();
|
||||
|
||||
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
|
||||
{
|
||||
var name = bsonReader.ReadName(Utf8NameDecoder.Instance);
|
||||
|
||||
var field = actualType.GetField(name,
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (field != null)
|
||||
{
|
||||
var value = BsonSerializer.Deserialize(bsonReader, field.FieldType);
|
||||
field.SetValue(obj, value);
|
||||
}
|
||||
}
|
||||
|
||||
bsonReader.ReadEndDocument();
|
||||
|
||||
return (TValue) obj;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ac9e1e3f21a01f40a9bda7f7cc4e667
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Helper/Proto.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Helper/Proto.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9557f6157233b304fb1f1669311f5fc9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
11
Assets/GameScripts/DotNet/Core/Helper/Proto/AProto.cs
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/Proto/AProto.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using ProtoBuf;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
[ProtoContract]
|
||||
public abstract class AProto
|
||||
{
|
||||
public virtual void AfterDeserialization() => EndInit();
|
||||
protected virtual void EndInit() { }
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/Proto/AProto.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/Proto/AProto.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3172691aec101574282e3e66d998d4dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using ProtoBuf;
|
||||
#pragma warning disable CS8604
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class ProtoBufHelper
|
||||
{
|
||||
public static object FromBytes(Type type, byte[] bytes, int index, int count)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, index, count);
|
||||
return Serializer.Deserialize(type, stream);
|
||||
}
|
||||
|
||||
public static T FromBytes<T>(byte[] bytes)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, 0, bytes.Length);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
// return FromBytes<T>(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public static T FromBytes<T>(byte[] bytes, int index, int count)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, index, count);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(object message)
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
Serializer.Serialize(stream, message);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
public static void ToStream(object message, MemoryStream stream)
|
||||
{
|
||||
Serializer.Serialize(stream, message);
|
||||
}
|
||||
|
||||
public static object FromStream(Type type, MemoryStream stream)
|
||||
{
|
||||
return Serializer.Deserialize(type, stream);
|
||||
}
|
||||
|
||||
public static T FromStream<T>(MemoryStream stream)
|
||||
{
|
||||
return (T) Serializer.Deserialize(typeof(T), stream);
|
||||
}
|
||||
|
||||
public static T Clone<T>(T t)
|
||||
{
|
||||
var bytes = ToBytes(t);
|
||||
using var stream = new MemoryStream(bytes, 0, bytes.Length);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27e0977e977a7834695b073736221239
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
251
Assets/GameScripts/DotNet/Core/Helper/RandomHelper.cs
Normal file
251
Assets/GameScripts/DotNet/Core/Helper/RandomHelper.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using TEngine.DataStructure;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static 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];
|
||||
|
||||
public static ulong RandUInt64()
|
||||
{
|
||||
Random.NextBytes(Byte8);
|
||||
return BitConverter.ToUInt64(Byte8, 0);
|
||||
}
|
||||
|
||||
public static long RandInt64()
|
||||
{
|
||||
Random.NextBytes(Byte8);
|
||||
return BitConverter.ToInt64(Byte8, 0);
|
||||
}
|
||||
|
||||
public static uint RandUInt32()
|
||||
{
|
||||
return (uint) Random.Next();
|
||||
}
|
||||
|
||||
public static ushort RandUInt16()
|
||||
{
|
||||
Random.NextBytes(Byte2);
|
||||
return BitConverter.ToUInt16(Byte2, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取lower与Upper之间的随机数,包含下限,不包含上限
|
||||
/// </summary>
|
||||
/// <param name="lower"></param>
|
||||
/// <param name="upper"></param>
|
||||
/// <returns></returns>
|
||||
public static int RandomNumber(int lower, int upper)
|
||||
{
|
||||
return Random.Next(lower, upper);
|
||||
}
|
||||
|
||||
public static bool RandomBool()
|
||||
{
|
||||
return Random.Next(2) == 0;
|
||||
}
|
||||
|
||||
public static T RandomArray<T>(this T[] array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count() - 1)];
|
||||
}
|
||||
|
||||
public static T RandomArray<T>(this List<T> array)
|
||||
{
|
||||
return array[RandomNumber(0, array.Count() - 1)];
|
||||
}
|
||||
|
||||
/// <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]);
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
/// 在4个Vector2点范围内随机出一个位置
|
||||
/// </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));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/RandomHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/RandomHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e66c86ada7ae8d4680728f4d6562245
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
36
Assets/GameScripts/DotNet/Core/Helper/TimeHelper.cs
Normal file
36
Assets/GameScripts/DotNet/Core/Helper/TimeHelper.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class TimeHelper
|
||||
{
|
||||
public const long Hour = 3600000; // 小时毫秒值 60 * 60 * 1000
|
||||
public const long Minute = 60000; // 分钟毫秒值 60 * 1000
|
||||
public const long OneDay = 86400000; // 天毫秒值 24 * 60 * 60 * 1000
|
||||
private const long Epoch = 621355968000000000L; // 1970年1月1日的Ticks
|
||||
|
||||
private static readonly DateTime Dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public static long Now => (DateTime.UtcNow.Ticks - Epoch) / 10000;
|
||||
|
||||
#if TENGINE_UNITY
|
||||
public static long TimeDiff;
|
||||
public static long ServerNow => Now + TimeDiff;
|
||||
#endif
|
||||
|
||||
public static long Transition(DateTime d)
|
||||
{
|
||||
return (d.Ticks - Epoch) / 10000;
|
||||
}
|
||||
|
||||
public static DateTime Transition(long timeStamp)
|
||||
{
|
||||
return Dt1970.AddTicks(timeStamp);
|
||||
}
|
||||
|
||||
public static DateTime TransitionLocal(long timeStamp)
|
||||
{
|
||||
return Dt1970.AddTicks(timeStamp).ToLocalTime();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/GameScripts/DotNet/Core/Helper/TimeHelper.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Helper/TimeHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4061d25ace5fcdf4f866a85e45e1a8d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user