Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
public class AESManager
|
||||
{
|
||||
private static readonly byte[] Key = Encoding.UTF8.GetBytes("01fsd56#@9AB183F"); // 16字节的密钥
|
||||
private static readonly byte[] IV = Encoding.UTF8.GetBytes("01fsd56789A*%#3F"); // 16字节的初始向量
|
||||
private static byte[] XWKey = Encoding.UTF8.GetBytes("183F01fsd56#@9AB"); // 16字节的密钥
|
||||
private static byte[] XWIV = Encoding.UTF8.GetBytes("h1O2Y7d689A*%#0X"); // 16字节的初始向量
|
||||
|
||||
//与 XGame.AESManager 对齐:静态构造里自动用口令初始化 XWKey/XWIV,
|
||||
//避免某条路径(如 WebGL 加载)忘了显式 Set() 而退化到上面写死的默认 key,导致解密全错。
|
||||
static AESManager()
|
||||
{
|
||||
Set(ChangeBytes(Encoding.UTF8.GetBytes("#@pOrhgf2026091211325890vciu%c-X")));
|
||||
}
|
||||
|
||||
public static byte[] ChangeBytes(byte[] src, byte xor = 0xb4)
|
||||
{
|
||||
for (int i = 0; i < src.Length; i++)
|
||||
{
|
||||
src[i] = (byte)(src[i] ^ xor);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
public static void Set(byte[] data)
|
||||
{
|
||||
if (data.Length != 32)
|
||||
throw new Exception("AES Key or IV length must be 32 characters");
|
||||
else
|
||||
{
|
||||
XWKey = new byte[16];
|
||||
Array.Copy(data, 0, XWKey, 0, 16);
|
||||
XWIV = new byte[16];
|
||||
Array.Copy(data, 16, XWIV, 0, 16);
|
||||
}
|
||||
}
|
||||
public static string Encrypt(string plainText)
|
||||
{
|
||||
string encryptedText = Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(plainText)));
|
||||
return encryptedText;
|
||||
}
|
||||
public static byte[] Encrypt(byte[] plainBytes, bool bXW = true)
|
||||
{
|
||||
using (Aes aes = Aes.Create())
|
||||
{
|
||||
if (bXW)
|
||||
{
|
||||
aes.Key = XWKey;
|
||||
aes.IV = XWIV;
|
||||
}
|
||||
else
|
||||
{
|
||||
aes.Key = Key;
|
||||
aes.IV = IV;
|
||||
}
|
||||
|
||||
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
|
||||
byte[] encryptedBytes = null;
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(plainBytes, 0, plainBytes.Length);
|
||||
cs.FlushFinalBlock();
|
||||
}
|
||||
encryptedBytes = ms.ToArray();
|
||||
}
|
||||
return encryptedBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] encryptedBytes, bool bXW = true)
|
||||
{
|
||||
using (Aes aes = Aes.Create())
|
||||
{
|
||||
if (bXW)
|
||||
{
|
||||
aes.Key = XWKey;
|
||||
aes.IV = XWIV;
|
||||
}
|
||||
else
|
||||
{
|
||||
aes.Key = Key;
|
||||
aes.IV = IV;
|
||||
}
|
||||
|
||||
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
|
||||
byte[] decryptedBytes = null;
|
||||
using (var ms = new System.IO.MemoryStream(encryptedBytes))
|
||||
{
|
||||
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
byte[] decryptedData = new byte[encryptedBytes.Length];
|
||||
int bytesRead = cs.Read(decryptedData, 0, decryptedData.Length);
|
||||
|
||||
if (bytesRead == decryptedData.Length)
|
||||
return decryptedData;
|
||||
// 如果你知道解密后的数据的确切大小,你可以根据需要进行截取
|
||||
byte[] trimmedData = new byte[bytesRead];
|
||||
Array.Copy(decryptedData, trimmedData, bytesRead);
|
||||
|
||||
return trimmedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static string Decrypt(string encryptedText)
|
||||
{
|
||||
string decryptedText = Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(encryptedText)));
|
||||
return decryptedText;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例:
|
||||
//string originalText = "Hello, World!";
|
||||
//string encryptedText = AESExample.Encrypt(originalText);
|
||||
//string decryptedText = AESExample.Decrypt(encryptedText);
|
||||
|
||||
//Console.WriteLine("Original Text: " + originalText);
|
||||
//Console.WriteLine("Encrypted Text: " + encryptedText);
|
||||
//Console.WriteLine("Decrypted Text: " + decryptedText);
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdb9a8c28654f854a912f1aa0df066a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Base",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:13ba8ce62aa80c74598530029cb2d649",
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 460855e1f37306f4889afbf96eb004f0
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
#if UNITY_WEBGL
|
||||
using WeChatWASM;
|
||||
#endif
|
||||
|
||||
public class XWBase
|
||||
{
|
||||
public static void StartScanQRCode(Action<string> callback)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eadfee1a6daccca42bc16821fb4bf3c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class ConsoleToSceen : MonoBehaviour
|
||||
{
|
||||
const int maxLines = 50;
|
||||
const int maxLineLength = 120;
|
||||
private string _logStr = "";
|
||||
|
||||
private readonly List<string> _lines = new List<string>();
|
||||
|
||||
public int fontSize = 15;
|
||||
|
||||
void OnEnable() { Application.logMessageReceived += Log; }
|
||||
void OnDisable() { Application.logMessageReceived -= Log; }
|
||||
void Update() { }
|
||||
|
||||
public void Log(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
foreach (var line in logString.Split('\n'))
|
||||
{
|
||||
if (line.Length <= maxLineLength)
|
||||
{
|
||||
_lines.Add(line);
|
||||
continue;
|
||||
}
|
||||
var lineCount = line.Length / maxLineLength + 1;
|
||||
for (int i = 0; i < lineCount; i++)
|
||||
{
|
||||
if ((i + 1) * maxLineLength <= line.Length)
|
||||
{
|
||||
_lines.Add(line.Substring(i * maxLineLength, maxLineLength));
|
||||
}
|
||||
else
|
||||
{
|
||||
_lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lines.Count > maxLines)
|
||||
{
|
||||
_lines.RemoveRange(0, _lines.Count - maxLines);
|
||||
}
|
||||
_logStr = string.Join("\n", _lines);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity,
|
||||
new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f));
|
||||
GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66b835502f6eddc43b33fbb7755912b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using UnityEngine;
|
||||
|
||||
// 内网联调配置:开发机探测 IP 的本地持久化(persistentDataPath/dev_server.txt,单行 IPv4)。
|
||||
// 跨网段时广播/组播都出不了网段(路由器不转发),只能向已知开发机 IP 单播——该 IP 由本文件持久化,
|
||||
// 首次由 DevServerPrompt 弹框录入并验证成功后写入(见 LoadDll.CoLocalDiscoverThenDownload)。
|
||||
// 注意:Application.persistentDataPath 只能在主线程访问,故 Load/Save 只能在主线程调;
|
||||
// 发现线程(后台)需要的值由调用方在主线程读好传入。
|
||||
// 热更层 DevServerDiscovery 内联了同语义的读取(XWorld.Link 未引用 Base 程序集),改格式时两边须同步。
|
||||
public static class DevServerConfig
|
||||
{
|
||||
public static string FilePath => Path.Combine(Application.persistentDataPath, "dev_server.txt");
|
||||
|
||||
// 读配置:文件不存在/IO 异常/内容不是合法 IPv4 → null(静默,等同"无配置")。
|
||||
public static string Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = FilePath;
|
||||
if (!File.Exists(path)) return null;
|
||||
string line = File.ReadAllText(path).Trim();
|
||||
if (line.Length == 0) return null;
|
||||
if (!IPAddress.TryParse(line, out IPAddress ip)) return null;
|
||||
if (ip.AddressFamily != AddressFamily.InterNetwork) return null; // 只认 IPv4
|
||||
return ip.ToString();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 写配置:单行覆盖;IO 异常静默吞(写不进只是下次还弹框,不致命)。
|
||||
public static void Save(string ip)
|
||||
{
|
||||
try { File.WriteAllText(FilePath, ip ?? ""); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f87f0551c5fcd924cb1849d77c03db5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using UnityEngine;
|
||||
|
||||
// 内网联调:发现失败时的开发机 IP 输入框(纯 IMGUI,风格同热更层 DevServerSwitchUI)。
|
||||
// 只负责输入与三态状态机,不做网络/文件 IO——探测与写配置由 LoadDll.CoLocalDiscoverThenDownload 编排:
|
||||
// [确定] → Confirmed,外部拿 InputIp 去探测;失败则外部调 ResetToWaiting(错误文案) 拨回弹框;
|
||||
// [跳过] → Skipped,外部走 127.0.0.1 回退。
|
||||
public sealed class DevServerPrompt : MonoBehaviour
|
||||
{
|
||||
public enum PromptState { Waiting, Confirmed, Skipped }
|
||||
|
||||
public PromptState State { get; private set; } = PromptState.Waiting;
|
||||
public string InputIp = "";
|
||||
public string Error = ""; // 非空则红字显示
|
||||
|
||||
// 外部探测失败后调用:显示错误并回到可输入状态。
|
||||
public void ResetToWaiting(string error)
|
||||
{
|
||||
Error = error ?? "";
|
||||
State = PromptState.Waiting;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (State != PromptState.Waiting) return;
|
||||
const int W = 560, H = 190;
|
||||
GUILayout.BeginArea(new Rect((Screen.width - W) / 2, (Screen.height - H) / 3, W, H), GUI.skin.box);
|
||||
GUILayout.Label("内网发现失败:请输入开发机 IP(网关须 --lan --devToken xw-dev)");
|
||||
InputIp = GUILayout.TextField(InputIp ?? "", GUILayout.Height(44));
|
||||
if (!string.IsNullOrEmpty(Error))
|
||||
{
|
||||
GUI.color = new Color(1f, 0.4f, 0.3f);
|
||||
GUILayout.Label(Error);
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("确定", GUILayout.Height(44))) { Error = ""; State = PromptState.Confirmed; }
|
||||
if (GUILayout.Button("跳过", GUILayout.Height(44), GUILayout.Width(120))) State = PromptState.Skipped;
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d11782d9f393fc49b80d320bc59ac38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
//查找局域网内的节点地址
|
||||
using System;
|
||||
|
||||
class FindLocalNode
|
||||
{
|
||||
public static void FindNode(Action<string> Result)
|
||||
{
|
||||
string rs = "";
|
||||
|
||||
Result(rs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf3b253ffaaa1854f82d8f8b4e7c395a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// 用于处理文件下载
|
||||
/// </summary>
|
||||
internal class GYDownloadHandler: DownloadHandlerScript
|
||||
{
|
||||
string m_SavePath = "";
|
||||
string m_TempFilePath = "";
|
||||
FileStream fs;
|
||||
|
||||
public long totalFileLen { get; private set; }
|
||||
public long downloadedFileLen { get; private set; }
|
||||
public string fileName { get; private set; }
|
||||
public string dirPath { get; private set; }
|
||||
|
||||
#region 事件
|
||||
|
||||
/// <summary>
|
||||
/// 每次下载到数据后回调进度
|
||||
/// </summary>
|
||||
public Action<float,float> OnProgress = null;
|
||||
|
||||
/// <summary>
|
||||
/// 当下载完成后回调下载的文件位置
|
||||
/// </summary>
|
||||
public Action<int> OnFinish = null;
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化下载句柄,定义每次下载的数据上限为1M
|
||||
/// </summary>
|
||||
/// <param name="filePath">保存到本地的文件路径</param>
|
||||
public GYDownloadHandler(string filePath) : base(new byte[1024 * 1024])
|
||||
{
|
||||
m_SavePath = filePath.Replace('\\', '/');
|
||||
fileName = m_SavePath.Substring(m_SavePath.LastIndexOf('/') + 1);
|
||||
dirPath = m_SavePath.Substring(0, m_SavePath.LastIndexOf('/'));
|
||||
m_TempFilePath = Path.Combine(dirPath, fileName + ".temp");
|
||||
if (!Directory.Exists(dirPath))
|
||||
{
|
||||
Directory.CreateDirectory(dirPath);
|
||||
}
|
||||
|
||||
fs = new FileStream(m_TempFilePath, FileMode.Append, FileAccess.Write);
|
||||
downloadedFileLen = fs.Length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从网络获取数据时候的回调,每帧调用一次
|
||||
/// </summary>
|
||||
/// <param name="data">接收到的数据字节流,总长度为构造函数定义的200kb,并非所有的数据都是新的</param>
|
||||
/// <param name="dataLength">接收到的数据长度,表示data字节流数组中有多少数据是新接收到的,即0-dataLength之间的数据是刚接收到的</param>
|
||||
/// <returns>返回true为继续下载,返回false为中断下载</returns>
|
||||
protected override bool ReceiveData(byte[] data, int dataLength)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
Debug.LogFormat("【下载中】 下载文件{0}中,没有获取到数据,下载终止", fileName);
|
||||
return false;
|
||||
}
|
||||
fs?.Write(data, 0, dataLength);
|
||||
downloadedFileLen += dataLength;
|
||||
|
||||
OnProgress?.Invoke(downloadedFileLen, totalFileLen);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当接受数据完成时的回调
|
||||
/// </summary>
|
||||
protected override void CompleteContent()
|
||||
{
|
||||
OnDispose();
|
||||
//Debug.LogFormat("【下载完成】 完成对{0}文件的下载,保存路径为{1}", fileName, m_SavePath);
|
||||
|
||||
if (File.Exists(m_TempFilePath))
|
||||
{
|
||||
if (File.Exists(m_SavePath))
|
||||
File.Delete(m_SavePath);
|
||||
File.Move(m_TempFilePath, m_SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogFormat("【下载失败】 下载文件{0}时失败", fileName);
|
||||
}
|
||||
OnFinish?.Invoke((int)downloadedFileLen);
|
||||
}
|
||||
|
||||
public void OnDispose()
|
||||
{
|
||||
fs?.Close();
|
||||
fs?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求下载时的第一个回调函数,会返回需要接收的文件总长度
|
||||
/// </summary>
|
||||
/// <param name="contentLength">如果是续传,则是剩下的文件大小;本地拷贝则是文件总长度</param>
|
||||
protected override void ReceiveContentLengthHeader(ulong contentLength)
|
||||
{
|
||||
if (contentLength == 0)
|
||||
{
|
||||
Debug.Log("【下载已经完成】");
|
||||
CompleteContent();
|
||||
}
|
||||
base.ReceiveContentLengthHeader(contentLength);
|
||||
totalFileLen = (long)contentLength + downloadedFileLen;
|
||||
}
|
||||
|
||||
public void ErrorDispose()
|
||||
{
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
if (File.Exists(m_TempFilePath))
|
||||
{
|
||||
File.Delete(m_TempFilePath);
|
||||
}
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
OnFinish = null;
|
||||
OnProgress = null;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc9b1a210663c5948aabe89b2224ece2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30e4f9ab3fb289843847ccf1e751c22c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
// 底包(AOT)专用:内网 CDN 广播发现。
|
||||
// 真机内网测试时 CDN 跑在开发机上,客户端写死的 127.0.0.1 在真机上指向手机自身、连不到开发机;
|
||||
// 故用 UDP 广播问开发机网关要它的 LAN CDN 根地址(形如 http://<开发机IP>:15081/)。
|
||||
//
|
||||
// 协议与热更层 XWorld.Framework.Protocol.DevDiscovery / DevServerDiscovery 完全一致
|
||||
// (magic "XWDL"、UDP 48923、LEB128 varuint + UTF8 长度前缀字符串、token=xw-dev),
|
||||
// 但 Base 程序集不能引用热更程序集(XWorld.Framework.Shared,运行时才由 LoadDll 加载),
|
||||
// 所以这里内联一份最小编解码,改协议时两边须同步。
|
||||
public static class LocalCdnDiscovery
|
||||
{
|
||||
private const int Port = 48923; // = DevDiscovery.Port
|
||||
private const uint ProtoVersion = 1; // = DevDiscovery.ProtoVersion
|
||||
private static readonly byte[] Magic = { 0x58, 0x57, 0x44, 0x4C }; // "XWDL" = DevDiscovery.Magic
|
||||
private const byte TypeQuery = 1; // = DevDiscovery.TypeQuery
|
||||
private const byte TypeReply = 2; // = DevDiscovery.TypeReply
|
||||
|
||||
// 阻塞式发现,须在后台线程调用(会最多阻塞 durationMs)。
|
||||
// 在窗口内周期重发查询(向广播 + 127.0.0.1 单播),收到首个合法应答即返回其 ResourceBaseUrl(CDN 根);超时返回 null。
|
||||
// extraProbeIp:额外单播探测目标(跨网段联调用,来自 dev_server.txt 或弹框输入,见 DevServerConfig/DevServerPrompt)。
|
||||
// 广播/定向广播不被路由器转发(RFC 2644),组播亦已实测不通(路由器未开组播路由),跨网段只能靠单播已知 IP。
|
||||
// 注意:本方法跑在后台线程,不能碰 Unity 主线程 API(如 persistentDataPath),配置由调用方在主线程读好传入。
|
||||
public static string Discover(string token, int durationMs = 1500, string extraProbeIp = null)
|
||||
{
|
||||
UdpClient udp = null;
|
||||
try
|
||||
{
|
||||
udp = new UdpClient(AddressFamily.InterNetwork);
|
||||
udp.EnableBroadcast = true;
|
||||
udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
udp.Client.Bind(new IPEndPoint(IPAddress.Any, 0));
|
||||
udp.Client.ReceiveTimeout = 250;
|
||||
|
||||
uint nonce = unchecked((uint)Guid.NewGuid().GetHashCode());
|
||||
byte[] query = EncodeQuery(nonce, token);
|
||||
// 同机广播常收不到(取决于网卡是否回环广播),故同时向 127.0.0.1 单播;并周期重发防网关刚起未监听。
|
||||
// 255.255.255.255(限制广播)在部分 Android WiFi 驱动上会被丢弃,故再按各网卡 IP|~mask 补发子网定向广播(如 192.168.1.255)。
|
||||
List<IPEndPoint> targetList = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.Broadcast, Port),
|
||||
new IPEndPoint(IPAddress.Loopback, Port),
|
||||
};
|
||||
foreach (IPAddress bc in GetDirectedBroadcasts())
|
||||
targetList.Add(new IPEndPoint(bc, Port));
|
||||
if (!string.IsNullOrEmpty(extraProbeIp) && IPAddress.TryParse(extraProbeIp, out IPAddress extra))
|
||||
targetList.Add(new IPEndPoint(extra, Port));
|
||||
IPEndPoint[] targets = targetList.ToArray();
|
||||
UnityEngine.Debug.Log("[Discovery] targets(" + targets.Length + "): " + string.Join(", ", Array.ConvertAll(targets, t => t.ToString())));
|
||||
|
||||
DateTime deadline = DateTime.UtcNow.AddMilliseconds(durationMs);
|
||||
DateTime nextSend = DateTime.MinValue;
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
if (DateTime.UtcNow >= nextSend)
|
||||
{
|
||||
foreach (IPEndPoint t in targets)
|
||||
{
|
||||
try { udp.Send(query, query.Length, t); } catch { }
|
||||
}
|
||||
nextSend = DateTime.UtcNow.AddMilliseconds(300);
|
||||
}
|
||||
|
||||
IPEndPoint from = new IPEndPoint(IPAddress.Any, 0);
|
||||
byte[] data;
|
||||
try { data = udp.Receive(ref from); }
|
||||
catch (SocketException) { continue; } // 250ms 接收超时,继续重发/等待
|
||||
if (TryDecodeReply(data, nonce, token, out string resourceBaseUrl)
|
||||
&& !string.IsNullOrEmpty(resourceBaseUrl))
|
||||
{
|
||||
return resourceBaseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 忽略:发现失败由调用方处理(弹框输入或回退 127.0.0.1)
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { udp?.Close(); } catch { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 枚举本机所有 Up 状态 IPv4 网卡,按 broadcast = ip | ~mask 算出各自的子网定向广播地址。
|
||||
// 关键:Android/Mono 下 IPv4Mask 常拿不到(返回 null/0.0.0.0 甚至抛异常),故对私有网段按 /24 兜底,
|
||||
// 且逐地址 try/catch,任何一块网卡取掩码失败都不中断整体枚举(否则会连累外层 return null)。
|
||||
private static List<IPAddress> GetDirectedBroadcasts()
|
||||
{
|
||||
List<IPAddress> list = new List<IPAddress>();
|
||||
NetworkInterface[] nics;
|
||||
try { nics = NetworkInterface.GetAllNetworkInterfaces(); }
|
||||
catch (Exception e) { UnityEngine.Debug.LogWarning("[Discovery] GetAllNetworkInterfaces failed: " + e.Message); return list; }
|
||||
|
||||
foreach (NetworkInterface ni in nics)
|
||||
{
|
||||
if (ni.OperationalStatus != OperationalStatus.Up) continue;
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
|
||||
|
||||
IPInterfaceProperties props;
|
||||
try { props = ni.GetIPProperties(); }
|
||||
catch { continue; }
|
||||
|
||||
foreach (UnicastIPAddressInformation ua in props.UnicastAddresses)
|
||||
{
|
||||
if (ua.Address.AddressFamily != AddressFamily.InterNetwork) continue; // 只处理 IPv4
|
||||
byte[] ip = ua.Address.GetAddressBytes();
|
||||
if (ip.Length != 4) continue;
|
||||
|
||||
byte[] mk = null;
|
||||
try { IPAddress m = ua.IPv4Mask; if (m != null) mk = m.GetAddressBytes(); }
|
||||
catch { mk = null; } // Android 上访问 IPv4Mask 可能抛,兜住
|
||||
|
||||
// 掩码不可用(null/长度不对/全 0)时,私有网段按 /24 兜底(最常见的家用/办公 WiFi),公网地址不猜。
|
||||
bool maskBad = mk == null || mk.Length != 4 || (mk[0] == 0 && mk[1] == 0 && mk[2] == 0 && mk[3] == 0);
|
||||
if (maskBad)
|
||||
{
|
||||
if (!IsPrivateV4(ip)) continue;
|
||||
mk = new byte[] { 255, 255, 255, 0 };
|
||||
}
|
||||
|
||||
byte[] bc = new byte[4];
|
||||
for (int i = 0; i < 4; i++) bc[i] = (byte)(ip[i] | (~mk[i] & 0xFF));
|
||||
IPAddress bcAddr = new IPAddress(bc);
|
||||
list.Add(bcAddr);
|
||||
UnityEngine.Debug.Log("[Discovery] nic=" + ni.Name + " ip=" + ua.Address + " mask=" + string.Join(".", mk) + " bc=" + bcAddr);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static bool IsPrivateV4(byte[] ip)
|
||||
{
|
||||
if (ip[0] == 10) return true; // 10.0.0.0/8
|
||||
if (ip[0] == 192 && ip[1] == 168) return true; // 192.168.0.0/16
|
||||
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return true; // 172.16.0.0/12
|
||||
return false;
|
||||
}
|
||||
|
||||
private static byte[] EncodeQuery(uint nonce, string token)
|
||||
{
|
||||
var buf = new List<byte>(32);
|
||||
for (int i = 0; i < Magic.Length; i++) buf.Add(Magic[i]);
|
||||
buf.Add(TypeQuery);
|
||||
WriteVarUInt(buf, ProtoVersion);
|
||||
WriteVarUInt(buf, nonce);
|
||||
WriteString(buf, token);
|
||||
return buf.ToArray();
|
||||
}
|
||||
|
||||
private static bool TryDecodeReply(byte[] data, uint expectNonce, string expectToken, out string resourceBaseUrl)
|
||||
{
|
||||
resourceBaseUrl = null;
|
||||
try
|
||||
{
|
||||
int pos = 0;
|
||||
for (int i = 0; i < Magic.Length; i++)
|
||||
if (ReadByte(data, ref pos) != Magic[i]) return false;
|
||||
if (ReadByte(data, ref pos) != TypeReply) return false;
|
||||
if (ReadVarUInt(data, ref pos) != ProtoVersion) return false;
|
||||
if (ReadVarUInt(data, ref pos) != expectNonce) return false;
|
||||
ReadString(data, ref pos); // ServerName(忽略)
|
||||
ReadString(data, ref pos); // GatewayUrl(忽略,LoadDll 只要 CDN)
|
||||
string resBase = ReadString(data, ref pos); // ResourceBaseUrl(CDN 根)
|
||||
string token = ReadString(data, ref pos);
|
||||
if (token != expectToken) return false;
|
||||
resourceBaseUrl = resBase;
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- LEB128 varuint + UTF8 长度前缀字符串:与 PacketWriter/PacketReader 对齐 ----
|
||||
|
||||
private static void WriteVarUInt(List<byte> buf, uint v)
|
||||
{
|
||||
while (v >= 0x80) { buf.Add((byte)(v | 0x80)); v >>= 7; }
|
||||
buf.Add((byte)v);
|
||||
}
|
||||
|
||||
private static void WriteString(List<byte> buf, string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) { WriteVarUInt(buf, 0); return; }
|
||||
byte[] b = Encoding.UTF8.GetBytes(s);
|
||||
WriteVarUInt(buf, (uint)b.Length);
|
||||
buf.AddRange(b);
|
||||
}
|
||||
|
||||
private static byte ReadByte(byte[] d, ref int pos)
|
||||
{
|
||||
if (pos >= d.Length) throw new FormatException("read past end");
|
||||
return d[pos++];
|
||||
}
|
||||
|
||||
private static uint ReadVarUInt(byte[] d, ref int pos)
|
||||
{
|
||||
uint result = 0;
|
||||
int shift = 0;
|
||||
while (true)
|
||||
{
|
||||
if (shift > 28) throw new FormatException("varuint too long");
|
||||
byte b = ReadByte(d, ref pos);
|
||||
result |= (uint)(b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) break;
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string ReadString(byte[] d, ref int pos)
|
||||
{
|
||||
uint len = ReadVarUInt(d, ref pos);
|
||||
if (len == 0) return string.Empty;
|
||||
if (pos + len > d.Length) throw new FormatException("string length exceeds buffer");
|
||||
string s = Encoding.UTF8.GetString(d, pos, (int)len);
|
||||
pos += (int)len;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27dfac6726f533c47b6ecf0b928245a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
public class MsvcStdextWorkaround : IPreprocessBuildWithReport
|
||||
{
|
||||
const string kWorkaroundFlag = "/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS";
|
||||
|
||||
public int callbackOrder => 0;
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
var clEnv = Environment.GetEnvironmentVariable("_CL_");
|
||||
|
||||
if (string.IsNullOrEmpty(clEnv))
|
||||
{
|
||||
Environment.SetEnvironmentVariable("_CL_", kWorkaroundFlag);
|
||||
}
|
||||
else if (!clEnv.Contains(kWorkaroundFlag))
|
||||
{
|
||||
clEnv += " " + kWorkaroundFlag;
|
||||
Environment.SetEnvironmentVariable("_CL_", clEnv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5ecc79a541cad34a9d4d208b7723e20
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f55d59eeda44a4749a04dd20d40bfb74
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,182 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2678de581933a474fbbca174e6ff8374
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 1024
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 50
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 1
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WeixinMiniGame
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: OpenHarmony
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,98 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: CloseScan
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves:
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_IsActive
|
||||
path: icon
|
||||
classID: 1
|
||||
script: {fileID: 0}
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings:
|
||||
- serializedVersion: 2
|
||||
path: 1704208859
|
||||
attribute: 2086281974
|
||||
script: {fileID: 0}
|
||||
typeID: 1
|
||||
customType: 0
|
||||
isPPtrCurve: 0
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 0
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 1
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves:
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_IsActive
|
||||
path: icon
|
||||
classID: 1
|
||||
script: {fileID: 0}
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce610a4580ab0624ba1cf2204bb1c579
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dfca4f2b0bd9c547ba36ec8e3a42989
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,211 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OpenScan
|
||||
serializedVersion: 7
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves:
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 2
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 3
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_IsActive
|
||||
path: icon
|
||||
classID: 1
|
||||
script: {fileID: 0}
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 220
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 136
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 2
|
||||
value: -185
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 136
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_AnchoredPosition.y
|
||||
path: icon
|
||||
classID: 224
|
||||
script: {fileID: 0}
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings:
|
||||
- serializedVersion: 2
|
||||
path: 1704208859
|
||||
attribute: 2086281974
|
||||
script: {fileID: 0}
|
||||
typeID: 1
|
||||
customType: 0
|
||||
isPPtrCurve: 0
|
||||
- serializedVersion: 2
|
||||
path: 1704208859
|
||||
attribute: 538195251
|
||||
script: {fileID: 0}
|
||||
typeID: 224
|
||||
customType: 28
|
||||
isPPtrCurve: 0
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 3
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 1
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves:
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 2
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 3
|
||||
value: 0
|
||||
inSlope: Infinity
|
||||
outSlope: Infinity
|
||||
tangentMode: 103
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_IsActive
|
||||
path: icon
|
||||
classID: 1
|
||||
script: {fileID: 0}
|
||||
- curve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 220
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 136
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 2
|
||||
value: -185
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 136
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
attribute: m_AnchoredPosition.y
|
||||
path: icon
|
||||
classID: 224
|
||||
script: {fileID: 0}
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events:
|
||||
- time: 1
|
||||
functionName: CheckQRCode
|
||||
data:
|
||||
objectReferenceParameter: {fileID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 2
|
||||
functionName: CheckQRCode
|
||||
data:
|
||||
objectReferenceParameter: {fileID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8458476133f302a4c9979e44c985f5d4
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,182 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f3261fa010350d4999fb44b43f5dc39
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 1024
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 50
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 1
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: 50
|
||||
textureCompression: 1
|
||||
compressionQuality: 100
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 1
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WeixinMiniGame
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: OpenHarmony
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,130 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1102 &-4687405366764051200
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OpenScan
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 92a0728491840cd4abe2cb366ca6130e, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!91 &9100000
|
||||
AnimatorController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: ScanQRCode
|
||||
serializedVersion: 5
|
||||
m_AnimatorParameters: []
|
||||
m_AnimatorLayers:
|
||||
- serializedVersion: 5
|
||||
m_Name: Base Layer
|
||||
m_StateMachine: {fileID: 8553602833493306391}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 0
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
--- !u!1102 &276816421219941405
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: CloseScan
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 9f46eb24c60b1bb469704d37bae22270, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &7425443740970095782
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: New State
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1107 &8553602833493306391
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Base Layer
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -4687405366764051200}
|
||||
m_Position: {x: 510, y: 60, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 276816421219941405}
|
||||
m_Position: {x: 520, y: 160, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 7425443740970095782}
|
||||
m_Position: {x: 260, y: 120, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions: []
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: 7425443740970095782}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 205a6eed700d24d46a02ce684196b212
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user