diff --git a/Tool/Channels/Dev/ChannelSetting.yaml b/Tool/Channels/Dev/ChannelSetting.yaml index a34b3f3fbdc..995db1a85b1 100644 --- a/Tool/Channels/Dev/ChannelSetting.yaml +++ b/Tool/Channels/Dev/ChannelSetting.yaml @@ -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 \ No newline at end of file diff --git a/Tool/py_tools/PreHandle.py b/Tool/py_tools/PreHandle.py new file mode 100644 index 00000000000..7052a260d0a --- /dev/null +++ b/Tool/py_tools/PreHandle.py @@ -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 ") + 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() \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs new file mode 100644 index 00000000000..508ec9506ee --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs @@ -0,0 +1,1835 @@ +#if !UNITY_WSA +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Threading; + +namespace TcpServer +{ + + /// + /// Represents a thread-safe collection of keys and values. + /// + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + /// + /// All public and protected members of are thread-safe and may be used + /// concurrently from multiple threads. + /// + [Serializable] + [ComVisible(false)] + [DebuggerDisplay("Count = {Count}")] + // [HostProtection(Synchronization = true, ExternalThreading = true)] + public class ConcurrentDictionary : IDictionary, IDictionary + { + [NonSerialized] + private volatile Node[] m_buckets; + // A singly-linked list for each bucket. + [NonSerialized] + private object[] m_locks; + // A set of locks, each guarding a section of the table. + [NonSerialized] + private volatile int[] m_countPerLock; + // The number of elements guarded by each lock. + private IEqualityComparer m_comparer; + // Key equality comparer + + private KeyValuePair[] m_serializationArray; + // Used for custom serialization + + private int m_serializationConcurrencyLevel; + // used to save the concurrency level in serialization + + private int m_serializationCapacity; + // used to save the capacity in serialization + + // The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the + // DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference + // and blocking, but also the more expensive operations that require all locks become (e.g. table + // resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good + // compromise. + private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4; + + // The default capacity, i.e. the initial # of buckets. When choosing this value, we are making + // a trade-off between the size of a very small dictionary, and the number of resizes when + // constructing a large dictionary. Also, the capacity should not be divisible by a small prime. + private const int DEFAULT_CAPACITY = 31; + + /// + /// Initializes a new instance of the + /// class that is empty, has the default concurrency level, has the default initial capacity, and + /// uses the default comparer for the key type. + /// + public ConcurrentDictionary() : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY) + { + } + + /// + /// Initializes a new instance of the + /// class that is empty, has the specified concurrency level and capacity, and uses the default + /// comparer for the key type. + /// + /// The estimated number of threads that will update the + /// concurrently. + /// The initial number of elements that the + /// can contain. + /// is + /// less than 1. + /// is less than + /// 0. + public ConcurrentDictionary(int concurrencyLevel, int capacity) : this(concurrencyLevel, capacity, EqualityComparer.Default) + { + } + + /// + /// Initializes a new instance of the + /// class that contains elements copied from the specified , has the default concurrency + /// level, has the default initial capacity, and uses the default comparer for the key type. + /// + /// The whose elements are copied to + /// the new + /// . + /// is a null reference + /// (Nothing in Visual Basic). + /// contains one or more + /// duplicate keys. + public ConcurrentDictionary(IEnumerable> collection) : this(collection, EqualityComparer.Default) + { + } + + /// + /// Initializes a new instance of the + /// class that is empty, has the specified concurrency level and capacity, and uses the specified + /// . + /// + /// The + /// implementation to use when comparing keys. + /// is a null reference + /// (Nothing in Visual Basic). + public ConcurrentDictionary(IEqualityComparer comparer) : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, comparer) + { + } + + /// + /// Initializes a new instance of the + /// class that contains elements copied from the specified , has the default concurrency level, has the default + /// initial capacity, and uses the specified + /// . + /// + /// The whose elements are copied to + /// the new + /// . + /// The + /// implementation to use when comparing keys. + /// is a null reference + /// (Nothing in Visual Basic). -or- + /// is a null reference (Nothing in Visual Basic). + /// + public ConcurrentDictionary(IEnumerable> collection, IEqualityComparer comparer) + : this(DefaultConcurrencyLevel, collection, comparer) + { + } + + /// + /// Initializes a new instance of the + /// class that contains elements copied from the specified , + /// has the specified concurrency level, has the specified initial capacity, and uses the specified + /// . + /// + /// The estimated number of threads that will update the + /// concurrently. + /// The whose elements are copied to the new + /// . + /// The implementation to use + /// when comparing keys. + /// + /// is a null reference (Nothing in Visual Basic). + /// -or- + /// is a null reference (Nothing in Visual Basic). + /// + /// + /// is less than 1. + /// + /// contains one or more duplicate keys. + public ConcurrentDictionary( + int concurrencyLevel, IEnumerable> collection, IEqualityComparer comparer) + : this(concurrencyLevel, DEFAULT_CAPACITY, comparer) + { + if (collection == null) + throw new ArgumentNullException("collection"); + if (comparer == null) + throw new ArgumentNullException("comparer"); + + InitializeFromCollection(collection); + } + + private void InitializeFromCollection(IEnumerable> collection) + { + TValue dummy; + foreach (KeyValuePair pair in collection) + { + if (pair.Key == null) + throw new ArgumentNullException("key"); + + if (!TryAddInternal(pair.Key, pair.Value, false, false, out dummy)) + { + throw new ArgumentException(GetResource("ConcurrentDictionary_SourceContainsDuplicateKeys")); + } + } + } + + /// + /// Initializes a new instance of the + /// class that is empty, has the specified concurrency level, has the specified initial capacity, and + /// uses the specified . + /// + /// The estimated number of threads that will update the + /// concurrently. + /// The initial number of elements that the + /// can contain. + /// The + /// implementation to use when comparing keys. + /// + /// is less than 1. -or- + /// is less than 0. + /// + /// is a null reference + /// (Nothing in Visual Basic). + public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer comparer) + { + if (concurrencyLevel < 1) + { + throw new ArgumentOutOfRangeException("concurrencyLevel", GetResource("ConcurrentDictionary_ConcurrencyLevelMustBePositive")); + } + if (capacity < 0) + { + throw new ArgumentOutOfRangeException("capacity", GetResource("ConcurrentDictionary_CapacityMustNotBeNegative")); + } + if (comparer == null) + throw new ArgumentNullException("comparer"); + + // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard + // any buckets. + if (capacity < concurrencyLevel) + { + capacity = concurrencyLevel; + } + + m_locks = new object[concurrencyLevel]; + for (int i = 0; i < m_locks.Length; i++) + { + m_locks[i] = new object(); + } + + m_countPerLock = new int[m_locks.Length]; + m_buckets = new Node[capacity]; + m_comparer = comparer; + } + + + /// + /// Attempts to add the specified key and value to the . + /// + /// The key of the element to add. + /// The value of the element to add. The value can be a null reference (Nothing + /// in Visual Basic) for reference types. + /// true if the key/value pair was added to the + /// successfully; otherwise, false. + /// is null reference + /// (Nothing in Visual Basic). + /// The + /// contains too many elements. + public bool TryAdd(TKey key, TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + TValue dummy; + return TryAddInternal(key, value, false, true, out dummy); + } + + /// + /// Determines whether the contains the specified + /// key. + /// + /// The key to locate in the . + /// true if the contains an element with + /// the specified key; otherwise, false. + /// is a null reference + /// (Nothing in Visual Basic). + public bool ContainsKey(TKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + TValue throwAwayValue; + return TryGetValue(key, out throwAwayValue); + } + + /// + /// Attempts to remove and return the the value with the specified key from the + /// . + /// + /// The key of the element to remove and return. + /// When this method returns, contains the object removed from the + /// or the default value of + /// if the operation failed. + /// true if an object was removed successfully; otherwise, false. + /// is a null reference + /// (Nothing in Visual Basic). + public bool TryRemove(TKey key, out TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + + return TryRemoveInternal(key, out value, false, default(TValue)); + } + + /// + /// Removes the specified key from the dictionary if it exists and returns its associated value. + /// If matchValue flag is set, the key will be removed only if is associated with a particular + /// value. + /// + /// The key to search for and remove if it exists. + /// The variable into which the removed value, if found, is stored. + /// Whether removal of the key is conditional on its value. + /// The conditional value to compare against if is true + /// + private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue) + { + while (true) + { + Node[] buckets = m_buckets; + + int bucketNo, lockNo; + GetBucketAndLockNo(m_comparer.GetHashCode(key), out bucketNo, out lockNo, buckets.Length); + + lock (m_locks[lockNo]) + { + // If the table just got resized, we may not be holding the right lock, and must retry. + // This should be a rare occurence. + if (buckets != m_buckets) + { + continue; + } + + Node prev = null; + for (Node curr = m_buckets[bucketNo]; curr != null; curr = curr.m_next) + { + Assert((prev == null && curr == m_buckets[bucketNo]) || prev.m_next == curr); + + if (m_comparer.Equals(curr.m_key, key)) + { + if (matchValue) + { + bool valuesMatch = EqualityComparer.Default.Equals(oldValue, curr.m_value); + if (!valuesMatch) + { + value = default(TValue); + return false; + } + } + + if (prev == null) + { + m_buckets[bucketNo] = curr.m_next; + } + else + { + prev.m_next = curr.m_next; + } + + value = curr.m_value; + m_countPerLock[lockNo]--; + return true; + } + prev = curr; + } + } + + value = default(TValue); + return false; + } + } + + /// + /// Attempts to get the value associated with the specified key from the . + /// + /// The key of the value to get. + /// When this method returns, contains the object from + /// the + /// with the spedified key or the default value of + /// , if the operation failed. + /// true if the key was found in the ; + /// otherwise, false. + /// is a null reference + /// (Nothing in Visual Basic). + public bool TryGetValue(TKey key, out TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + + int bucketNo, lockNoUnused; + + // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize. + Node[] buckets = m_buckets; + GetBucketAndLockNo(m_comparer.GetHashCode(key), out bucketNo, out lockNoUnused, buckets.Length); + + // We can get away w/out a lock here. + Node n = buckets[bucketNo]; + + // The memory barrier ensures that the load of the fields of 'n' doesn’t move before the load from buckets[i]. + Thread.MemoryBarrier(); + while (n != null) + { + if (m_comparer.Equals(n.m_key, key)) + { + value = n.m_value; + return true; + } + n = n.m_next; + } + + value = default(TValue); + return false; + } + + /// + /// Compares the existing value for the specified key with a specified value, and if they’re equal, + /// updates the key with a third value. + /// + /// The key whose value is compared with and + /// possibly replaced. + /// The value that replaces the value of the element with if the comparison results in equality. + /// The value that is compared to the value of the element with + /// . + /// true if the value with was equal to and replaced with ; otherwise, + /// false. + /// is a null + /// reference. + public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) + { + if (key == null) + throw new ArgumentNullException("key"); + + int hashcode = m_comparer.GetHashCode(key); + IEqualityComparer valueComparer = EqualityComparer.Default; + + while (true) + { + int bucketNo; + int lockNo; + + Node[] buckets = m_buckets; + GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, buckets.Length); + + lock (m_locks[lockNo]) + { + // If the table just got resized, we may not be holding the right lock, and must retry. + // This should be a rare occurence. + if (buckets != m_buckets) + { + continue; + } + + // Try to find this key in the bucket + Node prev = null; + for (Node node = buckets[bucketNo]; node != null; node = node.m_next) + { + Assert((prev == null && node == m_buckets[bucketNo]) || prev.m_next == node); + if (m_comparer.Equals(node.m_key, key)) + { + if (valueComparer.Equals(node.m_value, comparisonValue)) + { + // Replace the old node with a new node. Unfortunately, we cannot simply + // change node.m_value in place. We don't know the size of TValue, so + // its writes may not be atomic. + Node newNode = new Node(node.m_key, newValue, hashcode, node.m_next); + + if (prev == null) + { + buckets[bucketNo] = newNode; + } + else + { + prev.m_next = newNode; + } + + return true; + } + + return false; + } + + prev = node; + } + + //didn't find the key + return false; + } + } + } + + /// + /// Removes all keys and values from the . + /// + public void Clear() + { + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + + m_buckets = new Node[DEFAULT_CAPACITY]; + Array.Clear(m_countPerLock, 0, m_countPerLock.Length); + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Copies the elements of the to an array of + /// type , starting at the + /// specified array index. + /// + /// The one-dimensional array of type + /// that is the destination of the elements copied from the . The array must have zero-based indexing. + /// The zero-based index in at which copying + /// begins. + /// is a null reference + /// (Nothing in Visual Basic). + /// is less than + /// 0. + /// is equal to or greater than + /// the length of the . -or- The number of elements in the source + /// is greater than the available space from to the end of the destination + /// . + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative")); + + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + + int count = 0; + + for (int i = 0; i < m_locks.Length; i++) + { + count += m_countPerLock[i]; + } + + if (array.Length - count < index || count < 0) + { //"count" itself or "count + index" can overflow + throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough")); + } + + CopyToPairs(array, index); + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Copies the key and value pairs stored in the to a + /// new array. + /// + /// A new array containing a snapshot of key and value pairs copied from the . + public KeyValuePair[] ToArray() + { + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + int count = 0; + checked + { + for (int i = 0; i < m_locks.Length; i++) + { + count += m_countPerLock[i]; + } + } + + KeyValuePair[] array = new KeyValuePair[count]; + + CopyToPairs(array, 0); + return array; + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. + /// + /// Important: the caller must hold all locks in m_locks before calling CopyToPairs. + /// + private void CopyToPairs(KeyValuePair[] array, int index) + { + Node[] buckets = m_buckets; + for (int i = 0; i < buckets.Length; i++) + { + for (Node current = buckets[i]; current != null; current = current.m_next) + { + array[index] = new KeyValuePair(current.m_key, current.m_value); + index++; //this should never flow, CopyToPairs is only called when there's no overflow risk + } + } + } + + /// + /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. + /// + /// Important: the caller must hold all locks in m_locks before calling CopyToEntries. + /// + private void CopyToEntries(DictionaryEntry[] array, int index) + { + Node[] buckets = m_buckets; + for (int i = 0; i < buckets.Length; i++) + { + for (Node current = buckets[i]; current != null; current = current.m_next) + { + array[index] = new DictionaryEntry(current.m_key, current.m_value); + index++; //this should never flow, CopyToEntries is only called when there's no overflow risk + } + } + } + + /// + /// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. + /// + /// Important: the caller must hold all locks in m_locks before calling CopyToObjects. + /// + private void CopyToObjects(object[] array, int index) + { + Node[] buckets = m_buckets; + for (int i = 0; i < buckets.Length; i++) + { + for (Node current = buckets[i]; current != null; current = current.m_next) + { + array[index] = new KeyValuePair(current.m_key, current.m_value); + index++; //this should never flow, CopyToObjects is only called when there's no overflow risk + } + } + } + + /// Returns an enumerator that iterates through the . + /// An enumerator for the . + /// + /// The enumerator returned from the dictionary is safe to use concurrently with + /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot + /// of the dictionary. The contents exposed through the enumerator may contain modifications + /// made to the dictionary after was called. + /// + public IEnumerator> GetEnumerator() + { + Node[] buckets = m_buckets; + + for (int i = 0; i < buckets.Length; i++) + { + Node current = buckets[i]; + + // The memory barrier ensures that the load of the fields of 'current' doesn’t move before the load from buckets[i]. + Thread.MemoryBarrier(); + while (current != null) + { + yield return new KeyValuePair(current.m_key, current.m_value); + current = current.m_next; + } + } + } + + /// + /// Shared internal implementation for inserts and updates. + /// If key exists, we always return false; and if updateIfExists == true we force update with value; + /// If key doesn't exist, we always add value and return true; + /// + private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue) + { + int hashcode = m_comparer.GetHashCode(key); + + while (true) + { + int bucketNo, lockNo; + + Node[] buckets = m_buckets; + GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, buckets.Length); + + bool resizeDesired = false; + bool lockTaken = false; + try + { + if (acquireLock) + { + Monitor.Enter(m_locks[lockNo]); + lockTaken = true; + } + + // If the table just got resized, we may not be holding the right lock, and must retry. + // This should be a rare occurence. + if (buckets != m_buckets) + { + continue; + } + + // Try to find this key in the bucket + Node prev = null; + for (Node node = buckets[bucketNo]; node != null; node = node.m_next) + { + Assert((prev == null && node == m_buckets[bucketNo]) || prev.m_next == node); + if (m_comparer.Equals(node.m_key, key)) + { + // The key was found in the dictionary. If updates are allowed, update the value for that key. + // We need to create a new node for the update, in order to support TValue types that cannot + // be written atomically, since lock-free reads may be happening concurrently. + if (updateIfExists) + { + Node newNode = new Node(node.m_key, value, hashcode, node.m_next); + if (prev == null) + { + buckets[bucketNo] = newNode; + } + else + { + prev.m_next = newNode; + } + resultingValue = value; + } + else + { + resultingValue = node.m_value; + } + return false; + } + prev = node; + } + + // The key was not found in the bucket. Insert the key-value pair. + buckets[bucketNo] = new Node(key, value, hashcode, buckets[bucketNo]); + checked + { + m_countPerLock[lockNo]++; + } + + // + // If this lock has element / bucket ratio greater than 1, resize the entire table. + // Note: the formula is chosen to avoid overflow, but has a small inaccuracy due to + // rounding. + // + if (m_countPerLock[lockNo] > buckets.Length / m_locks.Length) + { + resizeDesired = true; + } + } + finally + { + if (lockTaken) + Monitor.Exit(m_locks[lockNo]); + } + + // + // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table. + // + // Concurrency notes: + // - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks. + // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0 + // and then verify that the table we passed to it as the argument is still the current table. + // + if (resizeDesired) + { + GrowTable(buckets); + } + + resultingValue = value; + return true; + } + } + + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. If the specified key is not found, a get + /// operation throws a + /// , and a set operation creates a new + /// element with the specified key. + /// is a null reference + /// (Nothing in Visual Basic). + /// The property is retrieved and + /// + /// does not exist in the collection. + public TValue this[TKey key] + { + get + { + TValue value; + if (!TryGetValue(key, out value)) + { + throw new KeyNotFoundException(); + } + return value; + } + set + { + if (key == null) + throw new ArgumentNullException("key"); + TValue dummy; + TryAddInternal(key, value, true, true, out dummy); + } + } + + /// + /// Gets the number of key/value pairs contained in the . + /// + /// The dictionary contains too many + /// elements. + /// The number of key/value paris contained in the . + /// Count has snapshot semantics and represents the number of items in the + /// at the moment when Count was accessed. + public int Count + { + get + { + int count = 0; + + int acquiredLocks = 0; + try + { + // Acquire all locks + AcquireAllLocks(ref acquiredLocks); + + // Compute the count, we allow overflow + for (int i = 0; i < m_countPerLock.Length; i++) + { + count += m_countPerLock[i]; + } + + } + finally + { + // Release locks that have been acquired earlier + ReleaseLocks(0, acquiredLocks); + } + + return count; + } + } + + /// + /// Adds a key/value pair to the + /// if the key does not already exist. + /// + /// The key of the element to add. + /// The function used to generate a value for the key + /// is a null reference + /// (Nothing in Visual Basic). + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// The value for the key. This will be either the existing value for the key if the + /// key is already in the dictionary, or the new value for the key as returned by valueFactory + /// if the key was not in the dictionary. + public TValue GetOrAdd(TKey key, Func valueFactory) + { + if (key == null) + throw new ArgumentNullException("key"); + if (valueFactory == null) + throw new ArgumentNullException("valueFactory"); + + TValue resultingValue; + if (TryGetValue(key, out resultingValue)) + { + return resultingValue; + } + TryAddInternal(key, valueFactory(key), false, true, out resultingValue); + return resultingValue; + } + + /// + /// Adds a key/value pair to the + /// if the key does not already exist. + /// + /// The key of the element to add. + /// the value to be added, if the key does not already exist + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// The value for the key. This will be either the existing value for the key if the + /// key is already in the dictionary, or the new value if the key was not in the dictionary. + public TValue GetOrAdd(TKey key, TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + + TValue resultingValue; + TryAddInternal(key, value, false, true, out resultingValue); + return resultingValue; + } + + /// + /// Adds a key/value pair to the if the key does not already + /// exist, or updates a key/value pair in the if the key + /// already exists. + /// + /// The key to be added or whose value should be updated + /// The function used to generate a value for an absent key + /// The function used to generate a new value for an existing key + /// based on the key's existing value + /// is a null reference + /// (Nothing in Visual Basic). + /// is a null reference + /// (Nothing in Visual Basic). + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// The new value for the key. This will be either be the result of addValueFactory (if the key was + /// absent) or the result of updateValueFactory (if the key was present). + public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory) + { + if (key == null) + throw new ArgumentNullException("key"); + if (addValueFactory == null) + throw new ArgumentNullException("addValueFactory"); + if (updateValueFactory == null) + throw new ArgumentNullException("updateValueFactory"); + + TValue newValue, resultingValue; + while (true) + { + TValue oldValue; + if (TryGetValue(key, out oldValue)) + { //key exists, try to update + newValue = updateValueFactory(key, oldValue); + if (TryUpdate(key, newValue, oldValue)) + { + return newValue; + } + } + else + { //try add + newValue = addValueFactory(key); + if (TryAddInternal(key, newValue, false, true, out resultingValue)) + { + return resultingValue; + } + } + } + } + + /// + /// Adds a key/value pair to the if the key does not already + /// exist, or updates a key/value pair in the if the key + /// already exists. + /// + /// The key to be added or whose value should be updated + /// The value to be added for an absent key + /// The function used to generate a new value for an existing key based on + /// the key's existing value + /// is a null reference + /// (Nothing in Visual Basic). + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// The new value for the key. This will be either be the result of addValueFactory (if the key was + /// absent) or the result of updateValueFactory (if the key was present). + public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory) + { + if (key == null) + throw new ArgumentNullException("key"); + if (updateValueFactory == null) + throw new ArgumentNullException("updateValueFactory"); + TValue newValue, resultingValue; + while (true) + { + TValue oldValue; + if (TryGetValue(key, out oldValue)) + { //key exists, try to update + newValue = updateValueFactory(key, oldValue); + if (TryUpdate(key, newValue, oldValue)) + { + return newValue; + } + } + else + { //try add + if (TryAddInternal(key, addValue, false, true, out resultingValue)) + { + return resultingValue; + } + } + } + } + + + + /// + /// Gets a value that indicates whether the is empty. + /// + /// true if the is empty; otherwise, + /// false. + public bool IsEmpty + { + get + { + int acquiredLocks = 0; + try + { + // Acquire all locks + AcquireAllLocks(ref acquiredLocks); + + for (int i = 0; i < m_countPerLock.Length; i++) + { + if (m_countPerLock[i] != 0) + { + return false; + } + } + } + finally + { + // Release locks that have been acquired earlier + ReleaseLocks(0, acquiredLocks); + } + + return true; + } + } + + #region IDictionary members + + /// + /// Adds the specified key and value to the . + /// + /// The object to use as the key of the element to add. + /// The object to use as the value of the element to add. + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// + /// An element with the same key already exists in the . + void IDictionary.Add(TKey key, TValue value) + { + if (!TryAdd(key, value)) + { + throw new ArgumentException(GetResource("ConcurrentDictionary_KeyAlreadyExisted")); + } + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// true if the element is successfully remove; otherwise false. This method also returns + /// false if + /// was not found in the original . + /// + /// is a null reference + /// (Nothing in Visual Basic). + bool IDictionary.Remove(TKey key) + { + TValue throwAwayValue; + return TryRemove(key, out throwAwayValue); + } + + /// + /// Gets a collection containing the keys in the . + /// + /// An containing the keys in the + /// . + public ICollection Keys + { + get { return GetKeys(); } + } + + /// + /// Gets a collection containing the values in the . + /// + /// An containing the values in + /// the + /// . + public ICollection Values + { + get { return GetValues(); } + } + + #endregion + + #region ICollection> Members + + /// + /// Adds the specified value to the + /// with the specified key. + /// + /// The + /// structure representing the key and value to add to the . + /// The of is null. + /// The + /// contains too many elements. + /// An element with the same key already exists in the + /// + void ICollection>.Add(KeyValuePair keyValuePair) + { + ((IDictionary)this).Add(keyValuePair.Key, keyValuePair.Value); + } + + /// + /// Determines whether the + /// contains a specific key and value. + /// + /// The + /// structure to locate in the . + /// true if the is found in the ; otherwise, false. + bool ICollection>.Contains(KeyValuePair keyValuePair) + { + TValue value; + if (!TryGetValue(keyValuePair.Key, out value)) + { + return false; + } + return EqualityComparer.Default.Equals(value, keyValuePair.Value); + } + + /// + /// Gets a value indicating whether the dictionary is read-only. + /// + /// true if the is + /// read-only; otherwise, false. For , this property always returns + /// false. + bool ICollection>.IsReadOnly + { + get { return false; } + } + + /// + /// Removes a key and value from the dictionary. + /// + /// The + /// structure representing the key and value to remove from the . + /// true if the key and value represented by is successfully + /// found and removed; otherwise, false. + /// The Key property of is a null reference (Nothing in Visual Basic). + bool ICollection>.Remove(KeyValuePair keyValuePair) + { + if (keyValuePair.Key == null) + throw new ArgumentNullException(GetResource("ConcurrentDictionary_ItemKeyIsNull")); + + TValue throwAwayValue; + return TryRemoveInternal(keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value); + } + + #endregion + + #region IEnumerable Members + + /// Returns an enumerator that iterates through the . + /// An enumerator for the . + /// + /// The enumerator returned from the dictionary is safe to use concurrently with + /// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot + /// of the dictionary. The contents exposed through the enumerator may contain modifications + /// made to the dictionary after was called. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((ConcurrentDictionary)this).GetEnumerator(); + } + + #endregion + + #region IDictionary Members + + /// + /// Adds the specified key and value to the dictionary. + /// + /// The object to use as the key. + /// The object to use as the value. + /// is a null reference + /// (Nothing in Visual Basic). + /// The dictionary contains too many + /// elements. + /// + /// is of a type that is not assignable to the key type of the . -or- + /// is of a type that is not assignable to , + /// the type of values in the . + /// -or- A value with the same key already exists in the . + /// + void IDictionary.Add(object key, object value) + { + if (key == null) + throw new ArgumentNullException("key"); + if (!(key is TKey)) + throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect")); + + TValue typedValue; + try + { + typedValue = (TValue)value; + } + catch (InvalidCastException) + { + throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect")); + } + + ((IDictionary)this).Add((TKey)key, typedValue); + } + + /// + /// Gets whether the contains an + /// element with the specified key. + /// + /// The key to locate in the . + /// true if the contains + /// an element with the specified key; otherwise, false. + /// is a null reference + /// (Nothing in Visual Basic). + bool IDictionary.Contains(object key) + { + if (key == null) + throw new ArgumentNullException("key"); + + return (key is TKey) && ((ConcurrentDictionary)this).ContainsKey((TKey)key); + } + + /// Provides an for the + /// . + /// An for the . + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return new DictionaryEnumerator(this); + } + + /// + /// Gets a value indicating whether the has a fixed size. + /// + /// true if the has a + /// fixed size; otherwise, false. For , this property always + /// returns false. + bool IDictionary.IsFixedSize + { + get { return false; } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// true if the is + /// read-only; otherwise, false. For , this property always + /// returns false. + bool IDictionary.IsReadOnly + { + get { return false; } + } + + /// + /// Gets an containing the keys of the . + /// + /// An containing the keys of the . + ICollection IDictionary.Keys + { + get { return GetKeys(); } + } + + /// + /// Removes the element with the specified key from the . + /// + /// The key of the element to remove. + /// is a null reference + /// (Nothing in Visual Basic). + void IDictionary.Remove(object key) + { + if (key == null) + throw new ArgumentNullException("key"); + + TValue throwAwayValue; + if (key is TKey) + { + this.TryRemove((TKey)key, out throwAwayValue); + } + } + + /// + /// Gets an containing the values in the . + /// + /// An containing the values in the . + ICollection IDictionary.Values + { + get { return GetValues(); } + } + + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key, or a null reference (Nothing in Visual Basic) + /// if is not in the dictionary or is of a type that is + /// not assignable to the key type of the . + /// is a null reference + /// (Nothing in Visual Basic). + /// + /// A value is being assigned, and is of a type that is not assignable to the + /// key type of the . -or- A value is being + /// assigned, and is of a type that is not assignable to the value type + /// of the + /// + object IDictionary.this[object key] + { + get + { + if (key == null) + throw new ArgumentNullException("key"); + + TValue value; + if (key is TKey && this.TryGetValue((TKey)key, out value)) + { + return value; + } + + return null; + } + set + { + if (key == null) + throw new ArgumentNullException("key"); + + if (!(key is TKey)) + throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfKeyIncorrect")); + if (!(value is TValue)) + throw new ArgumentException(GetResource("ConcurrentDictionary_TypeOfValueIncorrect")); + + ((ConcurrentDictionary)this)[(TKey)key] = (TValue)value; + } + } + + #endregion + + #region ICollection Members + + /// + /// Copies the elements of the to an array, starting + /// at the specified array index. + /// + /// The one-dimensional array that is the destination of the elements copied from + /// the . The array must have zero-based + /// indexing. + /// The zero-based index in at which copying + /// begins. + /// is a null reference + /// (Nothing in Visual Basic). + /// is less than + /// 0. + /// is equal to or greater than + /// the length of the . -or- The number of elements in the source + /// is greater than the available space from to the end of the destination + /// . + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index", GetResource("ConcurrentDictionary_IndexIsNegative")); + + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + + int count = 0; + + for (int i = 0; i < m_locks.Length; i++) + { + count += m_countPerLock[i]; + } + + if (array.Length - count < index || count < 0) + { //"count" itself or "count + index" can overflow + throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayNotLargeEnough")); + } + + // To be consistent with the behavior of ICollection.CopyTo() in Dictionary, + // we recognize three types of target arrays: + // - an array of KeyValuePair structs + // - an array of DictionaryEntry structs + // - an array of objects + + KeyValuePair[] pairs = array as KeyValuePair[]; + if (pairs != null) + { + CopyToPairs(pairs, index); + return; + } + + DictionaryEntry[] entries = array as DictionaryEntry[]; + if (entries != null) + { + CopyToEntries(entries, index); + return; + } + + object[] objects = array as object[]; + if (objects != null) + { + CopyToObjects(objects, index); + return; + } + + throw new ArgumentException(GetResource("ConcurrentDictionary_ArrayIncorrectType"), "array"); + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Gets a value indicating whether access to the is + /// synchronized with the SyncRoot. + /// + /// true if access to the is synchronized + /// (thread safe); otherwise, false. For , this property always + /// returns false. + bool ICollection.IsSynchronized + { + get { return false; } + } + + /// + /// Gets an object that can be used to synchronize access to the . This property is not supported. + /// + /// The SyncRoot property is not supported. + object ICollection.SyncRoot + { + get + { + throw new NotSupportedException(); + } + } + + #endregion + + /// + /// Replaces the internal table with a larger one. To prevent multiple threads from resizing the + /// table as a result of ----s, the table of buckets that was deemed too small is passed in as + /// an argument to GrowTable(). GrowTable() obtains a lock, and then checks whether the bucket + /// table has been replaced in the meantime or not. + /// + /// Reference to the bucket table that was deemed too small. + private void GrowTable(Node[] buckets) + { + int locksAcquired = 0; + try + { + // The thread that first obtains m_locks[0] will be the one doing the resize operation + AcquireLocks(0, 1, ref locksAcquired); + + // Make sure nobody resized the table while we were waiting for lock 0: + if (buckets != m_buckets) + { + // We assume that since the table reference is different, it was already resized. If we ever + // decide to do table shrinking, or replace the table for other reasons, we will have to revisit + // this logic. + return; + } + + // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by + // 2,3,5 or 7. We can consider a different table-sizing policy in the future. + int newLength; + try + { + checked + { + // Double the size of the buckets table and add one, so that we have an odd integer. + newLength = buckets.Length * 2 + 1; + + // Now, we only need to check odd integers, and find the first that is not divisible + // by 3, 5 or 7. + while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) + { + newLength += 2; + } + + Assert(newLength % 2 != 0); + } + } + catch (OverflowException) + { + // If we were to resize the table, its new size will not fit into a 32-bit signed int. Just return. + return; + } + + Node[] newBuckets = new Node[newLength]; + int[] newCountPerLock = new int[m_locks.Length]; + + // Now acquire all other locks for the table + AcquireLocks(1, m_locks.Length, ref locksAcquired); + + // Copy all data into a new table, creating new nodes for all elements + for (int i = 0; i < buckets.Length; i++) + { + Node current = buckets[i]; + while (current != null) + { + Node next = current.m_next; + int newBucketNo, newLockNo; + GetBucketAndLockNo(current.m_hashcode, out newBucketNo, out newLockNo, newBuckets.Length); + + newBuckets[newBucketNo] = new Node(current.m_key, current.m_value, current.m_hashcode, newBuckets[newBucketNo]); + + checked + { + newCountPerLock[newLockNo]++; + } + + current = next; + } + } + + // And finally adjust m_buckets and m_countPerLock to point to data for the new table + m_buckets = newBuckets; + m_countPerLock = newCountPerLock; + + } + finally + { + // Release all locks that we took earlier + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Computes the bucket and lock number for a particular key. + /// + private void GetBucketAndLockNo( + int hashcode, out int bucketNo, out int lockNo, int bucketCount) + { + bucketNo = (hashcode & 0x7fffffff) % bucketCount; + lockNo = bucketNo % m_locks.Length; + + Assert(bucketNo >= 0 && bucketNo < bucketCount); + Assert(lockNo >= 0 && lockNo < m_locks.Length); + } + + /// + /// The number of concurrent writes for which to optimize by default. + /// + private static int DefaultConcurrencyLevel + { + + get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; } + } + + /// + /// Acquires all locks for this hash table, and increments locksAcquired by the number + /// of locks that were successfully acquired. The locks are acquired in an increasing + /// order. + /// + private void AcquireAllLocks(ref int locksAcquired) + { + AcquireLocks(0, m_locks.Length, ref locksAcquired); + Assert(locksAcquired == m_locks.Length); + } + + /// + /// Acquires a contiguous range of locks for this hash table, and increments locksAcquired + /// by the number of locks that were successfully acquired. The locks are acquired in an + /// increasing order. + /// + private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired) + { + Assert(fromInclusive <= toExclusive); + + for (int i = fromInclusive; i < toExclusive; i++) + { + bool lockTaken = false; + try + { + Monitor.Enter(m_locks[i]); + lockTaken = true; + } + finally + { + if (lockTaken) + { + locksAcquired++; + } + } + } + } + + /// + /// Releases a contiguous range of locks. + /// + private void ReleaseLocks(int fromInclusive, int toExclusive) + { + Assert(fromInclusive <= toExclusive); + + for (int i = fromInclusive; i < toExclusive; i++) + { + Monitor.Exit(m_locks[i]); + } + } + + /// + /// Gets a collection containing the keys in the dictionary. + /// + private ReadOnlyCollection GetKeys() + { + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + List keys = new List(); + + for (int i = 0; i < m_buckets.Length; i++) + { + Node current = m_buckets[i]; + while (current != null) + { + keys.Add(current.m_key); + current = current.m_next; + } + } + + return new ReadOnlyCollection(keys); + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// Gets a collection containing the values in the dictionary. + /// + private ReadOnlyCollection GetValues() + { + int locksAcquired = 0; + try + { + AcquireAllLocks(ref locksAcquired); + List values = new List(); + + for (int i = 0; i < m_buckets.Length; i++) + { + Node current = m_buckets[i]; + while (current != null) + { + values.Add(current.m_value); + current = current.m_next; + } + } + + return new ReadOnlyCollection(values); + } + finally + { + ReleaseLocks(0, locksAcquired); + } + } + + /// + /// A helper method for asserts. + /// + [Conditional("DEBUG")] + private void Assert(bool condition) + { + if (!condition) + { + throw new Exception("Assertion failed."); + } + } + + /// + /// A helper function to obtain the string for a particular resource key. + /// + /// + /// + private string GetResource(string key) + { + Assert(key != null); + + return key; + } + + /// + /// A node in a singly-linked list representing a particular hash table bucket. + /// + private class Node + { + internal TKey m_key; + internal TValue m_value; + internal volatile Node m_next; + internal int m_hashcode; + + internal Node(TKey key, TValue value, int hashcode) + : this(key, value, hashcode, null) + { + } + + internal Node(TKey key, TValue value, int hashcode, Node next) + { + m_key = key; + m_value = value; + m_next = next; + m_hashcode = hashcode; + } + } + + /// + /// A private class to represent enumeration over the dictionary that implements the + /// IDictionaryEnumerator interface. + /// + private class DictionaryEnumerator : IDictionaryEnumerator + { + IEnumerator> m_enumerator; + // Enumerator over the dictionary. + + internal DictionaryEnumerator(ConcurrentDictionary dictionary) + { + m_enumerator = dictionary.GetEnumerator(); + } + + public DictionaryEntry Entry + { + get { return new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); } + } + + public object Key + { + get { return m_enumerator.Current.Key; } + } + + public object Value + { + get { return m_enumerator.Current.Value; } + } + + public object Current + { + get { return this.Entry; } + } + + public bool MoveNext() + { + return m_enumerator.MoveNext(); + } + + public void Reset() + { + m_enumerator.Reset(); + } + } + + /// + /// Get the data array to be serialized + /// + [OnSerializing] + private void OnSerializing(StreamingContext context) + { + // save the data into the serialization array to be saved + m_serializationArray = ToArray(); + m_serializationConcurrencyLevel = m_locks.Length; + m_serializationCapacity = m_buckets.Length; + } + + /// + /// Construct the dictionary from a previously seiralized one + /// + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + KeyValuePair[] array = m_serializationArray; + + m_buckets = new Node[m_serializationCapacity]; + m_countPerLock = new int[m_serializationConcurrencyLevel]; + + m_locks = new object[m_serializationConcurrencyLevel]; + for (int i = 0; i < m_locks.Length; i++) + { + m_locks[i] = new object(); + } + + InitializeFromCollection(array); + m_serializationArray = null; + + } + } +} + +#endif \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs.meta new file mode 100644 index 00000000000..122fa3a6bf8 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/ConcurrentDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3dd1ab5744138f429f858e52c38d482 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs new file mode 100644 index 00000000000..3a710ec7cdf --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs @@ -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(); + + foreach (var method in methods) + { + var attribute = method.GetCustomAttribute(); + + 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 listeners, + Dictionary 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(); + + 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(); + } + + var args = new List(); + + var remainingArgNames = new HashSet(data.ToObject>().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(); + } +} diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs.meta new file mode 100644 index 00000000000..e4bb183976a --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenerUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc41a3e97b2a6c24298dab601df56f5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs new file mode 100644 index 00000000000..ca60941b598 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs @@ -0,0 +1,3 @@ +using UnityEngine; + +public class PocoListenersBase : MonoBehaviour { } \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs.meta new file mode 100644 index 00000000000..66372eb482b --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoListenersBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a6c7fe1602ea044591568fb38b100d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs new file mode 100644 index 00000000000..ca749c87d45 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs @@ -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 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 inbox = new ConcurrentDictionary(); + private VRSupport vr_support = new VRSupport(); + private Dictionary debugProfilingData = new Dictionary() { + { "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(server_ClientConnected); + this.server.ClientDisconnected += + new EventHandler(server_ClientDisconnected); + this.server.DatagramReceived += + new EventHandler>(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 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 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 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 param) + { + return new float[] { Screen.width, Screen.height }; + } + + public void stopListening() + { + mRunning = false; + server?.Stop(); + } + + [RPC] + private object GetDebugProfilingData(List param) + { + return debugProfilingData; + } + + [RPC] + private object SetText(List param) + { + var instanceId = Convert.ToInt32(param[0]); + var textVal = param[1] as string; + foreach (var go in GameObject.FindObjectsOfType()) + { + if (go.GetInstanceID() == instanceId) + { + return UnityNode.SetText(go, textVal); + } + } + return false; + } + + [RPC] + private object SendMessage(List param) + { + if (MessageReceived == null) + { + return false; + } + + var textVal = param[0] as string; + + MessageReceived.Invoke(textVal); + + return true; + } + + [RPC] + private object GetSDKVersion(List param) + { + return versionCode; + } + + void Update() + { + foreach (TcpClientState client in inbox.Values) + { + List 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 param); + + protected Dictionary RPCHandler = new Dictionary(); + protected Dictionary Listeners = new Dictionary(); + + private JsonSerializerSettings settings = new JsonSerializerSettings() + { + StringEscapeHandling = StringEscapeHandling.EscapeNonAscii + }; + + public string HandleMessage(string json) + { + var data = JsonConvert.DeserializeObject>(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 param = null; + + if (data.TryGetValue("params", out var value)) + { + param = ((JArray)value).ToObject>(); + } + + 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 param = null) + { + Dictionary data = new Dictionary(); + 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 rpc = new Dictionary(); + 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 data, Exception e) + { + Dictionary rpc = new Dictionary(); + rpc["jsonrpc"] = "2.0"; + rpc["id"] = idAction; + + Dictionary errorDefinition = new Dictionary(); + 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); + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs.meta new file mode 100644 index 00000000000..f9216c708ef --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f4d95df30f3aed43b005fdacbeec3a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs new file mode 100644 index 00000000000..1fb7a364379 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs @@ -0,0 +1,11 @@ +using System; + +public sealed class PocoMethodAttribute : Attribute +{ + public string Name { get; private set; } + + public PocoMethodAttribute(string name) + { + Name = name; + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs.meta new file mode 100644 index 00000000000..d3a14b8ff17 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/PocoMethodAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52c4e3a313dae2f458aa48c582ec89bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md new file mode 100644 index 00000000000..901cf813422 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md @@ -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`作为脚本组件。 + diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md.meta new file mode 100644 index 00000000000..33f72513bc8 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0d77c44e79fe424408e4f0ab57c3732e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs new file mode 100644 index 00000000000..770a0c646d1 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs @@ -0,0 +1,28 @@ +using System; +using System.Net.Sockets; + +namespace TcpServer +{ + /// + /// 与客户端的连接已建立事件参数 + /// + public class TcpClientConnectedEventArgs : EventArgs + { + /// + /// 与客户端的连接已建立事件参数 + /// + /// 客户端 + public TcpClientConnectedEventArgs(TcpClient tcpClient) + { + if (tcpClient == null) + throw new ArgumentNullException("tcpClient"); + + this.TcpClient = tcpClient; + } + + /// + /// 客户端 + /// + public TcpClient TcpClient { get; private set; } + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs.meta new file mode 100644 index 00000000000..27506d3798c --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientConnectedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e0445c032c1c00409e9e24490782c5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs new file mode 100644 index 00000000000..74fb4fd915f --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs @@ -0,0 +1,28 @@ +using System; +using System.Net.Sockets; + +namespace TcpServer +{ + /// + /// 与客户端的连接已断开事件参数 + /// + public class TcpClientDisconnectedEventArgs : EventArgs + { + /// + /// 与客户端的连接已断开事件参数 + /// + /// 客户端状态 + public TcpClientDisconnectedEventArgs(TcpClient tcpClient) + { + if (tcpClient == null) + throw new ArgumentNullException("tcpClient"); + + this.TcpClient = tcpClient; + } + + /// + /// 客户端 + /// + public TcpClient TcpClient { get; private set; } + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs.meta new file mode 100644 index 00000000000..78d9c127074 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientDisconnectedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e512939ef291f604b98b4859c8b82ed6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs new file mode 100644 index 00000000000..2f2b50f2f62 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs @@ -0,0 +1,56 @@ +using System; +using System.Net.Sockets; + +namespace TcpServer +{ + /// + /// Internal class to join the TCP client and buffer together + /// for easy management in the server + /// + public class TcpClientState + { + /// + /// Constructor for a new Client + /// + /// The TCP client + /// The byte array buffer + /// The protocol filter + 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 (); + } + + /// + /// Gets the TCP Client + /// + public TcpClient TcpClient { get; private set; } + + /// + /// Gets the Buffer. + /// + public byte[] Buffer { get; private set; } + + public ProtoFilter Prot { get; private set; } + + /// + /// Gets the network stream + /// + public NetworkStream NetworkStream + { + get + { + return TcpClient.GetStream(); + } + } + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs.meta new file mode 100644 index 00000000000..f660972aa76 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpClientState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c44a013bdfbc42248ad83e6b6ef7e677 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs new file mode 100644 index 00000000000..416088cf09b --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Sockets; + +namespace TcpServer +{ + /// + /// 接收到数据报文事件参数 + /// + /// 报文类型 + public class TcpDatagramReceivedEventArgs : EventArgs + { + /// + /// 接收到数据报文事件参数 + /// + /// 客户端状态 + /// 报文 + public TcpDatagramReceivedEventArgs(TcpClientState tcpClientState, T datagram) + { + this.Client = tcpClientState; + this.TcpClient = tcpClientState.TcpClient; + this.Datagram = datagram; + } + + /// + /// 客户端状态 + /// + public TcpClientState Client { get; private set; } + + /// + /// 客户端 + /// + public TcpClient TcpClient { get; private set; } + + /// + /// 报文 + /// + public T Datagram { get; private set; } + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs.meta new file mode 100644 index 00000000000..88b294d3b87 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpDatagramReceivedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b315ab57e8d89a41be324a5bd5718af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs new file mode 100644 index 00000000000..ed06c63061b --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs @@ -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 swap_msgs(); + } + + public class SimpleProtocolFilter : ProtoFilter + { + /* 简单协议过滤器 + 协议按照 [有效数据字节数][有效数据] 这种协议包的格式进行打包和解包 + [有效数据字节数]长度HEADER_SIZE字节 + [有效数据]长度有效数据字节数字节 + 本类按照这种方式,顺序从数据流中取出数据进行拼接,一旦接收完一个完整的协议包,就会将协议包返回 + [有效数据]字段接收到后会按照utf-8进行解码,因为在传输过程中是用utf-8进行编码的 + 所有编解码的操作在该类中完成 + */ + + private byte[] buf = new byte[0]; + private int HEADER_SIZE = 4; + private List msgs = new List(); + + 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 swap_msgs() + { + List ret = msgs; + msgs = new List(); + 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; + } + } + + + /// + /// 异步TCP服务器 + /// + public class AsyncTcpServer : IDisposable + { + #region Fields + + private TcpListener _listener; + private ConcurrentDictionary _clients; + private bool _disposed = false; + + #endregion + + #region Ctors + + /// + /// 异步TCP服务器 + /// + /// 监听的端口 + public AsyncTcpServer(int listenPort) + : this(IPAddress.Any, listenPort) + { + } + + /// + /// 异步TCP服务器 + /// + /// 监听的终结点 + public AsyncTcpServer(IPEndPoint localEP) + : this(localEP.Address, localEP.Port) + { + } + + /// + /// 异步TCP服务器 + /// + /// 监听的IP地址 + /// 监听的端口 + public AsyncTcpServer(IPAddress localIPAddress, int listenPort) + { + this.Address = localIPAddress; + this.Port = listenPort; + this.Encoding = Encoding.Default; + + _clients = new ConcurrentDictionary(); + + _listener = new TcpListener(Address, Port); + // _listener.AllowNatTraversal(true); + } + + #endregion + + #region Properties + + /// + /// 服务器是否正在运行 + /// + public bool IsRunning { get; private set; } + + /// + /// 监听的IP地址 + /// + public IPAddress Address { get; private set; } + + /// + /// 监听的端口 + /// + public int Port { get; private set; } + + /// + /// 通信使用的编码 + /// + public Encoding Encoding { get; set; } + + #endregion + + #region Server + + /// + /// 启动服务器 + /// + /// 异步TCP服务器 + public AsyncTcpServer Start() + { + Debug.Log("start server"); + return Start(10); + } + + /// + /// 启动服务器 + /// + /// 服务器所允许的挂起连接序列的最大长度 + /// 异步TCP服务器 + public AsyncTcpServer Start(int backlog) + { + if (IsRunning) + return this; + + IsRunning = true; + + _listener.Start(backlog); + ContinueAcceptTcpClient(_listener); + + return this; + } + + /// + /// 停止服务器 + /// + /// 异步TCP服务器 + 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 + + /// + /// 接收到数据报文事件 + /// + public event EventHandler> DatagramReceived; + /// + /// 接收到数据报文明文事件 + /// + public event EventHandler> PlaintextReceived; + + private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram) + { + if (DatagramReceived != null) + { + DatagramReceived(this, new TcpDatagramReceivedEventArgs(sender, datagram)); + } + } + + private void RaisePlaintextReceived(TcpClientState sender, byte[] datagram) + { + if (PlaintextReceived != null) + { + PlaintextReceived(this, new TcpDatagramReceivedEventArgs(sender, this.Encoding.GetString(datagram, 0, datagram.Length))); + } + } + + /// + /// 与客户端的连接已建立事件 + /// + public event EventHandler ClientConnected; + /// + /// 与客户端的连接已断开事件 + /// + public event EventHandler 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."); + } + + /// + /// 发送报文至指定的客户端 + /// + /// 客户端 + /// 报文 + 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); + } + } + + /// + /// 发送报文至指定的客户端 + /// + /// 客户端 + /// 报文 + public void Send(TcpClient tcpClient, string datagram) + { + Send(tcpClient, this.Encoding.GetBytes(datagram)); + } + + /// + /// 发送报文至所有客户端 + /// + /// 报文 + public void SendToAll(byte[] datagram) + { + GuardRunning(); + + foreach (var client in _clients.Values) + { + Send(client.TcpClient, datagram); + } + } + + /// + /// 发送报文至所有客户端 + /// + /// 报文 + 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); + } + } + + /// + /// 发送报文至指定的客户端 + /// + /// 客户端 + /// 报文 + 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); + } + } + + /// + /// 发送报文至指定的客户端 + /// + /// 客户端 + /// 报文 + public void SyncSend(TcpClient tcpClient, string datagram) + { + SyncSend(tcpClient, this.Encoding.GetBytes(datagram)); + } + + /// + /// 发送报文至所有客户端 + /// + /// 报文 + public void SyncSendToAll(byte[] datagram) + { + GuardRunning(); + + foreach (var client in _clients.Values) + { + SyncSend(client.TcpClient, datagram); + } + } + + /// + /// 发送报文至所有客户端 + /// + /// 报文 + public void SyncSendToAll(string datagram) + { + GuardRunning(); + + SyncSendToAll(this.Encoding.GetBytes(datagram)); + } + + #endregion + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; + /// false to release only unmanaged resources. + 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 + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs.meta new file mode 100644 index 00000000000..278c06a8f50 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/TcpServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 595bd7c94c4f1f1438804b3e71092b67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs new file mode 100644 index 00000000000..d2afe1c489b --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs @@ -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 children = null; + + public RootNode() + { + children = new List(); + foreach (GameObject obj in Transform.FindObjectsOfType(typeof(GameObject))) + { + if (obj.transform.parent == null) + { + children.Add(new UnityNode(obj)); + } + } + } + + public override List getChildren() // + { + return children; + } + } +} diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs.meta new file mode 100644 index 00000000000..b85ac4df6a1 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/UnityDumper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65abe57f902b50b49a41d812b12f9e13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs new file mode 100644 index 00000000000..47cd0e17875 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs @@ -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 commands = new Queue(); + + public VRSupport() + { + commands = new Queue(); + } + + 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 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 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 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()) + { + if (cameraContainer.name.Equals(param[3])) + { + foreach (GameObject cameraFollower in GameObject.FindObjectsOfType()) + { + 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 param) + { + float speed = 0f; + if (param.Count > 3) + speed = Convert.ToSingle(param[3]); + foreach (GameObject toLookAt in GameObject.FindObjectsOfType()) + { + if (toLookAt.name.Equals(param[0])) + { + foreach (GameObject cameraContainer in GameObject.FindObjectsOfType()) + { + if (cameraContainer.name.Equals(param[1])) + { + foreach (GameObject cameraFollower in GameObject.FindObjectsOfType()) + { + 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; + } +} \ No newline at end of file diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs.meta new file mode 100644 index 00000000000..ac939a11fe3 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/VRSupport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84a1358e5fce5a04d8e96e749c5777cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk.meta new file mode 100644 index 00000000000..217bce56623 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3c6dd171143e15049a1cbf0f40a3a82e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs new file mode 100644 index 00000000000..d5e1c0305e4 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs @@ -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 dumpHierarchy() + { + return dumpHierarchyImpl(getRoot(), true); + } + public Dictionary dumpHierarchy(bool onlyVisibleNode) + { + return dumpHierarchyImpl(getRoot(), onlyVisibleNode); + } + + private Dictionary dumpHierarchyImpl(AbstractNode node, bool onlyVisibleNode) + { + if (node == null) + { + return null; + } + + Dictionary payload = node.enumerateAttrs(); + Dictionary result = new Dictionary(); + string name = (string)node.getAttr("name"); + result.Add("name", name); + result.Add("payload", payload); + + List children = new List(); + 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 getPortSize() + { + return null; + } + } + + public interface IDumper + { + AbstractNode getRoot(); + + Dictionary dumpHierarchy(); + Dictionary dumpHierarchy(bool onlyVisibleNode); + + List getPortSize(); + } +} diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs.meta new file mode 100644 index 00000000000..5d66ff300b5 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractDumper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2eef00c02b7e0d4da3c2ba9e68f207c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs new file mode 100644 index 00000000000..aba7bb9af5e --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + + +namespace Poco +{ + public class AbstractNode + { + private List requiredAttrs = new List { + "name", + "type", + "visible", + "pos", + "size", + "scale", + "anchorPoint", + "zOrders" + }; + + public virtual AbstractNode getParent () + { + return null; + } + + public virtual List getChildren () + { + return null; + } + + public virtual object getAttr (string attrName) + { + Dictionary defaultAttrs = new Dictionary () { + { "name", "" }, + { "type", "Root" }, + { "visible", true }, + { "pos", new List (){ 0.0f, 0.0f } }, + { "size", new List (){ 0.0f, 0.0f } }, + { "scale", new List (){ 1.0f, 1.0f } }, + { "anchorPoint", new List (){ 0.5f, 0.5f } }, + { "zOrders", new Dictionary (){ { "local", 0 }, { "global", 0 } } } + }; + return defaultAttrs.ContainsKey (attrName) ? defaultAttrs [attrName] : null; + } + + public virtual void setAttr (string attrName, object val) + { + + } + + public virtual Dictionary enumerateAttrs () + { + Dictionary ret = new Dictionary (); + foreach (string attr in requiredAttrs) { + ret.Add (attr, getAttr (attr)); + } + return ret; + } + } +} diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs.meta new file mode 100644 index 00000000000..02c4d8e5b46 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/sdk/AbstractNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80a9c1bd292d4184faf21890b350b53d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro.meta b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro.meta new file mode 100644 index 00000000000..addbd4a8eba --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 856f0a97e1121c943ad31245cb0b6545 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro/UnityNode.cs b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro/UnityNode.cs new file mode 100644 index 00000000000..94b96527ae7 --- /dev/null +++ b/Tool/sdk_packages/Poco/0.0.1/Assets/PhxhSDK/Phxh/SDKHelper/Poco/uguiWithTMPro/UnityNode.cs @@ -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 TypeNames = new Dictionary() { + { "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 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(); + rectTransform = gameObject.GetComponent(); + 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 getChildren() + { + List children = new List(); + 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() { 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 enumerateAttrs() + { + Dictionary payload = GetPayload(); + Dictionary ret = new Dictionary(); + foreach (KeyValuePair p in payload) + { + if (p.Value != null) + { + ret.Add(p.Key, p.Value); + } + } + return ret; + } + + + private Dictionary GetPayload() + { + Dictionary payload = new Dictionary() { + { "name", gameObject.name }, + { "type", GuessObjectTypeFromComponentNames (components) }, + { "visible", GameObjectVisible (renderer, components) }, + { "pos", GameObjectPosInScreen (objectPos, renderer, rectTransform, rect) }, + { "size", GameObjectSizeInScreen (rect, rectTransform) }, + { "scale", new List (){ 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 components) + { + List cns = new List(components); + cns.Reverse(); + foreach (string name in cns) + { + if (TypeNames.ContainsKey(name)) + { + return TypeNames[name]; + } + } + return DefaultTypeName; + } + + private bool GameObjectVisible(Renderer renderer, List 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 components) + { + Button button = gameObject.GetComponent