少年修仙传客户端基础资源
client_Hale
2019-04-09 fa036d24cd2e22ea9aa66c2d3962081a3850315c
6428 多人 前端嵌入PocoSDK方便编写自动测试脚本
14 文件已复制
1个文件已修改
15个文件已添加
57个文件已删除
3 文件已重命名
17288 ■■■■ 已修改文件
Assets/Editor/MemoryProfiler/ArrayTools.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ArrayTools.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/BytesAndOffset.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/BytesAndOffset.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/CrawledDataUnpacker.cs 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/CrawledDataUnpacker.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Crawler.cs 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Crawler.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/HighLevelAPI.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/HighLevelAPI.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Inspector.cs 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Inspector.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20.meta 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdb 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdb.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.pdb 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.pdb.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.xml 9793 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.xml.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/license.txt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/license.txt.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/readme.txt 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/JsonDotNet20/readme.txt.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/LowLevelAPI.cs 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/LowLevelAPI.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ManagedHeapExtensions.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ManagedHeapExtensions.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/MemoryProfilerWindow.cs 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PackedManagedObject.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PackedManagedObject.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PackedMemorySnapshotUtility.cs 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PackedMemorySnapshotUtility.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PrimitiveValueReader.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/PrimitiveValueReader.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ShortestPathToRootFinder.cs 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ShortestPathToRootFinder.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/StringTools.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/StringTools.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/TreeMapView.cs 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/TreeMapView.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap.meta 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Group.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Group.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/ITreemapRenderable.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/ITreemapRenderable.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Item.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Item.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Tests.meta 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Tests/TreemapTests.cs 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Tests/TreemapTests.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Utility.cs 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/Treemap/Utility.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/TypeTools.cs 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/TypeTools.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ZoomArea.cs 604 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/MemoryProfiler/ZoomArea.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/3rdLib.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/3rdLib/Newtonsoft.Json.dll 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/3rdLib/Newtonsoft.Json.dll.meta 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/3rdLib/link.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/3rdLib/link.xml.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/ConcurrentDictionary.cs 1679 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/ConcurrentDictionary.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/PocoManager.cs 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/PocoManager.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientConnectedEventArgs.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientConnectedEventArgs.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientDisconnectedEventArgs.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientDisconnectedEventArgs.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientState.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpClientState.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpDatagramReceivedEventArgs.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpDatagramReceivedEventArgs.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpServer.cs 607 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/TcpServer.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/UnityDumper.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/UnityDumper.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/VRSupport.cs 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/VRSupport.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/sdk.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/sdk/AbstractDumper.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/sdk/AbstractDumper.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/sdk/AbstractNode.cs 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/sdk/AbstractNode.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/ugui.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/ugui/UnityNode.cs 511 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/PocoSDK/ugui/UnityNode.cs.meta 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Tool/ClientPackage.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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.dll
Binary files differ
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdb
Binary files differ
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.dll.mdb.meta
File was deleted
Assets/Editor/MemoryProfiler/JsonDotNet20/Newtonsoft.Json.pdb
Binary 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.meta
copy 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.dll
Binary 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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.meta
copy 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();
        }