【打包】提交部分代码

main
刘涛 2025-07-25 14:38:13 +08:00
parent 75987c59d7
commit 2bb08348b7
38 changed files with 4289 additions and 0 deletions

View File

@ -34,3 +34,9 @@ AppKey: "uUi_UI1oMKWo0VOSN1zme17Z70-FlAv3Lcx2mNhoYe4="
CrashSight_Android:
id: "669a94ce69" # 崩溃上报的 App ID
key: "d9412717-2416-4270-b8bc-f7a0805f988f" # 崩溃上报的 Key
Packages:
- Poco@latest
- TalkingData@latest
- AliPay@latest
- WeichatPay@latest

View File

@ -0,0 +1,24 @@
# 传入参数 0:SDK_PATH 1:ChannelPath
import sys
import os
def main():
if len(sys.argv) < 3:
print("Usage: python PreHandle.py <SDK_PATH> <ChannelPath>")
return
sdk_path = sys.argv[1]
channel_path = sys.argv[2]
if not os.path.exists(sdk_path):
print(f"Error: SDK path '{sdk_path}' does not exist.")
return
if not os.path.exists(channel_path):
print(f"Error: Channel path '{channel_path}' does not exist.")
return
# 这里可以添加更多的处理逻辑
print(f"SDK Path: {sdk_path}")
print(f"Channel Path: {channel_path}")
main()

View File

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

View File

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static class PocoListenerUtils
{
public static void SubscribePocoListeners(RPCParser rpc, PocoListenersBase listeners)
{
var methods = listeners.GetType()
.GetMethods(BindingFlags.Public | BindingFlags.Instance);
var uniqueListeners = new HashSet<string>();
foreach (var method in methods)
{
var attribute = method.GetCustomAttribute<PocoMethodAttribute>();
if (attribute != null)
{
rpc.addListener(listeners, attribute.Name, method);
if (uniqueListeners.Add(attribute.Name) == false)
{
Debug.LogError($"Attempt to add non-unique Poco listener: `{attribute.Name}`, " +
$"please check attributes of listeners at `{listeners.GetType().Name}`");
}
}
}
}
public static object HandleInvocation(
Dictionary<string, (object instance, MethodInfo method)> listeners,
Dictionary<string, object> data)
{
if (listeners == null)
{
throw new ArgumentNullException(
nameof(listeners),
"To use `poco.invoke()`, please assign object " +
$"of class derived from {nameof(PocoListenersBase)} " +
$"to field at `{nameof(PocoManager)}`");
}
var paramsObject = (JObject)data["params"];
var listener = paramsObject["listener"].ToObject<string>();
if (listeners.TryGetValue(listener, out var listenerPair) == false)
{
throw new NotImplementedException(
$"Listener method for `{listener}` " +
$"marked with `{nameof(PocoMethodAttribute)}` was not found " +
$"at `{listeners.GetType().Name}`");
}
var (instance, method) = listenerPair;
var args = GetInvocationArgs(paramsObject, method);
var result = method.Invoke(instance, args);
return result;
}
private static object[] GetInvocationArgs(JObject paramsObject, MethodInfo method)
{
var parameters = method.GetParameters();
if (paramsObject.TryGetValue("data", out var data) == false)
{
if (parameters.Length > 0)
{
throw new ArgumentException(
$"Signature mismatch of method `{method}`: " +
"expected 0 arguments in listener, " +
$"received {parameters.Length} arguments");
}
return Array.Empty<object>();
}
var args = new List<object>();
var remainingArgNames = new HashSet<string>(data.ToObject<Dictionary<string, object>>().Keys);
foreach (var parameter in parameters)
{
var parameterName = parameter.Name;
var argToken = data[parameterName];
if (argToken == null)
{
throw new ArgumentException(
$"Signature mismatch of method `{method}`: " +
$"excess parameter `{parameterName}` in listener");
}
try
{
var argValue = argToken.ToObject(parameter.ParameterType);
args.Add(argValue);
remainingArgNames.Remove(parameterName);
}
catch (JsonReaderException exception)
{
throw new ArgumentException(
$"Signature mismatch of method `{method}`: " +
$"parameter `{parameterName}` type mismatch: " +
$"tried to parse received value `{argToken}`, " +
$"with type `{parameter.ParameterType.Name}` at listener",
exception);
}
}
if (remainingArgNames.Count > 0)
{
throw new ArgumentException(
$"Signature mismatch of method `{method}`: " +
$"missing parameters in listener: `{string.Join(", ", remainingArgNames)}`");
}
return args.ToArray();
}
}

View File

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

View File

@ -0,0 +1,3 @@
using UnityEngine;
public class PocoListenersBase : MonoBehaviour { }

View File

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

View File

@ -0,0 +1,365 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Poco;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Reflection;
using TcpServer;
using UnityEngine;
using Debug = UnityEngine.Debug;
public class PocoManager : MonoBehaviour
{
public event Action<string> MessageReceived;
public const int versionCode = 6;
public int port = 5001;
private bool mRunning;
public AsyncTcpServer server = null;
public PocoListenersBase pocoListenersBase;
private RPCParser rpc = null;
private SimpleProtocolFilter prot = null;
private UnityDumper dumper = new UnityDumper();
private ConcurrentDictionary<string, TcpClientState> inbox = new ConcurrentDictionary<string, TcpClientState>();
private VRSupport vr_support = new VRSupport();
private Dictionary<string, long> debugProfilingData = new Dictionary<string, long>() {
{ "dump", 0 },
{ "screenshot", 0 },
{ "handleRpcRequest", 0 },
{ "packRpcResponse", 0 },
{ "sendRpcResponse", 0 },
};
class RPC : Attribute
{
}
void Awake()
{
Application.runInBackground = true;
DontDestroyOnLoad(this);
prot = new SimpleProtocolFilter();
rpc = new RPCParser();
rpc.addRpcMethod("isVRSupported", vr_support.isVRSupported);
rpc.addRpcMethod("hasMovementFinished", vr_support.IsQueueEmpty);
rpc.addRpcMethod("RotateObject", vr_support.RotateObject);
rpc.addRpcMethod("ObjectLookAt", vr_support.ObjectLookAt);
rpc.addRpcMethod("Screenshot", Screenshot);
rpc.addRpcMethod("GetScreenSize", GetScreenSize);
rpc.addRpcMethod("Dump", Dump);
rpc.addRpcMethod("GetDebugProfilingData", GetDebugProfilingData);
rpc.addRpcMethod("SetText", SetText);
rpc.addRpcMethod("SendMessage", SendMessage);
rpc.addRpcMethod("GetSDKVersion", GetSDKVersion);
if (pocoListenersBase != null)
{
PocoListenerUtils.SubscribePocoListeners(rpc, pocoListenersBase);
}
mRunning = true;
for (int i = 0; i < 5; i++)
{
this.server = new AsyncTcpServer(port + i);
this.server.Encoding = Encoding.UTF8;
this.server.ClientConnected +=
new EventHandler<TcpClientConnectedEventArgs>(server_ClientConnected);
this.server.ClientDisconnected +=
new EventHandler<TcpClientDisconnectedEventArgs>(server_ClientDisconnected);
this.server.DatagramReceived +=
new EventHandler<TcpDatagramReceivedEventArgs<byte[]>>(server_Received);
try
{
this.server.Start();
Debug.Log(string.Format("Tcp server started and listening at {0}", server.Port));
break;
}
catch (SocketException e)
{
Debug.Log(string.Format("Tcp server bind to port {0} Failed!", server.Port));
Debug.Log("--- Failed Trace Begin ---");
Debug.LogError(e);
Debug.Log("--- Failed Trace End ---");
// try next available port
this.server = null;
}
}
if (this.server == null)
{
Debug.LogError(string.Format("Unable to find an unused port from {0} to {1}", port, port + 5));
}
vr_support.ClearCommands();
}
static void server_ClientConnected(object sender, TcpClientConnectedEventArgs e)
{
Debug.Log(string.Format("TCP client {0} has connected.",
e.TcpClient.Client.RemoteEndPoint.ToString()));
}
static void server_ClientDisconnected(object sender, TcpClientDisconnectedEventArgs e)
{
Debug.Log(string.Format("TCP client {0} has disconnected.",
e.TcpClient.Client.RemoteEndPoint.ToString()));
}
private void server_Received(object sender, TcpDatagramReceivedEventArgs<byte[]> e)
{
Debug.Log(string.Format("Client : {0} --> {1}",
e.Client.TcpClient.Client.RemoteEndPoint.ToString(), e.Datagram.Length));
TcpClientState internalClient = e.Client;
string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString();
inbox.AddOrUpdate(tcpClientKey, internalClient, (n, o) =>
{
return internalClient;
});
}
[RPC]
private object Dump(List<object> param)
{
var onlyVisibleNode = true;
if (param.Count > 0)
{
onlyVisibleNode = (bool)param[0];
}
var sw = new Stopwatch();
sw.Start();
var h = dumper.dumpHierarchy(onlyVisibleNode);
debugProfilingData["dump"] = sw.ElapsedMilliseconds;
return h;
}
[RPC]
private object Screenshot(List<object> param)
{
var sw = new Stopwatch();
sw.Start();
var tex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
tex.Apply(false);
byte[] fileBytes = tex.EncodeToJPG(80);
var b64img = Convert.ToBase64String(fileBytes);
debugProfilingData["screenshot"] = sw.ElapsedMilliseconds;
return new object[] { b64img, "jpg" };
}
[RPC]
private object GetScreenSize(List<object> param)
{
return new float[] { Screen.width, Screen.height };
}
public void stopListening()
{
mRunning = false;
server?.Stop();
}
[RPC]
private object GetDebugProfilingData(List<object> param)
{
return debugProfilingData;
}
[RPC]
private object SetText(List<object> param)
{
var instanceId = Convert.ToInt32(param[0]);
var textVal = param[1] as string;
foreach (var go in GameObject.FindObjectsOfType<GameObject>())
{
if (go.GetInstanceID() == instanceId)
{
return UnityNode.SetText(go, textVal);
}
}
return false;
}
[RPC]
private object SendMessage(List<object> param)
{
if (MessageReceived == null)
{
return false;
}
var textVal = param[0] as string;
MessageReceived.Invoke(textVal);
return true;
}
[RPC]
private object GetSDKVersion(List<object> param)
{
return versionCode;
}
void Update()
{
foreach (TcpClientState client in inbox.Values)
{
List<string> msgs = client.Prot.swap_msgs();
msgs.ForEach(delegate (string msg)
{
var sw = new Stopwatch();
sw.Start();
var t0 = sw.ElapsedMilliseconds;
string response = rpc.HandleMessage(msg);
var t1 = sw.ElapsedMilliseconds;
byte[] bytes = prot.pack(response);
var t2 = sw.ElapsedMilliseconds;
server.Send(client.TcpClient, bytes);
var t3 = sw.ElapsedMilliseconds;
debugProfilingData["handleRpcRequest"] = t1 - t0;
debugProfilingData["packRpcResponse"] = t2 - t1;
TcpClientState internalClientToBeThrowAway;
string tcpClientKey = client.TcpClient.Client.RemoteEndPoint.ToString();
inbox.TryRemove(tcpClientKey, out internalClientToBeThrowAway);
});
}
vr_support.PeekCommand();
}
void OnApplicationQuit()
{
// stop listening thread
stopListening();
}
void OnDestroy()
{
// stop listening thread
stopListening();
}
}
public class RPCParser
{
public delegate object RpcMethod(List<object> param);
protected Dictionary<string, RpcMethod> RPCHandler = new Dictionary<string, RpcMethod>();
protected Dictionary<string, (object instance, MethodInfo method)> Listeners = new Dictionary<string, (object, MethodInfo)>();
private JsonSerializerSettings settings = new JsonSerializerSettings()
{
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
};
public string HandleMessage(string json)
{
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);
if (data.TryGetValue("method", out var methodObj) == false)
{
Debug.Log("ignore message without method");
return null;
}
var method = methodObj.ToString();
var idAction = data.TryGetValue("id", out var id) ? id : null;
try
{
object result;
switch (method)
{
case "Invoke":
result = PocoListenerUtils.HandleInvocation(Listeners, data);
break;
default:
List<object> param = null;
if (data.TryGetValue("params", out var value))
{
param = ((JArray)value).ToObject<List<object>>();
}
result = RPCHandler[method](param);
break;
}
return formatResponse(idAction, result);
}
catch (Exception exception)
{
Debug.LogError(exception);
return formatResponseError(idAction, null, exception);
}
}
// Call a method in the server
public string formatRequest(string method, object idAction, List<object> param = null)
{
Dictionary<string, object> data = new Dictionary<string, object>();
data["jsonrpc"] = "2.0";
data["method"] = method;
if (param != null)
{
data["params"] = JsonConvert.SerializeObject(param, settings);
}
// if idAction is null, it is a notification
if (idAction != null)
{
data["id"] = idAction;
}
return JsonConvert.SerializeObject(data, settings);
}
// Send a response from a request the server made to this client
public string formatResponse(object idAction, object result)
{
Dictionary<string, object> rpc = new Dictionary<string, object>();
rpc["jsonrpc"] = "2.0";
rpc["id"] = idAction;
rpc["result"] = result;
return JsonConvert.SerializeObject(rpc, settings);
}
// Send a error to the server from a request it made to this client
public string formatResponseError(object idAction, IDictionary<string, object> data, Exception e)
{
Dictionary<string, object> rpc = new Dictionary<string, object>();
rpc["jsonrpc"] = "2.0";
rpc["id"] = idAction;
Dictionary<string, object> errorDefinition = new Dictionary<string, object>();
errorDefinition["code"] = 1;
errorDefinition["message"] = e.ToString();
if (data != null)
{
errorDefinition["data"] = data;
}
rpc["error"] = errorDefinition;
return JsonConvert.SerializeObject(rpc, settings);
}
public void addRpcMethod(string name, RpcMethod method)
{
RPCHandler[name] = method;
}
public void addListener(object instance, string name, MethodInfo method)
{
Listeners[name] = (instance, method);
}
}

View File

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

View File

@ -0,0 +1,11 @@
using System;
public sealed class PocoMethodAttribute : Attribute
{
public string Name { get; private set; }
public PocoMethodAttribute(string name)
{
Name = name;
}
}

View File

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

View File

@ -0,0 +1,26 @@
# Unity3D Integration Guide
PocoSDK supports Unity3D version 4 & 5 and above, ngui & ugui & fairygui, C# only for now.
1. Clone source code from [poco-sdk](https://github.com/AirtestProject/Poco-SDK) repo.
2. Copy the `Unity3D` folder to your unity project script folder.
3.
- If you are using ngui, just delete the sub folder `Unity3D/ugui` and `Unity3D/fairygui`.
- If you are using ugui, just delete the sub folder `Unity3D/ngui` and `Unity3D/fairygui`.
- If you are using fairygui, please refer [fairygui guide](https://github.com/AirtestProject/Poco-SDK/tree/master/Unity3D/fairygui)
4. Add `Unity3D/PocoManager.cs` as script component on any GameObject, generally on main camera.
## SDK接入指南
PocoSDK支持Unity3D版本4和5及更高版本支持ngui、ugui与fairygui的C#版本暂不支持lua调用。
1. 从[poco-sdk](https://github.comAirtestProjectPoco-SDK) 仓库克隆源代码。
2. 将`Unity3D`文件夹复制到您的unity项目的Scripts文件夹下。
3.
- 如果使用的是ngui需要删除子文件夹`Unity3D/ugui`和`Unity3D/fairygui`
- 如果您使用的是ugui只需删除子文件夹`Unity3D/ngui`和`Unity3D/fairygui`
- 如果您使用的是fairygui请参阅[fairygui指南](https://github.comAirtestProjectPoco-SDKtreemasterUnity3Dfairygui)
4. 在任何GameObject上通常在主摄像机上或创建一个新的空GameObject添加`Unity3DPocoManager.cs`作为脚本组件。

View File

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

View File

@ -0,0 +1,28 @@
using System;
using System.Net.Sockets;
namespace TcpServer
{
/// <summary>
/// 与客户端的连接已建立事件参数
/// </summary>
public class TcpClientConnectedEventArgs : EventArgs
{
/// <summary>
/// 与客户端的连接已建立事件参数
/// </summary>
/// <param name="tcpClient">客户端</param>
public TcpClientConnectedEventArgs(TcpClient tcpClient)
{
if (tcpClient == null)
throw new ArgumentNullException("tcpClient");
this.TcpClient = tcpClient;
}
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,28 @@
using System;
using System.Net.Sockets;
namespace TcpServer
{
/// <summary>
/// 与客户端的连接已断开事件参数
/// </summary>
public class TcpClientDisconnectedEventArgs : EventArgs
{
/// <summary>
/// 与客户端的连接已断开事件参数
/// </summary>
/// <param name="tcpClient">客户端状态</param>
public TcpClientDisconnectedEventArgs(TcpClient tcpClient)
{
if (tcpClient == null)
throw new ArgumentNullException("tcpClient");
this.TcpClient = tcpClient;
}
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,56 @@
using System;
using System.Net.Sockets;
namespace TcpServer
{
/// <summary>
/// Internal class to join the TCP client and buffer together
/// for easy management in the server
/// </summary>
public class TcpClientState
{
/// <summary>
/// Constructor for a new Client
/// </summary>
/// <param name="tcpClient">The TCP client</param>
/// <param name="buffer">The byte array buffer</param>
/// <param name="prot">The protocol filter</param>
public TcpClientState(TcpClient tcpClient, byte[] buffer, ProtoFilter prot)
{
if (tcpClient == null)
throw new ArgumentNullException("tcpClient");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (prot == null)
throw new ArgumentNullException("prot");
this.TcpClient = tcpClient;
this.Buffer = buffer;
this.Prot = prot;
// this.NetworkStream = tcpClient.GetStream ();
}
/// <summary>
/// Gets the TCP Client
/// </summary>
public TcpClient TcpClient { get; private set; }
/// <summary>
/// Gets the Buffer.
/// </summary>
public byte[] Buffer { get; private set; }
public ProtoFilter Prot { get; private set; }
/// <summary>
/// Gets the network stream
/// </summary>
public NetworkStream NetworkStream
{
get
{
return TcpClient.GetStream();
}
}
}
}

View File

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

View File

@ -0,0 +1,39 @@
using System;
using System.Net.Sockets;
namespace TcpServer
{
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <typeparam name="T">报文类型</typeparam>
public class TcpDatagramReceivedEventArgs<T> : EventArgs
{
/// <summary>
/// 接收到数据报文事件参数
/// </summary>
/// <param name="tcpClientState">客户端状态</param>
/// <param name="datagram">报文</param>
public TcpDatagramReceivedEventArgs(TcpClientState tcpClientState, T datagram)
{
this.Client = tcpClientState;
this.TcpClient = tcpClientState.TcpClient;
this.Datagram = datagram;
}
/// <summary>
/// 客户端状态
/// </summary>
public TcpClientState Client { get; private set; }
/// <summary>
/// 客户端
/// </summary>
public TcpClient TcpClient { get; private set; }
/// <summary>
/// 报文
/// </summary>
public T Datagram { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,607 @@
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
namespace TcpServer
{
public interface ProtoFilter
{
void input(byte[] data);
List<string> swap_msgs();
}
public class SimpleProtocolFilter : ProtoFilter
{
/*
[][]
[有效数据字节数]HEADER_SIZE
[有效数据]
[有效数据]utf-8utf-8
*/
private byte[] buf = new byte[0];
private int HEADER_SIZE = 4;
private List<string> msgs = new List<string>();
public void input(byte[] data)
{
buf = Combine(buf, data);
while (buf.Length > HEADER_SIZE)
{
int data_size = BitConverter.ToInt32(buf, 0);
if (buf.Length >= data_size + HEADER_SIZE)
{
byte[] data_body = Slice(buf, HEADER_SIZE, data_size + HEADER_SIZE);
string content = System.Text.Encoding.Default.GetString(data_body);
msgs.Add(content);
buf = Slice(buf, data_size + HEADER_SIZE, buf.Length);
}
else
{
break;
}
}
}
public List<string> swap_msgs()
{
List<string> ret = msgs;
msgs = new List<string>();
return ret;
}
public byte[] pack(String content)
{
int len = content.Length;
byte[] size = BitConverter.GetBytes(len);
if (!BitConverter.IsLittleEndian)
{
//reverse it so we get little endian.
Array.Reverse(size);
}
byte[] body = System.Text.Encoding.Default.GetBytes(content);
byte[] ret = Combine(size, body);
return ret;
}
private static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public byte[] Slice(byte[] source, int start, int end)
{
int length = end - start;
byte[] ret = new byte[length];
Array.Copy(source, start, ret, 0, length);
return ret;
}
}
/// <summary>
/// 异步TCP服务器
/// </summary>
public class AsyncTcpServer : IDisposable
{
#region Fields
private TcpListener _listener;
private ConcurrentDictionary<string, TcpClientState> _clients;
private bool _disposed = false;
#endregion
#region Ctors
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="listenPort">监听的端口</param>
public AsyncTcpServer(int listenPort)
: this(IPAddress.Any, listenPort)
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="localEP">监听的终结点</param>
public AsyncTcpServer(IPEndPoint localEP)
: this(localEP.Address, localEP.Port)
{
}
/// <summary>
/// 异步TCP服务器
/// </summary>
/// <param name="localIPAddress">监听的IP地址</param>
/// <param name="listenPort">监听的端口</param>
public AsyncTcpServer(IPAddress localIPAddress, int listenPort)
{
this.Address = localIPAddress;
this.Port = listenPort;
this.Encoding = Encoding.Default;
_clients = new ConcurrentDictionary<string, TcpClientState>();
_listener = new TcpListener(Address, Port);
// _listener.AllowNatTraversal(true);
}
#endregion
#region Properties
/// <summary>
/// 服务器是否正在运行
/// </summary>
public bool IsRunning { get; private set; }
/// <summary>
/// 监听的IP地址
/// </summary>
public IPAddress Address { get; private set; }
/// <summary>
/// 监听的端口
/// </summary>
public int Port { get; private set; }
/// <summary>
/// 通信使用的编码
/// </summary>
public Encoding Encoding { get; set; }
#endregion
#region Server
/// <summary>
/// 启动服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public AsyncTcpServer Start()
{
Debug.Log("start server");
return Start(10);
}
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="backlog">服务器所允许的挂起连接序列的最大长度</param>
/// <returns>异步TCP服务器</returns>
public AsyncTcpServer Start(int backlog)
{
if (IsRunning)
return this;
IsRunning = true;
_listener.Start(backlog);
ContinueAcceptTcpClient(_listener);
return this;
}
/// <summary>
/// 停止服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public AsyncTcpServer Stop()
{
if (!IsRunning)
return this;
try
{
_listener.Stop();
foreach (var client in _clients.Values)
{
client.TcpClient.Client.Disconnect(false);
}
_clients.Clear();
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
catch (SocketException ex)
{
Debug.LogException(ex);
}
IsRunning = false;
return this;
}
private void ContinueAcceptTcpClient(TcpListener tcpListener)
{
try
{
tcpListener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), tcpListener);
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
catch (SocketException ex)
{
Debug.LogException(ex);
}
}
#endregion
#region Receive
private void HandleTcpClientAccepted(IAsyncResult ar)
{
if (!IsRunning)
return;
TcpListener tcpListener = (TcpListener)ar.AsyncState;
TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar);
if (!tcpClient.Connected)
return;
byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
SimpleProtocolFilter prot = new SimpleProtocolFilter();
TcpClientState internalClient = new TcpClientState(tcpClient, buffer, prot);
// add client connection to cache
string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString();
_clients.AddOrUpdate(tcpClientKey, internalClient, (n, o) =>
{
return internalClient;
});
RaiseClientConnected(tcpClient);
// begin to read data
NetworkStream networkStream = internalClient.NetworkStream;
ContinueReadBuffer(internalClient, networkStream);
// keep listening to accept next connection
ContinueAcceptTcpClient(tcpListener);
}
private void HandleDatagramReceived(IAsyncResult ar)
{
if (!IsRunning)
return;
try
{
TcpClientState internalClient = (TcpClientState)ar.AsyncState;
if (!internalClient.TcpClient.Connected)
return;
NetworkStream networkStream = internalClient.NetworkStream;
int numberOfReadBytes = 0;
try
{
// if the remote host has shutdown its connection,
// read will immediately return with zero bytes.
numberOfReadBytes = networkStream.EndRead(ar);
}
catch (Exception ex)
{
Debug.LogException(ex);
numberOfReadBytes = 0;
}
if (numberOfReadBytes == 0)
{
// connection has been closed
TcpClientState internalClientToBeThrowAway;
string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString();
_clients.TryRemove(tcpClientKey, out internalClientToBeThrowAway);
RaiseClientDisconnected(internalClient.TcpClient);
return;
}
// received byte and trigger event notification
var receivedBytes = new byte[numberOfReadBytes];
Array.Copy(internalClient.Buffer, 0, receivedBytes, 0, numberOfReadBytes);
// input bytes into protofilter
internalClient.Prot.input(receivedBytes);
RaiseDatagramReceived(internalClient, receivedBytes);
// RaisePlaintextReceived(internalClient.TcpClient, receivedBytes);
// continue listening for tcp datagram packets
ContinueReadBuffer(internalClient, networkStream);
}
catch (InvalidOperationException ex)
{
Debug.LogException(ex);
}
}
private void ContinueReadBuffer(TcpClientState internalClient, NetworkStream networkStream)
{
try
{
networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, HandleDatagramReceived, internalClient);
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
}
#endregion
#region Events
/// <summary>
/// 接收到数据报文事件
/// </summary>
public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;
/// <summary>
/// 接收到数据报文明文事件
/// </summary>
public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;
private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram)
{
if (DatagramReceived != null)
{
DatagramReceived(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
}
}
private void RaisePlaintextReceived(TcpClientState sender, byte[] datagram)
{
if (PlaintextReceived != null)
{
PlaintextReceived(this, new TcpDatagramReceivedEventArgs<string>(sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
}
}
/// <summary>
/// 与客户端的连接已建立事件
/// </summary>
public event EventHandler<TcpClientConnectedEventArgs> ClientConnected;
/// <summary>
/// 与客户端的连接已断开事件
/// </summary>
public event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected;
private void RaiseClientConnected(TcpClient tcpClient)
{
if (ClientConnected != null)
{
ClientConnected(this, new TcpClientConnectedEventArgs(tcpClient));
}
}
private void RaiseClientDisconnected(TcpClient tcpClient)
{
if (ClientDisconnected != null)
{
ClientDisconnected(this, new TcpClientDisconnectedEventArgs(tcpClient));
}
}
#endregion
#region Send
private void GuardRunning()
{
if (!IsRunning)
throw new InvalidProgramException("This TCP server has not been started yet.");
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient tcpClient, byte[] datagram)
{
GuardRunning();
if (tcpClient == null)
throw new ArgumentNullException("tcpClient");
if (datagram == null)
throw new ArgumentNullException("datagram");
try
{
NetworkStream stream = tcpClient.GetStream();
if (stream.CanWrite)
{
stream.BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient);
}
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void Send(TcpClient tcpClient, string datagram)
{
Send(tcpClient, this.Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(byte[] datagram)
{
GuardRunning();
foreach (var client in _clients.Values)
{
Send(client.TcpClient, datagram);
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SendToAll(string datagram)
{
GuardRunning();
SendToAll(this.Encoding.GetBytes(datagram));
}
private void HandleDatagramWritten(IAsyncResult ar)
{
try
{
((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
catch (InvalidOperationException ex)
{
Debug.LogException(ex);
}
catch (IOException ex)
{
Debug.LogException(ex);
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void SyncSend(TcpClient tcpClient, byte[] datagram)
{
GuardRunning();
if (tcpClient == null)
throw new ArgumentNullException("tcpClient");
if (datagram == null)
throw new ArgumentNullException("datagram");
try
{
NetworkStream stream = tcpClient.GetStream();
if (stream.CanWrite)
{
stream.Write(datagram, 0, datagram.Length);
}
}
catch (ObjectDisposedException ex)
{
Debug.LogException(ex);
}
}
/// <summary>
/// 发送报文至指定的客户端
/// </summary>
/// <param name="tcpClient">客户端</param>
/// <param name="datagram">报文</param>
public void SyncSend(TcpClient tcpClient, string datagram)
{
SyncSend(tcpClient, this.Encoding.GetBytes(datagram));
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SyncSendToAll(byte[] datagram)
{
GuardRunning();
foreach (var client in _clients.Values)
{
SyncSend(client.TcpClient, datagram);
}
}
/// <summary>
/// 发送报文至所有客户端
/// </summary>
/// <param name="datagram">报文</param>
public void SyncSendToAll(string datagram)
{
GuardRunning();
SyncSendToAll(this.Encoding.GetBytes(datagram));
}
#endregion
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
try
{
Stop();
if (_listener != null)
{
_listener = null;
}
}
catch (SocketException ex)
{
Debug.LogException(ex);
}
}
_disposed = true;
}
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Poco
{
public class UnityDumper : AbstractDumper
{
public override AbstractNode getRoot()
{
return new RootNode();
}
}
public class RootNode : AbstractNode
{
private List<AbstractNode> children = null;
public RootNode()
{
children = new List<AbstractNode>();
foreach (GameObject obj in Transform.FindObjectsOfType(typeof(GameObject)))
{
if (obj.transform.parent == null)
{
children.Add(new UnityNode(obj));
}
}
}
public override List<AbstractNode> getChildren() //<Modified>
{
return children;
}
}
}

View File

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

View File

@ -0,0 +1,243 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Poco;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using TcpServer;
using UnityEngine;
using Debug = UnityEngine.Debug;
public class VRSupport
{
private static Queue<Action> commands = new Queue<Action>();
public VRSupport()
{
commands = new Queue<Action>();
}
public void ClearCommands()
{
commands.Clear();
}
public void PeekCommand()
{
if (null != commands && commands.Count > 0)
{
Debug.Log("command executed " + commands.Count);
commands.Peek()();
}
}
public object isVRSupported(List<object> param)
{
#if UNITY_3 || UNITY_4
return false;
#elif UNITY_5 || UNITY_2017_1
return UnityEngine.VR.VRSettings.loadedDeviceName.Equals("CARDBOARD");
#else
return UnityEngine.XR.XRSettings.loadedDeviceName.Equals("CARDBOARD");
#endif
}
public object IsQueueEmpty(List<object> param)
{
Debug.Log("Checking queue");
if (commands != null && commands.Count > 0)
{
return null;
}
else
{
Thread.Sleep(1000); // we wait a bit and check again just in case we run in between calls
if (commands != null && commands.Count > 0)
{
return null;
}
}
return commands.Count;
}
public object RotateObject(List<object> param)
{
var xRotation = Convert.ToSingle(param[0]);
var yRotation = Convert.ToSingle(param[1]);
var zRotation = Convert.ToSingle(param[2]);
float speed = 0f;
if (param.Count > 5)
speed = Convert.ToSingle(param[5]);
Vector3 mousePosition = new Vector3(xRotation, yRotation, zRotation);
foreach (GameObject cameraContainer in GameObject.FindObjectsOfType<GameObject>())
{
if (cameraContainer.name.Equals(param[3]))
{
foreach (GameObject cameraFollower in GameObject.FindObjectsOfType<GameObject>())
{
if (cameraFollower.name.Equals(param[4]))
{
lock (commands)
{
commands.Enqueue(() => recoverOffset(cameraFollower, cameraContainer, speed));
}
lock (commands)
{
var currentRotation = cameraContainer.transform.rotation;
commands.Enqueue(() => rotate(cameraContainer, currentRotation, mousePosition, speed));
}
return true;
}
}
return true;
}
}
return false;
}
public object ObjectLookAt(List<object> param)
{
float speed = 0f;
if (param.Count > 3)
speed = Convert.ToSingle(param[3]);
foreach (GameObject toLookAt in GameObject.FindObjectsOfType<GameObject>())
{
if (toLookAt.name.Equals(param[0]))
{
foreach (GameObject cameraContainer in GameObject.FindObjectsOfType<GameObject>())
{
if (cameraContainer.name.Equals(param[1]))
{
foreach (GameObject cameraFollower in GameObject.FindObjectsOfType<GameObject>())
{
if (cameraFollower.name.Equals(param[2]))
{
lock (commands)
{
commands.Enqueue(() => recoverOffset(cameraFollower, cameraContainer, speed));
}
lock (commands)
{
commands.Enqueue(() => objectLookAt(cameraContainer, toLookAt, speed));
}
return true;
}
}
}
}
}
}
return false;
}
protected void rotate(GameObject go, Quaternion originalRotation, Vector3 mousePosition, float speed)
{
Debug.Log("rotating");
if (!RotateObject(originalRotation, mousePosition, go, speed))
{
lock (commands)
{
commands.Dequeue();
}
}
}
protected void objectLookAt(GameObject go, GameObject toLookAt, float speed)
{
Debug.Log("looking at " + toLookAt.name);
Debug.Log("from " + go.name);
if (!ObjectLookAtObject(toLookAt, go, speed))
{
lock (commands)
{
commands.Dequeue();
}
}
}
protected void recoverOffset(GameObject subcontainter, GameObject cameraContainer, float speed)
{
Debug.Log("recovering " + cameraContainer.name);
if (!ObjectRecoverOffset(subcontainter, cameraContainer, speed))
{
lock (commands)
{
commands.Dequeue();
}
}
}
protected bool RotateObject(Quaternion originalPosition, Vector3 mousePosition, GameObject cameraContainer, float rotationSpeed = 0.125f)
{
if (null == cameraContainer)
{
return false;
}
var expectedx = originalPosition.eulerAngles.x + mousePosition.x;
var expectedy = originalPosition.eulerAngles.y + mousePosition.y;
var expectedz = originalPosition.eulerAngles.z + mousePosition.z;
var toRotation = Quaternion.Euler(new Vector3(expectedx, expectedy, expectedz));
cameraContainer.transform.rotation = Quaternion.Lerp(cameraContainer.transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
var angle = Quaternion.Angle(cameraContainer.transform.rotation, toRotation);
if (angle == 0)
{
return false;
}
return true;
}
protected bool ObjectLookAtObject(GameObject go, GameObject cameraContainer, float rotationSpeed = 0.125f)
{
if (null == go || null == cameraContainer)
{
Debug.Log("exception - item null");
return false;
}
var toRotation = Quaternion.LookRotation(go.transform.position - (cameraContainer.transform.localPosition));
cameraContainer.transform.rotation = Quaternion.Lerp(cameraContainer.transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
// It should not be needed but sometimes the difference of eurlerAngles might be small and this would ensure it works fine
if (Quaternion.Angle(cameraContainer.transform.rotation, toRotation) == 0)
{
return false;
}
return true;
}
protected bool ObjectRecoverOffset(GameObject subcontainer, GameObject cameraContainer, float rotationSpeed = 0.125f)
{
if (null == cameraContainer)
{
Debug.Log("exception - item null");
return false;
}
// add offset with the camera
var cameraRotation = Camera.main.transform.localRotation;
var toRotate = new Quaternion(-cameraRotation.x, -cameraRotation.y, -cameraRotation.z, cameraRotation.w);
subcontainer.transform.localRotation = Quaternion.Lerp(subcontainer.transform.localRotation, toRotate, rotationSpeed * Time.deltaTime);
// It should not be needed but sometimes the difference of eurlerAngles might be small and this would ensure it works fine
if (Quaternion.Angle(subcontainer.transform.localRotation, toRotate) == 0)
{
return false;
}
return true;
}
}

View File

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

View File

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

View File

@ -0,0 +1,68 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Poco
{
public class AbstractDumper : IDumper
{
public virtual AbstractNode getRoot()
{
return null;
}
public Dictionary<string, object> dumpHierarchy()
{
return dumpHierarchyImpl(getRoot(), true);
}
public Dictionary<string, object> dumpHierarchy(bool onlyVisibleNode)
{
return dumpHierarchyImpl(getRoot(), onlyVisibleNode);
}
private Dictionary<string, object> dumpHierarchyImpl(AbstractNode node, bool onlyVisibleNode)
{
if (node == null)
{
return null;
}
Dictionary<string, object> payload = node.enumerateAttrs();
Dictionary<string, object> result = new Dictionary<string, object>();
string name = (string)node.getAttr("name");
result.Add("name", name);
result.Add("payload", payload);
List<object> children = new List<object>();
foreach (AbstractNode child in node.getChildren())
{
if (!onlyVisibleNode || (bool)child.getAttr("visible"))
{
children.Add(dumpHierarchyImpl(child, onlyVisibleNode));
}
}
if (children.Count > 0)
{
result.Add("children", children);
}
return result;
}
public virtual List<float> getPortSize()
{
return null;
}
}
public interface IDumper
{
AbstractNode getRoot();
Dictionary<string, object> dumpHierarchy();
Dictionary<string, object> dumpHierarchy(bool onlyVisibleNode);
List<float> getPortSize();
}
}

View File

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

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Poco
{
public class AbstractNode
{
private List<string> requiredAttrs = new List<string> {
"name",
"type",
"visible",
"pos",
"size",
"scale",
"anchorPoint",
"zOrders"
};
public virtual AbstractNode getParent ()
{
return null;
}
public virtual List<AbstractNode> getChildren ()
{
return null;
}
public virtual object getAttr (string attrName)
{
Dictionary<string, object> defaultAttrs = new Dictionary<string, object> () {
{ "name", "<Root>" },
{ "type", "Root" },
{ "visible", true },
{ "pos", new List<float> (){ 0.0f, 0.0f } },
{ "size", new List<float> (){ 0.0f, 0.0f } },
{ "scale", new List<float> (){ 1.0f, 1.0f } },
{ "anchorPoint", new List<float> (){ 0.5f, 0.5f } },
{ "zOrders", new Dictionary<string, object> (){ { "local", 0 }, { "global", 0 } } }
};
return defaultAttrs.ContainsKey (attrName) ? defaultAttrs [attrName] : null;
}
public virtual void setAttr (string attrName, object val)
{
}
public virtual Dictionary<string, object> enumerateAttrs ()
{
Dictionary<string, object> ret = new Dictionary<string, object> ();
foreach (string attr in requiredAttrs) {
ret.Add (attr, getAttr (attr));
}
return ret;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,533 @@
using System;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using TMPro;
namespace Poco
{
public class UnityNode : AbstractNode
{
public static Dictionary<string, string> TypeNames = new Dictionary<string, string>() {
{ "Text", "Text" },
{ "Gradient Text", "Gradient.Text" },
{ "Image", "Image" },
{ "RawImage", "Raw.Image" },
{ "Mask", "Mask" },
{ "2DRectMask", "2D-Rect.Mask" },
{ "Button", "Button" },
{ "InputField", "InputField" },
{ "Toggle", "Toggle" },
{ "Toggle Group", "ToggleGroup" },
{ "Slider", "Slider" },
{ "ScrollBar", "ScrollBar" },
{ "DropDown", "DropDown" },
{ "ScrollRect", "ScrollRect" },
{ "Selectable", "Selectable" },
{ "Camera", "Camera" },
{ "RectTransform", "Node" },
{ "TextMeshProUGUI","TMPROUGUI" },
{ "TMP_Text","TMPRO" },
};
public static string DefaultTypeName = "GameObject";
private GameObject gameObject;
private Renderer renderer;
private RectTransform rectTransform;
private Rect rect;
private Vector2 objectPos;
private List<string> components;
private Camera camera;
public UnityNode(GameObject obj)
{
gameObject = obj;
camera = Camera.main;
foreach (var cam in Camera.allCameras)
{
// skip the main camera
// we want to use specified camera first then fallback to main camera if no other cameras
// for further advanced cases, we could test whether the game object is visible within the camera
if (cam == Camera.main)
{
continue;
}
if ((cam.cullingMask & (1 << gameObject.layer)) != 0)
{
camera = cam;
}
}
renderer = gameObject.GetComponent<Renderer>();
rectTransform = gameObject.GetComponent<RectTransform>();
rect = GameObjectRect(renderer, rectTransform);
objectPos = renderer ? WorldToGUIPoint(camera, renderer.bounds.center) : Vector2.zero;
components = GameObjectAllComponents();
}
public override AbstractNode getParent()
{
GameObject parentObj = gameObject.transform.parent.gameObject;
return new UnityNode(parentObj);
}
public override List<AbstractNode> getChildren()
{
List<AbstractNode> children = new List<AbstractNode>();
foreach (Transform child in gameObject.transform)
{
children.Add(new UnityNode(child.gameObject));
}
return children;
}
public override object getAttr(string attrName)
{
switch (attrName)
{
case "name":
return gameObject.name;
case "type":
return GuessObjectTypeFromComponentNames(components);
case "visible":
return GameObjectVisible(renderer, components);
case "pos":
return GameObjectPosInScreen(objectPos, renderer, rectTransform, rect);
case "size":
return GameObjectSizeInScreen(rect, rectTransform);
case "scale":
return new List<float>() { 1.0f, 1.0f };
case "anchorPoint":
return GameObjectAnchorInScreen(renderer, rect, objectPos);
case "zOrders":
return GameObjectzOrders();
case "clickable":
return GameObjectClickable(components);
case "text":
return GameObjectText();
case "components":
return components;
case "texture":
return GetImageSourceTexture();
case "tag":
return GameObjectTag();
case "layer":
return GameObjectLayerName();
case "_ilayer":
return GameObjectLayer();
case "_instanceId":
return gameObject.GetInstanceID();
default:
return null;
}
}
public override Dictionary<string, object> enumerateAttrs()
{
Dictionary<string, object> payload = GetPayload();
Dictionary<string, object> ret = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> p in payload)
{
if (p.Value != null)
{
ret.Add(p.Key, p.Value);
}
}
return ret;
}
private Dictionary<string, object> GetPayload()
{
Dictionary<string, object> payload = new Dictionary<string, object>() {
{ "name", gameObject.name },
{ "type", GuessObjectTypeFromComponentNames (components) },
{ "visible", GameObjectVisible (renderer, components) },
{ "pos", GameObjectPosInScreen (objectPos, renderer, rectTransform, rect) },
{ "size", GameObjectSizeInScreen (rect, rectTransform) },
{ "scale", new List<float> (){ 1.0f, 1.0f } },
{ "anchorPoint", GameObjectAnchorInScreen (renderer, rect, objectPos) },
{ "zOrders", GameObjectzOrders () },
{ "clickable", GameObjectClickable (components) },
{ "text", GameObjectText () },
{ "components", components },
{ "texture", GetImageSourceTexture () },
{ "tag", GameObjectTag () },
{ "_ilayer", GameObjectLayer() },
{ "layer", GameObjectLayerName() },
{ "_instanceId", gameObject.GetInstanceID () },
};
return payload;
}
private string GuessObjectTypeFromComponentNames(List<string> components)
{
List<string> cns = new List<string>(components);
cns.Reverse();
foreach (string name in cns)
{
if (TypeNames.ContainsKey(name))
{
return TypeNames[name];
}
}
return DefaultTypeName;
}
private bool GameObjectVisible(Renderer renderer, List<string> components)
{
if (gameObject.activeInHierarchy)
{
bool light = components.Contains("Light");
// bool mesh = components.Contains ("MeshRenderer") && components.Contains ("MeshFilter");
bool particle = components.Contains("ParticleSystem") && components.Contains("ParticleSystemRenderer");
if (light || particle)
{
return false;
}
else
{
return renderer ? renderer.isVisible : true;
}
}
else
{
return false;
}
}
private int GameObjectLayer()
{
return gameObject.layer;
}
private string GameObjectLayerName()
{
return LayerMask.LayerToName(gameObject.layer);
}
private bool GameObjectClickable(List<string> components)
{
Button button = gameObject.GetComponent<Button>();
return button ? button.isActiveAndEnabled : false;
}
private string GameObjectText()
{
TMP_Text tmpText = gameObject.GetComponent<TMP_Text>();
if (tmpText)
{
return tmpText.GetParsedText();
}
TextMeshProUGUI tmpUIText = gameObject.GetComponent<TextMeshProUGUI>();
if (tmpUIText)
{
return tmpUIText.GetParsedText();
}
Text text = gameObject.GetComponent<Text>();
return text ? text.text : null;
}
private string GameObjectTag()
{
string tag;
try
{
tag = !gameObject.CompareTag("Untagged") ? gameObject.tag : null;
}
catch (UnityException)
{
tag = null;
}
return tag;
}
private List<string> GameObjectAllComponents()
{
List<string> components = new List<string>();
Component[] allComponents = gameObject.GetComponents<Component>();
if (allComponents != null)
{
foreach (Component ac in allComponents)
{
if (ac != null)
{
components.Add(ac.GetType().Name);
}
}
}
return components;
}
private Dictionary<string, float> GameObjectzOrders()
{
float CameraViewportPoint = 0;
if (camera != null)
{
CameraViewportPoint = Math.Abs(camera.WorldToViewportPoint(gameObject.transform.position).z);
}
Dictionary<string, float> zOrders = new Dictionary<string, float>() {
{ "global", 0f },
{ "local", -1 * CameraViewportPoint }
};
return zOrders;
}
private Rect GameObjectRect(Renderer renderer, RectTransform rectTransform)
{
Rect rect = new Rect(0, 0, 0, 0);
if (renderer)
{
rect = RendererToScreenSpace(camera, renderer);
}
else if (rectTransform)
{
rect = RectTransformToScreenSpace(rectTransform);
}
return rect;
}
private float[] GameObjectPosInScreen(Vector3 objectPos, Renderer renderer, RectTransform rectTransform, Rect rect)
{
float[] pos = { 0f, 0f };
if (renderer)
{
// 3d object
pos[0] = objectPos.x / (float)Screen.width;
pos[1] = objectPos.y / (float)Screen.height;
}
else if (rectTransform)
{
// ui object (rendered on screen space, other render modes may be different)
// use center pos for now
Canvas rootCanvas = GetRootCanvas(gameObject);
RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode();
switch (renderMode)
{
case RenderMode.ScreenSpaceCamera:
//上一个方案经过实际测试发现还有两个问题存在
//1.在有Canvas Scaler修改了RootCanvas的Scale的情况下坐标的抓取仍然不对影响到了ScreenSpaceCameram模式在不同分辨率和屏幕比例下识别的兼容性。
//2.RectTransformUtility转的 rectTransform.transform.position本质上得到的是RectTransform.pivot中心轴在屏幕上的坐标如果pivot不等于(0.5,0.5)
//那么获取到的position就不等于图形的中心点。
//试了一晚上,找到了解决办法。
//用MainCanvas转一次屏幕坐标
Vector2 position = RectTransformUtility.WorldToScreenPoint(rootCanvas.worldCamera, rectTransform.transform.position);
//注意: 这里的position其实是Pivot点在Screen上的坐标并不是图形意义上的中心点,在经过下列玄学公式换算才是真的图形中心在屏幕的位置。
//公式内算上了rootCanvas.scaleFactor 缩放因子经测试至少在Canvas Scaler.Expand模式下什么分辨率和屏幕比都抓的很准兼容性很强其他的有待测试。
//由于得出来的坐标是左下角为原点触控输入是左上角为原点所以要上下反转一下Poco才能用,所以y坐标用Screen.height减去。
position.Set(
position.x - rectTransform.rect.width * rootCanvas.scaleFactor * (rectTransform.pivot.x - 0.5f),
Screen.height - (position.y - rectTransform.rect.height * rootCanvas.scaleFactor * (rectTransform.pivot.y - 0.5f))
);
pos[0] = position.x / Screen.width;
pos[1] = position.y / Screen.height;
break;
case RenderMode.WorldSpace:
Vector2 _pos = RectTransformUtility.WorldToScreenPoint(rootCanvas.worldCamera, rectTransform.transform.position);
pos[0] = _pos.x / Screen.width;
pos[1] = (Screen.height - _pos.y) / Screen.height;
break;
default:
pos[0] = rect.center.x / (float)Screen.width;
pos[1] = rect.center.y / (float)Screen.height;
break;
}
}
return pos;
}
private Canvas GetRootCanvas(GameObject gameObject)
{
Canvas canvas = gameObject.GetComponentInParent<Canvas>();
// 如果unity版本小于unity5.5就用递归的方式取吧没法直接取rootCanvas
// 如果有用到4.6以下版本的话就自己手动在这里添加条件吧
#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4
if (canvas && canvas.isRootCanvas)
{
return canvas;
}
else
{
if (gameObject.transform.parent.gameObject != null)
{
return GetRootCanvas(gameObject.transform.parent.gameObject);
}
else
{
return null;
}
}
#else
if (canvas && canvas.isRootCanvas)
{
return canvas;
}
else if (canvas)
{
return canvas.rootCanvas;
}
else
{
return null;
}
#endif
}
private float[] GameObjectSizeInScreen(Rect rect, RectTransform rectTransform)
{
float[] size = { 0f, 0f };
if (rectTransform)
{
Canvas rootCanvas = GetRootCanvas(gameObject);
RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode();
switch (renderMode)
{
case RenderMode.ScreenSpaceCamera:
Rect _rect = RectTransformUtility.PixelAdjustRect(rectTransform, rootCanvas);
size = new float[] {
_rect.width * rootCanvas.scaleFactor / (float)Screen.width,
_rect.height * rootCanvas.scaleFactor / (float)Screen.height
};
break;
case RenderMode.WorldSpace:
Rect rect_ = rectTransform.rect;
RectTransform canvasTransform = rootCanvas.GetComponent<RectTransform>();
size = new float[] { rect_.width / canvasTransform.rect.width, rect_.height / canvasTransform.rect.height };
break;
default:
size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height };
break;
}
}
else
{
size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height };
}
return size;
}
private float[] GameObjectAnchorInScreen(Renderer renderer, Rect rect, Vector3 objectPos)
{
float[] defaultValue = { 0.5f, 0.5f };
if (rectTransform)
{
Vector2 data = rectTransform.pivot;
defaultValue[0] = data[0];
defaultValue[1] = 1 - data[1];
return defaultValue;
}
if (!renderer)
{
//<Modified> some object do not have renderer
return defaultValue;
}
float[] anchor = { (objectPos.x - rect.xMin) / rect.width, (objectPos.y - rect.yMin) / rect.height };
if (Double.IsNaN(anchor[0]) || Double.IsNaN(anchor[1]))
{
return defaultValue;
}
else if (Double.IsPositiveInfinity(anchor[0]) || Double.IsPositiveInfinity(anchor[1]))
{
return defaultValue;
}
else if (Double.IsNegativeInfinity(anchor[0]) || Double.IsNegativeInfinity(anchor[1]))
{
return defaultValue;
}
else
{
return anchor;
}
}
private string GetImageSourceTexture()
{
Image image = gameObject.GetComponent<Image>();
if (image != null && image.sprite != null)
{
return image.sprite.name;
}
RawImage rawImage = gameObject.GetComponent<RawImage>();
if (rawImage != null && rawImage.texture != null)
{
return rawImage.texture.name;
}
SpriteRenderer spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
if (spriteRenderer != null && spriteRenderer.sprite != null)
{
return spriteRenderer.sprite.name;
}
Renderer render = gameObject.GetComponent<Renderer>();
if (renderer != null && renderer.material != null)
{
return renderer.material.color.ToString();
}
return null;
}
protected static Vector2 WorldToGUIPoint(Camera camera, Vector3 world)
{
Vector2 screenPoint = Vector2.zero;
if (camera != null)
{
screenPoint = camera.WorldToScreenPoint(world);
screenPoint.y = (float)Screen.height - screenPoint.y;
}
return screenPoint;
}
protected static Rect RendererToScreenSpace(Camera camera, Renderer renderer)
{
Vector3 cen = renderer.bounds.center;
Vector3 ext = renderer.bounds.extents;
Vector2[] extentPoints = new Vector2[8] {
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z - ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z - ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z + ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z + ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z - ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z - ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z + ext.z)),
WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z + ext.z))
};
Vector2 min = extentPoints[0];
Vector2 max = extentPoints[0];
foreach (Vector2 v in extentPoints)
{
min = Vector2.Min(min, v);
max = Vector2.Max(max, v);
}
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
}
protected static Rect RectTransformToScreenSpace(RectTransform rectTransform)
{
Vector2 size = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
Rect rect = new Rect(rectTransform.position.x, Screen.height - rectTransform.position.y, size.x, size.y);
rect.x -= (rectTransform.pivot.x * size.x);
rect.y -= ((1.0f - rectTransform.pivot.y) * size.y);
return rect;
}
public static bool SetText(GameObject go, string textVal)
{
if (go != null)
{
var inputField = go.GetComponent<InputField>();
if (inputField != null)
{
inputField.text = textVal;
return true;
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,2 @@
Define_list:
- SDK_POCO

View File

@ -0,0 +1 @@
0.0.1