Assets/Editor/MemoryProfiler/ArrayTools.cs
File was deleted Assets/Editor/MemoryProfiler/ArrayTools.cs.meta
File was deleted Assets/Editor/MemoryProfiler/BytesAndOffset.cs
File was deleted Assets/Editor/MemoryProfiler/BytesAndOffset.cs.meta
File was deleted Assets/Editor/MemoryProfiler/CrawledDataUnpacker.cs
File was deleted Assets/Editor/MemoryProfiler/CrawledDataUnpacker.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Crawler.cs
File was deleted Assets/Editor/MemoryProfiler/Crawler.cs.meta
File was deleted Assets/Editor/MemoryProfiler/HighLevelAPI.cs
File was deleted Assets/Editor/MemoryProfiler/HighLevelAPI.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Inspector.cs
File was deleted Assets/Editor/MemoryProfiler/Inspector.cs.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dllBinary files differ
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdbBinary files differ
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdb.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.pdbBinary files differ
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.pdb.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.xml
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.xml.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/license.txt
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/license.txt.meta
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/readme.txt
File was deleted Assets/Editor/MemoryProfiler/JsonDotNet20/readme.txt.meta
File was deleted Assets/Editor/MemoryProfiler/LowLevelAPI.cs
File was deleted Assets/Editor/MemoryProfiler/LowLevelAPI.cs.meta
File was deleted Assets/Editor/MemoryProfiler/ManagedHeapExtensions.cs
File was deleted Assets/Editor/MemoryProfiler/ManagedHeapExtensions.cs.meta
File was deleted Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs
File was deleted Assets/Editor/MemoryProfiler/PackedManagedObject.cs
File was deleted Assets/Editor/MemoryProfiler/PackedManagedObject.cs.meta
File was deleted Assets/Editor/MemoryProfiler/PackedMemorySnapshotUtility.cs
File was deleted Assets/Editor/MemoryProfiler/PackedMemorySnapshotUtility.cs.meta
File was deleted Assets/Editor/MemoryProfiler/PrimitiveValueReader.cs
File was deleted Assets/Editor/MemoryProfiler/PrimitiveValueReader.cs.meta
File was deleted Assets/Editor/MemoryProfiler/ShortestPathToRootFinder.cs
File was deleted Assets/Editor/MemoryProfiler/ShortestPathToRootFinder.cs.meta
File was deleted Assets/Editor/MemoryProfiler/StringTools.cs
File was deleted Assets/Editor/MemoryProfiler/StringTools.cs.meta
File was deleted Assets/Editor/MemoryProfiler/TreeMapView.cs
File was deleted Assets/Editor/MemoryProfiler/TreeMapView.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/Group.cs
File was deleted Assets/Editor/MemoryProfiler/Treemap/Group.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/ITreemapRenderable.cs
File was deleted Assets/Editor/MemoryProfiler/Treemap/ITreemapRenderable.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/Item.cs
File was deleted Assets/Editor/MemoryProfiler/Treemap/Item.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/Tests.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/Tests/TreemapTests.cs
File was deleted Assets/Editor/MemoryProfiler/Treemap/Tests/TreemapTests.cs.meta
File was deleted Assets/Editor/MemoryProfiler/Treemap/Utility.cs
File was deleted Assets/Editor/MemoryProfiler/Treemap/Utility.cs.meta
File was deleted Assets/Editor/MemoryProfiler/TypeTools.cs
File was deleted Assets/Editor/MemoryProfiler/TypeTools.cs.meta
File was deleted Assets/Editor/MemoryProfiler/ZoomArea.cs
File was deleted Assets/Editor/MemoryProfiler/ZoomArea.cs.meta
File was deleted Assets/Editor/PocoSDK.meta
File was renamed from Assets/Editor/MemoryProfiler.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: c0c186c64e2b22248b9679739a21009d guid: eac2a04956ca3a14ebe2b627c7a48009 folderAsset: yes timeCreated: 1526655628 timeCreated: 1554811207 licenseType: Pro DefaultImporter: userData: Assets/Editor/PocoSDK/3rdLib.metacopy from Assets/Editor/MemoryProfiler.meta copy to Assets/Editor/PocoSDK/3rdLib.meta
File was copied from Assets/Editor/MemoryProfiler.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: c0c186c64e2b22248b9679739a21009d guid: 933dc089270978548ac6297842f696cc folderAsset: yes timeCreated: 1526655628 timeCreated: 1554808814 licenseType: Pro DefaultImporter: userData: Assets/Editor/PocoSDK/3rdLib/Newtonsoft.Json.dllBinary files differ
Assets/Editor/PocoSDK/3rdLib/Newtonsoft.Json.dll.meta
File was renamed from Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: 7e5fdeab20ac61449acd7a10279dbbb9 timeCreated: 1489321824 licenseType: Free guid: 85bedaf57f122f34e9a40ccfe7983c81 timeCreated: 1554808822 licenseType: Pro PluginImporter: serializedVersion: 2 iconMap: {} Assets/Editor/PocoSDK/3rdLib/link.xml
New file @@ -0,0 +1,7 @@ <?xml version="1.0" encoding="utf-8" ?> <linker> <assembly fullname="System"> <type fullname="System.ComponentModel.TypeConverter" preserve="all" /> <!-- <namespace fullname="System.ComponentModel" preserve="all" /> --> </assembly> </linker> Assets/Editor/PocoSDK/3rdLib/link.xml.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: 2982b518c355b2c44972fc85080a89b7 timeCreated: 1554808823 licenseType: Pro TextScriptImporter: userData: assetBundleName: assetBundleVariant: Assets/Editor/PocoSDK/ConcurrentDictionary.cs
New file @@ -0,0 +1,1679 @@ #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 { /// <summary> /// Represents a thread-safe collection of keys and values. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <remarks> /// All public and protected members of <see cref="ConcurrentDictionary{TKey,TValue}"/> are thread-safe and may be used /// concurrently from multiple threads. /// </remarks> [Serializable] [ComVisible (false)] [DebuggerDisplay ("Count = {Count}")] // [HostProtection(Synchronization = true, ExternalThreading = true)] public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, 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<TKey> m_comparer; // Key equality comparer private KeyValuePair<TKey, TValue>[] 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; /// <summary> /// Initializes a new instance of the <see /// cref="ConcurrentDictionary{TKey,TValue}"/> /// class that is empty, has the default concurrency level, has the default initial capacity, and /// uses the default comparer for the key type. /// </summary> public ConcurrentDictionary () : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY) { } /// <summary> /// Initializes a new instance of the <see /// cref="ConcurrentDictionary{TKey,TValue}"/> /// class that is empty, has the specified concurrency level and capacity, and uses the default /// comparer for the key type. /// </summary> /// <param name="concurrencyLevel">The estimated number of threads that will update the /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> /// <param name="capacity">The initial number of elements that the <see /// cref="ConcurrentDictionary{TKey,TValue}"/> /// can contain.</param> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="concurrencyLevel"/> is /// less than 1.</exception> /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="capacity"/> is less than /// 0.</exception> public ConcurrentDictionary (int concurrencyLevel, int capacity) : this (concurrencyLevel, capacity, EqualityComparer<TKey>.Default) { } /// <summary> /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// class that contains elements copied from the specified <see /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/>, has the default concurrency /// level, has the default initial capacity, and uses the default comparer for the key type. /// </summary> /// <param name="collection">The <see /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to /// the new /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more /// duplicate keys.</exception> public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> collection) : this (collection, EqualityComparer<TKey>.Default) { } /// <summary> /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// class that is empty, has the specified concurrency level and capacity, and uses the specified /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. /// </summary> /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> /// implementation to use when comparing keys.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference /// (Nothing in Visual Basic).</exception> public ConcurrentDictionary (IEqualityComparer<TKey> comparer) : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY, comparer) { } /// <summary> /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// class that contains elements copied from the specified <see /// cref="T:System.Collections.IEnumerable"/>, has the default concurrency level, has the default /// initial capacity, and uses the specified /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. /// </summary> /// <param name="collection">The <see /// cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to /// the new /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> /// implementation to use when comparing keys.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference /// (Nothing in Visual Basic). -or- /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). /// </exception> public ConcurrentDictionary (IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : this (DefaultConcurrencyLevel, collection, comparer) { } /// <summary> /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// class that contains elements copied from the specified <see cref="T:System.Collections.IEnumerable"/>, /// has the specified concurrency level, has the specified initial capacity, and uses the specified /// <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. /// </summary> /// <param name="concurrencyLevel">The estimated number of threads that will update the /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> /// <param name="collection">The <see cref="T:System.Collections.IEnumerable{KeyValuePair{TKey,TValue}}"/> whose elements are copied to the new /// <see cref="ConcurrentDictionary{TKey,TValue}"/>.</param> /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> implementation to use /// when comparing keys.</param> /// <exception cref="T:System.ArgumentNullException"> /// <paramref name="collection"/> is a null reference (Nothing in Visual Basic). /// -or- /// <paramref name="comparer"/> is a null reference (Nothing in Visual Basic). /// </exception> /// <exception cref="T:System.ArgumentOutOfRangeException"> /// <paramref name="concurrencyLevel"/> is less than 1. /// </exception> /// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more duplicate keys.</exception> public ConcurrentDictionary ( int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> 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<KeyValuePair<TKey, TValue>> collection) { TValue dummy; foreach (KeyValuePair<TKey, TValue> 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")); } } } /// <summary> /// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// class that is empty, has the specified concurrency level, has the specified initial capacity, and /// uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>. /// </summary> /// <param name="concurrencyLevel">The estimated number of threads that will update the /// <see cref="ConcurrentDictionary{TKey,TValue}"/> concurrently.</param> /// <param name="capacity">The initial number of elements that the <see /// cref="ConcurrentDictionary{TKey,TValue}"/> /// can contain.</param> /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/> /// implementation to use when comparing keys.</param> /// <exception cref="T:System.ArgumentOutOfRangeException"> /// <paramref name="concurrencyLevel"/> is less than 1. -or- /// <paramref name="capacity"/> is less than 0. /// </exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="comparer"/> is a null reference /// (Nothing in Visual Basic).</exception> public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer<TKey> 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; } /// <summary> /// Attempts to add the specified key and value to the <see cref="ConcurrentDictionary{TKey, /// TValue}"/>. /// </summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">The value of the element to add. The value can be a null reference (Nothing /// in Visual Basic) for reference types.</param> /// <returns>true if the key/value pair was added to the <see cref="ConcurrentDictionary{TKey, /// TValue}"/> /// successfully; otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The <see cref="ConcurrentDictionary{TKey, TValue}"/> /// contains too many elements.</exception> public bool TryAdd (TKey key, TValue value) { if (key == null) throw new ArgumentNullException ("key"); TValue dummy; return TryAddInternal (key, value, false, true, out dummy); } /// <summary> /// Determines whether the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains the specified /// key. /// </summary> /// <param name="key">The key to locate in the <see cref="ConcurrentDictionary{TKey, /// TValue}"/>.</param> /// <returns>true if the <see cref="ConcurrentDictionary{TKey, TValue}"/> contains an element with /// the specified key; otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> public bool ContainsKey (TKey key) { if (key == null) throw new ArgumentNullException ("key"); TValue throwAwayValue; return TryGetValue (key, out throwAwayValue); } /// <summary> /// Attempts to remove and return the the value with the specified key from the /// <see cref="ConcurrentDictionary{TKey, TValue}"/>. /// </summary> /// <param name="key">The key of the element to remove and return.</param> /// <param name="value">When this method returns, <paramref name="value"/> contains the object removed from the /// <see cref="ConcurrentDictionary{TKey,TValue}"/> or the default value of <typeparamref /// name="TValue"/> /// if the operation failed.</param> /// <returns>true if an object was removed successfully; otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> public bool TryRemove (TKey key, out TValue value) { if (key == null) throw new ArgumentNullException ("key"); return TryRemoveInternal (key, out value, false, default(TValue)); } /// <summary> /// 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. /// </summary> /// <param name="key">The key to search for and remove if it exists.</param> /// <param name="value">The variable into which the removed value, if found, is stored.</param> /// <param name="matchValue">Whether removal of the key is conditional on its value.</param> /// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param> /// <returns></returns> 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<TValue>.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; } } /// <summary> /// Attempts to get the value associated with the specified key from the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>. /// </summary> /// <param name="key">The key of the value to get.</param> /// <param name="value">When this method returns, <paramref name="value"/> contains the object from /// the /// <see cref="ConcurrentDictionary{TKey,TValue}"/> with the spedified key or the default value of /// <typeparamref name="TValue"/>, if the operation failed.</param> /// <returns>true if the key was found in the <see cref="ConcurrentDictionary{TKey,TValue}"/>; /// otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> 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; } /// <summary> /// Compares the existing value for the specified key with a specified value, and if they’re equal, /// updates the key with a third value. /// </summary> /// <param name="key">The key whose value is compared with <paramref name="comparisonValue"/> and /// possibly replaced.</param> /// <param name="newValue">The value that replaces the value of the element with <paramref /// name="key"/> if the comparison results in equality.</param> /// <param name="comparisonValue">The value that is compared to the value of the element with /// <paramref name="key"/>.</param> /// <returns>true if the value with <paramref name="key"/> was equal to <paramref /// name="comparisonValue"/> and replaced with <paramref name="newValue"/>; otherwise, /// false.</returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null /// reference.</exception> public bool TryUpdate (TKey key, TValue newValue, TValue comparisonValue) { if (key == null) throw new ArgumentNullException ("key"); int hashcode = m_comparer.GetHashCode (key); IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.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; } } } /// <summary> /// Removes all keys and values from the <see cref="ConcurrentDictionary{TKey,TValue}"/>. /// </summary> 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); } } /// <summary> /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection"/> to an array of /// type <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/>, starting at the /// specified array index. /// </summary> /// <param name="array">The one-dimensional array of type <see /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> /// that is the destination of the <see /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> elements copied from the <see /// cref="T:System.Collections.ICollection"/>. The array must have zero-based indexing.</param> /// <param name="index">The zero-based index in <paramref name="array"/> at which copying /// begins.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than /// 0.</exception> /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see /// cref="T:System.Collections.ICollection"/> /// is greater than the available space from <paramref name="index"/> to the end of the destination /// <paramref name="array"/>.</exception> void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] 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); } } /// <summary> /// Copies the key and value pairs stored in the <see cref="ConcurrentDictionary{TKey,TValue}"/> to a /// new array. /// </summary> /// <returns>A new array containing a snapshot of key and value pairs copied from the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> public KeyValuePair<TKey, TValue>[] 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<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count]; CopyToPairs (array, 0); return array; } finally { ReleaseLocks (0, locksAcquired); } } /// <summary> /// 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. /// </summary> private void CopyToPairs (KeyValuePair<TKey, TValue>[] 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<TKey, TValue> (current.m_key, current.m_value); index++; //this should never flow, CopyToPairs is only called when there's no overflow risk } } } /// <summary> /// 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. /// </summary> 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 } } } /// <summary> /// 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. /// </summary> 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<TKey, TValue> (current.m_key, current.m_value); index++; //this should never flow, CopyToObjects is only called when there's no overflow risk } } } /// <summary>Returns an enumerator that iterates through the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> /// <remarks> /// 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 <see cref="GetEnumerator"/> was called. /// </remarks> public IEnumerator<KeyValuePair<TKey, TValue>> 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<TKey, TValue> (current.m_key, current.m_value); current = current.m_next; } } } /// <summary> /// 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; /// </summary> 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; } } /// <summary> /// Gets or sets the value associated with the specified key. /// </summary> /// <param name="key">The key of the value to get or set.</param> /// <value>The value associated with the specified key. If the specified key is not found, a get /// operation throws a /// <see cref="T:Sytem.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new /// element with the specified key.</value> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and /// <paramref name="key"/> /// does not exist in the collection.</exception> 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); } } /// <summary> /// Gets the number of key/value pairs contained in the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>. /// </summary> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <value>The number of key/value paris contained in the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>.</value> /// <remarks>Count has snapshot semantics and represents the number of items in the <see /// cref="ConcurrentDictionary{TKey,TValue}"/> /// at the moment when Count was accessed.</remarks> 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; } } /// <summary> /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// if the key does not already exist. /// </summary> /// <param name="key">The key of the element to add.</param> /// <param name="valueFactory">The function used to generate a value for the key</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="valueFactory"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <returns>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.</returns> public TValue GetOrAdd (TKey key, Func<TKey, TValue> 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; } /// <summary> /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> /// if the key does not already exist. /// </summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">the value to be added, if the key does not already exist</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <returns>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.</returns> 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; } /// <summary> /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key /// already exists. /// </summary> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValueFactory">The function used to generate a value for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key /// based on the key's existing value</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="addValueFactory"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <returns>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).</returns> public TValue AddOrUpdate (TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> 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; } } } } /// <summary> /// Adds a key/value pair to the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key does not already /// exist, or updates a key/value pair in the <see cref="ConcurrentDictionary{TKey,TValue}"/> if the key /// already exists. /// </summary> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValue">The value to be added for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on /// the key's existing value</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="updateValueFactory"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <returns>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).</returns> public TValue AddOrUpdate (TKey key, TValue addValue, Func<TKey, TValue, TValue> 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; } } } } /// <summary> /// Gets a value that indicates whether the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty. /// </summary> /// <value>true if the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty; otherwise, /// false.</value> 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<TKey,TValue> members /// <summary> /// Adds the specified key and value to the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. /// </summary> /// <param name="key">The object to use as the key of the element to add.</param> /// <param name="value">The object to use as the value of the element to add.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <exception cref="T:System.ArgumentException"> /// An element with the same key already exists in the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>.</exception> void IDictionary<TKey, TValue>.Add (TKey key, TValue value) { if (!TryAdd (key, value)) { throw new ArgumentException (GetResource ("ConcurrentDictionary_KeyAlreadyExisted")); } } /// <summary> /// Removes the element with the specified key from the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. /// </summary> /// <param name="key">The key of the element to remove.</param> /// <returns>true if the element is successfully remove; otherwise false. This method also returns /// false if /// <paramref name="key"/> was not found in the original <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> bool IDictionary<TKey, TValue>.Remove (TKey key) { TValue throwAwayValue; return TryRemove (key, out throwAwayValue); } /// <summary> /// Gets a collection containing the keys in the <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. /// </summary> /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}"/> containing the keys in the /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> public ICollection<TKey> Keys { get { return GetKeys (); } } /// <summary> /// Gets a collection containing the values in the <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. /// </summary> /// <value>An <see cref="T:System.Collections.Generic.ICollection{TValue}"/> containing the values in /// the /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> public ICollection<TValue> Values { get { return GetValues (); } } #endregion #region ICollection<KeyValuePair<TKey,TValue>> Members /// <summary> /// Adds the specified value to the <see cref="T:System.Collections.Generic.ICollection{TValue}"/> /// with the specified key. /// </summary> /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> /// structure representing the key and value to add to the <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> /// <exception cref="T:System.ArgumentNullException">The <paramref name="keyValuePair"/> of <paramref /// name="keyValuePair"/> is null.</exception> /// <exception cref="T:System.OverflowException">The <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/> /// contains too many elements.</exception> /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/></exception> void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair) { ((IDictionary<TKey, TValue>)this).Add (keyValuePair.Key, keyValuePair.Value); } /// <summary> /// Determines whether the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> /// contains a specific key and value. /// </summary> /// <param name="keyValuePair">The <see cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> /// structure to locate in the <see /// cref="T:System.Collections.Generic.ICollection{TValue}"/>.</param> /// <returns>true if the <paramref name="keyValuePair"/> is found in the <see /// cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/>; otherwise, false.</returns> bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) { TValue value; if (!TryGetValue (keyValuePair.Key, out value)) { return false; } return EqualityComparer<TValue>.Default.Equals (value, keyValuePair.Value); } /// <summary> /// Gets a value indicating whether the dictionary is read-only. /// </summary> /// <value>true if the <see cref="T:System.Collections.Generic.ICollection{TKey,TValue}"/> is /// read-only; otherwise, false. For <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>, this property always returns /// false.</value> bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return false; } } /// <summary> /// Removes a key and value from the dictionary. /// </summary> /// <param name="keyValuePair">The <see /// cref="T:System.Collections.Generic.KeyValuePair{TKey,TValue}"/> /// structure representing the key and value to remove from the <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</param> /// <returns>true if the key and value represented by <paramref name="keyValuePair"/> is successfully /// found and removed; otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException">The Key property of <paramref /// name="keyValuePair"/> is a null reference (Nothing in Visual Basic).</exception> bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> 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 /// <summary>Returns an enumerator that iterates through the <see /// cref="ConcurrentDictionary{TKey,TValue}"/>.</summary> /// <returns>An enumerator for the <see cref="ConcurrentDictionary{TKey,TValue}"/>.</returns> /// <remarks> /// 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 <see cref="GetEnumerator"/> was called. /// </remarks> IEnumerator IEnumerable.GetEnumerator () { return ((ConcurrentDictionary<TKey, TValue>)this).GetEnumerator (); } #endregion #region IDictionary Members /// <summary> /// Adds the specified key and value to the dictionary. /// </summary> /// <param name="key">The object to use as the key.</param> /// <param name="value">The object to use as the value.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.OverflowException">The dictionary contains too many /// elements.</exception> /// <exception cref="T:System.ArgumentException"> /// <paramref name="key"/> is of a type that is not assignable to the key type <typeparamref /// name="TKey"/> of the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. -or- /// <paramref name="value"/> is of a type that is not assignable to <typeparamref name="TValue"/>, /// the type of values in the <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. /// -or- A value with the same key already exists in the <see /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>. /// </exception> 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<TKey, TValue>)this).Add ((TKey)key, typedValue); } /// <summary> /// Gets whether the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains an /// element with the specified key. /// </summary> /// <param name="key">The key to locate in the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</param> /// <returns>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> contains /// an element with the specified key; otherwise, false.</returns> /// <exception cref="T:System.ArgumentNullException"> <paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> bool IDictionary.Contains (object key) { if (key == null) throw new ArgumentNullException ("key"); return (key is TKey) && ((ConcurrentDictionary<TKey, TValue>)this).ContainsKey ((TKey)key); } /// <summary>Provides an <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the /// <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</summary> /// <returns>An <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</returns> IDictionaryEnumerator IDictionary.GetEnumerator () { return new DictionaryEnumerator (this); } /// <summary> /// Gets a value indicating whether the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a fixed size. /// </summary> /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> has a /// fixed size; otherwise, false. For <see /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always /// returns false.</value> bool IDictionary.IsFixedSize { get { return false; } } /// <summary> /// Gets a value indicating whether the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is read-only. /// </summary> /// <value>true if the <see cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/> is /// read-only; otherwise, false. For <see /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always /// returns false.</value> bool IDictionary.IsReadOnly { get { return false; } } /// <summary> /// Gets an <see cref="T:System.Collections.ICollection"/> containing the keys of the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>. /// </summary> /// <value>An <see cref="T:System.Collections.ICollection"/> containing the keys of the <see /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</value> ICollection IDictionary.Keys { get { return GetKeys (); } } /// <summary> /// Removes the element with the specified key from the <see /// cref="T:System.Collections.IDictionary"/>. /// </summary> /// <param name="key">The key of the element to remove.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> void IDictionary.Remove (object key) { if (key == null) throw new ArgumentNullException ("key"); TValue throwAwayValue; if (key is TKey) { this.TryRemove ((TKey)key, out throwAwayValue); } } /// <summary> /// Gets an <see cref="T:System.Collections.ICollection"/> containing the values in the <see /// cref="T:System.Collections.IDictionary"/>. /// </summary> /// <value>An <see cref="T:System.Collections.ICollection"/> containing the values in the <see /// cref="T:System.Collections.IDictionary"/>.</value> ICollection IDictionary.Values { get { return GetValues (); } } /// <summary> /// Gets or sets the value associated with the specified key. /// </summary> /// <param name="key">The key of the value to get or set.</param> /// <value>The value associated with the specified key, or a null reference (Nothing in Visual Basic) /// if <paramref name="key"/> is not in the dictionary or <paramref name="key"/> is of a type that is /// not assignable to the key type <typeparamref name="TKey"/> of the <see /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>.</value> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentException"> /// A value is being assigned, and <paramref name="key"/> is of a type that is not assignable to the /// key type <typeparamref name="TKey"/> of the <see /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>. -or- A value is being /// assigned, and <paramref name="key"/> is of a type that is not assignable to the value type /// <typeparamref name="TValue"/> of the <see /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/> /// </exception> 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<TKey, TValue>)this) [(TKey)key] = (TValue)value; } } #endregion #region ICollection Members /// <summary> /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an array, starting /// at the specified array index. /// </summary> /// <param name="array">The one-dimensional array that is the destination of the elements copied from /// the <see cref="T:System.Collections.ICollection"/>. The array must have zero-based /// indexing.</param> /// <param name="index">The zero-based index in <paramref name="array"/> at which copying /// begins.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is a null reference /// (Nothing in Visual Basic).</exception> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> is less than /// 0.</exception> /// <exception cref="T:System.ArgumentException"><paramref name="index"/> is equal to or greater than /// the length of the <paramref name="array"/>. -or- The number of elements in the source <see /// cref="T:System.Collections.ICollection"/> /// is greater than the available space from <paramref name="index"/> to the end of the destination /// <paramref name="array"/>.</exception> 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<TKey,TValue>, // we recognize three types of target arrays: // - an array of KeyValuePair<TKey, TValue> structs // - an array of DictionaryEntry structs // - an array of objects KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[]; 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); } } /// <summary> /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is /// synchronized with the SyncRoot. /// </summary> /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized /// (thread safe); otherwise, false. For <see /// cref="T:System.Collections.Concurrent.ConcurrentDictionary{TKey,TValue}"/>, this property always /// returns false.</value> bool ICollection.IsSynchronized { get { return false; } } /// <summary> /// Gets an object that can be used to synchronize access to the <see /// cref="T:System.Collections.ICollection"/>. This property is not supported. /// </summary> /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception> object ICollection.SyncRoot { get { throw new NotSupportedException (); } } #endregion /// <summary> /// 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. /// </summary> /// <param name="buckets">Reference to the bucket table that was deemed too small.</param> 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); } } /// <summary> /// Computes the bucket and lock number for a particular key. /// </summary> 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); } /// <summary> /// The number of concurrent writes for which to optimize by default. /// </summary> private static int DefaultConcurrencyLevel { get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; } } /// <summary> /// 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. /// </summary> private void AcquireAllLocks (ref int locksAcquired) { AcquireLocks (0, m_locks.Length, ref locksAcquired); Assert (locksAcquired == m_locks.Length); } /// <summary> /// 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. /// </summary> 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++; } } } } /// <summary> /// Releases a contiguous range of locks. /// </summary> private void ReleaseLocks (int fromInclusive, int toExclusive) { Assert (fromInclusive <= toExclusive); for (int i = fromInclusive; i < toExclusive; i++) { Monitor.Exit (m_locks [i]); } } /// <summary> /// Gets a collection containing the keys in the dictionary. /// </summary> private ReadOnlyCollection<TKey> GetKeys () { int locksAcquired = 0; try { AcquireAllLocks (ref locksAcquired); List<TKey> keys = new List<TKey> (); 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<TKey> (keys); } finally { ReleaseLocks (0, locksAcquired); } } /// <summary> /// Gets a collection containing the values in the dictionary. /// </summary> private ReadOnlyCollection<TValue> GetValues () { int locksAcquired = 0; try { AcquireAllLocks (ref locksAcquired); List<TValue> values = new List<TValue> (); 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<TValue> (values); } finally { ReleaseLocks (0, locksAcquired); } } /// <summary> /// A helper method for asserts. /// </summary> [Conditional ("DEBUG")] private void Assert (bool condition) { if (!condition) { throw new Exception ("Assertion failed."); } } /// <summary> /// A helper function to obtain the string for a particular resource key. /// </summary> /// <param name="key"></param> /// <returns></returns> private string GetResource (string key) { Assert (key != null); return key; } /// <summary> /// A node in a singly-linked list representing a particular hash table bucket. /// </summary> 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; } } /// <summary> /// A private class to represent enumeration over the dictionary that implements the /// IDictionaryEnumerator interface. /// </summary> private class DictionaryEnumerator : IDictionaryEnumerator { IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator; // Enumerator over the dictionary. internal DictionaryEnumerator (ConcurrentDictionary<TKey, TValue> 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 (); } } /// <summary> /// Get the data array to be serialized /// </summary> [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; } /// <summary> /// Construct the dictionary from a previously seiralized one /// </summary> [OnDeserialized] private void OnDeserialized (StreamingContext context) { KeyValuePair<TKey, TValue>[] 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 Assets/Editor/PocoSDK/ConcurrentDictionary.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/ConcurrentDictionary.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: d317b93ea1a19f24fac4b7065262ea42 timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/PocoManager.cs
New file @@ -0,0 +1,319 @@ 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 System.Net.Sockets; using TcpServer; using UnityEngine; using Debug = UnityEngine.Debug; public class PocoManager : MonoBehaviour { public const int versionCode = 6; public int port = 5001; private bool mRunning; public AsyncTcpServer server = null; private RPCParser rpc = null; private SimpleProtocolFilter prot = null; private UnityDumper dumper = new UnityDumper(); private ConcurrentDictionary<string, TcpClientState> inbox = new ConcurrentDictionary<string, TcpClientState>(); private VRSupport vr_support = new VRSupport(); private Dictionary<string, long> debugProfilingData = new Dictionary<string, long>() { { "dump", 0 }, { "screenshot", 0 }, { "handleRpcRequest", 0 }, { "packRpcResponse", 0 }, { "sendRpcResponse", 0 }, }; class RPC : Attribute { } void Awake() { Application.runInBackground = true; DontDestroyOnLoad(this); prot = new SimpleProtocolFilter(); rpc = new RPCParser(); rpc.addRpcMethod("isVRSupported", vr_support.isVRSupported); rpc.addRpcMethod("hasMovementFinished", vr_support.IsQueueEmpty); rpc.addRpcMethod("RotateObject", vr_support.RotateObject); rpc.addRpcMethod("ObjectLookAt", vr_support.ObjectLookAt); rpc.addRpcMethod("Screenshot", Screenshot); rpc.addRpcMethod("GetScreenSize", GetScreenSize); rpc.addRpcMethod("Dump", Dump); rpc.addRpcMethod("GetDebugProfilingData", GetDebugProfilingData); rpc.addRpcMethod("SetText", SetText); rpc.addRpcMethod("GetSDKVersion", GetSDKVersion); mRunning = true; for (int i = 0; i < 5; i++) { this.server = new AsyncTcpServer (port + i); this.server.Encoding = Encoding.UTF8; this.server.ClientConnected += new EventHandler<TcpClientConnectedEventArgs> (server_ClientConnected); this.server.ClientDisconnected += new EventHandler<TcpClientDisconnectedEventArgs> (server_ClientDisconnected); this.server.DatagramReceived += new EventHandler<TcpDatagramReceivedEventArgs<byte[]>> (server_Received); try { this.server.Start (); Debug.Log (string.Format("Tcp server started and listening at {0}", server.Port)); break; } catch (SocketException e) { // try next available port this.server = null; } } if (this.server == null) { Debug.LogError (string.Format("Unable to find an unused port from {0} to {1}", port, port + 5)); } vr_support.ClearCommands(); } static void server_ClientConnected(object sender, TcpClientConnectedEventArgs e) { Debug.Log(string.Format("TCP client {0} has connected.", e.TcpClient.Client.RemoteEndPoint.ToString())); } static void server_ClientDisconnected(object sender, TcpClientDisconnectedEventArgs e) { Debug.Log(string.Format("TCP client {0} has disconnected.", e.TcpClient.Client.RemoteEndPoint.ToString())); } private void server_Received(object sender, TcpDatagramReceivedEventArgs<byte[]> e) { Debug.Log(string.Format("Client : {0} --> {1}", e.Client.TcpClient.Client.RemoteEndPoint.ToString(), e.Datagram.Length)); TcpClientState internalClient = e.Client; string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString(); inbox.AddOrUpdate(tcpClientKey, internalClient, (n, o) => { return internalClient; }); } [RPC] private object Dump(List<object> param) { var onlyVisibleNode = true; if (param.Count > 0) { onlyVisibleNode = (bool)param[0]; } var sw = new Stopwatch(); sw.Start(); var h = dumper.dumpHierarchy(onlyVisibleNode); debugProfilingData["dump"] = sw.ElapsedMilliseconds; return h; } [RPC] private object Screenshot(List<object> param) { var sw = new Stopwatch(); sw.Start(); var tex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); tex.Apply(false); byte[] fileBytes = tex.EncodeToJPG(80); var b64img = Convert.ToBase64String(fileBytes); debugProfilingData["screenshot"] = sw.ElapsedMilliseconds; return new object[] { b64img, "jpg" }; } [RPC] private object GetScreenSize(List<object> param) { return new float[] { Screen.width, Screen.height }; } public void stopListening() { mRunning = false; server.Stop(); } [RPC] private object GetDebugProfilingData(List<object> param) { return debugProfilingData; } [RPC] private object SetText(List<object> param) { var instanceId = Convert.ToInt32(param[0]); var textVal = param[1] as string; foreach (var go in GameObject.FindObjectsOfType<GameObject>()) { if (go.GetInstanceID() == instanceId) { return UnityNode.SetText(go, textVal); } } return false; } [RPC] private object GetSDKVersion(List<object> param) { return versionCode; } void Update() { foreach (TcpClientState client in inbox.Values) { List<string> msgs = client.Prot.swap_msgs(); msgs.ForEach(delegate (string msg) { var sw = new Stopwatch(); sw.Start(); var t0 = sw.ElapsedMilliseconds; string response = rpc.HandleMessage(msg); var t1 = sw.ElapsedMilliseconds; byte[] bytes = prot.pack(response); var t2 = sw.ElapsedMilliseconds; server.Send(client.TcpClient, bytes); var t3 = sw.ElapsedMilliseconds; debugProfilingData["handleRpcRequest"] = t1 - t0; debugProfilingData["packRpcResponse"] = t2 - t1; TcpClientState internalClientToBeThrowAway; string tcpClientKey = client.TcpClient.Client.RemoteEndPoint.ToString(); inbox.TryRemove(tcpClientKey, out internalClientToBeThrowAway); }); } vr_support.PeekCommand(); } void OnApplicationQuit() { // stop listening thread stopListening(); } } public class RPCParser { public delegate object RpcMethod(List<object> param); protected Dictionary<string, RpcMethod> RPCHandler = new Dictionary<string, RpcMethod>(); private JsonSerializerSettings settings = new JsonSerializerSettings() { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii }; public string HandleMessage(string json) { Dictionary<string, object> data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings); if (data.ContainsKey("method")) { string method = data["method"].ToString(); List<object> param = null; if (data.ContainsKey("params")) { param = ((JArray)(data["params"])).ToObject<List<object>>(); } object idAction = null; if (data.ContainsKey("id")) { // if it have id, it is a request idAction = data["id"]; } string response = null; object result = null; try { result = RPCHandler[method](param); } catch (Exception e) { // return error response Debug.Log(e); response = formatResponseError(idAction, null, e); return response; } // return result response response = formatResponse(idAction, result); return response; } else { // do not handle response Debug.Log("ignore message without method"); return null; } } // Call a method in the server public string formatRequest(string method, object idAction, List<object> param = null) { Dictionary<string, object> data = new Dictionary<string, object>(); data["jsonrpc"] = "2.0"; data["method"] = method; if (param != null) { data["params"] = JsonConvert.SerializeObject(param, settings); } // if idAction is null, it is a notification if (idAction != null) { data["id"] = idAction; } return JsonConvert.SerializeObject(data, settings); } // Send a response from a request the server made to this client public string formatResponse(object idAction, object result) { Dictionary<string, object> rpc = new Dictionary<string, object>(); rpc["jsonrpc"] = "2.0"; rpc["id"] = idAction; rpc["result"] = result; return JsonConvert.SerializeObject(rpc, settings); } // Send a error to the server from a request it made to this client public string formatResponseError(object idAction, IDictionary<string, object> data, Exception e) { Dictionary<string, object> rpc = new Dictionary<string, object>(); rpc["jsonrpc"] = "2.0"; rpc["id"] = idAction; Dictionary<string, object> errorDefinition = new Dictionary<string, object>(); errorDefinition["code"] = 1; errorDefinition["message"] = e.ToString(); if (data != null) { errorDefinition["data"] = data; } rpc["error"] = errorDefinition; return JsonConvert.SerializeObject(rpc, settings); } public void addRpcMethod(string name, RpcMethod method) { RPCHandler[name] = method; } } Assets/Editor/PocoSDK/PocoManager.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/PocoManager.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: ecd691064e30ab44bb0b8956e7af433e timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/TcpClientConnectedEventArgs.cs
New file @@ -0,0 +1,28 @@ using System; using System.Net.Sockets; namespace TcpServer { /// <summary> /// 与客户端的连接已建立事件参数 /// </summary> public class TcpClientConnectedEventArgs : EventArgs { /// <summary> /// 与客户端的连接已建立事件参数 /// </summary> /// <param name="tcpClient">客户端</param> public TcpClientConnectedEventArgs (TcpClient tcpClient) { if (tcpClient == null) throw new ArgumentNullException ("tcpClient"); this.TcpClient = tcpClient; } /// <summary> /// 客户端 /// </summary> public TcpClient TcpClient { get; private set; } } } Assets/Editor/PocoSDK/TcpClientConnectedEventArgs.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/TcpClientConnectedEventArgs.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 3cc0a58d25d35744fa048b10f5339a7f timeCreated: 1554808822 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/TcpClientDisconnectedEventArgs.cs
New file @@ -0,0 +1,28 @@ using System; using System.Net.Sockets; namespace TcpServer { /// <summary> /// 与客户端的连接已断开事件参数 /// </summary> public class TcpClientDisconnectedEventArgs : EventArgs { /// <summary> /// 与客户端的连接已断开事件参数 /// </summary> /// <param name="tcpClient">客户端状态</param> public TcpClientDisconnectedEventArgs (TcpClient tcpClient) { if (tcpClient == null) throw new ArgumentNullException ("tcpClient"); this.TcpClient = tcpClient; } /// <summary> /// 客户端 /// </summary> public TcpClient TcpClient { get; private set; } } } Assets/Editor/PocoSDK/TcpClientDisconnectedEventArgs.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/TcpClientDisconnectedEventArgs.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: caebc603b28d1f646a989d20aa85c236 timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/TcpClientState.cs
New file @@ -0,0 +1,54 @@ using System; using System.Net.Sockets; namespace TcpServer { /// <summary> /// Internal class to join the TCP client and buffer together /// for easy management in the server /// </summary> public class TcpClientState { /// <summary> /// Constructor for a new Client /// </summary> /// <param name="tcpClient">The TCP client</param> /// <param name="buffer">The byte array buffer</param> /// <param name="prot">The protocol filter</param> public TcpClientState (TcpClient tcpClient, byte[] buffer, ProtoFilter prot) { if (tcpClient == null) throw new ArgumentNullException ("tcpClient"); if (buffer == null) throw new ArgumentNullException ("buffer"); if (prot == null) throw new ArgumentNullException ("prot"); this.TcpClient = tcpClient; this.Buffer = buffer; this.Prot = prot; // this.NetworkStream = tcpClient.GetStream (); } /// <summary> /// Gets the TCP Client /// </summary> public TcpClient TcpClient { get; private set; } /// <summary> /// Gets the Buffer. /// </summary> public byte[] Buffer { get; private set; } public ProtoFilter Prot { get; private set; } /// <summary> /// Gets the network stream /// </summary> public NetworkStream NetworkStream { get { return TcpClient.GetStream (); } } } } Assets/Editor/PocoSDK/TcpClientState.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/TcpClientState.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 851837b5f1d286148b7226f592e58338 timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/TcpDatagramReceivedEventArgs.cs
New file @@ -0,0 +1,39 @@ using System; using System.Net.Sockets; namespace TcpServer { /// <summary> /// 接收到数据报文事件参数 /// </summary> /// <typeparam name="T">报文类型</typeparam> public class TcpDatagramReceivedEventArgs<T> : EventArgs { /// <summary> /// 接收到数据报文事件参数 /// </summary> /// <param name="tcpClientState">客户端状态</param> /// <param name="datagram">报文</param> public TcpDatagramReceivedEventArgs (TcpClientState tcpClientState, T datagram) { this.Client = tcpClientState; this.TcpClient = tcpClientState.TcpClient; this.Datagram = datagram; } /// <summary> /// 客户端状态 /// </summary> public TcpClientState Client { get; private set; } /// <summary> /// 客户端 /// </summary> public TcpClient TcpClient { get; private set; } /// <summary> /// 报文 /// </summary> public T Datagram { get; private set; } } } Assets/Editor/PocoSDK/TcpDatagramReceivedEventArgs.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/TcpDatagramReceivedEventArgs.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 8dd557257e2442544b62c31786faea23 timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/TcpServer.cs
New file @@ -0,0 +1,607 @@ using System; using System.Threading; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine; namespace TcpServer { public interface ProtoFilter { void input(byte[] data); List<string> swap_msgs(); } public class SimpleProtocolFilter : ProtoFilter { /* 简单协议过滤器 协议按照 [有效数据字节数][有效数据] 这种协议包的格式进行打包和解包 [有效数据字节数]长度HEADER_SIZE字节 [有效数据]长度有效数据字节数字节 本类按照这种方式,顺序从数据流中取出数据进行拼接,一旦接收完一个完整的协议包,就会将协议包返回 [有效数据]字段接收到后会按照utf-8进行解码,因为在传输过程中是用utf-8进行编码的 所有编解码的操作在该类中完成 */ private byte[] buf = new byte[0]; private int HEADER_SIZE = 4; private List<string> msgs = new List<string>(); public void input(byte[] data) { buf = Combine(buf, data); while (buf.Length > HEADER_SIZE) { int data_size = BitConverter.ToInt32(buf, 0); if (buf.Length >= data_size + HEADER_SIZE) { byte[] data_body = Slice(buf, HEADER_SIZE, data_size + HEADER_SIZE); string content = System.Text.Encoding.Default.GetString(data_body); msgs.Add(content); buf = Slice(buf, data_size + HEADER_SIZE, buf.Length); } else { break; } } } public List<string> swap_msgs() { List<string> ret = msgs; msgs = new List<string>(); return ret; } public byte[] pack(String content) { int len = content.Length; byte[] size = BitConverter.GetBytes(len); if (!BitConverter.IsLittleEndian) { //reverse it so we get little endian. Array.Reverse(size); } byte[] body = System.Text.Encoding.Default.GetBytes(content); byte[] ret = Combine(size, body); return ret; } private static byte[] Combine(byte[] first, byte[] second) { byte[] ret = new byte[first.Length + second.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); return ret; } public byte[] Slice(byte[] source, int start, int end) { int length = end - start; byte[] ret = new byte[length]; Array.Copy(source, start, ret, 0, length); return ret; } } /// <summary> /// 异步TCP服务器 /// </summary> public class AsyncTcpServer : IDisposable { #region Fields private TcpListener _listener; private ConcurrentDictionary<string, TcpClientState> _clients; private bool _disposed = false; #endregion #region Ctors /// <summary> /// 异步TCP服务器 /// </summary> /// <param name="listenPort">监听的端口</param> public AsyncTcpServer(int listenPort) : this(IPAddress.Any, listenPort) { } /// <summary> /// 异步TCP服务器 /// </summary> /// <param name="localEP">监听的终结点</param> public AsyncTcpServer(IPEndPoint localEP) : this(localEP.Address, localEP.Port) { } /// <summary> /// 异步TCP服务器 /// </summary> /// <param name="localIPAddress">监听的IP地址</param> /// <param name="listenPort">监听的端口</param> public AsyncTcpServer(IPAddress localIPAddress, int listenPort) { this.Address = localIPAddress; this.Port = listenPort; this.Encoding = Encoding.Default; _clients = new ConcurrentDictionary<string, TcpClientState>(); _listener = new TcpListener(Address, Port); // _listener.AllowNatTraversal(true); } #endregion #region Properties /// <summary> /// 服务器是否正在运行 /// </summary> public bool IsRunning { get; private set; } /// <summary> /// 监听的IP地址 /// </summary> public IPAddress Address { get; private set; } /// <summary> /// 监听的端口 /// </summary> public int Port { get; private set; } /// <summary> /// 通信使用的编码 /// </summary> public Encoding Encoding { get; set; } #endregion #region Server /// <summary> /// 启动服务器 /// </summary> /// <returns>异步TCP服务器</returns> public AsyncTcpServer Start() { Debug.Log("start server"); return Start(10); } /// <summary> /// 启动服务器 /// </summary> /// <param name="backlog">服务器所允许的挂起连接序列的最大长度</param> /// <returns>异步TCP服务器</returns> public AsyncTcpServer Start(int backlog) { if (IsRunning) return this; IsRunning = true; _listener.Start(backlog); ContinueAcceptTcpClient(_listener); return this; } /// <summary> /// 停止服务器 /// </summary> /// <returns>异步TCP服务器</returns> public AsyncTcpServer Stop() { if (!IsRunning) return this; try { _listener.Stop(); foreach (var client in _clients.Values) { client.TcpClient.Client.Disconnect(false); } _clients.Clear(); } catch (ObjectDisposedException ex) { Debug.LogException(ex); } catch (SocketException ex) { Debug.LogException(ex); } IsRunning = false; return this; } private void ContinueAcceptTcpClient(TcpListener tcpListener) { try { tcpListener.BeginAcceptTcpClient(new AsyncCallback(HandleTcpClientAccepted), tcpListener); } catch (ObjectDisposedException ex) { Debug.LogException(ex); } catch (SocketException ex) { Debug.LogException(ex); } } #endregion #region Receive private void HandleTcpClientAccepted(IAsyncResult ar) { if (!IsRunning) return; TcpListener tcpListener = (TcpListener)ar.AsyncState; TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar); if (!tcpClient.Connected) return; byte[] buffer = new byte[tcpClient.ReceiveBufferSize]; SimpleProtocolFilter prot = new SimpleProtocolFilter(); TcpClientState internalClient = new TcpClientState(tcpClient, buffer, prot); // add client connection to cache string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString(); _clients.AddOrUpdate(tcpClientKey, internalClient, (n, o) => { return internalClient; }); RaiseClientConnected(tcpClient); // begin to read data NetworkStream networkStream = internalClient.NetworkStream; ContinueReadBuffer(internalClient, networkStream); // keep listening to accept next connection ContinueAcceptTcpClient(tcpListener); } private void HandleDatagramReceived(IAsyncResult ar) { if (!IsRunning) return; try { TcpClientState internalClient = (TcpClientState)ar.AsyncState; if (!internalClient.TcpClient.Connected) return; NetworkStream networkStream = internalClient.NetworkStream; int numberOfReadBytes = 0; try { // if the remote host has shutdown its connection, // read will immediately return with zero bytes. numberOfReadBytes = networkStream.EndRead(ar); } catch (Exception ex) { Debug.LogException(ex); numberOfReadBytes = 0; } if (numberOfReadBytes == 0) { // connection has been closed TcpClientState internalClientToBeThrowAway; string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString(); _clients.TryRemove(tcpClientKey, out internalClientToBeThrowAway); RaiseClientDisconnected(internalClient.TcpClient); return; } // received byte and trigger event notification var receivedBytes = new byte[numberOfReadBytes]; Array.Copy(internalClient.Buffer, 0, receivedBytes, 0, numberOfReadBytes); // input bytes into protofilter internalClient.Prot.input(receivedBytes); RaiseDatagramReceived(internalClient, receivedBytes); // RaisePlaintextReceived(internalClient.TcpClient, receivedBytes); // continue listening for tcp datagram packets ContinueReadBuffer(internalClient, networkStream); } catch (InvalidOperationException ex) { Debug.LogException(ex); } } private void ContinueReadBuffer(TcpClientState internalClient, NetworkStream networkStream) { try { networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, HandleDatagramReceived, internalClient); } catch (ObjectDisposedException ex) { Debug.LogException(ex); } } #endregion #region Events /// <summary> /// 接收到数据报文事件 /// </summary> public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived; /// <summary> /// 接收到数据报文明文事件 /// </summary> public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived; private void RaiseDatagramReceived(TcpClientState sender, byte[] datagram) { if (DatagramReceived != null) { DatagramReceived(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram)); } } private void RaisePlaintextReceived(TcpClientState sender, byte[] datagram) { if (PlaintextReceived != null) { PlaintextReceived(this, new TcpDatagramReceivedEventArgs<string>(sender, this.Encoding.GetString(datagram, 0, datagram.Length))); } } /// <summary> /// 与客户端的连接已建立事件 /// </summary> public event EventHandler<TcpClientConnectedEventArgs> ClientConnected; /// <summary> /// 与客户端的连接已断开事件 /// </summary> public event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected; private void RaiseClientConnected(TcpClient tcpClient) { if (ClientConnected != null) { ClientConnected(this, new TcpClientConnectedEventArgs(tcpClient)); } } private void RaiseClientDisconnected(TcpClient tcpClient) { if (ClientDisconnected != null) { ClientDisconnected(this, new TcpClientDisconnectedEventArgs(tcpClient)); } } #endregion #region Send private void GuardRunning() { if (!IsRunning) throw new InvalidProgramException("This TCP server has not been started yet."); } /// <summary> /// 发送报文至指定的客户端 /// </summary> /// <param name="tcpClient">客户端</param> /// <param name="datagram">报文</param> public void Send(TcpClient tcpClient, byte[] datagram) { GuardRunning(); if (tcpClient == null) throw new ArgumentNullException("tcpClient"); if (datagram == null) throw new ArgumentNullException("datagram"); try { NetworkStream stream = tcpClient.GetStream(); if (stream.CanWrite) { stream.BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient); } } catch (ObjectDisposedException ex) { Debug.LogException(ex); } } /// <summary> /// 发送报文至指定的客户端 /// </summary> /// <param name="tcpClient">客户端</param> /// <param name="datagram">报文</param> public void Send(TcpClient tcpClient, string datagram) { Send(tcpClient, this.Encoding.GetBytes(datagram)); } /// <summary> /// 发送报文至所有客户端 /// </summary> /// <param name="datagram">报文</param> public void SendToAll(byte[] datagram) { GuardRunning(); foreach (var client in _clients.Values) { Send(client.TcpClient, datagram); } } /// <summary> /// 发送报文至所有客户端 /// </summary> /// <param name="datagram">报文</param> public void SendToAll(string datagram) { GuardRunning(); SendToAll(this.Encoding.GetBytes(datagram)); } private void HandleDatagramWritten(IAsyncResult ar) { try { ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar); } catch (ObjectDisposedException ex) { Debug.LogException(ex); } catch (InvalidOperationException ex) { Debug.LogException(ex); } catch (IOException ex) { Debug.LogException(ex); } } /// <summary> /// 发送报文至指定的客户端 /// </summary> /// <param name="tcpClient">客户端</param> /// <param name="datagram">报文</param> public void SyncSend(TcpClient tcpClient, byte[] datagram) { GuardRunning(); if (tcpClient == null) throw new ArgumentNullException("tcpClient"); if (datagram == null) throw new ArgumentNullException("datagram"); try { NetworkStream stream = tcpClient.GetStream(); if (stream.CanWrite) { stream.Write(datagram, 0, datagram.Length); } } catch (ObjectDisposedException ex) { Debug.LogException(ex); } } /// <summary> /// 发送报文至指定的客户端 /// </summary> /// <param name="tcpClient">客户端</param> /// <param name="datagram">报文</param> public void SyncSend(TcpClient tcpClient, string datagram) { SyncSend(tcpClient, this.Encoding.GetBytes(datagram)); } /// <summary> /// 发送报文至所有客户端 /// </summary> /// <param name="datagram">报文</param> public void SyncSendToAll(byte[] datagram) { GuardRunning(); foreach (var client in _clients.Values) { SyncSend(client.TcpClient, datagram); } } /// <summary> /// 发送报文至所有客户端 /// </summary> /// <param name="datagram">报文</param> public void SyncSendToAll(string datagram) { GuardRunning(); SyncSendToAll(this.Encoding.GetBytes(datagram)); } #endregion #region IDisposable Members /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; /// <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { try { Stop(); if (_listener != null) { _listener = null; } } catch (SocketException ex) { Debug.LogException(ex); } } _disposed = true; } } #endregion } } Assets/Editor/PocoSDK/TcpServer.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/TcpServer.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 3b30fb5322c03cb42a3f9ae2557ac0cb timeCreated: 1554808822 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/UnityDumper.cs
New file @@ -0,0 +1,34 @@ using System; using System.Collections.Generic; using UnityEngine; namespace Poco { public class UnityDumper: AbstractDumper { public override AbstractNode getRoot () { return new RootNode (); } } public class RootNode: AbstractNode { private List<AbstractNode> children = null; public RootNode () { children = new List<AbstractNode> (); foreach (GameObject obj in Transform.FindObjectsOfType(typeof(GameObject))) { if (obj.transform.parent == null) { children.Add (new UnityNode (obj)); } } } public override List<AbstractNode> getChildren () //<Modified> { return children; } } } Assets/Editor/PocoSDK/UnityDumper.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/UnityDumper.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 8095fe2507f02b941903d532ef532994 timeCreated: 1554808822 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/VRSupport.cs
New file @@ -0,0 +1,243 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Poco; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using System.Threading; using TcpServer; using UnityEngine; using Debug = UnityEngine.Debug; public class VRSupport { private static Queue<Action> commands = new Queue<Action>(); public VRSupport() { commands = new Queue<Action>(); } public void ClearCommands() { commands.Clear(); } public void PeekCommand() { if (null != commands && commands.Count > 0) { Debug.Log("command executed " + commands.Count); commands.Peek()(); } } public object isVRSupported(List<object> param) { #if UNITY_3 || UNITY_4 return false; #elif UNITY_5 || UNITY_2017_1 return UnityEngine.VR.VRSettings.loadedDeviceName.Equals("CARDBOARD"); #else return UnityEngine.XR.XRSettings.loadedDeviceName.Equals("CARDBOARD"); #endif } public object IsQueueEmpty(List<object> param) { Debug.Log("Checking queue"); if (commands != null && commands.Count > 0) { return null; } else { Thread.Sleep(1000); // we wait a bit and check again just in case we run in between calls if (commands != null && commands.Count > 0) { return null; } } return commands.Count; } public object RotateObject(List<object> param) { var xRotation = Convert.ToSingle(param[0]); var yRotation = Convert.ToSingle(param[1]); var zRotation = Convert.ToSingle(param[2]); float speed = 0f; if (param.Count > 5) speed = Convert.ToSingle(param[5]); Vector3 mousePosition = new Vector3(xRotation, yRotation, zRotation); foreach (GameObject cameraContainer in GameObject.FindObjectsOfType<GameObject>()) { if (cameraContainer.name.Equals(param[3])) { foreach (GameObject cameraFollower in GameObject.FindObjectsOfType<GameObject>()) { if (cameraFollower.name.Equals(param[4])) { lock (commands) { commands.Enqueue(() => recoverOffset(cameraFollower, cameraContainer, speed)); } lock (commands) { var currentRotation = cameraContainer.transform.rotation; commands.Enqueue(() => rotate(cameraContainer, currentRotation, mousePosition, speed)); } return true; } } return true; } } return false; } public object ObjectLookAt(List<object> param) { float speed = 0f; if (param.Count > 3) speed = Convert.ToSingle(param[3]); foreach (GameObject toLookAt in GameObject.FindObjectsOfType<GameObject>()) { if (toLookAt.name.Equals(param[0])) { foreach (GameObject cameraContainer in GameObject.FindObjectsOfType<GameObject>()) { if (cameraContainer.name.Equals(param[1])) { foreach (GameObject cameraFollower in GameObject.FindObjectsOfType<GameObject>()) { if (cameraFollower.name.Equals(param[2])) { lock (commands) { commands.Enqueue(() => recoverOffset(cameraFollower, cameraContainer, speed)); } lock (commands) { commands.Enqueue(() => objectLookAt(cameraContainer, toLookAt, speed)); } return true; } } } } } } return false; } protected void rotate(GameObject go, Quaternion originalRotation, Vector3 mousePosition, float speed) { Debug.Log("rotating"); if (!RotateObject(originalRotation, mousePosition, go, speed)) { lock (commands) { commands.Dequeue(); } } } protected void objectLookAt(GameObject go, GameObject toLookAt, float speed) { Debug.Log("looking at " + toLookAt.name); Debug.Log("from " + go.name); if (!ObjectLookAtObject(toLookAt, go, speed)) { lock (commands) { commands.Dequeue(); } } } protected void recoverOffset(GameObject subcontainter, GameObject cameraContainer, float speed) { Debug.Log("recovering " + cameraContainer.name); if (!ObjectRecoverOffset(subcontainter, cameraContainer, speed)) { lock (commands) { commands.Dequeue(); } } } protected bool RotateObject(Quaternion originalPosition, Vector3 mousePosition, GameObject cameraContainer, float rotationSpeed = 0.125f) { if (null == cameraContainer) { return false; } var expectedx = originalPosition.eulerAngles.x + mousePosition.x; var expectedy = originalPosition.eulerAngles.y + mousePosition.y; var expectedz = originalPosition.eulerAngles.z + mousePosition.z; var toRotation = Quaternion.Euler(new Vector3(expectedx, expectedy, expectedz)); cameraContainer.transform.rotation = Quaternion.Lerp(cameraContainer.transform.rotation, toRotation, rotationSpeed * Time.deltaTime); var angle = Quaternion.Angle(cameraContainer.transform.rotation, toRotation); if (angle == 0) { return false; } return true; } protected bool ObjectLookAtObject(GameObject go, GameObject cameraContainer, float rotationSpeed = 0.125f) { if (null == go || null == cameraContainer) { Debug.Log("exception - item null"); return false; } var toRotation = Quaternion.LookRotation(go.transform.position - (cameraContainer.transform.localPosition)); cameraContainer.transform.rotation = Quaternion.Lerp(cameraContainer.transform.rotation, toRotation, rotationSpeed * Time.deltaTime); // It should not be needed but sometimes the difference of eurlerAngles might be small and this would ensure it works fine if (Quaternion.Angle(cameraContainer.transform.rotation, toRotation) == 0) { return false; } return true; } protected bool ObjectRecoverOffset(GameObject subcontainer, GameObject cameraContainer, float rotationSpeed = 0.125f) { if (null == cameraContainer) { Debug.Log("exception - item null"); return false; } // add offset with the camera var cameraRotation = Camera.main.transform.localRotation; var toRotate = new Quaternion(-cameraRotation.x, -cameraRotation.y, -cameraRotation.z, cameraRotation.w); subcontainer.transform.localRotation = Quaternion.Lerp(subcontainer.transform.localRotation, toRotate, rotationSpeed * Time.deltaTime); // It should not be needed but sometimes the difference of eurlerAngles might be small and this would ensure it works fine if (Quaternion.Angle(subcontainer.transform.localRotation, toRotate) == 0) { return false; } return true; } } Assets/Editor/PocoSDK/VRSupport.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/VRSupport.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 0ea2e8bcefeaa1343bb0f0217f61f874 timeCreated: 1554808822 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/sdk.metacopy from Assets/Editor/MemoryProfiler.meta copy to Assets/Editor/PocoSDK/sdk.meta
File was copied from Assets/Editor/MemoryProfiler.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: c0c186c64e2b22248b9679739a21009d guid: 09ae50ef29ab4a44aa2836d5629fbf66 folderAsset: yes timeCreated: 1526655628 timeCreated: 1554808814 licenseType: Pro DefaultImporter: userData: Assets/Editor/PocoSDK/sdk/AbstractDumper.cs
New file @@ -0,0 +1,64 @@ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Poco { public class AbstractDumper: IDumper { public virtual AbstractNode getRoot () { return null; } public Dictionary<string, object> dumpHierarchy () { return dumpHierarchyImpl (getRoot (), true); } public Dictionary<string, object> dumpHierarchy (bool onlyVisibleNode) { return dumpHierarchyImpl (getRoot (), onlyVisibleNode); } private Dictionary<string, object> dumpHierarchyImpl (AbstractNode node, bool onlyVisibleNode) { if (node == null) { return null; } Dictionary<string, object> payload = node.enumerateAttrs (); Dictionary<string, object> result = new Dictionary<string, object> (); string name = (string)node.getAttr ("name"); result.Add ("name", name); result.Add ("payload", payload); List<object> children = new List<object> (); foreach (AbstractNode child in node.getChildren()) { if (!onlyVisibleNode || (bool)child.getAttr ("visible")) { children.Add (dumpHierarchyImpl (child, onlyVisibleNode)); } } if (children.Count > 0) { result.Add ("children", children); } return result; } public virtual List<float> getPortSize () { return null; } } public interface IDumper { AbstractNode getRoot (); Dictionary<string, object> dumpHierarchy (); Dictionary<string, object> dumpHierarchy (bool onlyVisibleNode); List<float> getPortSize (); } } Assets/Editor/PocoSDK/sdk/AbstractDumper.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/sdk/AbstractDumper.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: e16dd48ed463a0b458b5978fd1f0a5df timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/sdk/AbstractNode.cs
New file @@ -0,0 +1,60 @@ using System; using System.Collections.Generic; using UnityEngine; namespace Poco { public class AbstractNode { private List<string> requiredAttrs = new List<string> { "name", "type", "visible", "pos", "size", "scale", "anchorPoint", "zOrders" }; public virtual AbstractNode getParent () { return null; } public virtual List<AbstractNode> getChildren () { return null; } public virtual object getAttr (string attrName) { Dictionary<string, object> defaultAttrs = new Dictionary<string, object> () { { "name", "<Root>" }, { "type", "Root" }, { "visible", true }, { "pos", new List<float> (){ 0.0f, 0.0f } }, { "size", new List<float> (){ 0.0f, 0.0f } }, { "scale", new List<float> (){ 1.0f, 1.0f } }, { "anchorPoint", new List<float> (){ 0.5f, 0.5f } }, { "zOrders", new Dictionary<string, object> (){ { "local", 0 }, { "global", 0 } } } }; return defaultAttrs.ContainsKey (attrName) ? defaultAttrs [attrName] : null; } public virtual void setAttr (string attrName, object val) { } public virtual Dictionary<string, object> enumerateAttrs () { Dictionary<string, object> ret = new Dictionary<string, object> (); foreach (string attr in requiredAttrs) { ret.Add (attr, getAttr (attr)); } return ret; } } } Assets/Editor/PocoSDK/sdk/AbstractNode.cs.metacopy from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta copy to Assets/Editor/PocoSDK/sdk/AbstractNode.cs.meta
File was copied from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: e30b39707ec557f4fa75d7255e25e035 timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/PocoSDK/ugui.metacopy from Assets/Editor/MemoryProfiler.meta copy to Assets/Editor/PocoSDK/ugui.meta
File was copied from Assets/Editor/MemoryProfiler.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: c0c186c64e2b22248b9679739a21009d guid: 49fe508b50a4bff42a403524860056b1 folderAsset: yes timeCreated: 1526655628 timeCreated: 1554808814 licenseType: Pro DefaultImporter: userData: Assets/Editor/PocoSDK/ugui/UnityNode.cs
New file @@ -0,0 +1,511 @@ using System; using System.Collections.Generic; using UnityEngine.UI; using UnityEngine; namespace Poco { public class UnityNode : AbstractNode { public static Dictionary<string, string> TypeNames = new Dictionary<string, string>() { { "Text", "Text" }, { "Gradient Text", "Gradient.Text" }, { "Image", "Image" }, { "RawImage", "Raw.Image" }, { "Mask", "Mask" }, { "2DRectMask", "2D-Rect.Mask" }, { "Button", "Button" }, { "InputField", "InputField" }, { "Toggle", "Toggle" }, { "Toggle Group", "ToggleGroup" }, { "Slider", "Slider" }, { "ScrollBar", "ScrollBar" }, { "DropDown", "DropDown" }, { "ScrollRect", "ScrollRect" }, { "Selectable", "Selectable" }, { "Camera", "Camera" }, { "RectTransform", "Node" }, }; public static string DefaultTypeName = "GameObject"; private GameObject gameObject; private Renderer renderer; private RectTransform rectTransform; private Rect rect; private Vector2 objectPos; private List<string> components; private Camera camera; public UnityNode(GameObject obj) { gameObject = obj; camera = Camera.main; foreach (var cam in Camera.allCameras) { // skip the main camera // we want to use specified camera first then fallback to main camera if no other cameras // for further advanced cases, we could test whether the game object is visible within the camera if (cam == Camera.main) { continue; } if ((cam.cullingMask & (1 << gameObject.layer)) != 0) { camera = cam; } } renderer = gameObject.GetComponent<Renderer>(); rectTransform = gameObject.GetComponent<RectTransform>(); rect = GameObjectRect(renderer, rectTransform); objectPos = renderer ? WorldToGUIPoint(camera, renderer.bounds.center) : Vector2.zero; components = GameObjectAllComponents(); } public override AbstractNode getParent() { GameObject parentObj = gameObject.transform.parent.gameObject; return new UnityNode(parentObj); } public override List<AbstractNode> getChildren() { List<AbstractNode> children = new List<AbstractNode>(); foreach (Transform child in gameObject.transform) { children.Add(new UnityNode(child.gameObject)); } return children; } public override object getAttr(string attrName) { switch (attrName) { case "name": return gameObject.name; case "type": return GuessObjectTypeFromComponentNames(components); case "visible": return GameObjectVisible(renderer, components); case "pos": return GameObjectPosInScreen(objectPos, renderer, rectTransform, rect); case "size": return GameObjectSizeInScreen(rect, rectTransform); case "scale": return new List<float>() { 1.0f, 1.0f }; case "anchorPoint": return GameObjectAnchorInScreen(renderer, rect, objectPos); case "zOrders": return GameObjectzOrders(); case "clickable": return GameObjectClickable(components); case "text": return GameObjectText(); case "components": return components; case "texture": return GetImageSourceTexture(); case "tag": return GameObjectTag(); case "layer": return GameObjectLayerName (); case "_ilayer": return GameObjectLayer (); case "_instanceId": return gameObject.GetInstanceID (); default: return null; } } public override Dictionary<string, object> enumerateAttrs() { Dictionary<string, object> payload = GetPayload(); Dictionary<string, object> ret = new Dictionary<string, object>(); foreach (KeyValuePair<string, object> p in payload) { if (p.Value != null) { ret.Add(p.Key, p.Value); } } return ret; } private Dictionary<string, object> GetPayload() { Dictionary<string, object> payload = new Dictionary<string, object>() { { "name", gameObject.name }, { "type", GuessObjectTypeFromComponentNames (components) }, { "visible", GameObjectVisible (renderer, components) }, { "pos", GameObjectPosInScreen (objectPos, renderer, rectTransform, rect) }, { "size", GameObjectSizeInScreen (rect, rectTransform) }, { "scale", new List<float> (){ 1.0f, 1.0f } }, { "anchorPoint", GameObjectAnchorInScreen (renderer, rect, objectPos) }, { "zOrders", GameObjectzOrders () }, { "clickable", GameObjectClickable (components) }, { "text", GameObjectText () }, { "components", components }, { "texture", GetImageSourceTexture () }, { "tag", GameObjectTag () }, { "_ilayer", GameObjectLayer() }, { "layer", GameObjectLayerName() }, { "_instanceId", gameObject.GetInstanceID () }, }; return payload; } private string GuessObjectTypeFromComponentNames(List<string> components) { List<string> cns = new List<string>(components); cns.Reverse(); foreach (string name in cns) { if (TypeNames.ContainsKey (name)) { return TypeNames [name]; } } return DefaultTypeName; } private bool GameObjectVisible(Renderer renderer, List<string> components) { if (gameObject.activeInHierarchy) { bool light = components.Contains("Light"); // bool mesh = components.Contains ("MeshRenderer") && components.Contains ("MeshFilter"); bool particle = components.Contains("ParticleSystem") && components.Contains("ParticleSystemRenderer"); if (light || particle) { return false; } else { return renderer ? renderer.isVisible : true; } } else { return false; } } private int GameObjectLayer() { return gameObject.layer; } private string GameObjectLayerName() { return LayerMask.LayerToName (gameObject.layer); } private bool GameObjectClickable(List<string> components) { Button button = gameObject.GetComponent<Button>(); return button ? button.isActiveAndEnabled : false; } private string GameObjectText() { Text text = gameObject.GetComponent<Text>(); return text ? text.text : null; } private string GameObjectTag() { string tag; try { tag = !gameObject.CompareTag("Untagged") ? gameObject.tag : null; } catch (UnityException) { tag = null; } return tag; } private List<string> GameObjectAllComponents() { List<string> components = new List<string> (); Component[] allComponents = gameObject.GetComponents<Component> (); if (allComponents != null) { foreach (Component ac in allComponents) { if (ac != null) { components.Add (ac.GetType ().Name); } } } return components; } private Dictionary<string, float> GameObjectzOrders() { float CameraViewportPoint = 0; if (camera != null) { CameraViewportPoint = Math.Abs(camera.WorldToViewportPoint(gameObject.transform.position).z); } Dictionary<string, float> zOrders = new Dictionary<string, float>() { { "global", 0f }, { "local", -1 * CameraViewportPoint } }; return zOrders; } private Rect GameObjectRect(Renderer renderer, RectTransform rectTransform) { Rect rect = new Rect(0, 0, 0, 0); if (renderer) { rect = RendererToScreenSpace(camera, renderer); } else if (rectTransform) { rect = RectTransformToScreenSpace(rectTransform); } return rect; } private float[] GameObjectPosInScreen(Vector3 objectPos, Renderer renderer, RectTransform rectTransform, Rect rect) { float[] pos = { 0f, 0f }; if (renderer) { // 3d object pos[0] = objectPos.x / (float)Screen.width; pos[1] = objectPos.y / (float)Screen.height; } else if (rectTransform) { // ui object (rendered on screen space, other render modes may be different) // use center pos for now Canvas rootCanvas = GetRootCanvas (gameObject); RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode (); switch (renderMode) { case RenderMode.ScreenSpaceCamera: //上一个方案经过实际测试发现还有两个问题存在 //1.在有Canvas Scaler修改了RootCanvas的Scale的情况下坐标的抓取仍然不对,影响到了ScreenSpaceCameram模式在不同分辨率和屏幕比例下识别的兼容性。 //2.RectTransformUtility转的 rectTransform.transform.position本质上得到的是RectTransform.pivot中心轴在屏幕上的坐标,如果pivot不等于(0.5,0.5), //那么获取到的position就不等于图形的中心点。 //试了一晚上,找到了解决办法。 //用MainCanvas转一次屏幕坐标 Vector2 position = RectTransformUtility.WorldToScreenPoint(rootCanvas.worldCamera, rectTransform.transform.position); //注意: 这里的position其实是Pivot点在Screen上的坐标,并不是图形意义上的中心点,在经过下列玄学公式换算才是真的图形中心在屏幕的位置。 //公式内算上了rootCanvas.scaleFactor 缩放因子,经测试至少在Canvas Scaler.Expand模式下,什么分辨率和屏幕比都抓的很准,兼容性很强,其他的有待测试。 //由于得出来的坐标是左下角为原点,触控输入是左上角为原点,所以要上下反转一下Poco才能用,所以y坐标用Screen.height减去。 position.Set( position.x - rectTransform.rect.width * rootCanvas.scaleFactor * (rectTransform.pivot.x - 0.5f), Screen.height - (position.y - rectTransform.rect.height * rootCanvas.scaleFactor * (rectTransform.pivot.y - 0.5f)) ); pos[0] = position.x / Screen.width; pos[1] = position.y / Screen.height; break; case RenderMode.WorldSpace: Vector2 _pos = RectTransformUtility.WorldToScreenPoint (rootCanvas.worldCamera, rectTransform.transform.position); pos [0] = _pos.x / Screen.width; pos [1] = (Screen.height - _pos.y) / Screen.height; break; default: pos[0] = rect.center.x / (float)Screen.width; pos[1] = rect.center.y / (float)Screen.height; break; } } return pos; } private Canvas GetRootCanvas (GameObject gameObject) { Canvas canvas = gameObject.GetComponentInParent<Canvas> (); // 如果unity版本小于unity5.5,就用递归的方式取吧,没法直接取rootCanvas // 如果有用到4.6以下版本的话就自己手动在这里添加条件吧 #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4 if (canvas && canvas.isRootCanvas) { return canvas; } else { if (gameObject.transform.parent.gameObject != null) { return GetRootCanvas(gameObject.transform.parent.gameObject); } else { return null; } } #else if (canvas && canvas.isRootCanvas) { return canvas; } else if (canvas) { return canvas.rootCanvas; } else { return null; } #endif } private float[] GameObjectSizeInScreen(Rect rect, RectTransform rectTransform) { float[] size = { 0f, 0f }; if (rectTransform) { Canvas rootCanvas = GetRootCanvas (gameObject); RenderMode renderMode = rootCanvas != null ? rootCanvas.renderMode : new RenderMode (); switch (renderMode) { case RenderMode.ScreenSpaceCamera: Rect _rect = RectTransformUtility.PixelAdjustRect (rectTransform, rootCanvas); size = new float[] { _rect.width * rootCanvas.scaleFactor / (float)Screen.width, _rect.height * rootCanvas.scaleFactor / (float)Screen.height }; break; case RenderMode.WorldSpace: Rect rect_ = rectTransform.rect; RectTransform canvasTransform = rootCanvas.GetComponent<RectTransform>(); size = new float[] { rect_.width / canvasTransform.rect.width, rect_.height / canvasTransform.rect.height }; break; default: size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height }; break; } } else { size = new float[] { rect.width / (float)Screen.width, rect.height / (float)Screen.height }; } return size; } private float[] GameObjectAnchorInScreen(Renderer renderer, Rect rect, Vector3 objectPos) { float[] defaultValue = { 0.5f, 0.5f }; if (rectTransform) { Vector2 data = rectTransform.pivot; defaultValue[0] = data[0]; defaultValue[1] = 1 - data[1]; return defaultValue; } if (!renderer) { //<Modified> some object do not have renderer return defaultValue; } float[] anchor = { (objectPos.x - rect.xMin) / rect.width, (objectPos.y - rect.yMin) / rect.height }; if (Double.IsNaN(anchor[0]) || Double.IsNaN(anchor[1])) { return defaultValue; } else if (Double.IsPositiveInfinity(anchor[0]) || Double.IsPositiveInfinity(anchor[1])) { return defaultValue; } else if (Double.IsNegativeInfinity(anchor[0]) || Double.IsNegativeInfinity(anchor[1])) { return defaultValue; } else { return anchor; } } private string GetImageSourceTexture() { Image image = gameObject.GetComponent<Image>(); if (image != null && image.sprite != null) { return image.sprite.name; } RawImage rawImage = gameObject.GetComponent<RawImage>(); if (rawImage != null && rawImage.texture != null) { return rawImage.texture.name; } SpriteRenderer spriteRenderer = gameObject.GetComponent<SpriteRenderer>(); if (spriteRenderer != null && spriteRenderer.sprite != null) { return spriteRenderer.sprite.name; } Renderer render = gameObject.GetComponent<Renderer>(); if (renderer != null && renderer.material != null) { return renderer.material.color.ToString(); } return null; } protected static Vector2 WorldToGUIPoint(Camera camera, Vector3 world) { Vector2 screenPoint = Vector2.zero; if (camera != null) { screenPoint = camera.WorldToScreenPoint(world); screenPoint.y = (float)Screen.height - screenPoint.y; } return screenPoint; } protected static Rect RendererToScreenSpace(Camera camera, Renderer renderer) { Vector3 cen = renderer.bounds.center; Vector3 ext = renderer.bounds.extents; Vector2[] extentPoints = new Vector2[8] { WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z - ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z - ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y - ext.y, cen.z + ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y - ext.y, cen.z + ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z - ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z - ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x - ext.x, cen.y + ext.y, cen.z + ext.z)), WorldToGUIPoint (camera, new Vector3 (cen.x + ext.x, cen.y + ext.y, cen.z + ext.z)) }; Vector2 min = extentPoints[0]; Vector2 max = extentPoints[0]; foreach (Vector2 v in extentPoints) { min = Vector2.Min(min, v); max = Vector2.Max(max, v); } return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); } protected static Rect RectTransformToScreenSpace(RectTransform rectTransform) { Vector2 size = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale); Rect rect = new Rect(rectTransform.position.x, Screen.height - rectTransform.position.y, size.x, size.y); rect.x -= (rectTransform.pivot.x * size.x); rect.y -= ((1.0f - rectTransform.pivot.y) * size.y); return rect; } public static bool SetText (GameObject go, string textVal) { if (go != null) { var inputField = go.GetComponent<InputField>(); if (inputField != null) { inputField.text = textVal; return true; } } return false; } } } Assets/Editor/PocoSDK/ugui/UnityNode.cs.meta
File was renamed from Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: e9b3c90455aa77e41a907149f43e2d87 timeCreated: 1439890712 guid: 937eefd44f562c547bb892d207f07a5a timeCreated: 1554808823 licenseType: Pro MonoImporter: serializedVersion: 2 Assets/Editor/Tool/ClientPackage.cs
@@ -16,17 +16,20 @@ public static string auditOutTime = string.Empty; public static string SDK_PLUGIN_PROJECT { public static string SDK_PLUGIN_PROJECT { get { return LocalSave.GetString("SDK_PROJECT_PATH"); } set { LocalSave.SetString("SDK_PROJECT_PATH", value); } } public static bool obfuscatorEnabled { public static bool obfuscatorEnabled { get { return LocalSave.GetBool("obfuscatorEnabled", false); } set { LocalSave.SetBool("obfuscatorEnabled", value); } } public static int AssetPrior { public static int AssetPrior { get { return LocalSave.GetInt("HalfAssetPrior", 1); } set { LocalSave.SetInt("HalfAssetPrior", value); } } @@ -352,7 +355,6 @@ public static void BuildApk(string _sdkPath, string _output, string _publisher, int _buildIndex, bool _development) { PreBuild(_publisher, _buildIndex); var auditTimeFile = StringUtility.Contact(Application.dataPath, Path.DirectorySeparatorChar, "Resources/Config/AuditTime.txt"); if (File.Exists(auditTimeFile)) { @@ -435,6 +437,17 @@ var apkName = StringUtility.Contact(_output, "/", versionName, "_", versionConfig.clientPackageFlag, "_v", versionConfig.m_Version, "_", _buildIndex, ".apk"); BuildPipeline.BuildPlayer(baseLevels, apkName, BuildTarget.Android, BuildOptions.None); } FileUtil.MoveFileOrDirectory(Application.dataPath + "/Scripts/PocoSDK", Application.dataPath + "/Editor/PocoSDK"); var _content = File.ReadAllText(Application.dataPath + "/Scripts/Core/GameEngine/Login/Launch.cs"); int _start = _content.IndexOf("// ## PocoManager Start") + "// ## PocoManager Start".Length; int _end = _content.IndexOf("// ## PocoManager End"); var _front = _content.Substring(0, _start); var _back = _content.Substring(_end); _content = _front + "\r\n// ## PocoManager\r\n" + _back; File.WriteAllText(Application.dataPath + "/Scripts/Core/GameEngine/Login/Launch.cs", _content); } public static void BuildIpa(string _sdkPath, string _publisher, int _buildIndex, bool _replace) @@ -519,6 +532,32 @@ PlayerSettings.allowedAutorotateToPortraitUpsideDown = false; PlayerSettings.bundleVersion = VersionConfig.GetVersionNumber(newVersionConfig.version).ToString(); if (VersionConfig.Get().appId.Equals("test")) { FileUtil.MoveFileOrDirectory(Application.dataPath + "/Editor/PocoSDK", Application.dataPath + "/Scripts/PocoSDK"); var _launchScript = File.ReadAllText(Application.dataPath + "/Scripts/Core/GameEngine/Login/Launch.cs"); if (_launchScript != null) { if (_launchScript.IndexOf("// ## PocoManager") == -1) { Debug.LogError("找不到替代字符串"); } else { _launchScript.Replace("// ## PocoManager", "if (VersionConfig.Get().appId.Equals(\"test\"))" + "{" + "SnxxzGame.Instance.gameObject.AddComponent<PocoManager>();" + "}"); File.WriteAllText(Application.dataPath + "/Scripts/Core/GameEngine/Login/Launch.cs", _launchScript); } } } CSObjectWrapEditor.Generator.ClearAll(); CSObjectWrapEditor.Generator.GenAll(); }